Commit c550bbce authored by Zejun Lin's avatar Zejun Lin Committed by QuanluZhang
Browse files

modify loguniform and lognormal (#395)

* modify loguniform and lognormal

* fix bug

* fix bug

* update doc

* update doc

* fix

* update tpe for loguniform

* update tpe for loguniform

* update for loguniform

* update for loguniform

* update loguniform and qloguniform

* update doc

* update

* revert

* revert

* revert

* revert
parent a3872505
...@@ -40,12 +40,12 @@ The candidate type and value for variable is here: ...@@ -40,12 +40,12 @@ The candidate type and value for variable is here:
<br/> <br/>
* {"_type":"loguniform","_value":[low, high]} * {"_type":"loguniform","_value":[low, high]}
* Which means the variable value is a value drawn according to exp(uniform(low, high)) so that the logarithm of the return value is uniformly distributed. * Which means the variable value is a value drawn from a range [low, high] according to a loguniform distribution like exp(uniform(log(low), log(high))), so that the logarithm of the return value is uniformly distributed.
* When optimizing, this variable is constrained to the interval [exp(low), exp(high)]. * When optimizing, this variable is constrained to be positive.
<br/> <br/>
* {"_type":"qloguniform","_value":[low, high, q]} * {"_type":"qloguniform","_value":[low, high, q]}
* Which means the variable value is a value like round(exp(uniform(low, high)) / q) * q * Which means the variable value is a value like round(loguniform(low, high)) / q) * q
* Suitable for a discrete variable with respect to which the objective is "smooth" and gets smoother with the size of the value, but which should be bounded both above and below. * Suitable for a discrete variable with respect to which the objective is "smooth" and gets smoother with the size of the value, but which should be bounded both above and below.
<br/> <br/>
...@@ -68,3 +68,7 @@ The candidate type and value for variable is here: ...@@ -68,3 +68,7 @@ The candidate type and value for variable is here:
<br/> <br/>
Note that SMAC only supports a subset of the types above, including `choice`, `randint`, `uniform`, `loguniform`, `quniform(q=1)`. In the current version, SMAC does not support cascaded search space (i.e., conditional variable in SMAC). Note that SMAC only supports a subset of the types above, including `choice`, `randint`, `uniform`, `loguniform`, `quniform(q=1)`. In the current version, SMAC does not support cascaded search space (i.e., conditional variable in SMAC).
Note that GridSearch Tuner only supports a subset of the types above, including `choic`, `quniform` and `qloguniform`, where q here specifies the number of values that will be sampled. Details about the last two type as follows
* 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 [log(low), log(high)] and sample and then change the sampled value back.
\ No newline at end of file
...@@ -35,7 +35,7 @@ import random ...@@ -35,7 +35,7 @@ import random
import numpy as np import numpy as np
from nni.tuner import Tuner from nni.tuner import Tuner
from . import parameter_expressions from .. import parameter_expressions
@unique @unique
......
...@@ -44,7 +44,7 @@ class GridSearchTuner(Tuner): ...@@ -44,7 +44,7 @@ class GridSearchTuner(Tuner):
Note that q should be at least 2. 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. 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)] Type 'qloguniform' behaves like 'quniform' except that it will first change the range to [log(low), log(high)]
and sample and then change the sampled value back. and sample and then change the sampled value back.
''' '''
...@@ -102,8 +102,8 @@ class GridSearchTuner(Tuner): ...@@ -102,8 +102,8 @@ class GridSearchTuner(Tuner):
if param_type == 'quniform': if param_type == 'quniform':
return self._parse_quniform(param_value) return self._parse_quniform(param_value)
if param_type == 'qloguniform': if param_type == 'qloguniform':
param_value[:2] = np.log10(param_value[:2]) param_value[:2] = np.log(param_value[:2])
return list(np.power(10, self._parse_quniform(param_value))) return list(np.exp(self._parse_quniform(param_value)))
raise RuntimeError("Not supported type: %s" % param_type) raise RuntimeError("Not supported type: %s" % param_type)
......
...@@ -61,6 +61,8 @@ def json2space(in_x, name=ROOT): ...@@ -61,6 +61,8 @@ def json2space(in_x, name=ROOT):
if _type == 'choice': if _type == 'choice':
out_y = eval('hp.hp.'+_type)(name, _value) out_y = eval('hp.hp.'+_type)(name, _value)
else: else:
if _type in ['loguniform', 'qloguniform']:
_value[:2] = np.log(_value[:2])
out_y = eval('hp.hp.' + _type)(name, *_value) out_y = eval('hp.hp.' + _type)(name, *_value)
else: else:
out_y = dict() out_y = dict()
...@@ -75,7 +77,7 @@ def json2space(in_x, name=ROOT): ...@@ -75,7 +77,7 @@ def json2space(in_x, name=ROOT):
return out_y return out_y
def json2paramater(in_x, paramater, name=ROOT): def json2parameter(in_x, parameter, name=ROOT):
''' '''
Change json to parameters. Change json to parameters.
''' '''
...@@ -85,22 +87,22 @@ def json2paramater(in_x, paramater, name=ROOT): ...@@ -85,22 +87,22 @@ def json2paramater(in_x, paramater, name=ROOT):
_type = in_x[TYPE] _type = in_x[TYPE]
name = name + '-' + _type name = name + '-' + _type
if _type == 'choice': if _type == 'choice':
_index = paramater[name] _index = parameter[name]
out_y = { out_y = {
INDEX: _index, INDEX: _index,
VALUE: json2paramater(in_x[VALUE][_index], paramater, name=name+'[%d]' % _index) VALUE: json2parameter(in_x[VALUE][_index], parameter, name=name+'[%d]' % _index)
} }
else: else:
out_y = paramater[name] out_y = parameter[name]
else: else:
out_y = dict() out_y = dict()
for key in in_x.keys(): for key in in_x.keys():
out_y[key] = json2paramater( out_y[key] = json2parameter(
in_x[key], paramater, name + '[%s]' % str(key)) in_x[key], parameter, name + '[%s]' % str(key))
elif isinstance(in_x, list): elif isinstance(in_x, list):
out_y = list() out_y = list()
for i, x_i in enumerate(in_x): for i, x_i in enumerate(in_x):
out_y.append(json2paramater(x_i, paramater, name + '[%d]' % i)) out_y.append(json2parameter(x_i, parameter, name + '[%d]' % i))
else: else:
logger.info('in_x is not a dict or a list in json2space fuinction %s', str(in_x)) logger.info('in_x is not a dict or a list in json2space fuinction %s', str(in_x))
return out_y return out_y
...@@ -201,7 +203,7 @@ class HyperoptTuner(Tuner): ...@@ -201,7 +203,7 @@ class HyperoptTuner(Tuner):
parameter[key] = None parameter[key] = None
# remove '_index' from json2parameter and save params-id # remove '_index' from json2parameter and save params-id
total_params = json2paramater(self.json, parameter) total_params = json2parameter(self.json, parameter)
self.total_data[parameter_id] = total_params self.total_data[parameter_id] = total_params
params = _split_index(total_params) params = _split_index(total_params)
return params return params
......
...@@ -46,6 +46,7 @@ def uniform(low, high, random_state): ...@@ -46,6 +46,7 @@ def uniform(low, high, random_state):
high: an float that represent an upper bound high: an float that represent an upper bound
random_state: an object of numpy.random.RandomState random_state: an object of numpy.random.RandomState
''' '''
assert high > low, 'Upper bound must be larger than lower bound'
return random_state.uniform(low, high) return random_state.uniform(low, high)
...@@ -65,7 +66,8 @@ def loguniform(low, high, random_state): ...@@ -65,7 +66,8 @@ def loguniform(low, high, random_state):
high: an float that represent an upper bound high: an float that represent an upper bound
random_state: an object of numpy.random.RandomState random_state: an object of numpy.random.RandomState
''' '''
return np.exp(uniform(low, high, random_state)) assert low > 0, 'Lower bound must be positive'
return np.exp(uniform(np.log(low), np.log(high), random_state))
def qloguniform(low, high, q, random_state): def qloguniform(low, high, q, random_state):
......
...@@ -55,10 +55,12 @@ if env_args.platform is None: ...@@ -55,10 +55,12 @@ if env_args.platform is None:
return random.uniform(low, high) return random.uniform(low, high)
def quniform(low, high, q, name=None): def quniform(low, high, q, name=None):
assert high > low, 'Upper bound must be larger than lower bound'
return round(random.uniform(low, high) / q) * q return round(random.uniform(low, high) / q) * q
def loguniform(low, high, name=None): def loguniform(low, high, name=None):
return math.exp(random.uniform(low, high)) assert low > 0, 'Lower bound must be positive'
return np.exp(random.uniform(np.log(low), np.log(high)))
def qloguniform(low, high, q, name=None): def qloguniform(low, high, q, name=None):
return round(loguniform(low, high) / q) * q return round(loguniform(low, high) / q) * q
...@@ -70,7 +72,7 @@ if env_args.platform is None: ...@@ -70,7 +72,7 @@ if env_args.platform is None:
return round(random.gauss(mu, sigma) / q) * q return round(random.gauss(mu, sigma) / q) * q
def lognormal(mu, sigma, name=None): def lognormal(mu, sigma, name=None):
return math.exp(random.gauss(mu, sigma)) return np.exp(random.gauss(mu, sigma))
def qlognormal(mu, sigma, q, name=None): def qlognormal(mu, sigma, q, name=None):
return round(lognormal(mu, sigma) / q) * q return round(lognormal(mu, sigma) / q) * q
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment