Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tune] Support initial parameters for SkOpt search algorithm #4341

Merged
merged 15 commits into from
Mar 17, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion python/ray/tune/examples/skopt_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,23 @@ def easy_objective(config, reporter):
}
}
optimizer = Optimizer([(0, 20), (-100, 100)])
previously_run_params = [[10, 0], [15, -20]]
known_rewards = [-189, -1144]
algo = SkOptSearch(
optimizer, ["width", "height"],
max_concurrent=4,
reward_attr="neg_mean_loss")
reward_attr="neg_mean_loss",
points_to_evaluate=previously_run_params,
evaluated_rewards=known_rewards)
scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss")
run_experiments(config, search_alg=algo, scheduler=scheduler)

# Now run the experiment without known rewards

algo = SkOptSearch(
optimizer, ["width", "height"],
max_concurrent=4,
reward_attr="neg_mean_loss",
points_to_evaluate=previously_run_params)
scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss")
run_experiments(config, search_alg=algo, scheduler=scheduler)
66 changes: 63 additions & 3 deletions python/ray/tune/suggest/skopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,38 @@
from ray.tune.suggest.suggestion import SuggestionAlgorithm


def _validate_warmstart(parameter_names, points_to_evaluate,
evaluated_rewards):
dimension = len(parameter_names)
if points_to_evaluate:
if not isinstance(points_to_evaluate, list):
raise TypeError(
"points_to_evaluate expected to be a list, got {}.".format(
type(points_to_evaluate)))
for point in points_to_evaluate:
if not isinstance(point, list):
raise TypeError(
"points_to_evaluate expected to include list, got {}.".
format(point))

if not len(points_to_evaluate) == dimension:
raise ValueError(
"Dim of points_to_evaluate {}".format(points_to_evaluate) +
" and parameter_names {}".format(parameter_names) +
" do not match.")

if points_to_evaluate and evaluated_rewards:
if not isinstance(evaluated_rewards, list):
raise TypeError(
"evaluated_rewards expected to be a list, got {}.".format(
type(evaluated_rewards)))
if not len(evaluated_rewards) == dimension:
raise ValueError(
"Dim of evaluated_rewards {}".format(evaluated_rewards) +
" and parameter_names {}".format(parameter_names) +
" do not match.")


class SkOptSearch(SuggestionAlgorithm):
"""A wrapper around skopt to provide trial suggestions.

Expand All @@ -24,10 +56,22 @@ class SkOptSearch(SuggestionAlgorithm):
to 10.
reward_attr (str): The training result objective value attribute.
This refers to an increasing value.
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
the optimiser select good values. Each point is a list of the
parameters using the order definition given by parameter_names.
evaluated_rewards (list): If you have previously evaluated the
parameters passed in as points_to_evaluate you can avoid
re-running those trials by passing in the reward attributes
as a list so the optimiser can be told the results without
needing to re-compute the trial. Must be the same length as
points_to_evaluate. (See tune/examples/skopt_example.py)

Example:
>>> from skopt import Optimizer
>>> optimizer = Optimizer([(0,20),(-100,100)])
>>> current_best_params = [[10, 0], [15, -20]]
>>> config = {
>>> "my_exp": {
>>> "run": "exp",
Expand All @@ -38,20 +82,32 @@ class SkOptSearch(SuggestionAlgorithm):
>>> }
>>> }
>>> algo = SkOptSearch(optimizer,
>>> ["width", "height"], max_concurrent=4,
>>> reward_attr="neg_mean_loss")
>>> ["width", "height"],
>>> max_concurrent=4,
>>> reward_attr="neg_mean_loss",
>>> points_to_evaluate=current_best_params)
"""

def __init__(self,
optimizer,
parameter_names,
max_concurrent=10,
reward_attr="episode_reward_mean",
points_to_evaluate=None,
evaluated_rewards=None,
**kwargs):
assert skopt is not None, """skopt must be installed!
You can install Skopt with the command:
`pip install scikit-optimize`."""
assert type(max_concurrent) is int and max_concurrent > 0
_validate_warmstart(parameter_names, points_to_evaluate,
evaluated_rewards)

self._initial_points = []
if points_to_evaluate and evaluated_rewards:
markgoodhead marked this conversation as resolved.
Show resolved Hide resolved
optimizer.tell(points_to_evaluate, evaluated_rewards)
elif points_to_evaluate:
self._initial_points = points_to_evaluate
self._max_concurrent = max_concurrent
self._parameters = parameter_names
self._reward_attr = reward_attr
Expand All @@ -62,7 +118,11 @@ def __init__(self,
def _suggest(self, trial_id):
if self._num_live_trials() >= self._max_concurrent:
return None
suggested_config = self._skopt_opt.ask()
if self._initial_points:
suggested_config = self._initial_points[0]
del self._initial_points[0]
else:
suggested_config = self._skopt_opt.ask()
self._live_trial_mapping[trial_id] = suggested_config
return dict(zip(self._parameters, suggested_config))

Expand Down