[tune] Directional metrics for components (#4120) (#4915)

This commit is contained in:
Hersh Godse
2019-06-02 22:13:40 -07:00
committed by Richard Liaw
parent 084b22181e
commit 89722ff003
31 changed files with 354 additions and 131 deletions
@@ -59,7 +59,8 @@ if __name__ == "__main__":
# which is automatically filled by Tune.
ahb = AsyncHyperBandScheduler(
time_attr="training_iteration",
reward_attr="episode_reward_mean",
metric="episode_reward_mean",
mode="max",
grace_period=5,
max_t=100)
+1 -1
View File
@@ -112,5 +112,5 @@ if __name__ == "__main__":
outcome_constraints=["l2norm <= 1.25"], # Optional.
)
algo = AxSearch(client, max_concurrent=4)
scheduler = AsyncHyperBandScheduler(reward_attr="hartmann6")
scheduler = AsyncHyperBandScheduler(metric="hartmann6", mode="max")
run(easy_objective, name="ax", search_alg=algo, **config)
+4 -4
View File
@@ -18,8 +18,7 @@ def easy_objective(config, reporter):
for i in range(config["iterations"]):
reporter(
timesteps_total=i,
neg_mean_loss=-(config["height"] - 14)**2 +
abs(config["width"] - 3))
mean_loss=(config["height"] - 14)**2 - abs(config["width"] - 3))
time.sleep(0.02)
@@ -46,13 +45,14 @@ if __name__ == "__main__":
algo = BayesOptSearch(
space,
max_concurrent=4,
reward_attr="neg_mean_loss",
metric="mean_loss",
mode="min",
utility_kwargs={
"kind": "ucb",
"kappa": 2.5,
"xi": 0.0
})
scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss")
scheduler = AsyncHyperBandScheduler(metric="mean_loss", mode="min")
run(easy_objective,
name="my_exp",
search_alg=algo,
+1 -1
View File
@@ -50,7 +50,7 @@ if __name__ == "__main__":
reward_attr="neg_mean_loss",
max_generation=2 if args.smoke_test else 10,
population_size=10 if args.smoke_test else 50)
scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss")
scheduler = AsyncHyperBandScheduler(metric="neg_mean_loss", mode="max")
run(michalewicz_function,
name="my_exp",
search_alg=algo,
@@ -58,7 +58,8 @@ if __name__ == "__main__":
# which is automatically filled by Tune.
hyperband = HyperBandScheduler(
time_attr="training_iteration",
reward_attr="episode_reward_mean",
metric="episode_reward_mean",
mode="max",
max_t=100)
exp = Experiment(
+4 -4
View File
@@ -20,8 +20,7 @@ def easy_objective(config, reporter):
for i in range(config["iterations"]):
reporter(
timesteps_total=i,
neg_mean_loss=-(config["height"] - 14)**2 +
abs(config["width"] - 3))
mean_loss=(config["height"] - 14)**2 - abs(config["width"] - 3))
time.sleep(0.02)
@@ -66,7 +65,8 @@ if __name__ == "__main__":
algo = HyperOptSearch(
space,
max_concurrent=4,
reward_attr="neg_mean_loss",
metric="mean_loss",
mode="min",
points_to_evaluate=current_best_params)
scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss")
scheduler = AsyncHyperBandScheduler(metric="mean_loss", mode="min")
run(easy_objective, search_alg=algo, scheduler=scheduler, **config)
+2 -1
View File
@@ -161,7 +161,8 @@ if __name__ == "__main__":
ray.init()
sched = AsyncHyperBandScheduler(
time_attr="training_iteration",
reward_attr="neg_mean_loss",
metric="mean_loss",
mode="min",
max_t=400,
grace_period=20)
tune.register_trainable(
@@ -180,7 +180,7 @@ if __name__ == "__main__":
ray.init(redis_address=args.redis_address)
sched = HyperBandScheduler(
time_attr="training_iteration", reward_attr="neg_mean_loss")
time_attr="training_iteration", metric="mean_loss", mode="min")
tune.run(
TrainMNIST,
scheduler=sched,
@@ -18,8 +18,7 @@ def easy_objective(config, reporter):
for i in range(config["iterations"]):
reporter(
timesteps_total=i,
neg_mean_loss=-(config["height"] - 14)**2 +
abs(config["width"] - 3))
mean_loss=(config["height"] - 14)**2 - abs(config["width"] - 3))
time.sleep(0.02)
@@ -55,8 +54,9 @@ if __name__ == "__main__":
optimizer,
parameter_names,
max_concurrent=4,
reward_attr="neg_mean_loss")
scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss")
metric="mean_loss",
mode="min")
scheduler = AsyncHyperBandScheduler(metric="mean_loss", mode="min")
run(easy_objective,
name="nevergrad",
search_alg=algo,
+2 -1
View File
@@ -96,7 +96,8 @@ if __name__ == "__main__":
pbt = PopulationBasedTraining(
time_attr="training_iteration",
reward_attr="mean_accuracy",
metric="mean_accuracy",
mode="max",
perturbation_interval=20,
hyperparam_mutations={
# distribution for resampling
+2 -1
View File
@@ -30,7 +30,8 @@ if __name__ == "__main__":
pbt = PopulationBasedTraining(
time_attr="time_total_s",
reward_attr="episode_reward_mean",
metric="episode_reward_mean",
mode="max",
perturbation_interval=120,
resample_probability=0.25,
# Specifies the mutations of these hyperparams
@@ -206,7 +206,8 @@ if __name__ == "__main__":
pbt = PopulationBasedTraining(
time_attr="training_iteration",
reward_attr="mean_accuracy",
metric="mean_accuracy",
mode="max",
perturbation_interval=10,
hyperparam_mutations={
"dropout": lambda _: np.random.uniform(0, 1),
+4 -4
View File
@@ -18,8 +18,7 @@ def easy_objective(config, reporter):
for i in range(config["iterations"]):
reporter(
timesteps_total=i,
neg_mean_loss=-(config["height"] - 14)**2 +
abs(config["width"] - 3))
mean_loss=(config["height"] - 14)**2 - abs(config["width"] - 3))
time.sleep(0.02)
@@ -68,8 +67,9 @@ if __name__ == "__main__":
space,
name="SigOpt Example Experiment",
max_concurrent=1,
reward_attr="neg_mean_loss")
scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss")
metric="mean_loss",
mode="min")
scheduler = AsyncHyperBandScheduler(metric="mean_loss", mode="min")
run(easy_objective,
name="my_exp",
search_alg=algo,
+7 -6
View File
@@ -18,8 +18,7 @@ def easy_objective(config, reporter):
for i in range(config["iterations"]):
reporter(
timesteps_total=i,
neg_mean_loss=-(config["height"] - 14)**2 +
abs(config["width"] - 3))
mean_loss=(config["height"] - 14)**2 - abs(config["width"] - 3))
time.sleep(0.02)
@@ -48,10 +47,11 @@ if __name__ == "__main__":
algo = SkOptSearch(
optimizer, ["width", "height"],
max_concurrent=4,
reward_attr="neg_mean_loss",
metric="mean_loss",
mode="min",
points_to_evaluate=previously_run_params,
evaluated_rewards=known_rewards)
scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss")
scheduler = AsyncHyperBandScheduler(metric="mean_loss", mode="min")
run(easy_objective,
name="skopt_exp_with_warmstart",
search_alg=algo,
@@ -63,9 +63,10 @@ if __name__ == "__main__":
algo = SkOptSearch(
optimizer, ["width", "height"],
max_concurrent=4,
reward_attr="neg_mean_loss",
metric="mean_loss",
mode="min",
points_to_evaluate=previously_run_params)
scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss")
scheduler = AsyncHyperBandScheduler(metric="mean_loss", mode="min")
run(easy_objective,
name="skopt_exp",
search_alg=algo,
@@ -192,7 +192,8 @@ if __name__ == "__main__":
elif args.scheduler == "asynchyperband":
sched = AsyncHyperBandScheduler(
time_attr="training_iteration",
reward_attr="neg_mean_loss",
metric="mean_loss",
mode="min",
max_t=400,
grace_period=60)
else:
@@ -240,7 +240,8 @@ if __name__ == "__main__":
name="tune_mnist_test",
scheduler=AsyncHyperBandScheduler(
time_attr="timesteps_total",
reward_attr="mean_accuracy",
metric="mean_accuracy",
mode="max",
max_t=600,
),
**mnist_spec)
+2 -1
View File
@@ -64,7 +64,8 @@ if __name__ == "__main__":
ray.init()
sched = AsyncHyperBandScheduler(
time_attr="timesteps_total",
reward_attr="mean_accuracy",
metric="mean_accuracy",
mode="max",
max_t=400,
grace_period=20)
@@ -233,7 +233,10 @@ if __name__ == "__main__":
ray.init()
hyperband = HyperBandScheduler(
time_attr="training_iteration", reward_attr="mean_accuracy", max_t=10)
time_attr="training_iteration",
metric="mean_accuracy",
mode="max",
max_t=10)
tune.run(
TrainMNIST,
+23 -6
View File
@@ -25,9 +25,10 @@ class AsyncHyperBandScheduler(FIFOScheduler):
Note that you can pass in something non-temporal such as
`training_iteration` as a measure of progress, the only requirement
is that the attribute should increase monotonically.
reward_attr (str): The training result objective value attribute. As
with `time_attr`, this may refer to any objective value. Stopping
metric (str): The training result objective value attribute. Stopping
procedures will use this attribute.
mode (str): One of {min, max}. Determines whether objective is
minimizing or maximizing the metric attribute.
max_t (float): max time units per trial. Trials will be stopped after
max_t time units (determined by time_attr) have passed.
grace_period (float): Only stop trials at least this old in time.
@@ -40,7 +41,9 @@ class AsyncHyperBandScheduler(FIFOScheduler):
def __init__(self,
time_attr="training_iteration",
reward_attr="episode_reward_mean",
reward_attr=None,
metric="episode_reward_mean",
mode="max",
max_t=100,
grace_period=10,
reduction_factor=4,
@@ -50,6 +53,16 @@ class AsyncHyperBandScheduler(FIFOScheduler):
assert grace_period > 0, "grace_period must be positive!"
assert reduction_factor > 1, "Reduction Factor not valid!"
assert brackets > 0, "brackets must be positive!"
assert mode in ["min", "max"], "`mode` must be 'min' or 'max'!"
if reward_attr is not None:
mode = "max"
metric = reward_attr
logger.warning(
"`reward_attr` is deprecated and will be removed in a future "
"version of Tune. "
"Setting `metric={}` and `mode=max`.".format(reward_attr))
FIFOScheduler.__init__(self)
self._reduction_factor = reduction_factor
self._max_t = max_t
@@ -63,7 +76,11 @@ class AsyncHyperBandScheduler(FIFOScheduler):
]
self._counter = 0 # for
self._num_stopped = 0
self._reward_attr = reward_attr
self._metric = metric
if mode == "max":
self._metric_op = 1.
elif mode == "min":
self._metric_op = -1.
self._time_attr = time_attr
def on_trial_add(self, trial_runner, trial):
@@ -80,7 +97,7 @@ class AsyncHyperBandScheduler(FIFOScheduler):
else:
bracket = self._trial_info[trial.trial_id]
action = bracket.on_result(trial, result[self._time_attr],
result[self._reward_attr])
self._metric_op * result[self._metric])
if action == TrialScheduler.STOP:
self._num_stopped += 1
return action
@@ -88,7 +105,7 @@ class AsyncHyperBandScheduler(FIFOScheduler):
def on_trial_complete(self, trial_runner, trial, result):
bracket = self._trial_info[trial.trial_id]
bracket.on_result(trial, result[self._time_attr],
result[self._reward_attr])
self._metric_op * result[self._metric])
del self._trial_info[trial.trial_id]
def on_trial_remove(self, trial_runner, trial):
+30 -10
View File
@@ -43,8 +43,9 @@ class HyperBandScheduler(FIFOScheduler):
To use this implementation of HyperBand with Tune, all you need
to do is specify the max length of time a trial can run `max_t`, the time
units `time_attr`, and the name of the reported objective value
`reward_attr`. We automatically determine reasonable values for the other
units `time_attr`, the name of the reported objective value `metric`,
and if `metric` is to be maximized or minimized (`mode`).
We automatically determine reasonable values for the other
HyperBand parameters based on the given values.
For example, to limit trials to 10 minutes and early stop based on the
@@ -62,9 +63,10 @@ class HyperBandScheduler(FIFOScheduler):
Note that you can pass in something non-temporal such as
`training_iteration` as a measure of progress, the only requirement
is that the attribute should increase monotonically.
reward_attr (str): The training result objective value attribute. As
with `time_attr`, this may refer to any objective value. Stopping
metric (str): The training result objective value attribute. Stopping
procedures will use this attribute.
mode (str): One of {min, max}. Determines whether objective is
minimizing or maximizing the metric attribute.
max_t (int): max time units per trial. Trials will be stopped after
max_t time units (determined by time_attr) have passed.
The scheduler will terminate trials after this time has passed.
@@ -74,16 +76,28 @@ class HyperBandScheduler(FIFOScheduler):
def __init__(self,
time_attr="training_iteration",
reward_attr="episode_reward_mean",
reward_attr=None,
metric="episode_reward_mean",
mode="max",
max_t=81):
assert max_t > 0, "Max (time_attr) not valid!"
assert mode in ["min", "max"], "`mode` must be 'min' or 'max'!"
if reward_attr is not None:
mode = "max"
metric = reward_attr
logger.warning(
"`reward_attr` is deprecated and will be removed in a future "
"version of Tune. "
"Setting `metric={}` and `mode=max`.".format(reward_attr))
FIFOScheduler.__init__(self)
self._eta = 3
self._s_max_1 = 5
self._max_t_attr = max_t
# bracket max trials
self._get_n0 = lambda s: int(
np.ceil(self._s_max_1/(s+1) * self._eta**s))
np.ceil(self._s_max_1 / (s + 1) * self._eta**s))
# bracket initial iterations
self._get_r0 = lambda s: int((max_t * self._eta**(-s)))
self._hyperbands = [[]] # list of hyperband iterations
@@ -92,7 +106,11 @@ class HyperBandScheduler(FIFOScheduler):
# Tracks state for new trial add
self._state = {"bracket": None, "band_idx": 0}
self._num_stopped = 0
self._reward_attr = reward_attr
self._metric = metric
if mode == "max":
self._metric_op = 1.
elif mode == "min":
self._metric_op = -1.
self._time_attr = time_attr
def on_trial_add(self, trial_runner, trial):
@@ -173,7 +191,8 @@ class HyperBandScheduler(FIFOScheduler):
bracket.cleanup_full(trial_runner)
return TrialScheduler.STOP
good, bad = bracket.successive_halving(self._reward_attr)
good, bad = bracket.successive_halving(self._metric,
self._metric_op)
# kill bad trials
self._num_stopped += len(bad)
for t in bad:
@@ -322,7 +341,7 @@ class Bracket():
return len(self._live_trials) == self._n
def successive_halving(self, reward_attr):
def successive_halving(self, metric, metric_op):
assert self._halves > 0
self._halves -= 1
self._n /= self._eta
@@ -332,7 +351,8 @@ class Bracket():
self._r = int(min(self._r, self._max_t_attr - self._cumul_r))
self._cumul_r += self._r
sorted_trials = sorted(
self._live_trials, key=lambda t: self._live_trials[t][reward_attr])
self._live_trials,
key=lambda t: metric_op * self._live_trials[t][metric])
good, bad = sorted_trials[-self._n:], sorted_trials[:-self._n]
return good, bad
@@ -22,9 +22,10 @@ class MedianStoppingRule(FIFOScheduler):
Note that you can pass in something non-temporal such as
`training_iteration` as a measure of progress, the only requirement
is that the attribute should increase monotonically.
reward_attr (str): The training result objective value attribute. As
with `time_attr`, this may refer to any objective value that
is supposed to increase with time.
metric (str): The training result objective value attribute. Stopping
procedures will use this attribute.
mode (str): One of {min, max}. Determines whether objective is
minimizing or maximizing the metric attribute.
grace_period (float): Only stop trials at least this old in time.
The units are the same as the attribute named by `time_attr`.
min_samples_required (int): Min samples to compute median over.
@@ -37,18 +38,34 @@ class MedianStoppingRule(FIFOScheduler):
def __init__(self,
time_attr="time_total_s",
reward_attr="episode_reward_mean",
reward_attr=None,
metric="episode_reward_mean",
mode="max",
grace_period=60.0,
min_samples_required=3,
hard_stop=True,
verbose=True):
assert mode in ["min", "max"], "`mode` must be 'min' or 'max'!"
if reward_attr is not None:
mode = "max"
metric = reward_attr
logger.warning(
"`reward_attr` is deprecated and will be removed in a future "
"version of Tune. "
"Setting `metric={}` and `mode=max`.".format(reward_attr))
FIFOScheduler.__init__(self)
self._stopped_trials = set()
self._completed_trials = set()
self._results = collections.defaultdict(list)
self._grace_period = grace_period
self._min_samples_required = min_samples_required
self._reward_attr = reward_attr
self._metric = metric
if mode == "max":
self._metric_op = 1.
elif mode == "min":
self._metric_op = -1.
self._time_attr = time_attr
self._hard_stop = hard_stop
self._verbose = verbose
@@ -110,11 +127,9 @@ class MedianStoppingRule(FIFOScheduler):
results = self._results[trial]
# TODO(ekl) we could do interpolation to be more precise, but for now
# assume len(results) is large and the time diffs are roughly equal
return np.mean([
r[self._reward_attr] for r in results
if r[self._time_attr] <= t_max
])
return self._metric_op * np.mean(
[r[self._metric] for r in results if r[self._time_attr] <= t_max])
def _best_result(self, trial):
results = self._results[trial]
return max(r[self._reward_attr] for r in results)
return max(self._metric_op * r[self._metric] for r in results)
+25 -6
View File
@@ -120,9 +120,10 @@ class PopulationBasedTraining(FIFOScheduler):
Note that you can pass in something non-temporal such as
`training_iteration` as a measure of progress, the only requirement
is that the attribute should increase monotonically.
reward_attr (str): The training result objective value attribute. As
with `time_attr`, this may refer to any objective value. Stopping
metric (str): The training result objective value attribute. Stopping
procedures will use this attribute.
mode (str): One of {min, max}. Determines whether objective is
minimizing or maximizing the metric attribute.
perturbation_interval (float): Models will be considered for
perturbation at this interval of `time_attr`. Note that
perturbation incurs checkpoint overhead, so you shouldn't set this
@@ -149,7 +150,8 @@ class PopulationBasedTraining(FIFOScheduler):
Example:
>>> pbt = PopulationBasedTraining(
>>> time_attr="training_iteration",
>>> reward_attr="episode_reward_mean",
>>> metric="episode_reward_mean",
>>> mode="max",
>>> perturbation_interval=10, # every 10 `time_attr` units
>>> # (training_iterations in this case)
>>> hyperparam_mutations={
@@ -165,7 +167,9 @@ class PopulationBasedTraining(FIFOScheduler):
def __init__(self,
time_attr="time_total_s",
reward_attr="episode_reward_mean",
reward_attr=None,
metric="episode_reward_mean",
mode="max",
perturbation_interval=60.0,
hyperparam_mutations={},
resample_probability=0.25,
@@ -175,8 +179,23 @@ class PopulationBasedTraining(FIFOScheduler):
raise TuneError(
"You must specify at least one of `hyperparam_mutations` or "
"`custom_explore_fn` to use PBT.")
assert mode in ["min", "max"], "`mode` must be 'min' or 'max'!"
if reward_attr is not None:
mode = "max"
metric = reward_attr
logger.warning(
"`reward_attr` is deprecated and will be removed in a future "
"version of Tune. "
"Setting `metric={}` and `mode=max`.".format(reward_attr))
FIFOScheduler.__init__(self)
self._reward_attr = reward_attr
self._metric = metric
if mode == "max":
self._metric_op = 1.
elif mode == "min":
self._metric_op = -1.
self._time_attr = time_attr
self._perturbation_interval = perturbation_interval
self._hyperparam_mutations = hyperparam_mutations
@@ -199,7 +218,7 @@ class PopulationBasedTraining(FIFOScheduler):
if time - state.last_perturbation_time < self._perturbation_interval:
return TrialScheduler.CONTINUE # avoid checkpoint overhead
score = result[self._reward_attr]
score = self._metric_op * result[self._metric]
state.last_score = score
state.last_perturbation_time = time
lower_quantile, upper_quantile = self._quantiles()
+26 -6
View File
@@ -3,6 +3,7 @@ from __future__ import division
from __future__ import print_function
import copy
import logging
try: # Python 3 only -- needed for lint test.
import bayes_opt as byo
except ImportError:
@@ -10,6 +11,8 @@ except ImportError:
from ray.tune.suggest.suggestion import SuggestionAlgorithm
logger = logging.getLogger(__name__)
class BayesOptSearch(SuggestionAlgorithm):
"""A wrapper around BayesOpt to provide trial suggestions.
@@ -22,8 +25,9 @@ class BayesOptSearch(SuggestionAlgorithm):
this space which will be used to run trials.
max_concurrent (int): Number of maximum concurrent trials. Defaults
to 10.
reward_attr (str): The training result objective value attribute.
This refers to an increasing value.
metric (str): The training result objective value attribute.
mode (str): One of {min, max}. Determines whether objective is
minimizing or maximizing the metric attribute.
utility_kwargs (dict): Parameters to define the utility function. Must
provide values for the keys `kind`, `kappa`, and `xi`.
random_state (int): Used to initialize BayesOpt.
@@ -35,13 +39,15 @@ class BayesOptSearch(SuggestionAlgorithm):
>>> 'height': (-100, 100),
>>> }
>>> algo = BayesOptSearch(
>>> space, max_concurrent=4, reward_attr="neg_mean_loss")
>>> space, max_concurrent=4, metric="mean_loss", mode="min")
"""
def __init__(self,
space,
max_concurrent=10,
reward_attr="episode_reward_mean",
reward_attr=None,
metric="episode_reward_mean",
mode="max",
utility_kwargs=None,
random_state=1,
verbose=0,
@@ -52,8 +58,22 @@ class BayesOptSearch(SuggestionAlgorithm):
assert type(max_concurrent) is int and max_concurrent > 0
assert utility_kwargs is not None, (
"Must define arguments for the utiliy function!")
assert mode in ["min", "max"], "`mode` must be 'min' or 'max'!"
if reward_attr is not None:
mode = "max"
metric = reward_attr
logger.warning(
"`reward_attr` is deprecated and will be removed in a future "
"version of Tune. "
"Setting `metric={}` and `mode=max`.".format(reward_attr))
self._max_concurrent = max_concurrent
self._reward_attr = reward_attr
self._metric = metric
if mode == "max":
self._metric_op = 1.
elif mode == "min":
self._metric_op = -1.
self._live_trial_mapping = {}
self.optimizer = byo.BayesianOptimization(
@@ -85,7 +105,7 @@ class BayesOptSearch(SuggestionAlgorithm):
if result:
self.optimizer.register(
params=self._live_trial_mapping[trial_id],
target=result[self._reward_attr])
target=self._metric_op * result[self._metric])
del self._live_trial_mapping[trial_id]
+26 -6
View File
@@ -15,6 +15,8 @@ except ImportError:
from ray.tune.error import TuneError
from ray.tune.suggest.suggestion import SuggestionAlgorithm
logger = logging.getLogger(__name__)
class HyperOptSearch(SuggestionAlgorithm):
"""A wrapper around HyperOpt to provide trial suggestions.
@@ -30,8 +32,9 @@ class HyperOptSearch(SuggestionAlgorithm):
parameters generated in the variant generation process.
max_concurrent (int): Number of maximum concurrent trials. Defaults
to 10.
reward_attr (str): The training result objective value attribute.
This refers to an increasing value.
metric (str): The training result objective value attribute.
mode (str): One of {min, max}. Determines whether objective is
minimizing or maximizing the metric attribute.
points_to_evaluate (list): Initial parameter suggestions to be run
first. This is for when you already have some good parameters
you want hyperopt to run first to help the TPE algorithm
@@ -52,21 +55,38 @@ class HyperOptSearch(SuggestionAlgorithm):
>>> 'activation': 0, # The index of "relu"
>>> }]
>>> algo = HyperOptSearch(
>>> space, max_concurrent=4, reward_attr="neg_mean_loss",
>>> space, max_concurrent=4, metric="mean_loss", mode="min",
>>> points_to_evaluate=current_best_params)
"""
def __init__(self,
space,
max_concurrent=10,
reward_attr="episode_reward_mean",
reward_attr=None,
metric="episode_reward_mean",
mode="max",
points_to_evaluate=None,
**kwargs):
assert hpo is not None, "HyperOpt must be installed!"
from hyperopt.fmin import generate_trials_to_calculate
assert type(max_concurrent) is int and max_concurrent > 0
assert mode in ["min", "max"], "`mode` must be 'min' or 'max'!"
if reward_attr is not None:
mode = "max"
metric = reward_attr
logger.warning(
"`reward_attr` is deprecated and will be removed in a future "
"version of Tune. "
"Setting `metric={}` and `mode=max`.".format(reward_attr))
self._max_concurrent = max_concurrent
self._reward_attr = reward_attr
self._metric = metric
# hyperopt internally minimizes, so "max" => -1
if mode == "max":
self._metric_op = -1.
elif mode == "min":
self._metric_op = 1.
self.algo = hpo.tpe.suggest
self.domain = hpo.Domain(lambda spc: spc, space)
if points_to_evaluate is None:
@@ -151,7 +171,7 @@ class HyperOptSearch(SuggestionAlgorithm):
del self._live_trial_mapping[trial_id]
def _to_hyperopt_result(self, result):
return {"loss": -result[self._reward_attr], "status": "ok"}
return {"loss": self._metric_op * result[self._metric], "status": "ok"}
def _get_hyperopt_trial(self, trial_id):
if trial_id not in self._live_trial_mapping:
+29 -7
View File
@@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import logging
try:
import nevergrad as ng
except ImportError:
@@ -9,6 +10,8 @@ except ImportError:
from ray.tune.suggest.suggestion import SuggestionAlgorithm
logger = logging.getLogger(__name__)
class NevergradSearch(SuggestionAlgorithm):
"""A wrapper around Nevergrad to provide trial suggestions.
@@ -28,15 +31,16 @@ class NevergradSearch(SuggestionAlgorithm):
(see nevergrad v0.2.0+).
max_concurrent (int): Number of maximum concurrent trials. Defaults
to 10.
reward_attr (str): The training result objective value attribute.
This refers to an increasing value.
metric (str): The training result objective value attribute.
mode (str): One of {min, max}. Determines whether objective is
minimizing or maximizing the metric attribute.
Example:
>>> from nevergrad.optimization import optimizerlib
>>> instrumentation = 1
>>> optimizer = optimizerlib.OnePlusOne(instrumentation, budget=100)
>>> algo = NevergradSearch(optimizer, ["lr"], max_concurrent=4,
>>> reward_attr="neg_mean_loss")
>>> metric="mean_loss", mode="min")
Note:
In nevergrad v0.2.0+, optimizers can be instrumented.
@@ -49,7 +53,7 @@ class NevergradSearch(SuggestionAlgorithm):
>>> instrumentation = inst.Instrumentation(lr=lr)
>>> optimizer = optimizerlib.OnePlusOne(instrumentation, budget=100)
>>> algo = NevergradSearch(optimizer, None, max_concurrent=4,
>>> reward_attr="neg_mean_loss")
>>> metric="mean_loss", mode="min")
"""
@@ -57,13 +61,30 @@ class NevergradSearch(SuggestionAlgorithm):
optimizer,
parameter_names,
max_concurrent=10,
reward_attr="episode_reward_mean",
reward_attr=None,
metric="episode_reward_mean",
mode="max",
**kwargs):
assert ng is not None, "Nevergrad must be installed!"
assert type(max_concurrent) is int and max_concurrent > 0
assert mode in ["min", "max"], "`mode` must be 'min' or 'max'!"
if reward_attr is not None:
mode = "max"
metric = reward_attr
logger.warning(
"`reward_attr` is deprecated and will be removed in a future "
"version of Tune. "
"Setting `metric={}` and `mode=max`.".format(reward_attr))
self._max_concurrent = max_concurrent
self._parameters = parameter_names
self._reward_attr = reward_attr
self._metric = metric
# nevergrad.tell internally minimizes, so "max" => -1
if mode == "max":
self._metric_op = -1.
elif mode == "min":
self._metric_op = 1.
self._nevergrad_opt = optimizer
self._live_trial_mapping = {}
super(NevergradSearch, self).__init__(**kwargs)
@@ -119,7 +140,8 @@ class NevergradSearch(SuggestionAlgorithm):
"""
ng_trial_info = self._live_trial_mapping.pop(trial_id)
if result:
self._nevergrad_opt.tell(ng_trial_info, -result[self._reward_attr])
self._nevergrad_opt.tell(ng_trial_info,
self._metric_op * result[self._metric])
def _num_live_trials(self):
return len(self._live_trial_mapping)
+26 -6
View File
@@ -4,6 +4,7 @@ from __future__ import print_function
import copy
import os
import logging
try:
import sigopt as sgo
except ImportError:
@@ -11,6 +12,8 @@ except ImportError:
from ray.tune.suggest.suggestion import SuggestionAlgorithm
logger = logging.getLogger(__name__)
class SigOptSearch(SuggestionAlgorithm):
"""A wrapper around SigOpt to provide trial suggestions.
@@ -25,8 +28,9 @@ class SigOptSearch(SuggestionAlgorithm):
name (str): Name of experiment. Required by SigOpt.
max_concurrent (int): Number of maximum concurrent trials supported
based on the user's SigOpt plan. Defaults to 1.
reward_attr (str): The training result objective value attribute.
This refers to an increasing value.
metric (str): The training result objective value attribute.
mode (str): One of {min, max}. Determines whether objective is
minimizing or maximizing the metric attribute.
Example:
>>> space = [
@@ -49,21 +53,37 @@ class SigOptSearch(SuggestionAlgorithm):
>>> ]
>>> algo = SigOptSearch(
>>> space, name="SigOpt Example Experiment",
>>> max_concurrent=1, reward_attr="neg_mean_loss")
>>> max_concurrent=1, metric="mean_loss", mode="min")
"""
def __init__(self,
space,
name="Default Tune Experiment",
max_concurrent=1,
reward_attr="episode_reward_mean",
reward_attr=None,
metric="episode_reward_mean",
mode="max",
**kwargs):
assert sgo is not None, "SigOpt must be installed!"
assert type(max_concurrent) is int and max_concurrent > 0
assert "SIGOPT_KEY" in os.environ, \
"SigOpt API key must be stored as environ variable at SIGOPT_KEY"
assert mode in ["min", "max"], "`mode` must be 'min' or 'max'!"
if reward_attr is not None:
mode = "max"
metric = reward_attr
logger.warning(
"`reward_attr` is deprecated and will be removed in a future "
"version of Tune. "
"Setting `metric={}` and `mode=max`.".format(reward_attr))
self._max_concurrent = max_concurrent
self._reward_attr = reward_attr
self._metric = metric
if mode == "max":
self._metric_op = 1.
elif mode == "min":
self._metric_op = -1.
self._live_trial_mapping = {}
# Create a connection with SigOpt API, requires API key
@@ -108,7 +128,7 @@ class SigOptSearch(SuggestionAlgorithm):
if result:
self.conn.experiments(self.experiment.id).observations().create(
suggestion=self._live_trial_mapping[trial_id].id,
value=result[self._reward_attr],
value=self._metric_op * result[self._metric],
)
# Update the experiment object
self.experiment = self.conn.experiments(self.experiment.id).fetch()
+28 -6
View File
@@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import logging
try:
import skopt as sko
except ImportError:
@@ -9,6 +10,8 @@ except ImportError:
from ray.tune.suggest.suggestion import SuggestionAlgorithm
logger = logging.getLogger(__name__)
def _validate_warmstart(parameter_names, points_to_evaluate,
evaluated_rewards):
@@ -52,8 +55,9 @@ class SkOptSearch(SuggestionAlgorithm):
the dimension of the optimizer output.
max_concurrent (int): Number of maximum concurrent trials. Defaults
to 10.
reward_attr (str): The training result objective value attribute.
This refers to an increasing value.
metric (str): The training result objective value attribute.
mode (str): One of {min, max}. Determines whether objective is
minimizing or maximizing the metric attribute.
points_to_evaluate (list of lists): A list of points you'd like to run
first before sampling from the optimiser, e.g. these could be
parameter configurations you already know work well to help
@@ -73,7 +77,8 @@ class SkOptSearch(SuggestionAlgorithm):
>>> algo = SkOptSearch(optimizer,
>>> ["width", "height"],
>>> max_concurrent=4,
>>> reward_attr="neg_mean_loss",
>>> metric="mean_loss",
>>> mode="min",
>>> points_to_evaluate=current_best_params)
"""
@@ -81,7 +86,9 @@ class SkOptSearch(SuggestionAlgorithm):
optimizer,
parameter_names,
max_concurrent=10,
reward_attr="episode_reward_mean",
reward_attr=None,
metric="episode_reward_mean",
mode="max",
points_to_evaluate=None,
evaluated_rewards=None,
**kwargs):
@@ -91,6 +98,15 @@ class SkOptSearch(SuggestionAlgorithm):
assert type(max_concurrent) is int and max_concurrent > 0
_validate_warmstart(parameter_names, points_to_evaluate,
evaluated_rewards)
assert mode in ["min", "max"], "`mode` must be 'min' or 'max'!"
if reward_attr is not None:
mode = "max"
metric = reward_attr
logger.warning(
"`reward_attr` is deprecated and will be removed in a future "
"version of Tune. "
"Setting `metric={}` and `mode=max`.".format(reward_attr))
self._initial_points = []
if points_to_evaluate and evaluated_rewards:
@@ -99,7 +115,12 @@ class SkOptSearch(SuggestionAlgorithm):
self._initial_points = points_to_evaluate
self._max_concurrent = max_concurrent
self._parameters = parameter_names
self._reward_attr = reward_attr
self._metric = metric
# Skopt internally minimizes, so "max" => -1
if mode == "max":
self._metric_op = -1.
elif mode == "min":
self._metric_op = 1.
self._skopt_opt = optimizer
self._live_trial_mapping = {}
super(SkOptSearch, self).__init__(**kwargs)
@@ -131,7 +152,8 @@ class SkOptSearch(SuggestionAlgorithm):
"""
skopt_trial_info = self._live_trial_mapping.pop(trial_id)
if result:
self._skopt_opt.tell(skopt_trial_info, -result[self._reward_attr])
self._skopt_opt.tell(skopt_trial_info,
self._metric_op * result[self._metric])
def _num_live_trials(self):
return len(self._live_trial_mapping)
@@ -36,7 +36,8 @@ class ExperimentAnalysisSuite(unittest.TestCase):
def run_test_exp(self):
ahb = AsyncHyperBandScheduler(
time_attr="training_iteration",
reward_attr=self.metric,
metric=self.metric,
mode="max",
grace_period=5,
max_t=100)
+59 -28
View File
@@ -135,33 +135,43 @@ class EarlyStoppingSuite(unittest.TestCase):
rule.on_trial_result(None, t3, result(2, 260)),
TrialScheduler.PAUSE)
def testAlternateMetrics(self):
def result2(t, rew):
return dict(training_iteration=t, neg_mean_loss=rew)
def _test_metrics(self, result_func, metric, mode):
rule = MedianStoppingRule(
grace_period=0,
min_samples_required=1,
time_attr="training_iteration",
reward_attr="neg_mean_loss")
metric=metric,
mode=mode)
t1 = Trial("PPO") # mean is 450, max 900, t_max=10
t2 = Trial("PPO") # mean is 450, max 450, t_max=5
for i in range(10):
self.assertEqual(
rule.on_trial_result(None, t1, result2(i, i * 100)),
rule.on_trial_result(None, t1, result_func(i, i * 100)),
TrialScheduler.CONTINUE)
for i in range(5):
self.assertEqual(
rule.on_trial_result(None, t2, result2(i, 450)),
rule.on_trial_result(None, t2, result_func(i, 450)),
TrialScheduler.CONTINUE)
rule.on_trial_complete(None, t1, result2(10, 1000))
rule.on_trial_complete(None, t1, result_func(10, 1000))
self.assertEqual(
rule.on_trial_result(None, t2, result2(5, 450)),
rule.on_trial_result(None, t2, result_func(5, 450)),
TrialScheduler.CONTINUE)
self.assertEqual(
rule.on_trial_result(None, t2, result2(6, 0)),
rule.on_trial_result(None, t2, result_func(6, 0)),
TrialScheduler.CONTINUE)
def testAlternateMetrics(self):
def result2(t, rew):
return dict(training_iteration=t, neg_mean_loss=rew)
self._test_metrics(result2, "neg_mean_loss", "max")
def testAlternateMetricsMin(self):
def result2(t, rew):
return dict(training_iteration=t, mean_loss=-rew)
self._test_metrics(result2, "mean_loss", "min")
class _MockTrialExecutor(TrialExecutor):
def start_trial(self, trial, checkpoint_obj=None):
@@ -495,14 +505,9 @@ class HyperbandSuite(unittest.TestCase):
TrialScheduler.PAUSE,
sched.on_trial_result(mock_runner, t, result(new_units, 12)))
def testAlternateMetrics(self):
"""Checking that alternate metrics will pass."""
def result2(t, rew):
return dict(time_total_s=t, neg_mean_loss=rew)
def _test_metrics(self, result_func, metric, mode):
sched = HyperBandScheduler(
time_attr="time_total_s", reward_attr="neg_mean_loss")
time_attr="time_total_s", metric=metric, mode=mode)
stats = self.default_statistics()
for i in range(stats["max_trials"]):
@@ -518,13 +523,29 @@ class HyperbandSuite(unittest.TestCase):
# Provides results from 0 to 8 in order, keeping the last one running
for i, trl in enumerate(big_bracket.current_trials()):
action = sched.on_trial_result(runner, trl, result2(1, i))
action = sched.on_trial_result(runner, trl, result_func(1, i))
runner.process_action(trl, action)
new_length = len(big_bracket.current_trials())
self.assertEqual(action, TrialScheduler.CONTINUE)
self.assertEqual(new_length, self.downscale(current_length, sched))
def testAlternateMetrics(self):
"""Checking that alternate metrics will pass."""
def result2(t, rew):
return dict(time_total_s=t, neg_mean_loss=rew)
self._test_metrics(result2, "neg_mean_loss", "max")
def testAlternateMetricsMin(self):
"""Checking that alternate metrics will pass."""
def result2(t, rew):
return dict(time_total_s=t, mean_loss=-rew)
self._test_metrics(result2, "mean_loss", "min")
def testJumpingTime(self):
sched, mock_runner = self.schedulerSetup(81)
big_bracket = sched._hyperbands[0][-1]
@@ -1015,14 +1036,12 @@ class AsyncHyperBandSuite(unittest.TestCase):
scheduler.on_trial_result(None, t3, result(2, 260)),
TrialScheduler.STOP)
def testAlternateMetrics(self):
def result2(t, rew):
return dict(training_iteration=t, neg_mean_loss=rew)
def _test_metrics(self, result_func, metric, mode):
scheduler = AsyncHyperBandScheduler(
grace_period=1,
time_attr="training_iteration",
reward_attr="neg_mean_loss",
metric=metric,
mode=mode,
brackets=1)
t1 = Trial("PPO") # mean is 450, max 900, t_max=10
t2 = Trial("PPO") # mean is 450, max 450, t_max=5
@@ -1030,20 +1049,32 @@ class AsyncHyperBandSuite(unittest.TestCase):
scheduler.on_trial_add(None, t2)
for i in range(10):
self.assertEqual(
scheduler.on_trial_result(None, t1, result2(i, i * 100)),
scheduler.on_trial_result(None, t1, result_func(i, i * 100)),
TrialScheduler.CONTINUE)
for i in range(5):
self.assertEqual(
scheduler.on_trial_result(None, t2, result2(i, 450)),
scheduler.on_trial_result(None, t2, result_func(i, 450)),
TrialScheduler.CONTINUE)
scheduler.on_trial_complete(None, t1, result2(10, 1000))
scheduler.on_trial_complete(None, t1, result_func(10, 1000))
self.assertEqual(
scheduler.on_trial_result(None, t2, result2(5, 450)),
scheduler.on_trial_result(None, t2, result_func(5, 450)),
TrialScheduler.CONTINUE)
self.assertEqual(
scheduler.on_trial_result(None, t2, result2(6, 0)),
scheduler.on_trial_result(None, t2, result_func(6, 0)),
TrialScheduler.CONTINUE)
def testAlternateMetrics(self):
def result2(t, rew):
return dict(training_iteration=t, neg_mean_loss=rew)
self._test_metrics(result2, "neg_mean_loss", "max")
def testAlternateMetricsMin(self):
def result2(t, rew):
return dict(training_iteration=t, mean_loss=-rew)
self._test_metrics(result2, "mean_loss", "min")
if __name__ == "__main__":
unittest.main(verbosity=2)