From 7c33411dfcd4a1a8c4c0e9c07a46907fd27c849b Mon Sep 17 00:00:00 2001 From: Crysple Date: Tue, 13 Nov 2018 13:53:21 +0800 Subject: [PATCH 01/12] add gridsearch tuner --- .../pynni/nni/gridsearch_tuner/__init__.py | 0 .../nni/gridsearch_tuner/gridsearch_tuner.py | 103 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/sdk/pynni/nni/gridsearch_tuner/__init__.py create mode 100644 src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py diff --git a/src/sdk/pynni/nni/gridsearch_tuner/__init__.py b/src/sdk/pynni/nni/gridsearch_tuner/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py new file mode 100644 index 0000000000..1e8313853a --- /dev/null +++ b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py @@ -0,0 +1,103 @@ +# Copyright (c) Microsoft Corporation +# All rights reserved. +# +# MIT License +# +# Permission is hereby granted, free of charge, +# to any person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and +# to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# 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. +''' +batch_tuner.py including: + class BatchTuner +''' + +import copy +from enum import Enum, unique +import random + +import numpy as np + +import nni +from nni.tuner import Tuner + +TYPE = '_type' +CHOICE = 'choice' +VALUE = '_value' + + +class GridSearchTuner(Tuner): + ''' + GridSearchTuner will search all the possible configures that the user define in the searchSpace. + ''' + + def __init__(self): + self.count = -1 + self.expanded_search_space = [] + + def parse(self, param_value): + q = param_value[2] + func = lambda x:int(round(x/q)) + lower, upper = func(param_value[0]), func(param_value[1]) + return [float(i*q) for i in range(lower, upper+1)] + + def parse_parameter(self, param_type, param_value): + if param_type in ['quniform', 'qnormal']: + return self.parse(param_value, lambda v:v) + elif param_type in ['qloguniform', 'qlognormal']: + param_value[0] = np.exp(param_value[0]) + param_value[1] = np.exp(param_value[1]) + return self.parse(param_value) + else: + raise RuntimeError("Not supported type: %s" % param_type) + + def expand_parameters(self, para): + if len(para) == 1: + for key, values in para.items(): + return list(map(lambda v:{key:v}, values)) + + key = list(para)[0] + values = para.pop(key) + rest_para = self.expand_parameters(para) + ret_para = list() + for val in values: + for config in rest_para: + config[key] = val + ret_para.append(dict(config)) + return ret_para + + def update_search_space(self, search_space): + ''' + Check if the search space is valid and expand it: only contains 'choice' type or other types beginnning with the letter 'q' + ''' + ss = dict() + for param in search_space: + param_type = search_space[param][TYPE] + param_value = search_space[param][VALUE] + if param_type == CHOICE: + ss[param] = param_value + elif param_type[0] == 'q': + ss[param] = parse_parameter(param_type, param_value) + else: + raise RuntimeError("GridSearchTuner only supprt the 'choice' type or other types beginnning with the letter 'q'") + + self.expanded_search_space = self.expand_parameters(ss) + + def generate_parameters(self, parameter_id): + self.count +=1 + if self.count > len(self.expanded_search_space)-1: + raise nni.NoMoreTrialError('no more parameters now.') + return self.expanded_search_space[self.count] + + def receive_trial_result(self, parameter_id, parameters, value): + pass \ No newline at end of file From 9a0fd47923c80bf706101a2b43bf6b410d7544cd Mon Sep 17 00:00:00 2001 From: Crysple Date: Tue, 13 Nov 2018 17:13:12 +0800 Subject: [PATCH 02/12] add gridsearchtuner --- src/nni_manager/rest_server/restValidationSchemas.ts | 2 +- src/sdk/pynni/nni/constants.py | 4 +++- src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py | 4 ++-- tools/nni_cmd/config_schema.py | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/nni_manager/rest_server/restValidationSchemas.ts b/src/nni_manager/rest_server/restValidationSchemas.ts index a95c81765c..32e8ef5215 100644 --- a/src/nni_manager/rest_server/restValidationSchemas.ts +++ b/src/nni_manager/rest_server/restValidationSchemas.ts @@ -61,7 +61,7 @@ export namespace ValidationSchemas { maxExecDuration: joi.number().min(0).required(), multiPhase: joi.boolean(), tuner: joi.object({ - builtinTunerName: joi.string().valid('TPE', 'Random', 'Anneal', 'Evolution', 'SMAC', 'BatchTuner'), + builtinTunerName: joi.string().valid('TPE', 'Random', 'Anneal', 'Evolution', 'SMAC', 'BatchTuner', 'GridSearch'), codeDir: joi.string(), classFileName: joi.string(), className: joi.string(), diff --git a/src/sdk/pynni/nni/constants.py b/src/sdk/pynni/nni/constants.py index 0e4fff8663..5eb27b762d 100644 --- a/src/sdk/pynni/nni/constants.py +++ b/src/sdk/pynni/nni/constants.py @@ -25,7 +25,8 @@ 'Evolution': 'nni.evolution_tuner.evolution_tuner', 'SMAC': 'nni.smac_tuner.smac_tuner', 'BatchTuner': 'nni.batch_tuner.batch_tuner', - + 'GridSearch': 'nni.gridsearch_tuner.gridsearch_tuner', + 'Medianstop': 'nni.medianstop_assessor.medianstop_assessor' } @@ -36,6 +37,7 @@ 'Evolution': 'EvolutionTuner', 'SMAC': 'SMACTuner', 'BatchTuner': 'BatchTuner', + 'GridSearch': 'GridSearchTuner', 'Medianstop': 'MedianstopAssessor' } diff --git a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py index 1e8313853a..ddd3dfa7b4 100644 --- a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py +++ b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py @@ -41,7 +41,7 @@ class GridSearchTuner(Tuner): GridSearchTuner will search all the possible configures that the user define in the searchSpace. ''' - def __init__(self): + def __init__(self, optimize_mode): self.count = -1 self.expanded_search_space = [] @@ -87,7 +87,7 @@ def update_search_space(self, search_space): if param_type == CHOICE: ss[param] = param_value elif param_type[0] == 'q': - ss[param] = parse_parameter(param_type, param_value) + ss[param] = self.parse_parameter(param_type, param_value) else: raise RuntimeError("GridSearchTuner only supprt the 'choice' type or other types beginnning with the letter 'q'") diff --git a/tools/nni_cmd/config_schema.py b/tools/nni_cmd/config_schema.py index 80626037e1..19e36fea98 100644 --- a/tools/nni_cmd/config_schema.py +++ b/tools/nni_cmd/config_schema.py @@ -33,7 +33,7 @@ Optional('multiPhase'): bool, 'useAnnotation': bool, 'tuner': Or({ - 'builtinTunerName': Or('TPE', 'Random', 'Anneal', 'Evolution', 'SMAC', 'BatchTuner'), + 'builtinTunerName': Or('TPE', 'Random', 'Anneal', 'Evolution', 'SMAC', 'BatchTuner', 'GridSearch'), 'classArgs': { 'optimize_mode': Or('maximize', 'minimize'), Optional('speed'): int From 22a1d809620c25f776a24b397d3916c65f21c100 Mon Sep 17 00:00:00 2001 From: Crysple Date: Tue, 13 Nov 2018 17:18:41 +0800 Subject: [PATCH 03/12] add gridsearchtuner --- src/sdk/pynni/nni/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sdk/pynni/nni/README.md b/src/sdk/pynni/nni/README.md index fc3dd20a91..5ae7b17849 100644 --- a/src/sdk/pynni/nni/README.md +++ b/src/sdk/pynni/nni/README.md @@ -46,6 +46,9 @@ Note that SMAC only supports a subset of the types in [search space spec](../../ Batch allows users to simply provide several configurations (i.e., choices of hyper-parameters) for their trial code. After finishing all the configurations, the experiment is done. +**Gridsearch** + +Gridsearch performs an exhaustive searching through a manually specified subset of the hyperparameter space defined in the searchspace file ## 2. How to use the tuner algorithm in NNI? From 9a89a0d5ca82734e185f80c6da8c8a7697432f6f Mon Sep 17 00:00:00 2001 From: Crysple Date: Tue, 13 Nov 2018 17:20:42 +0800 Subject: [PATCH 04/12] add gridsearchtuner --- src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py index ddd3dfa7b4..9ba6fb46fa 100644 --- a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py +++ b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py @@ -18,14 +18,10 @@ # 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. ''' -batch_tuner.py including: - class BatchTuner +gridsearch_tuner.py including: + class GridSearchTuner ''' -import copy -from enum import Enum, unique -import random - import numpy as np import nni From 830b48e19ab2f6512f9f84e25dd5e2c8ed2281e9 Mon Sep 17 00:00:00 2001 From: Crysple Date: Wed, 14 Nov 2018 16:39:36 +0800 Subject: [PATCH 05/12] update gridsearch tuner --- .../nni/gridsearch_tuner/gridsearch_tuner.py | 69 +++++++++++++------ 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py index 9ba6fb46fa..9f17103e6d 100644 --- a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py +++ b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py @@ -41,23 +41,63 @@ def __init__(self, optimize_mode): self.count = -1 self.expanded_search_space = [] + def json2paramater(self, ss_spec): + ''' + Randomly generate values for hyperparameters from hyperparameter space i.e., x. + ss_spec: hyperparameter space + ''' + if isinstance(ss_spec, dict): + if '_type' in ss_spec.keys(): + _type = ss_spec['_type'] + _value = ss_spec['_value'] + chosen_params = list() + if _type == 'choice': + for value in _value: + choice = self.json2paramater(value) + if isinstance(choice, list): + chosen_params.extend(choice) + else: + chosen_params.append(choice) + else: + chosen_params = self.parse_parameter(_type, _value) + else: + chosen_params = dict() + for key in ss_spec.keys(): + chosen_params[key] = self.json2paramater(ss_spec[key]) + return self.expand_parameters(chosen_params) + elif isinstance(ss_spec, list): + chosen_params = list() + for subspec in ss_spec[1:]: + choice = self.json2paramater(subspec) + if isinstance(choice, list): + chosen_params.extend(choice) + else: + chosen_params.append(choice) + chosen_params = list(map(lambda v: {ss_spec[0]: v}, chosen_params)) + else: + chosen_params = copy.deepcopy(ss_spec) + return chosen_params + def parse(self, param_value): - q = param_value[2] - func = lambda x:int(round(x/q)) - lower, upper = func(param_value[0]), func(param_value[1]) - return [float(i*q) for i in range(lower, upper+1)] + low, high, q = param_value[0], param_value[1], max(2, param_value[2]) + interval = (high - low) / (q - 1) + return [float(low + interval * i) for i in range(q)] def parse_parameter(self, param_type, param_value): + parse = lambda para: [para[3]] if param_type in ['quniform', 'qnormal']: - return self.parse(param_value, lambda v:v) - elif param_type in ['qloguniform', 'qlognormal']: - param_value[0] = np.exp(param_value[0]) - param_value[1] = np.exp(param_value[1]) return self.parse(param_value) + elif param_type in ['qloguniform', 'qlognormal']: + param_value[:2] = np.log10(param_value[:2]) + return list(np.power(10, self.parse(param_value))) else: raise RuntimeError("Not supported type: %s" % param_type) def expand_parameters(self, para): + ''' + para: {key1: [v11, v12, ...], key2: [v21, v22, ...], ...} + return: {{key1: v11, key2: v21, ...}, {key1: v11, key2: v22, ...}, ...} + ''' if len(para) == 1: for key, values in para.items(): return list(map(lambda v:{key:v}, values)) @@ -76,18 +116,7 @@ def update_search_space(self, search_space): ''' Check if the search space is valid and expand it: only contains 'choice' type or other types beginnning with the letter 'q' ''' - ss = dict() - for param in search_space: - param_type = search_space[param][TYPE] - param_value = search_space[param][VALUE] - if param_type == CHOICE: - ss[param] = param_value - elif param_type[0] == 'q': - ss[param] = self.parse_parameter(param_type, param_value) - else: - raise RuntimeError("GridSearchTuner only supprt the 'choice' type or other types beginnning with the letter 'q'") - - self.expanded_search_space = self.expand_parameters(ss) + self.expand_parameters = self.json2paramater(search_space) def generate_parameters(self, parameter_id): self.count +=1 From d965d9bb55607ad810ba8a8b09aeb39d162393e3 Mon Sep 17 00:00:00 2001 From: Crysple Date: Wed, 14 Nov 2018 16:40:55 +0800 Subject: [PATCH 06/12] update gridsearch tuner --- src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py index 9f17103e6d..6c80c1d2ca 100644 --- a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py +++ b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py @@ -43,7 +43,7 @@ def __init__(self, optimize_mode): def json2paramater(self, ss_spec): ''' - Randomly generate values for hyperparameters from hyperparameter space i.e., x. + generate all possible config for hyperparameters from hyperparameter space. ss_spec: hyperparameter space ''' if isinstance(ss_spec, dict): From c7aadd9eeff9d58be3cb93c139223cbef73b35bc Mon Sep 17 00:00:00 2001 From: Crysple Date: Wed, 14 Nov 2018 18:22:52 +0800 Subject: [PATCH 07/12] update gridsearch tuner --- .../nni/gridsearch_tuner/gridsearch_tuner.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py index 6c80c1d2ca..fb65706448 100644 --- a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py +++ b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py @@ -35,6 +35,17 @@ class GridSearchTuner class GridSearchTuner(Tuner): ''' GridSearchTuner will search all the possible configures that the user define in the searchSpace. + The only acceptable types of search space are 'quniform', 'qloguniform' and 'choice' + + Type 'choice' will select one of the options. Note that it can also be nested. + + Type 'quniform' will receive three values [low, high, q], where [low, high] specifies a range + and 'q' specifies the number of values that will be sampled evenly. + It will be sampled in a way that the first sampled value is 'low', and each of the following values is (high-low)/q + larger that the value in front of it. + + Type 'qloguniform' behaves like 'quniform' except that it will first change the range to [log10(low), log10(high)] + and sample and then change the sampled value back. ''' def __init__(self, optimize_mode): @@ -85,9 +96,9 @@ def parse(self, param_value): def parse_parameter(self, param_type, param_value): parse = lambda para: [para[3]] - if param_type in ['quniform', 'qnormal']: + if param_type == 'quniform': return self.parse(param_value) - elif param_type in ['qloguniform', 'qlognormal']: + elif param_type == 'qloguniform': param_value[:2] = np.log10(param_value[:2]) return list(np.power(10, self.parse(param_value))) else: From 932dd9a6d670af907770cf934c845f40a43507ed Mon Sep 17 00:00:00 2001 From: Crysple Date: Wed, 14 Nov 2018 18:31:59 +0800 Subject: [PATCH 08/12] update gridsearch tuner --- src/sdk/pynni/nni/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sdk/pynni/nni/README.md b/src/sdk/pynni/nni/README.md index 5ae7b17849..d6d38cffc0 100644 --- a/src/sdk/pynni/nni/README.md +++ b/src/sdk/pynni/nni/README.md @@ -50,6 +50,12 @@ Batch allows users to simply provide several configurations (i.e., choices of hy Gridsearch performs an exhaustive searching through a manually specified subset of the hyperparameter space defined in the searchspace file +Note that the only acceptable types of search space are 'quniform', 'qloguniform' and 'choice': + +* Type 'choice' will select one of the options. Note that it can also be nested. +* Type 'quniform' will receive three values [low, high, q], where [low, high] specifies a range and 'q' specifies the number of values that will be sampled evenly. It will be sampled in a way that the first sampled value is 'low', and each of the following values is (high-low)/q larger that the value in front of it. +* Type 'qloguniform' behaves like 'quniform' except that it will first change the range to [log10(low), log10(high)] and sample and then change the sampled value back. + ## 2. How to use the tuner algorithm in NNI? User only need to do one thing: choose a Tuner```config.yaml```. From d057b1e0d799148081760dde6703de2146ba23ac Mon Sep 17 00:00:00 2001 From: Crysple Date: Wed, 14 Nov 2018 20:09:25 +0800 Subject: [PATCH 09/12] update gridsearch tuner --- .../nni/gridsearch_tuner/gridsearch_tuner.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py index fb65706448..9fe48967bf 100644 --- a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py +++ b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py @@ -36,7 +36,7 @@ class GridSearchTuner(Tuner): ''' GridSearchTuner will search all the possible configures that the user define in the searchSpace. The only acceptable types of search space are 'quniform', 'qloguniform' and 'choice' - + Type 'choice' will select one of the options. Note that it can also be nested. Type 'quniform' will receive three values [low, high, q], where [low, high] specifies a range @@ -47,7 +47,7 @@ class GridSearchTuner(Tuner): Type 'qloguniform' behaves like 'quniform' except that it will first change the range to [log10(low), log10(high)] and sample and then change the sampled value back. ''' - + def __init__(self, optimize_mode): self.count = -1 self.expanded_search_space = [] @@ -95,7 +95,6 @@ def parse(self, param_value): return [float(low + interval * i) for i in range(q)] def parse_parameter(self, param_type, param_value): - parse = lambda para: [para[3]] if param_type == 'quniform': return self.parse(param_value) elif param_type == 'qloguniform': @@ -111,8 +110,8 @@ def expand_parameters(self, para): ''' if len(para) == 1: for key, values in para.items(): - return list(map(lambda v:{key:v}, values)) - + return list(map(lambda v: {key: v}, values)) + key = list(para)[0] values = para.pop(key) rest_para = self.expand_parameters(para) @@ -127,13 +126,13 @@ def update_search_space(self, search_space): ''' Check if the search space is valid and expand it: only contains 'choice' type or other types beginnning with the letter 'q' ''' - self.expand_parameters = self.json2paramater(search_space) + self.expanded_search_space = self.json2paramater(search_space) def generate_parameters(self, parameter_id): - self.count +=1 + self.count += 1 if self.count > len(self.expanded_search_space)-1: raise nni.NoMoreTrialError('no more parameters now.') return self.expanded_search_space[self.count] def receive_trial_result(self, parameter_id, parameters, value): - pass \ No newline at end of file + pass From bac7f2bc685245b324e0644823eab33532688a6f Mon Sep 17 00:00:00 2001 From: Crysple Date: Wed, 14 Nov 2018 20:26:46 +0800 Subject: [PATCH 10/12] update gridsearch tuner --- .../nni/gridsearch_tuner/gridsearch_tuner.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py index 9fe48967bf..7e6b774e8a 100644 --- a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py +++ b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py @@ -22,6 +22,7 @@ class GridSearchTuner ''' +import copy import numpy as np import nni @@ -70,7 +71,7 @@ def json2paramater(self, ss_spec): else: chosen_params.append(choice) else: - chosen_params = self.parse_parameter(_type, _value) + chosen_params = self._parse_parameter(_type, _value) else: chosen_params = dict() for key in ss_spec.keys(): @@ -89,19 +90,19 @@ def json2paramater(self, ss_spec): chosen_params = copy.deepcopy(ss_spec) return chosen_params - def parse(self, param_value): - low, high, q = param_value[0], param_value[1], max(2, param_value[2]) - interval = (high - low) / (q - 1) - return [float(low + interval * i) for i in range(q)] + def _parse(self, param_value): + low, high, count = param_value[0], param_value[1], max(2, param_value[2]) + interval = (high - low) / (count - 1) + return [float(low + interval * i) for i in range(count)] - def parse_parameter(self, param_type, param_value): + def _parse_parameter(self, param_type, param_value): if param_type == 'quniform': - return self.parse(param_value) - elif param_type == 'qloguniform': + return self._parse(param_value) + if param_type == 'qloguniform': param_value[:2] = np.log10(param_value[:2]) - return list(np.power(10, self.parse(param_value))) - else: - raise RuntimeError("Not supported type: %s" % param_type) + return list(np.power(10, self._parse(param_value))) + + raise RuntimeError("Not supported type: %s" % param_type) def expand_parameters(self, para): ''' @@ -119,7 +120,7 @@ def expand_parameters(self, para): for val in values: for config in rest_para: config[key] = val - ret_para.append(dict(config)) + ret_para.append(copy.deepcopy(config)) return ret_para def update_search_space(self, search_space): From 7f9d8e6435d307cdc05d421dc533cf6e2cd8e9ce Mon Sep 17 00:00:00 2001 From: Crysple Date: Thu, 15 Nov 2018 11:27:43 +0800 Subject: [PATCH 11/12] update gridsearch tuner --- .../nni/gridsearch_tuner/gridsearch_tuner.py | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py index 7e6b774e8a..18caa523d7 100644 --- a/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py +++ b/src/sdk/pynni/nni/gridsearch_tuner/gridsearch_tuner.py @@ -40,10 +40,9 @@ class GridSearchTuner(Tuner): Type 'choice' will select one of the options. Note that it can also be nested. - Type 'quniform' will receive three values [low, high, q], where [low, high] specifies a range - and 'q' specifies the number of values that will be sampled evenly. - It will be sampled in a way that the first sampled value is 'low', and each of the following values is (high-low)/q - larger that the value in front of it. + Type 'quniform' will receive three values [low, high, q], where [low, high] specifies a range and 'q' specifies the number of values that will be sampled evenly. + Note that q should be at least 2. + It will be sampled in a way that the first sampled value is 'low', and each of the following values is (high-low)/q larger that the value in front of it. Type 'qloguniform' behaves like 'quniform' except that it will first change the range to [log10(low), log10(high)] and sample and then change the sampled value back. @@ -55,7 +54,7 @@ def __init__(self, optimize_mode): def json2paramater(self, ss_spec): ''' - generate all possible config for hyperparameters from hyperparameter space. + generate all possible configs for hyperparameters from hyperparameter space. ss_spec: hyperparameter space ''' if isinstance(ss_spec, dict): @@ -71,7 +70,7 @@ def json2paramater(self, ss_spec): else: chosen_params.append(choice) else: - chosen_params = self._parse_parameter(_type, _value) + chosen_params = self.parse_qtype(_type, _value) else: chosen_params = dict() for key in ss_spec.keys(): @@ -90,22 +89,27 @@ def json2paramater(self, ss_spec): chosen_params = copy.deepcopy(ss_spec) return chosen_params - def _parse(self, param_value): - low, high, count = param_value[0], param_value[1], max(2, param_value[2]) + def _parse_quniform(self, param_value): + '''parse type of quniform parameter and return a list''' + if param_value[2] < 2: + raise RuntimeError("The number of values sampled (q) should be at least 2") + low, high, count = param_value[0], param_value[1], param_value[2] interval = (high - low) / (count - 1) return [float(low + interval * i) for i in range(count)] - def _parse_parameter(self, param_type, param_value): + def parse_qtype(self, param_type, param_value): + '''parse type of quniform or qloguniform''' if param_type == 'quniform': - return self._parse(param_value) + return self._parse_quniform(param_value) if param_type == 'qloguniform': param_value[:2] = np.log10(param_value[:2]) - return list(np.power(10, self._parse(param_value))) + return list(np.power(10, self._parse_quniform(param_value))) raise RuntimeError("Not supported type: %s" % param_type) def expand_parameters(self, para): ''' + Enumerate all possible combinations of all parameters para: {key1: [v11, v12, ...], key2: [v21, v22, ...], ...} return: {{key1: v11, key2: v21, ...}, {key1: v11, key2: v22, ...}, ...} ''' From cdc2202e6902c75d4029a617f740cdf07bacb621 Mon Sep 17 00:00:00 2001 From: Crysple Date: Thu, 15 Nov 2018 12:40:34 +0800 Subject: [PATCH 12/12] update gridsearch and pylint --- pylintrc | 24 ++++++------------------ src/sdk/pynni/nni/README.md | 1 + 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/pylintrc b/pylintrc index 673d8e058c..57ec53011e 100644 --- a/pylintrc +++ b/pylintrc @@ -1,30 +1,18 @@ # Usage: -# pylint --rcfile=PATH_TO_THIS_FILE PACKAGE_NAME +# python3 -m pylint --rcfile=PATH_TO_THIS_FILE PACKAGE_NAME # or -# pylint --rcfile=PATH_TO_THIS_FILE SOURCE_FILE.py +# python3 -m pylint --rcfile=PATH_TO_THIS_FILE SOURCE_FILE.py [SETTINGS] max-line-length=140 -max-args=5 +max-args=8 max-locals=15 max-statements=50 -max-attributes=7 +max-attributes=15 const-naming-style=any -disable=all - -enable=F, - E, - unreachable, - duplicate-key, - unnecessary-semicolon, - global-variable-not-assigned, - binary-op-exception, - bad-format-string, - anomalous-backslash-in-string, - bad-open-mode - -extension-pkg-whitelist=numpy \ No newline at end of file +disable=duplicate-code, + super-init-not-called \ No newline at end of file diff --git a/src/sdk/pynni/nni/README.md b/src/sdk/pynni/nni/README.md index d6d38cffc0..259edf2f77 100644 --- a/src/sdk/pynni/nni/README.md +++ b/src/sdk/pynni/nni/README.md @@ -6,6 +6,7 @@ For now, NNI has supported the following tuner algorithms. Note that NNI install - Random Search - Anneal - Naive Evolution + - Grid Search - SMAC (to install through `nnictl`) - ENAS (ongoing) - Batch (ongoing)