Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
nni
Commits
22316800
Unverified
Commit
22316800
authored
Oct 29, 2019
by
Yuge Zhang
Committed by
GitHub
Oct 29, 2019
Browse files
SMAC tuner bug fixes (#1644)
parent
041c3f0e
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
64 additions
and
75 deletions
+64
-75
src/sdk/pynni/nni/smac_tuner/__init__.py
src/sdk/pynni/nni/smac_tuner/__init__.py
+1
-0
src/sdk/pynni/nni/smac_tuner/convert_ss_to_scenario.py
src/sdk/pynni/nni/smac_tuner/convert_ss_to_scenario.py
+34
-37
src/sdk/pynni/nni/smac_tuner/smac_tuner.py
src/sdk/pynni/nni/smac_tuner/smac_tuner.py
+29
-38
No files found.
src/sdk/pynni/nni/smac_tuner/__init__.py
View file @
22316800
from
.smac_tuner
import
SMACTuner
\ No newline at end of file
src/sdk/pynni/nni/smac_tuner/convert_ss_to_scenario.py
View file @
22316800
...
@@ -18,18 +18,17 @@
...
@@ -18,18 +18,17 @@
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import
os
import
json
import
json
import
numpy
as
np
import
numpy
as
np
def
get_json_content
(
file_path
):
def
get_json_content
(
file_path
):
"""Load json file content
"""Load json file content
Parameters
Parameters
----------
----------
file_path:
file_path:
path to the file
path to the file
Raises
Raises
------
------
TypeError
TypeError
...
@@ -42,10 +41,10 @@ def get_json_content(file_path):
...
@@ -42,10 +41,10 @@ def get_json_content(file_path):
print
(
'Error: '
,
err
)
print
(
'Error: '
,
err
)
return
None
return
None
def
generate_pcs
(
nni_search_space_content
):
def
generate_pcs
(
nni_search_space_content
):
"""Generate the Parameter Configuration Space (PCS) which defines the
"""Generate the Parameter Configuration Space (PCS) which defines the
legal ranges of the parameters to be optimized and their default values.
legal ranges of the parameters to be optimized and their default values.
Generally, the format is:
Generally, the format is:
# parameter_name categorical {value_1, ..., value_N} [default value]
# parameter_name categorical {value_1, ..., value_N} [default value]
# parameter_name ordinal {value_1, ..., value_N} [default value]
# parameter_name ordinal {value_1, ..., value_N} [default value]
...
@@ -53,19 +52,15 @@ def generate_pcs(nni_search_space_content):
...
@@ -53,19 +52,15 @@ def generate_pcs(nni_search_space_content):
# parameter_name integer [min_value, max_value] [default value] log
# parameter_name integer [min_value, max_value] [default value] log
# parameter_name real [min_value, max_value] [default value]
# parameter_name real [min_value, max_value] [default value]
# parameter_name real [min_value, max_value] [default value] log
# parameter_name real [min_value, max_value] [default value] log
Reference: https://automl.github.io/SMAC3/stable/options.html
Reference: https://automl.github.io/SMAC3/stable/options.html
Parameters
Parameters
----------
----------
nni_search_space_content: search_space
nni_search_space_content: search_space
The search space in this experiment in nni
The search space in this experiment in nni
Returns
Returns
-------
-------
Parameter Configuration Space (PCS)
Parameter Configuration Space (PCS)
the legal ranges of the parameters to be optimized and their default values
the legal ranges of the parameters to be optimized and their default values
Raises
Raises
------
------
RuntimeError
RuntimeError
...
@@ -73,41 +68,45 @@ def generate_pcs(nni_search_space_content):
...
@@ -73,41 +68,45 @@ def generate_pcs(nni_search_space_content):
"""
"""
categorical_dict
=
{}
categorical_dict
=
{}
search_space
=
nni_search_space_content
search_space
=
nni_search_space_content
def
dump_categorical
(
fd
,
key
,
categories
):
choice_len
=
len
(
categories
)
if
key
in
categorical_dict
:
raise
RuntimeError
(
'%s has already existed, please make sure search space has no duplicate key.'
%
key
)
categorical_dict
[
key
]
=
search_space
[
key
][
'_value'
]
fd
.
write
(
'%s categorical {%s} [0]
\n
'
%
(
key
,
','
.
join
(
map
(
str
,
range
(
choice_len
)))))
with
open
(
'param_config_space.pcs'
,
'w'
)
as
pcs_fd
:
with
open
(
'param_config_space.pcs'
,
'w'
)
as
pcs_fd
:
if
isinstance
(
search_space
,
dict
):
if
isinstance
(
search_space
,
dict
):
for
key
in
search_space
.
keys
():
for
key
in
search_space
.
keys
():
if
isinstance
(
search_space
[
key
],
dict
):
if
isinstance
(
search_space
[
key
],
dict
):
try
:
try
:
if
search_space
[
key
][
'_type'
]
==
'choice'
:
if
search_space
[
key
][
'_type'
]
==
'choice'
:
choice_len
=
len
(
search_space
[
key
][
'_value'
])
dump_categorical
(
pcs_fd
,
key
,
search_space
[
key
][
'_value'
])
pcs_fd
.
write
(
'%s categorical {%s} [%s]
\n
'
%
(
key
,
json
.
dumps
(
list
(
range
(
choice_len
)))[
1
:
-
1
],
json
.
dumps
(
0
)))
if
key
in
categorical_dict
:
raise
RuntimeError
(
'%s has already existed, please make sure search space has no duplicate key.'
%
key
)
categorical_dict
[
key
]
=
search_space
[
key
][
'_value'
]
elif
search_space
[
key
][
'_type'
]
==
'randint'
:
elif
search_space
[
key
][
'_type'
]
==
'randint'
:
pcs_fd
.
write
(
'%s integer [%d, %d] [%d]
\n
'
%
(
lower
,
upper
=
search_space
[
key
][
'_value'
]
key
,
if
lower
+
1
==
upper
:
search_space
[
key
][
'_value'
][
0
],
dump_categorical
(
pcs_fd
,
key
,
[
lower
])
search_space
[
key
][
'_value'
][
1
]
-
1
,
else
:
search_space
[
key
][
'_value'
][
0
]
))
pcs_fd
.
write
(
'%s integer [%d, %d] [%d]
\n
'
%
(
key
,
lower
,
upper
-
1
,
lower
))
elif
search_space
[
key
][
'_type'
]
==
'uniform'
:
elif
search_space
[
key
][
'_type'
]
==
'uniform'
:
pcs_fd
.
write
(
'%s real %s [%s]
\n
'
%
(
low
,
high
=
search_space
[
key
][
'_value'
]
key
,
if
low
==
high
:
json
.
dumps
(
search_space
[
key
][
'_value'
]),
dump_categorical
(
pcs_fd
,
key
,
[
low
])
json
.
dumps
(
search_space
[
key
][
'_value'
][
0
])))
else
:
pcs_fd
.
write
(
'%s real [%s, %s] [%s]
\n
'
%
(
key
,
low
,
high
,
low
))
elif
search_space
[
key
][
'_type'
]
==
'loguniform'
:
elif
search_space
[
key
][
'_type'
]
==
'loguniform'
:
# use np.round here to ensure that the rounded defaut value is in the range, which will be rounded in configure_space package
# use np.round here to ensure that the rounded default value is in the range,
search_space
[
key
][
'_value'
]
=
list
(
np
.
round
(
np
.
log
(
search_space
[
key
][
'_value'
]),
10
))
# which will be rounded in configure_space package
pcs_fd
.
write
(
'%s real %s [%s]
\n
'
%
(
low
,
high
=
list
(
np
.
round
(
np
.
log
(
search_space
[
key
][
'_value'
]),
10
))
key
,
if
low
==
high
:
json
.
dumps
(
search_space
[
key
][
'_value'
]),
dump_categorical
(
pcs_fd
,
key
,
[
search_space
[
key
][
'_value'
][
0
]])
json
.
dumps
(
search_space
[
key
][
'_value'
][
0
])))
else
:
pcs_fd
.
write
(
'%s real [%s, %s] [%s]
\n
'
%
(
key
,
low
,
high
,
low
))
elif
search_space
[
key
][
'_type'
]
==
'quniform'
:
elif
search_space
[
key
][
'_type'
]
==
'quniform'
:
low
,
high
,
q
=
search_space
[
key
][
'_value'
][
0
:
3
]
low
,
high
,
q
=
search_space
[
key
][
'_value'
][
0
:
3
]
vals
=
np
.
clip
(
np
.
arange
(
np
.
round
(
low
/
q
),
np
.
round
(
high
/
q
)
+
1
)
*
q
,
low
,
high
).
tolist
()
vals
=
np
.
clip
(
np
.
arange
(
np
.
round
(
low
/
q
),
np
.
round
(
high
/
q
)
+
1
)
*
q
,
low
,
high
).
tolist
()
pcs_fd
.
write
(
'%s ordinal {%s} [%s]
\n
'
%
(
pcs_fd
.
write
(
'%s ordinal {%s} [%s]
\n
'
%
(
key
,
key
,
json
.
dumps
(
vals
)[
1
:
-
1
],
json
.
dumps
(
vals
)[
1
:
-
1
],
...
@@ -121,17 +120,15 @@ def generate_pcs(nni_search_space_content):
...
@@ -121,17 +120,15 @@ def generate_pcs(nni_search_space_content):
return
categorical_dict
return
categorical_dict
return
None
return
None
def
generate_scenario
(
ss_content
):
def
generate_scenario
(
ss_content
):
"""Generate the scenario. The scenario-object (smac.scenario.scenario.Scenario) is used to configure SMAC and
"""Generate the scenario. The scenario-object (smac.scenario.scenario.Scenario) is used to configure SMAC and
can be constructed either by providing an actual scenario-object, or by specifing the options in a scenario file.
can be constructed either by providing an actual scenario-object, or by specifing the options in a scenario file.
Reference: https://automl.github.io/SMAC3/stable/options.html
Reference: https://automl.github.io/SMAC3/stable/options.html
The format of the scenario file is one option per line:
The format of the scenario file is one option per line:
OPTION1 = VALUE1
OPTION1 = VALUE1
OPTION2 = VALUE2
OPTION2 = VALUE2
...
...
Parameters
Parameters
----------
----------
abort_on_first_run_crash: bool
abort_on_first_run_crash: bool
...
@@ -194,7 +191,6 @@ def generate_scenario(ss_content):
...
@@ -194,7 +191,6 @@ def generate_scenario(ss_content):
wallclock_limit: int
wallclock_limit: int
Maximum amount of wallclock-time used for optimization. Default: inf.
Maximum amount of wallclock-time used for optimization. Default: inf.
Use default because this is controlled by nni
Use default because this is controlled by nni
Returns
Returns
-------
-------
Scenario:
Scenario:
...
@@ -203,11 +199,12 @@ def generate_scenario(ss_content):
...
@@ -203,11 +199,12 @@ def generate_scenario(ss_content):
"""
"""
with
open
(
'scenario.txt'
,
'w'
)
as
sce_fd
:
with
open
(
'scenario.txt'
,
'w'
)
as
sce_fd
:
sce_fd
.
write
(
'deterministic = 0
\n
'
)
sce_fd
.
write
(
'deterministic = 0
\n
'
)
#sce_fd.write('output_dir = \n')
#
sce_fd.write('output_dir = \n')
sce_fd
.
write
(
'paramfile = param_config_space.pcs
\n
'
)
sce_fd
.
write
(
'paramfile = param_config_space.pcs
\n
'
)
sce_fd
.
write
(
'run_obj = quality
\n
'
)
sce_fd
.
write
(
'run_obj = quality
\n
'
)
return
generate_pcs
(
ss_content
)
return
generate_pcs
(
ss_content
)
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
generate_scenario
(
'search_space.json'
)
generate_scenario
(
'search_space.json'
)
src/sdk/pynni/nni/smac_tuner/smac_tuner.py
View file @
22316800
...
@@ -21,34 +21,30 @@
...
@@ -21,34 +21,30 @@
smac_tuner.py
smac_tuner.py
"""
"""
import
sys
import
logging
import
logging
import
numpy
as
np
import
sys
import
numpy
as
np
from
ConfigSpaceNNI
import
Configuration
from
nni.tuner
import
Tuner
from
nni.tuner
import
Tuner
from
nni.utils
import
OptimizeMode
,
extract_scalar_reward
from
nni.utils
import
OptimizeMode
,
extract_scalar_reward
from
smac.utils.io.cmd_reader
import
CMDReader
from
smac.scenario.scenario
import
Scenario
from
smac.facade.smac_facade
import
SMAC
from
smac.facade.roar_facade
import
ROAR
from
smac.facade.epils_facade
import
EPILS
from
smac.facade.epils_facade
import
EPILS
from
ConfigSpaceNNI
import
Configuration
from
smac.facade.roar_facade
import
ROAR
from
smac.facade.smac_facade
import
SMAC
from
smac.scenario.scenario
import
Scenario
from
smac.utils.io.cmd_reader
import
CMDReader
from
.convert_ss_to_scenario
import
generate_scenario
from
.convert_ss_to_scenario
import
generate_scenario
from
nni.tuner
import
Tuner
from
nni.utils
import
OptimizeMode
,
extract_scalar_reward
class
SMACTuner
(
Tuner
):
class
SMACTuner
(
Tuner
):
"""
"""
Parameters
Parameters
----------
----------
optimize_mode: str
optimize_mode: str
optimize mode, 'maximize' or 'minimize'
optimize mode, 'maximize' or 'minimize'
, by default 'maximize'
"""
"""
def
__init__
(
self
,
optimize_mode
):
def
__init__
(
self
,
optimize_mode
=
"maximize"
):
"""Constructor"""
"""Constructor"""
self
.
logger
=
logging
.
getLogger
(
self
.
logger
=
logging
.
getLogger
(
self
.
__module__
+
"."
+
self
.
__class__
.
__name__
)
self
.
__module__
+
"."
+
self
.
__class__
.
__name__
)
...
@@ -64,7 +60,6 @@ class SMACTuner(Tuner):
...
@@ -64,7 +60,6 @@ class SMACTuner(Tuner):
def
_main_cli
(
self
):
def
_main_cli
(
self
):
"""Main function of SMAC for CLI interface
"""Main function of SMAC for CLI interface
Returns
Returns
-------
-------
instance
instance
...
@@ -130,15 +125,17 @@ class SMACTuner(Tuner):
...
@@ -130,15 +125,17 @@ class SMACTuner(Tuner):
return
optimizer
return
optimizer
def
update_search_space
(
self
,
search_space
):
def
update_search_space
(
self
,
search_space
):
"""TODO: this is urgly, we put all the initialization work in this method, because initialization relies
"""
on search space, also because update_search_space is called at the beginning.
NOTE: updating search space is not supported.
NOTE: updating search space is not supported.
Parameters
Parameters
----------
----------
search_space:
search_space:
dict
search space
search space
"""
"""
# TODO: this is ugly, we put all the initialization work in this method, because initialization relies
# on search space, also because update_search_space is called at the beginning.
if
not
self
.
update_ss_done
:
if
not
self
.
update_ss_done
:
self
.
categorical_dict
=
generate_scenario
(
search_space
)
self
.
categorical_dict
=
generate_scenario
(
search_space
)
if
self
.
categorical_dict
is
None
:
if
self
.
categorical_dict
is
None
:
...
@@ -152,7 +149,6 @@ class SMACTuner(Tuner):
...
@@ -152,7 +149,6 @@ class SMACTuner(Tuner):
def
receive_trial_result
(
self
,
parameter_id
,
parameters
,
value
,
**
kwargs
):
def
receive_trial_result
(
self
,
parameter_id
,
parameters
,
value
,
**
kwargs
):
"""receive_trial_result
"""receive_trial_result
Parameters
Parameters
----------
----------
parameter_id: int
parameter_id: int
...
@@ -161,7 +157,6 @@ class SMACTuner(Tuner):
...
@@ -161,7 +157,6 @@ class SMACTuner(Tuner):
parameters
parameters
value:
value:
value
value
Raises
Raises
------
------
RuntimeError
RuntimeError
...
@@ -179,17 +174,16 @@ class SMACTuner(Tuner):
...
@@ -179,17 +174,16 @@ class SMACTuner(Tuner):
else
:
else
:
self
.
smbo_solver
.
nni_smac_receive_runs
(
self
.
total_data
[
parameter_id
],
reward
)
self
.
smbo_solver
.
nni_smac_receive_runs
(
self
.
total_data
[
parameter_id
],
reward
)
def
convert_loguniform_categorical
(
self
,
challenger_dict
):
def
param_postprocess
(
self
,
challenger_dict
):
"""
Convert the values of type `loguniform` back to their initial range
"""
Also, we convert categorical
:
Postprocessing for a set of parameter includes
:
categorical values in search space are changed to list of numbers before,
1. Convert the values of type `loguniform` back to their initial range.
those original values will be changed back in this function
2. Convert categorical: categorical values in search space are changed to list of numbers before,
those original values will be changed back in this function.
Parameters
Parameters
----------
----------
challenger_dict: dict
challenger_dict: dict
challenger dict
challenger dict
Returns
Returns
-------
-------
dict
dict
...
@@ -210,12 +204,10 @@ class SMACTuner(Tuner):
...
@@ -210,12 +204,10 @@ class SMACTuner(Tuner):
def
generate_parameters
(
self
,
parameter_id
,
**
kwargs
):
def
generate_parameters
(
self
,
parameter_id
,
**
kwargs
):
"""generate one instance of hyperparameters
"""generate one instance of hyperparameters
Parameters
Parameters
----------
----------
parameter_id: int
parameter_id: int
parameter id
parameter id
Returns
Returns
-------
-------
list
list
...
@@ -224,21 +216,19 @@ class SMACTuner(Tuner):
...
@@ -224,21 +216,19 @@ class SMACTuner(Tuner):
if
self
.
first_one
:
if
self
.
first_one
:
init_challenger
=
self
.
smbo_solver
.
nni_smac_start
()
init_challenger
=
self
.
smbo_solver
.
nni_smac_start
()
self
.
total_data
[
parameter_id
]
=
init_challenger
self
.
total_data
[
parameter_id
]
=
init_challenger
return
self
.
convert_loguniform_categorical
(
init_challenger
.
get_dictionary
())
return
self
.
param_postprocess
(
init_challenger
.
get_dictionary
())
else
:
else
:
challengers
=
self
.
smbo_solver
.
nni_smac_request_challengers
()
challengers
=
self
.
smbo_solver
.
nni_smac_request_challengers
()
for
challenger
in
challengers
:
for
challenger
in
challengers
:
self
.
total_data
[
parameter_id
]
=
challenger
self
.
total_data
[
parameter_id
]
=
challenger
return
self
.
convert_loguniform_categorical
(
challenger
.
get_dictionary
())
return
self
.
param_postprocess
(
challenger
.
get_dictionary
())
def
generate_multiple_parameters
(
self
,
parameter_id_list
,
**
kwargs
):
def
generate_multiple_parameters
(
self
,
parameter_id_list
,
**
kwargs
):
"""generate mutiple instances of hyperparameters
"""generate mutiple instances of hyperparameters
Parameters
Parameters
----------
----------
parameter_id_list: list
parameter_id_list: list
list of parameter id
list of parameter id
Returns
Returns
-------
-------
list
list
...
@@ -249,7 +239,7 @@ class SMACTuner(Tuner):
...
@@ -249,7 +239,7 @@ class SMACTuner(Tuner):
for
one_id
in
parameter_id_list
:
for
one_id
in
parameter_id_list
:
init_challenger
=
self
.
smbo_solver
.
nni_smac_start
()
init_challenger
=
self
.
smbo_solver
.
nni_smac_start
()
self
.
total_data
[
one_id
]
=
init_challenger
self
.
total_data
[
one_id
]
=
init_challenger
params
.
append
(
self
.
convert_loguniform_categorical
(
init_challenger
.
get_dictionary
()))
params
.
append
(
self
.
param_postprocess
(
init_challenger
.
get_dictionary
()))
else
:
else
:
challengers
=
self
.
smbo_solver
.
nni_smac_request_challengers
()
challengers
=
self
.
smbo_solver
.
nni_smac_request_challengers
()
cnt
=
0
cnt
=
0
...
@@ -258,16 +248,17 @@ class SMACTuner(Tuner):
...
@@ -258,16 +248,17 @@ class SMACTuner(Tuner):
if
cnt
>=
len
(
parameter_id_list
):
if
cnt
>=
len
(
parameter_id_list
):
break
break
self
.
total_data
[
parameter_id_list
[
cnt
]]
=
challenger
self
.
total_data
[
parameter_id_list
[
cnt
]]
=
challenger
params
.
append
(
self
.
convert_loguniform_categorical
(
challenger
.
get_dictionary
()))
params
.
append
(
self
.
param_postprocess
(
challenger
.
get_dictionary
()))
cnt
+=
1
cnt
+=
1
return
params
return
params
def
import_data
(
self
,
data
):
def
import_data
(
self
,
data
):
"""Import additional data for tuning
"""
Import additional data for tuning
Parameters
Parameters
----------
----------
data:
data:
list of dict
a list of dictionarys, e
ach of which has at least two keys,
'
parameter
'
and
'
value
'
E
ach of which has at least two keys,
`
parameter
`
and
`
value
`.
"""
"""
_completed_num
=
0
_completed_num
=
0
for
trial_info
in
data
:
for
trial_info
in
data
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment