diff --git a/python/ray/tune/suggest/nevergrad.py b/python/ray/tune/suggest/nevergrad.py index 901ba4978..67cb3db55 100644 --- a/python/ray/tune/suggest/nevergrad.py +++ b/python/ray/tune/suggest/nevergrad.py @@ -1,6 +1,6 @@ import logging import pickle -from typing import Dict, Optional, Union +from typing import Dict, Optional, Union, List, Sequence from ray.tune.sample import Categorical, Domain, Float, Integer, LogUniform, \ Quantized @@ -48,6 +48,11 @@ class NevergradSearch(Searcher): 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 + make better suggestions for future parameters. Needs to be + a list of dict of hyperopt-named variables. use_early_stopped_trials: Deprecated. max_concurrent: Deprecated. @@ -63,10 +68,17 @@ class NevergradSearch(Searcher): "activation": tune.choice(["relu", "tanh"]) } + current_best_params = [{ + "width": 10, + "height": 0, + "activation": relu", + }] + ng_search = NevergradSearch( optimizer=ng.optimizers.OnePlusOne, metric="mean_loss", - mode="min") + mode="min", + points_to_evaluate=current_best_params) run(my_trainable, config=config, search_alg=ng_search) @@ -99,6 +111,7 @@ class NevergradSearch(Searcher): metric: Optional[str] = None, mode: Optional[str] = None, max_concurrent: Optional[int] = None, + points_to_evaluate: Optional[List[Dict]] = None, **kwargs): assert ng is not None, """Nevergrad must be installed! You can install Nevergrad with the command: @@ -113,6 +126,16 @@ class NevergradSearch(Searcher): self._opt_factory = None self._nevergrad_opt = None + if points_to_evaluate is None: + self._points_to_evaluate = None + elif not isinstance(points_to_evaluate, Sequence): + raise ValueError( + f"Invalid object type passed for `points_to_evaluate`: " + "{type(points_to_evaluate)}. " + f"Please pass a list of points (dictionaries) instead.") + else: + self._points_to_evaluate = list(points_to_evaluate) + if isinstance(space, dict) and space: resolved_vars, domain_vars, grid_vars = parse_spec_vars(space) if domain_vars or grid_vars: @@ -204,7 +227,13 @@ class NevergradSearch(Searcher): if self.max_concurrent: if len(self._live_trial_mapping) >= self.max_concurrent: return None + + if self._points_to_evaluate is not None: + if len(self._points_to_evaluate) > 0: + point_to_evaluate = self._points_to_evaluate.pop(0) + self._nevergrad_opt.suggest(point_to_evaluate) suggested_config = self._nevergrad_opt.ask() + self._live_trial_mapping[trial_id] = suggested_config # in v0.2.0+, output of ask() is a Candidate, # with fields args and kwargs diff --git a/python/ray/tune/tests/test_sample.py b/python/ray/tune/tests/test_sample.py index af79684fe..4f7b4a83d 100644 --- a/python/ray/tune/tests/test_sample.py +++ b/python/ray/tune/tests/test_sample.py @@ -567,6 +567,49 @@ class SearchSpaceTest(unittest.TestCase): self.assertTrue(5 <= config["a"] <= 6) self.assertTrue(8 <= config["b"] <= 9) + def testNevergradBestParams(self): + from ray.tune.suggest.nevergrad import NevergradSearch + import nevergrad as ng + + config = { + "metric": tune.sample.Categorical([1, 2, 3, 4]).uniform(), + "a": tune.sample.Categorical(["t1", "t2", "t3", "t4"]).uniform(), + "b": tune.sample.Integer(0, 5), + "c": tune.sample.Float(1e-4, 1e-1).loguniform() + } + + best_params = [{ + "metric": 1, + "a": "t1", + "b": 1, + "c": 1e-1 + }, { + "metric": 2, + "a": "t2", + "b": 2, + "c": 1e-2 + }] + + searcher = NevergradSearch( + optimizer=ng.optimizers.OnePlusOne, points_to_evaluate=best_params) + analysis = tune.run( + _mock_objective, + config=config, + metric="metric", + mode="max", + search_alg=searcher, + num_samples=5) + + for i in range(len(best_params)): + trial_config = analysis.trials[i].config + trial_config_dict = { + "metric": trial_config["metric"], + "a": trial_config["a"], + "b": trial_config["b"], + "c": trial_config["c"] + } + self.assertDictEqual(trial_config_dict, best_params[i]) + def testConvertOptuna(self): from ray.tune.suggest.optuna import OptunaSearch, param from optuna.samplers import RandomSampler