diff --git a/doc/source/tune-examples.rst b/doc/source/tune-examples.rst index 65b419c33..f11cd1b86 100644 --- a/doc/source/tune-examples.rst +++ b/doc/source/tune-examples.rst @@ -14,7 +14,7 @@ General Examples - `async_hyperband_example `__: Example of using a Trainable class with AsyncHyperBandScheduler. - `hyperband_example `__: - Example of using a Trainable class with HyperBandScheduler. Also uses the Experiment class API for specifying the experiment configuration. + Example of using a Trainable class with HyperBandScheduler. - `hyperopt_example `__: Optimizes a basic function using the function-based API and the HyperOptSearch (SearchAlgorithm wrapper for HyperOpt TPE). Also uses the AsyncHyperBandScheduler. diff --git a/doc/source/tune-schedulers.rst b/doc/source/tune-schedulers.rst index 74399836e..4c6919fb3 100644 --- a/doc/source/tune-schedulers.rst +++ b/doc/source/tune-schedulers.rst @@ -5,7 +5,7 @@ By default, Tune schedules trials in serial order with the ``FIFOScheduler`` cla .. code-block:: python - tune.run_experiments({...}, scheduler=AsyncHyperBandScheduler()) + tune.run( ... , scheduler=AsyncHyperBandScheduler()) Tune includes distributed implementations of early stopping algorithms such as `Median Stopping Rule `__, `HyperBand `__, and an `asynchronous version of HyperBand `__. These algorithms are very resource efficient and can outperform Bayesian Optimization methods in `many cases `__. Currently, all schedulers take in a ``reward_attr``, which is assumed to be maximized. @@ -19,7 +19,7 @@ Current Available Trial Schedulers: Population Based Training (PBT) ------------------------------- -Tune includes a distributed implementation of `Population Based Training (PBT) `__. This can be enabled by setting the ``scheduler`` parameter of ``run_experiments``, e.g. +Tune includes a distributed implementation of `Population Based Training (PBT) `__. This can be enabled by setting the ``scheduler`` parameter of ``tune.run``, e.g. .. code-block:: python @@ -32,7 +32,7 @@ Tune includes a distributed implementation of `Population Based Training (PBT) < "alpha": lambda: random.uniform(0.0, 1.0), ... }) - run_experiments({...}, scheduler=pbt_scheduler) + tune.run( ... , scheduler=pbt_scheduler) When the PBT scheduler is enabled, each trial variant is treated as a member of the population. Periodically, top-performing trials are checkpointed (this requires your Trainable to support `checkpointing `__). Low-performing trials clone the checkpoints of top performers and perturb the configurations in the hope of discovering an even better variation. @@ -46,7 +46,7 @@ You can run this `toy PBT example `__ scheduler can be used by setting the ``scheduler`` parameter of ``run_experiments``, e.g. +The `asynchronous version of HyperBand `__ scheduler can be used by setting the ``scheduler`` parameter of ``tune.run``, e.g. .. code-block:: python @@ -57,7 +57,7 @@ The `asynchronous version of HyperBand `__. **We recommend using this over the standard HyperBand scheduler.** @@ -73,7 +73,7 @@ Tune also implements the `standard version of HyperBand `__. The progress of one such HyperBand run is shown below. @@ -143,7 +143,7 @@ The Median Stopping Rule implements the simple strategy of stopping a trial if i .. code-block:: python - run_experiments({...}, scheduler=MedianStoppingRule()) + tune.run( ... , scheduler=MedianStoppingRule()) .. autoclass:: ray.tune.schedulers.MedianStoppingRule :noindex: diff --git a/doc/source/tune-searchalg.rst b/doc/source/tune-searchalg.rst index 546fc5ee1..f670727af 100644 --- a/doc/source/tune-searchalg.rst +++ b/doc/source/tune-searchalg.rst @@ -7,7 +7,7 @@ You can utilize these search algorithms as follows: .. code-block:: python - run_experiments(experiments, search_alg=SearchAlgorithm(...)) + tune.run(my_function, search_alg=SearchAlgorithm(...)) Currently, Tune offers the following search algorithms (and library integrations): @@ -22,7 +22,7 @@ Currently, Tune offers the following search algorithms (and library integrations Variant Generation (Grid Search/Random Search) ---------------------------------------------- -By default, Tune uses the `default search space and variant generation process `__ to create and queue trials. This supports random search and grid search as specified by the ``config`` parameter of the Experiment. +By default, Tune uses the `default search space and variant generation process `__ to create and queue trials. This supports random search and grid search as specified by the ``config`` parameter of ``tune.run``. .. autoclass:: ray.tune.suggest.BasicVariantGenerator :show-inheritance: @@ -46,7 +46,7 @@ This algorithm requires `setting a search space and defining a utility function .. code-block:: python - run_experiments(experiment_config, search_alg=BayesOptSearch(bayesopt_space, utility_kwargs=utility_params, ... )) + tune.run(... , search_alg=BayesOptSearch(bayesopt_space, utility_kwargs=utility_params, ... )) An example of this can be found in `bayesopt_example.py `__. @@ -69,7 +69,7 @@ This algorithm requires using the `HyperOpt search space specification `__. @@ -98,7 +98,7 @@ This algorithm requires using the `SigOpt experiment and space specification `__. @@ -123,7 +123,7 @@ This algorithm requires using an optimizer provided by ``nevergrad``, of which t .. code-block:: python - run_experiments(experiment_config, search_alg=NevergradSearch(optimizer, parameter_names, ... )) + tune.run(... , search_alg=NevergradSearch(optimizer, parameter_names, ... )) An example of this can be found in `nevergrad_example.py `__. @@ -147,7 +147,7 @@ This algorithm requires using the `Scikit-Optimize ask and tell interface `__. diff --git a/doc/source/tune-usage.rst b/doc/source/tune-usage.rst index 82ae5a33b..cbac45287 100644 --- a/doc/source/tune-usage.rst +++ b/doc/source/tune-usage.rst @@ -62,43 +62,12 @@ Both the Trainable and function-based API will have `autofilled metrics `__ section on how to specify and execute your training. -Specifying Experiments -~~~~~~~~~~~~~~~~~~~~~~ +Launching an Experiment +~~~~~~~~~~~~~~~~~~~~~~~ -There are two ways to specify the configuration for an experiment - one via Python and one via JSON. +Tune provides a ``run`` function that generates and runs the trials. -**Using Python**: specify a configuration is to create an Experiment object. - -.. autoclass:: ray.tune.Experiment - :noindex: - -An example of this can be found in `hyperband_example.py `__. - -**Using JSON/Dict**: This uses the same fields as the ``ray.tune.Experiment``, except the experiment name is the key of the top level -dictionary. Tune will convert the dict into an ``ray.tune.Experiment`` object. - -.. code-block:: python - - experiment_spec = { - "my_experiment_name": { - "run": my_func, - "stop": { "mean_accuracy": 100 }, - "config": { - "alpha": tune.grid_search([0.2, 0.4, 0.6]), - "beta": tune.grid_search([1, 2]), - }, - "resources_per_trial": { "cpu": 1, "gpu": 0 }, - "num_samples": 10, - "local_dir": "~/ray_results", - "upload_dir": "s3://your_bucket/path", - "checkpoint_freq": 10, - "max_failures": 2 - } - } - -Tune provides a ``run_experiments`` function that generates and runs the trials. - -.. autofunction:: ray.tune.run_experiments +.. autofunction:: ray.tune.run :noindex: This function will report status on the command line until all Trials stop: @@ -117,14 +86,11 @@ This function will report status on the command line until all Trials stop: - train_func_5_lr=0.6,momentum=2: TERMINATED [pid=6809], 10 s, 2164 ts, 100 acc -An example of this can be found in `async_hyperband_example.py `__. - - Custom Trial Names ~~~~~~~~~~~~~~~~~~ To specify custom trial names, you can pass use the ``trial_name_creator`` argument -in the Experiment object. This takes a function with the following signature, and +to `tune.run`. This takes a function with the following signature, and be sure to wrap it with `tune.function`: .. code-block:: python @@ -139,9 +105,9 @@ be sure to wrap it with `tune.function`: """ return str(trial) - exp = Experiment( + tune.run( + MyTrainableClass, name="hyperband_test", - run=MyTrainableClass, num_samples=1, trial_name_creator=tune.function(trial_name_string) ) @@ -166,19 +132,18 @@ The following shows grid search over two nested parameters combined with random .. code-block:: python :emphasize-lines: 4-11 - run_experiments({ - "my_experiment_name": { - "run": my_trainable, - "config": { - "alpha": tune.sample_from(lambda spec: np.random.uniform(100)), - "beta": tune.sample_from(lambda spec: spec.config.alpha * np.random.normal()), - "nn_layers": [ - tune.grid_search([16, 64, 256]), - tune.grid_search([16, 64, 256]), - ], - } + tune.run( + my_trainable, + name="my_trainable", + config={ + "alpha": tune.sample_from(lambda spec: np.random.uniform(100)), + "beta": tune.sample_from(lambda spec: spec.config.alpha * np.random.normal()), + "nn_layers": [ + tune.grid_search([16, 64, 256]), + tune.grid_search([16, 64, 256]), + ], } - }) + ) .. note:: @@ -194,22 +159,21 @@ By default, each random variable and grid search point is sampled once. To take .. code-block:: python :emphasize-lines: 12 - run_experiments({ - "my_experiment_name": { - "run": my_trainable, - "config": { - "alpha": tune.sample_from(lambda spec: np.random.uniform(100)), - "beta": tune.sample_from(lambda spec: spec.config.alpha * np.random.normal()), - "nn_layers": [ - tune.grid_search([16, 64, 256]), - tune.grid_search([16, 64, 256]), - ], - }, - "num_samples": 10 - } - }) + tune.run( + my_trainable, + name="my_trainable", + config={ + "alpha": tune.sample_from(lambda spec: np.random.uniform(100)), + "beta": tune.sample_from(lambda spec: spec.config.alpha * np.random.normal()), + "nn_layers": [ + tune.grid_search([16, 64, 256]), + tune.grid_search([16, 64, 256]), + ], + }, + num_samples=10 + ) -E.g. in the above, ``"num_samples": 10`` repeats the 3x3 grid search 10 times, for a total of 90 trials, each with randomly sampled values of ``alpha`` and ``beta``. +E.g. in the above, ``num_samples=10`` repeats the 3x3 grid search 10 times, for a total of 90 trials, each with randomly sampled values of ``alpha`` and ``beta``. Using GPUs (Resource Allocation) @@ -228,16 +192,15 @@ If your trainable function / class creates further Ray actors or tasks that also .. code-block:: python :emphasize-lines: 4-8 - run_experiments({ - "my_experiment_name": { - "run": my_trainable, - "resources_per_trial": { - "cpu": 1, - "gpu": 1, - "extra_gpu": 4 - } + tune.run( + my_trainable, + name="my_trainable", + resources_per_trial={ + "cpu": 1, + "gpu": 1, + "extra_gpu": 4 } - }) + ) Trial Checkpointing @@ -268,18 +231,16 @@ For TensorFlow model training, this would look something like this `(full tensor return self.saver.restore(self.sess, path) -Additionally, checkpointing can be used to provide fault-tolerance for experiments. This can be enabled by setting ``checkpoint_freq: N`` and ``max_failures: M`` to checkpoint trials every *N* iterations and recover from up to *M* crashes per trial, e.g.: +Additionally, checkpointing can be used to provide fault-tolerance for experiments. This can be enabled by setting ``checkpoint_freq=N`` and ``max_failures=M`` to checkpoint trials every *N* iterations and recover from up to *M* crashes per trial, e.g.: .. code-block:: python :emphasize-lines: 4,5 - run_experiments({ - "my_experiment_name": { - "run": my_trainable - "checkpoint_freq": 10, - "max_failures": 5, - }, - }) + tune.run( + my_trainable, + checkpoint_freq=10, + max_failures=5, + ) The checkpoint_freq may not coincide with the exact end of an experiment. If you want a checkpoint to be created at the end of a trial, you can additionally set the checkpoint_at_end to True. An example is shown below: @@ -287,14 +248,13 @@ of a trial, you can additionally set the checkpoint_at_end to True. An example i .. code-block:: python :emphasize-lines: 5 - run_experiments({ - "my_experiment_name": { - "run": my_trainable - "checkpoint_freq": 10, - "checkpoint_at_end": True, - "max_failures": 5, - }, - }) + tune.run( + my_trainable, + checkpoint_freq=10, + checkpoint_at_end=True, + max_failures=5, + ) + Recovering From Failures (Experimental) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -307,13 +267,12 @@ E.g.: .. code-block:: python - run_experiments({ - "my_experiment_name": { - "run": my_trainable - "checkpoint_freq": 10, - "local_dir": "~/path/to/results" - }, - }, resume=True) + tune.run( + my_trainable, + checkpoint_freq=10, + local_dir="~/path/to/results", + resume=True + ) Upon a second run, this will restore the entire experiment state from ``~/path/to/results/my_experiment_name``. Importantly, any changes to the experiment specification upon resume will be ignored. @@ -329,7 +288,7 @@ You often will want to compute a large object (e.g., training data, model weight .. code-block:: python import ray - from ray.tune import run_experiments + from ray import tune from ray.tune.util import pin_in_object_store, get_pinned_object import numpy as np @@ -343,11 +302,8 @@ You often will want to compute a large object (e.g., training data, model weight X = get_pinned_object(X_id) # use X - run_experiments({ - "my_experiment_name": { - "run": f - } - }) + tune.run(f) + Auto-Filled Results ------------------- @@ -411,16 +367,15 @@ Finally, to view the results with a `parallel coordinates visualization `__ for massive parallelism. diff --git a/python/ray/tune/__init__.py b/python/ray/tune/__init__.py index 8d8ae1179..b1d865338 100644 --- a/python/ray/tune/__init__.py +++ b/python/ray/tune/__init__.py @@ -3,7 +3,7 @@ from __future__ import division from __future__ import print_function from ray.tune.error import TuneError -from ray.tune.tune import run_experiments +from ray.tune.tune import run_experiments, run from ray.tune.experiment import Experiment from ray.tune.registry import register_env, register_trainable from ray.tune.trainable import Trainable @@ -11,6 +11,6 @@ from ray.tune.suggest import grid_search, function, sample_from __all__ = [ "Trainable", "TuneError", "grid_search", "register_env", - "register_trainable", "run_experiments", "Experiment", "function", + "register_trainable", "run", "run_experiments", "Experiment", "function", "sample_from" ] diff --git a/python/ray/tune/examples/async_hyperband_example.py b/python/ray/tune/examples/async_hyperband_example.py index a2e4b63e4..1569ced23 100644 --- a/python/ray/tune/examples/async_hyperband_example.py +++ b/python/ray/tune/examples/async_hyperband_example.py @@ -12,7 +12,7 @@ import random import numpy as np import ray -from ray.tune import Trainable, run_experiments, sample_from +from ray.tune import Trainable, run, sample_from from ray.tune.schedulers import AsyncHyperBandScheduler @@ -63,24 +63,21 @@ if __name__ == "__main__": grace_period=5, max_t=100) - run_experiments( - { - "asynchyperband_test": { - "run": MyTrainableClass, - "stop": { - "training_iteration": 1 if args.smoke_test else 99999 - }, - "num_samples": 20, - "resources_per_trial": { - "cpu": 1, - "gpu": 0 - }, - "config": { - "width": sample_from( - lambda spec: 10 + int(90 * random.random())), - "height": sample_from( - lambda spec: int(100 * random.random())), - }, - } - }, - scheduler=ahb) + run(MyTrainableClass, + name="asynchyperband_test", + scheduler=ahb, + **{ + "stop": { + "training_iteration": 1 if args.smoke_test else 99999 + }, + "num_samples": 20, + "resources_per_trial": { + "cpu": 1, + "gpu": 0 + }, + "config": { + "width": sample_from( + lambda spec: 10 + int(90 * random.random())), + "height": sample_from(lambda spec: int(100 * random.random())), + }, + }) diff --git a/python/ray/tune/examples/bayesopt_example.py b/python/ray/tune/examples/bayesopt_example.py index 5f543612f..c18240333 100644 --- a/python/ray/tune/examples/bayesopt_example.py +++ b/python/ray/tune/examples/bayesopt_example.py @@ -7,7 +7,7 @@ from __future__ import division from __future__ import print_function import ray -from ray.tune import run_experiments, register_trainable +from ray.tune import run from ray.tune.schedulers import AsyncHyperBandScheduler from ray.tune.suggest import BayesOptSearch @@ -32,20 +32,15 @@ if __name__ == "__main__": args, _ = parser.parse_known_args() ray.init() - register_trainable("exp", easy_objective) - space = {'width': (0, 20), 'height': (-100, 100)} config = { - "my_exp": { - "run": "exp", - "num_samples": 10 if args.smoke_test else 1000, - "config": { - "iterations": 100, - }, - "stop": { - "timesteps_total": 100 - }, + "num_samples": 10 if args.smoke_test else 1000, + "config": { + "iterations": 100, + }, + "stop": { + "timesteps_total": 100 } } algo = BayesOptSearch( @@ -58,4 +53,8 @@ if __name__ == "__main__": "xi": 0.0 }) scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss") - run_experiments(config, search_alg=algo, scheduler=scheduler) + run(easy_objective, + name="my_exp", + search_alg=algo, + scheduler=scheduler, + **config) diff --git a/python/ray/tune/examples/genetic_example.py b/python/ray/tune/examples/genetic_example.py index 357fcdb61..09a292be7 100644 --- a/python/ray/tune/examples/genetic_example.py +++ b/python/ray/tune/examples/genetic_example.py @@ -7,7 +7,7 @@ from __future__ import division from __future__ import print_function import ray -from ray.tune import run_experiments, register_trainable +from ray.tune import run from ray.tune.schedulers import AsyncHyperBandScheduler from ray.tune.automl import GeneticSearch from ray.tune.automl import ContinuousSpace, DiscreteSpace, SearchSpace @@ -36,8 +36,6 @@ if __name__ == "__main__": args, _ = parser.parse_known_args() ray.init() - register_trainable("exp", michalewicz_function) - space = SearchSpace({ ContinuousSpace('x1', 0, 4, 100), ContinuousSpace('x2', -2, 2, 100), @@ -46,18 +44,15 @@ if __name__ == "__main__": DiscreteSpace('x5', [-1, 0, 1, 2, 3]), }) - config = { - "my_exp": { - "run": "exp", - "stop": { - "training_iteration": 100 - }, - } - } + config = {"stop": {"training_iteration": 100}} algo = GeneticSearch( space, 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") - run_experiments(config, search_alg=algo, scheduler=scheduler) + run(michalewicz_function, + name="my_exp", + search_alg=algo, + scheduler=scheduler, + **config) diff --git a/python/ray/tune/examples/hyperband_example.py b/python/ray/tune/examples/hyperband_example.py index d403a0e0f..d5ad24dbc 100755 --- a/python/ray/tune/examples/hyperband_example.py +++ b/python/ray/tune/examples/hyperband_example.py @@ -12,7 +12,7 @@ import random import numpy as np import ray -from ray.tune import Trainable, run_experiments, Experiment, sample_from +from ray.tune import Trainable, run, Experiment, sample_from from ray.tune.schedulers import HyperBandScheduler @@ -71,4 +71,4 @@ if __name__ == "__main__": "height": sample_from(lambda spec: int(100 * random.random())) }) - run_experiments(exp, scheduler=hyperband) + run(exp, scheduler=hyperband) diff --git a/python/ray/tune/examples/hyperopt_example.py b/python/ray/tune/examples/hyperopt_example.py index 0dc394c2d..54fe3005d 100644 --- a/python/ray/tune/examples/hyperopt_example.py +++ b/python/ray/tune/examples/hyperopt_example.py @@ -7,7 +7,7 @@ from __future__ import division from __future__ import print_function import ray -from ray.tune import run_experiments, register_trainable +from ray.tune import run from ray.tune.schedulers import AsyncHyperBandScheduler from ray.tune.suggest import HyperOptSearch @@ -35,8 +35,6 @@ if __name__ == "__main__": args, _ = parser.parse_known_args() ray.init() - register_trainable("exp", easy_objective) - space = { 'width': hp.uniform('width', 0, 20), 'height': hp.uniform('height', -100, 100), @@ -57,16 +55,13 @@ if __name__ == "__main__": ] config = { - "my_exp": { - "run": "exp", - "num_samples": 10 if args.smoke_test else 1000, - "config": { - "iterations": 100, - }, - "stop": { - "timesteps_total": 100 - }, - } + "num_samples": 10 if args.smoke_test else 1000, + "config": { + "iterations": 100, + }, + "stop": { + "timesteps_total": 100 + }, } algo = HyperOptSearch( space, @@ -74,4 +69,4 @@ if __name__ == "__main__": reward_attr="neg_mean_loss", points_to_evaluate=current_best_params) scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss") - run_experiments(config, search_alg=algo, scheduler=scheduler) + run(easy_objective, search_alg=algo, scheduler=scheduler, **config) diff --git a/python/ray/tune/examples/logging_example.py b/python/ray/tune/examples/logging_example.py index f120ab10d..5c42d5687 100755 --- a/python/ray/tune/examples/logging_example.py +++ b/python/ray/tune/examples/logging_example.py @@ -13,7 +13,7 @@ import numpy as np import ray from ray import tune -from ray.tune import Trainable, run_experiments, Experiment +from ray.tune import Trainable, run, Experiment class TestLogger(tune.logger.Logger): @@ -74,4 +74,4 @@ if __name__ == "__main__": "height": tune.sample_from(lambda spec: int(100 * random.random())) }) - trials = run_experiments(exp) + trials = run(exp) diff --git a/python/ray/tune/examples/mnist_pytorch.py b/python/ray/tune/examples/mnist_pytorch.py index ee23297d5..0bfdaaf9e 100644 --- a/python/ray/tune/examples/mnist_pytorch.py +++ b/python/ray/tune/examples/mnist_pytorch.py @@ -165,28 +165,28 @@ if __name__ == "__main__": reward_attr="neg_mean_loss", max_t=400, grace_period=20) - tune.register_trainable("train_mnist", - lambda cfg, rprtr: train_mnist(args, cfg, rprtr)) - tune.run_experiments( - { - "exp": { - "stop": { - "mean_accuracy": 0.98, - "training_iteration": 1 if args.smoke_test else 20 - }, - "resources_per_trial": { - "cpu": 3, - "gpu": int(not args.no_cuda) - }, - "run": "train_mnist", - "num_samples": 1 if args.smoke_test else 10, - "config": { - "lr": tune.sample_from( - lambda spec: np.random.uniform(0.001, 0.1)), - "momentum": tune.sample_from( - lambda spec: np.random.uniform(0.1, 0.9)), - } - } - }, + tune.register_trainable( + "TRAIN_FN", + lambda config, reporter: train_mnist(args, config, reporter)) + tune.run( + "TRAIN_FN", + name="exp", verbose=0, - scheduler=sched) + scheduler=sched, + **{ + "stop": { + "mean_accuracy": 0.98, + "training_iteration": 1 if args.smoke_test else 20 + }, + "resources_per_trial": { + "cpu": 3, + "gpu": int(not args.no_cuda) + }, + "num_samples": 1 if args.smoke_test else 10, + "config": { + "lr": tune.sample_from( + lambda spec: np.random.uniform(0.001, 0.1)), + "momentum": tune.sample_from( + lambda spec: np.random.uniform(0.1, 0.9)), + } + }) diff --git a/python/ray/tune/examples/mnist_pytorch_trainable.py b/python/ray/tune/examples/mnist_pytorch_trainable.py index b4856c462..cf8cd612e 100644 --- a/python/ray/tune/examples/mnist_pytorch_trainable.py +++ b/python/ray/tune/examples/mnist_pytorch_trainable.py @@ -177,28 +177,26 @@ if __name__ == "__main__": ray.init() sched = HyperBandScheduler( time_attr="training_iteration", reward_attr="neg_mean_loss") - tune.run_experiments( - { - "exp": { - "stop": { - "mean_accuracy": 0.95, - "training_iteration": 1 if args.smoke_test else 20, - }, - "resources_per_trial": { - "cpu": 3, - "gpu": int(not args.no_cuda) - }, - "run": TrainMNIST, - "num_samples": 1 if args.smoke_test else 20, - "checkpoint_at_end": True, - "config": { - "args": args, - "lr": tune.sample_from( - lambda spec: np.random.uniform(0.001, 0.1)), - "momentum": tune.sample_from( - lambda spec: np.random.uniform(0.1, 0.9)), - } - } - }, + tune.run( + TrainMNIST, verbose=0, - scheduler=sched) + scheduler=sched, + **{ + "stop": { + "mean_accuracy": 0.95, + "training_iteration": 1 if args.smoke_test else 20, + }, + "resources_per_trial": { + "cpu": 3, + "gpu": int(not args.no_cuda) + }, + "num_samples": 1 if args.smoke_test else 20, + "checkpoint_at_end": True, + "config": { + "args": args, + "lr": tune.sample_from( + lambda spec: np.random.uniform(0.001, 0.1)), + "momentum": tune.sample_from( + lambda spec: np.random.uniform(0.1, 0.9)), + } + }) diff --git a/python/ray/tune/examples/nevergrad_example.py b/python/ray/tune/examples/nevergrad_example.py index cff8fd795..462d1dd51 100644 --- a/python/ray/tune/examples/nevergrad_example.py +++ b/python/ray/tune/examples/nevergrad_example.py @@ -7,7 +7,7 @@ from __future__ import division from __future__ import print_function import ray -from ray.tune import run_experiments, register_trainable +from ray.tune import run from ray.tune.schedulers import AsyncHyperBandScheduler from ray.tune.suggest import NevergradSearch @@ -33,18 +33,13 @@ if __name__ == "__main__": args, _ = parser.parse_known_args() ray.init() - register_trainable("exp", easy_objective) - config = { - "nevergrad": { - "run": "exp", - "num_samples": 10 if args.smoke_test else 50, - "config": { - "iterations": 100, - }, - "stop": { - "timesteps_total": 100 - }, + "num_samples": 10 if args.smoke_test else 50, + "config": { + "iterations": 100, + }, + "stop": { + "timesteps_total": 100 } } optimizer = optimizerlib.OnePlusOne(dimension=2) @@ -53,4 +48,8 @@ if __name__ == "__main__": max_concurrent=4, reward_attr="neg_mean_loss") scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss") - run_experiments(config, search_alg=algo, scheduler=scheduler) + run(easy_objective, + name="nevergrad", + search_alg=algo, + scheduler=scheduler, + **config) diff --git a/python/ray/tune/examples/pbt_example.py b/python/ray/tune/examples/pbt_example.py index 9cc387e5b..8ff0bea0c 100755 --- a/python/ray/tune/examples/pbt_example.py +++ b/python/ray/tune/examples/pbt_example.py @@ -11,7 +11,7 @@ import random import time import ray -from ray.tune import Trainable, run_experiments +from ray.tune import Trainable, run from ray.tune.schedulers import PopulationBasedTraining @@ -81,20 +81,18 @@ if __name__ == "__main__": }) # Try to find the best factor 1 and factor 2 - run_experiments( - { - "pbt_test": { - "run": MyTrainableClass, - "stop": { - "training_iteration": 20 if args.smoke_test else 99999 - }, - "num_samples": 10, - "config": { - "factor_1": 4.0, - "factor_2": 1.0, - }, - } - }, + run(MyTrainableClass, + name="pbt_test", scheduler=pbt, reuse_actors=True, - verbose=False) + verbose=False, + **{ + "stop": { + "training_iteration": 20 if args.smoke_test else 99999 + }, + "num_samples": 10, + "config": { + "factor_1": 4.0, + "factor_2": 1.0, + }, + }) diff --git a/python/ray/tune/examples/pbt_ppo_example.py b/python/ray/tune/examples/pbt_ppo_example.py index a81d4109f..7ccd7aa84 100755 --- a/python/ray/tune/examples/pbt_ppo_example.py +++ b/python/ray/tune/examples/pbt_ppo_example.py @@ -13,7 +13,7 @@ from __future__ import print_function import random import ray -from ray.tune import run_experiments, sample_from +from ray.tune import run, sample_from from ray.tune.schedulers import PopulationBasedTraining if __name__ == "__main__": @@ -45,31 +45,30 @@ if __name__ == "__main__": custom_explore_fn=explore) ray.init() - run_experiments( - { - "pbt_humanoid_test": { - "run": "PPO", - "env": "Humanoid-v1", - "num_samples": 8, - "config": { - "kl_coeff": 1.0, - "num_workers": 8, - "num_gpus": 1, - "model": { - "free_log_std": True - }, - # These params are tuned from a fixed starting value. - "lambda": 0.95, - "clip_param": 0.2, - "lr": 1e-4, - # These params start off randomly drawn from a set. - "num_sgd_iter": sample_from( - lambda spec: random.choice([10, 20, 30])), - "sgd_minibatch_size": sample_from( - lambda spec: random.choice([128, 512, 2048])), - "train_batch_size": sample_from( - lambda spec: random.choice([10000, 20000, 40000])) + run( + "PPO", + name="pbt_humanoid_test", + scheduler=pbt, + **{ + "env": "Humanoid-v1", + "num_samples": 8, + "config": { + "kl_coeff": 1.0, + "num_workers": 8, + "num_gpus": 1, + "model": { + "free_log_std": True }, + # These params are tuned from a fixed starting value. + "lambda": 0.95, + "clip_param": 0.2, + "lr": 1e-4, + # These params start off randomly drawn from a set. + "num_sgd_iter": sample_from( + lambda spec: random.choice([10, 20, 30])), + "sgd_minibatch_size": sample_from( + lambda spec: random.choice([128, 512, 2048])), + "train_batch_size": sample_from( + lambda spec: random.choice([10000, 20000, 40000])) }, - }, - scheduler=pbt) + }) diff --git a/python/ray/tune/examples/pbt_tune_cifar10_with_keras.py b/python/ray/tune/examples/pbt_tune_cifar10_with_keras.py index 2b7520aeb..52327c1f1 100755 --- a/python/ray/tune/examples/pbt_tune_cifar10_with_keras.py +++ b/python/ray/tune/examples/pbt_tune_cifar10_with_keras.py @@ -23,7 +23,7 @@ from tensorflow.python.keras.models import Model from tensorflow.python.keras.preprocessing.image import ImageDataGenerator import ray -from ray.tune import grid_search, run_experiments, sample_from +from ray.tune import grid_search, run, sample_from from ray.tune import Trainable from ray.tune.schedulers import PopulationBasedTraining @@ -180,7 +180,6 @@ if __name__ == "__main__": args, _ = parser.parse_known_args() train_spec = { - "run": Cifar10Model, "resources_per_trial": { "cpu": 1, "gpu": 1 @@ -213,4 +212,4 @@ if __name__ == "__main__": "dropout": lambda _: np.random.uniform(0, 1), }) - run_experiments({"pbt_cifar10": train_spec}, scheduler=pbt) + run(Cifar10Model, name="pbt_cifar10", scheduler=pbt, **train_spec) diff --git a/python/ray/tune/examples/sigopt_example.py b/python/ray/tune/examples/sigopt_example.py index 6c18cace6..7815dfa5b 100644 --- a/python/ray/tune/examples/sigopt_example.py +++ b/python/ray/tune/examples/sigopt_example.py @@ -7,7 +7,7 @@ from __future__ import division from __future__ import print_function import ray -from ray.tune import run_experiments, register_trainable +from ray.tune import run from ray.tune.schedulers import AsyncHyperBandScheduler from ray.tune.suggest import SigOptSearch @@ -36,8 +36,6 @@ if __name__ == "__main__": args, _ = parser.parse_known_args() ray.init() - register_trainable("exp", easy_objective) - space = [ { 'name': 'width', @@ -58,16 +56,13 @@ if __name__ == "__main__": ] config = { - "my_exp": { - "run": "exp", - "num_samples": 10 if args.smoke_test else 1000, - "config": { - "iterations": 100, - }, - "stop": { - "timesteps_total": 100 - }, - } + "num_samples": 10 if args.smoke_test else 1000, + "config": { + "iterations": 100, + }, + "stop": { + "timesteps_total": 100 + }, } algo = SigOptSearch( space, @@ -75,4 +70,8 @@ if __name__ == "__main__": max_concurrent=1, reward_attr="neg_mean_loss") scheduler = AsyncHyperBandScheduler(reward_attr="neg_mean_loss") - run_experiments(config, search_alg=algo, scheduler=scheduler) + run(easy_objective, + name="my_exp", + search_alg=algo, + scheduler=scheduler, + **config) diff --git a/python/ray/tune/examples/skopt_example.py b/python/ray/tune/examples/skopt_example.py index a120a329d..4f744b64c 100644 --- a/python/ray/tune/examples/skopt_example.py +++ b/python/ray/tune/examples/skopt_example.py @@ -7,7 +7,7 @@ from __future__ import division from __future__ import print_function import ray -from ray.tune import run_experiments, register_trainable +from ray.tune import run from ray.tune.schedulers import AsyncHyperBandScheduler from ray.tune.suggest import SkOptSearch @@ -33,19 +33,14 @@ if __name__ == "__main__": args, _ = parser.parse_known_args() ray.init() - register_trainable("exp", easy_objective) - config = { - "skopt_exp": { - "run": "exp", - "num_samples": 10 if args.smoke_test else 50, - "config": { - "iterations": 100, - }, - "stop": { - "timesteps_total": 100 - }, - } + "num_samples": 10 if args.smoke_test else 50, + "config": { + "iterations": 100, + }, + "stop": { + "timesteps_total": 100 + }, } optimizer = Optimizer([(0, 20), (-100, 100)]) previously_run_params = [[10, 0], [15, -20]] @@ -57,7 +52,11 @@ if __name__ == "__main__": 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) + run(easy_objective, + name="skopt_exp_with_warmstart", + search_alg=algo, + scheduler=scheduler, + **config) # Now run the experiment without known rewards @@ -67,4 +66,8 @@ if __name__ == "__main__": 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) + run(easy_objective, + name="skopt_exp", + search_alg=algo, + scheduler=scheduler, + **config) diff --git a/python/ray/tune/examples/tune_mnist_async_hyperband.py b/python/ray/tune/examples/tune_mnist_async_hyperband.py index 8bc104257..b99e39f5d 100755 --- a/python/ray/tune/examples/tune_mnist_async_hyperband.py +++ b/python/ray/tune/examples/tune_mnist_async_hyperband.py @@ -33,7 +33,7 @@ import tempfile import time import ray -from ray.tune import grid_search, run_experiments +from ray.tune import grid_search, run from tensorflow.examples.tutorials.mnist import input_data @@ -219,7 +219,6 @@ if __name__ == "__main__": args, _ = parser.parse_known_args() mnist_spec = { - 'run': train, 'num_samples': 10, 'stop': { 'mean_accuracy': 0.99, @@ -237,12 +236,11 @@ if __name__ == "__main__": ray.init() from ray.tune.schedulers import AsyncHyperBandScheduler - run_experiments( - { - 'tune_mnist_test': mnist_spec - }, + run(train, + name='tune_mnist_test', scheduler=AsyncHyperBandScheduler( time_attr="timesteps_total", reward_attr="mean_accuracy", max_t=600, - )) + ), + **mnist_spec) diff --git a/python/ray/tune/examples/tune_mnist_keras.py b/python/ray/tune/examples/tune_mnist_keras.py index 227aee38b..abe0452d4 100644 --- a/python/ray/tune/examples/tune_mnist_keras.py +++ b/python/ray/tune/examples/tune_mnist_keras.py @@ -176,32 +176,33 @@ if __name__ == "__main__": reward_attr="mean_accuracy", max_t=400, grace_period=20) - tune.register_trainable("train_mnist", - lambda cfg, rprtr: train_mnist(args, cfg, rprtr)) - tune.run_experiments( - { - "exp": { - "stop": { - "mean_accuracy": 0.99, - "timesteps_total": 10 if args.smoke_test else 300 - }, - "run": "train_mnist", - "num_samples": 1 if args.smoke_test else 10, - "resources_per_trial": { - "cpu": args.threads, - "gpu": 0.5 if args.use_gpu else 0 - }, - "config": { - "lr": tune.sample_from( - lambda spec: np.random.uniform(0.001, 0.1)), - "momentum": tune.sample_from( - lambda spec: np.random.uniform(0.1, 0.9)), - "hidden": tune.sample_from( - lambda spec: np.random.randint(32, 512)), - "dropout1": tune.sample_from( - lambda spec: np.random.uniform(0.2, 0.8)), - } - } - }, + + tune.register_trainable( + "TRAIN_FN", + lambda config, reporter: train_mnist(args, config, reporter)) + tune.run( + "TRAIN_FN", + name="exp", verbose=0, - scheduler=sched) + scheduler=sched, + **{ + "stop": { + "mean_accuracy": 0.99, + "timesteps_total": 10 if args.smoke_test else 300 + }, + "num_samples": 1 if args.smoke_test else 10, + "resources_per_trial": { + "cpu": args.threads, + "gpu": 0.5 if args.use_gpu else 0 + }, + "config": { + "lr": tune.sample_from( + lambda spec: np.random.uniform(0.001, 0.1)), + "momentum": tune.sample_from( + lambda spec: np.random.uniform(0.1, 0.9)), + "hidden": tune.sample_from( + lambda spec: np.random.randint(32, 512)), + "dropout1": tune.sample_from( + lambda spec: np.random.uniform(0.2, 0.8)), + } + }) diff --git a/python/ray/tune/examples/tune_mnist_ray.py b/python/ray/tune/examples/tune_mnist_ray.py index a8411ffe7..74c61df36 100755 --- a/python/ray/tune/examples/tune_mnist_ray.py +++ b/python/ray/tune/examples/tune_mnist_ray.py @@ -33,7 +33,8 @@ import tempfile import time import ray -from ray.tune import grid_search, run_experiments, register_trainable +from ray import tune +from ray.tune import grid_search, register_trainable from tensorflow.examples.tutorials.mnist import input_data import numpy as np @@ -221,7 +222,6 @@ if __name__ == "__main__": register_trainable('train_mnist', train) mnist_spec = { - 'run': 'train_mnist', 'stop': { 'mean_accuracy': 0.99, 'time_total_s': 600, @@ -238,4 +238,4 @@ if __name__ == "__main__": mnist_spec['stop']['training_iteration'] = 2 ray.init() - run_experiments({'tune_mnist_test': mnist_spec}) + tune.run('train_mnist', name='tune_mnist_test', **mnist_spec) diff --git a/python/ray/tune/examples/tune_mnist_ray_hyperband.py b/python/ray/tune/examples/tune_mnist_ray_hyperband.py index 80a8d97a0..69ebcdf87 100755 --- a/python/ray/tune/examples/tune_mnist_ray_hyperband.py +++ b/python/ray/tune/examples/tune_mnist_ray_hyperband.py @@ -30,8 +30,8 @@ import argparse import time import ray -from ray.tune import grid_search, run_experiments, register_trainable, \ - Trainable, sample_from +from ray import tune +from ray.tune import grid_search, Trainable, sample_from from ray.tune.schedulers import HyperBandScheduler from tensorflow.examples.tutorials.mnist import input_data @@ -214,10 +214,7 @@ if __name__ == "__main__": parser.add_argument( '--smoke-test', action='store_true', help='Finish quickly for testing') args, _ = parser.parse_known_args() - - register_trainable("my_class", TrainMNIST) mnist_spec = { - 'run': 'my_class', 'stop': { 'mean_accuracy': 0.99, 'time_total_s': 600, @@ -238,4 +235,8 @@ if __name__ == "__main__": hyperband = HyperBandScheduler( time_attr="training_iteration", reward_attr="mean_accuracy", max_t=10) - run_experiments({'mnist_hyperband_test': mnist_spec}, scheduler=hyperband) + tune.run( + TrainMNIST, + name='mnist_hyperband_test', + scheduler=hyperband, + **mnist_spec) diff --git a/python/ray/tune/experiment.py b/python/ray/tune/experiment.py index 15420972e..d6bf12cc0 100644 --- a/python/ray/tune/experiment.py +++ b/python/ray/tune/experiment.py @@ -35,59 +35,7 @@ def _raise_deprecation_note(deprecated, replacement, soft=False): class Experiment(object): """Tracks experiment specifications. - Parameters: - name (str): Name of experiment. - run (function|class|str): The algorithm or model to train. - This may refer to the name of a built-on algorithm - (e.g. RLLib's DQN or PPO), a user-defined trainable - function or class, or the string identifier of a - trainable function or class registered in the tune registry. - stop (dict): The stopping criteria. The keys may be any field in - the return result of 'train()', whichever is reached first. - Defaults to empty dict. - config (dict): Algorithm-specific configuration for Tune variant - generation (e.g. env, hyperparams). Defaults to empty dict. - Custom search algorithms may ignore this. - resources_per_trial (dict): Machine resources to allocate per trial, - e.g. ``{"cpu": 64, "gpu": 8}``. Note that GPUs will not be - assigned unless you specify them here. Defaults to 1 CPU and 0 - GPUs in ``Trainable.default_resource_request()``. - num_samples (int): Number of times to sample from the - hyperparameter space. Defaults to 1. If `grid_search` is - provided as an argument, the grid will be repeated - `num_samples` of times. - local_dir (str): Local dir to save training results to. - Defaults to ``~/ray_results``. - upload_dir (str): Optional URI to sync training results - to (e.g. ``s3://bucket``). - trial_name_creator (func): Optional function for generating - the trial string representation. - loggers (list): List of logger creators to be used with - each Trial. If None, defaults to ray.tune.logger.DEFAULT_LOGGERS. - See `ray/tune/logger.py`. - sync_function (func|str): Function for syncing the local_dir to - upload_dir. If string, then it must be a string template for - syncer to run. If not provided, the sync command defaults - to standard S3 or gsutil sync comamnds. - checkpoint_freq (int): How many training iterations between - checkpoints. A value of 0 (default) disables checkpointing. - checkpoint_at_end (bool): Whether to checkpoint at the end of the - experiment regardless of the checkpoint_freq. Default is False. - export_formats (list): List of formats that exported at the end of - the experiment. Default is None. - max_failures (int): Try to recover a trial from its last - checkpoint at least this many times. Only applies if - checkpointing is enabled. Setting to -1 will lead to infinite - recovery retries. Defaults to 3. - restore (str): Path to checkpoint. Only makes sense to set if - running 1 trial. Defaults to None. - repeat: Deprecated and will be removed in future versions of - Ray. Use `num_samples` instead. - trial_resources: Deprecated and will be removed in future versions of - Ray. Use `resources_per_trial` instead. - custom_loggers: Deprecated and will be removed in future versions of - Ray. Use `loggers` instead. - + Implicitly registers the Trainable if needed. Examples: >>> experiment_spec = Experiment( @@ -107,7 +55,6 @@ class Experiment(object): >>> upload_dir="s3://your_bucket/path", >>> checkpoint_freq=10, >>> max_failures=2) - """ def __init__(self, @@ -121,7 +68,6 @@ class Experiment(object): upload_dir=None, trial_name_creator=None, loggers=None, - custom_loggers=None, sync_function=None, checkpoint_freq=0, checkpoint_at_end=False, @@ -129,7 +75,8 @@ class Experiment(object): max_failures=3, restore=None, repeat=None, - trial_resources=None): + trial_resources=None, + custom_loggers=None): if sync_function: assert upload_dir, "Need `upload_dir` if sync_function given." @@ -137,13 +84,13 @@ class Experiment(object): _raise_deprecation_note("repeat", "num_samples", soft=False) if trial_resources: _raise_deprecation_note( - "trial_resources", "resources_per_trial", soft=True) - resources_per_trial = trial_resources + "trial_resources", "resources_per_trial", soft=False) if custom_loggers: _raise_deprecation_note("custom_loggers", "loggers", soft=False) + run_identifier = Experiment._register_if_needed(run) spec = { - "run": Experiment._register_if_needed(run), + "run": run_identifier, "stop": stop or {}, "config": config or {}, "resources_per_trial": resources_per_trial, @@ -160,7 +107,7 @@ class Experiment(object): "restore": restore } - self.name = name + self.name = name or run_identifier self.spec = spec @classmethod diff --git a/python/ray/tune/ray_trial_executor.py b/python/ray/tune/ray_trial_executor.py index f3af5f38d..326398b3a 100644 --- a/python/ray/tune/ray_trial_executor.py +++ b/python/ray/tune/ray_trial_executor.py @@ -368,7 +368,7 @@ class RayTrialExecutor(TrialExecutor): if not resources or "CPU" not in resources: raise TuneError("Cluster resources cannot be detected. " "You can resume this experiment by passing in " - "`resume=True` to `run_experiments`.") + "`resume=True` to `run`.") resources = resources.copy() num_cpus = resources.pop("CPU") @@ -418,7 +418,7 @@ class RayTrialExecutor(TrialExecutor): "may appear to hang until enough resources are added to the " "cluster (e.g., via autoscaling). You can disable this " "behavior by specifying `queue_trials=False` in " - "ray.tune.run_experiments().") + "ray.tune.run().") return True return False diff --git a/python/ray/tune/suggest/bayesopt.py b/python/ray/tune/suggest/bayesopt.py index 089f1b26d..ca17d4050 100644 --- a/python/ray/tune/suggest/bayesopt.py +++ b/python/ray/tune/suggest/bayesopt.py @@ -38,15 +38,6 @@ class BayesOptSearch(SuggestionAlgorithm): >>> 'width': (0, 20), >>> 'height': (-100, 100), >>> } - >>> config = { - >>> "my_exp": { - >>> "run": "exp", - >>> "num_samples": 10 if args.smoke_test else 1000, - >>> "stop": { - >>> "training_iteration": 100 - >>> }, - >>> } - >>> } >>> algo = BayesOptSearch( >>> space, max_concurrent=4, reward_attr="neg_mean_loss") """ diff --git a/python/ray/tune/suggest/hyperopt.py b/python/ray/tune/suggest/hyperopt.py index 62795cc6c..96cfa0db0 100644 --- a/python/ray/tune/suggest/hyperopt.py +++ b/python/ray/tune/suggest/hyperopt.py @@ -55,15 +55,6 @@ class HyperOptSearch(SuggestionAlgorithm): >>> 'height': 0, >>> 'activation': 0, # The index of "relu" >>> }] - >>> config = { - >>> "my_exp": { - >>> "run": "exp", - >>> "num_samples": 10 if args.smoke_test else 1000, - >>> "stop": { - >>> "training_iteration": 100 - >>> }, - >>> } - >>> } >>> algo = HyperOptSearch( >>> space, max_concurrent=4, reward_attr="neg_mean_loss", >>> points_to_evaluate=current_best_params) diff --git a/python/ray/tune/suggest/nevergrad.py b/python/ray/tune/suggest/nevergrad.py index a49b37eef..25ea89af4 100644 --- a/python/ray/tune/suggest/nevergrad.py +++ b/python/ray/tune/suggest/nevergrad.py @@ -32,15 +32,6 @@ class NevergradSearch(SuggestionAlgorithm): Example: >>> from nevergrad.optimization import optimizerlib >>> optimizer = optimizerlib.OnePlusOne(dimension=1, budget=100) - >>> config = { - >>> "my_exp": { - >>> "run": "exp", - >>> "num_samples": 10, - >>> "stop": { - >>> "training_iteration": 100 - >>> }, - >>> } - >>> } >>> algo = NevergradSearch( >>> optimizer, max_concurrent=4, reward_attr="neg_mean_loss") """ diff --git a/python/ray/tune/suggest/sigopt.py b/python/ray/tune/suggest/sigopt.py index 12b338f88..5f850ca5b 100644 --- a/python/ray/tune/suggest/sigopt.py +++ b/python/ray/tune/suggest/sigopt.py @@ -48,17 +48,8 @@ class SigOptSearch(SuggestionAlgorithm): >>> }, >>> }, >>> ] - >>> config = { - >>> "my_exp": { - >>> "run": "exp", - >>> "num_samples": 10 if args.smoke_test else 1000, - >>> "stop": { - >>> "training_iteration": 100 - >>> }, - >>> } - >>> } >>> algo = SigOptSearch( - >>> parameters, name="SigOpt Example Experiment", + >>> space, name="SigOpt Example Experiment", >>> max_concurrent=1, reward_attr="neg_mean_loss") """ diff --git a/python/ray/tune/suggest/skopt.py b/python/ray/tune/suggest/skopt.py index cff340f41..b7035bd9c 100644 --- a/python/ray/tune/suggest/skopt.py +++ b/python/ray/tune/suggest/skopt.py @@ -70,15 +70,6 @@ class SkOptSearch(SuggestionAlgorithm): >>> from skopt import Optimizer >>> optimizer = Optimizer([(0,20),(-100,100)]) >>> current_best_params = [[10, 0], [15, -20]] - >>> config = { - >>> "my_exp": { - >>> "run": "exp", - >>> "num_samples": 10, - >>> "stop": { - >>> "training_iteration": 100 - >>> }, - >>> } - >>> } >>> algo = SkOptSearch(optimizer, >>> ["width", "height"], >>> max_concurrent=4, diff --git a/python/ray/tune/tests/test_trial_runner.py b/python/ray/tune/tests/test_trial_runner.py index aee6db253..2da8d85b2 100644 --- a/python/ray/tune/tests/test_trial_runner.py +++ b/python/ray/tune/tests/test_trial_runner.py @@ -743,22 +743,6 @@ class RunExperimentTest(unittest.TestCase): self.assertRaises(TuneError, fail_trial) - def testDeprecatedResources(self): - class train(Trainable): - def _train(self): - return {"timesteps_this_iter": 1, "done": True} - - trials = run_experiments({ - "foo": { - "run": train, - "trial_resources": { - "cpu": 1 - } - } - }) - for trial in trials: - self.assertEqual(trial.status, Trial.TERMINATED) - def testCustomResources(self): ray.shutdown() ray.init(resources={"hi": 3}) diff --git a/python/ray/tune/trial_runner.py b/python/ray/tune/trial_runner.py index c13acfc00..b3e939b2f 100644 --- a/python/ray/tune/trial_runner.py +++ b/python/ray/tune/trial_runner.py @@ -245,7 +245,7 @@ class TrialRunner(object): ("Insufficient cluster resources to launch trial: " "trial requested {} but the cluster has only {}. " "Pass `queue_trials=True` in " - "ray.tune.run_experiments() or on the command " + "ray.tune.run() or on the command " "line to queue trials until the cluster scales " "up. {}").format( trial.resources.summary_string(), diff --git a/python/ray/tune/tune.py b/python/ray/tune/tune.py index febaf3575..f46d23fc6 100644 --- a/python/ray/tune/tune.py +++ b/python/ray/tune/tune.py @@ -8,7 +8,7 @@ import os import time from ray.tune.error import TuneError -from ray.tune.experiment import convert_to_experiment_list +from ray.tune.experiment import convert_to_experiment_list, Experiment from ray.tune.suggest import BasicVariantGenerator from ray.tune.trial import Trial, DEBUG_PRINT_INTERVAL from ray.tune.log_sync import wait_for_log_sync @@ -35,39 +35,113 @@ def _make_scheduler(args): args.scheduler, _SCHEDULERS.keys())) -def _find_checkpoint_dir(exp_list): - assert exp_list, "Experiments must be specified via `run_experiments`" - exp = exp_list[0] - # TODO(rliaw): Make sure this is resolved earlier. +def _find_checkpoint_dir(exp): + # TODO(rliaw): Make sure the checkpoint_dir is resolved earlier. + # Right now it is resolved somewhere far down the trial generation process return os.path.join(exp.spec["local_dir"], exp.name) -def try_restore_runner(checkpoint_dir, search_alg, scheduler, trial_executor): - new_runner = None - try: - new_runner = TrialRunner.restore(checkpoint_dir, search_alg, scheduler, - trial_executor) - except Exception: - logger.exception("Runner restore failed. Restarting experiment.") - return new_runner +def _prompt_restore(checkpoint_dir, resume): + restore = False + if TrialRunner.checkpoint_exists(checkpoint_dir): + if resume == "prompt": + msg = ("Found incomplete experiment at {}. " + "Would you like to resume it?".format(checkpoint_dir)) + restore = click.confirm(msg, default=False) + if restore: + logger.info("Tip: to always resume, " + "pass resume=True to run()") + else: + logger.info("Tip: to always start a new experiment, " + "pass resume=False to run()") + elif resume: + restore = True + else: + logger.info("Tip: to resume incomplete experiments, " + "pass resume='prompt' or resume=True to run()") + else: + logger.info( + "Did not find checkpoint file in {}.".format(checkpoint_dir)) + return restore -def run_experiments(experiments, - search_alg=None, - scheduler=None, - with_server=False, - server_port=TuneServer.DEFAULT_PORT, - verbose=2, - resume=False, - queue_trials=False, - reuse_actors=False, - trial_executor=None, - raise_on_failed_trial=True): - """Runs and blocks until all trials finish. +def run(run_or_experiment, + name=None, + stop=None, + config=None, + resources_per_trial=None, + num_samples=1, + local_dir=None, + upload_dir=None, + trial_name_creator=None, + loggers=None, + sync_function=None, + checkpoint_freq=0, + checkpoint_at_end=False, + export_formats=None, + max_failures=3, + restore=None, + search_alg=None, + scheduler=None, + with_server=False, + server_port=TuneServer.DEFAULT_PORT, + verbose=2, + resume=False, + queue_trials=False, + reuse_actors=False, + trial_executor=None, + raise_on_failed_trial=True): + """Executes training. Args: - experiments (Experiment | list | dict): Experiments to run. Will be - passed to `search_alg` via `add_configurations`. + run_or_experiment (function|class|str|Experiment): If + function|class|str, this is the algorithm or model to train. + This may refer to the name of a built-on algorithm + (e.g. RLLib's DQN or PPO), a user-defined trainable + function or class, or the string identifier of a + trainable function or class registered in the tune registry. + If Experiment, then Tune will execute training based on + Experiment.spec. + name (str): Name of experiment. + stop (dict): The stopping criteria. The keys may be any field in + the return result of 'train()', whichever is reached first. + Defaults to empty dict. + config (dict): Algorithm-specific configuration for Tune variant + generation (e.g. env, hyperparams). Defaults to empty dict. + Custom search algorithms may ignore this. + resources_per_trial (dict): Machine resources to allocate per trial, + e.g. ``{"cpu": 64, "gpu": 8}``. Note that GPUs will not be + assigned unless you specify them here. Defaults to 1 CPU and 0 + GPUs in ``Trainable.default_resource_request()``. + num_samples (int): Number of times to sample from the + hyperparameter space. Defaults to 1. If `grid_search` is + provided as an argument, the grid will be repeated + `num_samples` of times. + local_dir (str): Local dir to save training results to. + Defaults to ``~/ray_results``. + upload_dir (str): Optional URI to sync training results + to (e.g. ``s3://bucket``). + trial_name_creator (func): Optional function for generating + the trial string representation. + loggers (list): List of logger creators to be used with + each Trial. If None, defaults to ray.tune.logger.DEFAULT_LOGGERS. + See `ray/tune/logger.py`. + sync_function (func|str): Function for syncing the local_dir to + upload_dir. If string, then it must be a string template for + syncer to run. If not provided, the sync command defaults + to standard S3 or gsutil sync comamnds. + checkpoint_freq (int): How many training iterations between + checkpoints. A value of 0 (default) disables checkpointing. + checkpoint_at_end (bool): Whether to checkpoint at the end of the + experiment regardless of the checkpoint_freq. Default is False. + export_formats (list): List of formats that exported at the end of + the experiment. Default is None. + max_failures (int): Try to recover a trial from its last + checkpoint at least this many times. Only applies if + checkpointing is enabled. Setting to -1 will lead to infinite + recovery retries. Defaults to 3. + restore (str): Path to checkpoint. Only makes sense to set if + running 1 trial. Defaults to None. search_alg (SearchAlgorithm): Search Algorithm. Defaults to BasicVariantGenerator. scheduler (TrialScheduler): Scheduler for executing @@ -93,70 +167,54 @@ def run_experiments(experiments, raise_on_failed_trial (bool): Raise TuneError if there exists failed trial (of ERROR state) when the experiments complete. - Examples: - >>> experiment_spec = Experiment("experiment", my_func) - >>> run_experiments(experiments=experiment_spec) - - >>> experiment_spec = {"experiment": {"run": my_func}} - >>> run_experiments(experiments=experiment_spec) - - >>> run_experiments( - >>> experiments=experiment_spec, - >>> scheduler=MedianStoppingRule(...)) - - >>> run_experiments( - >>> experiments=experiment_spec, - >>> search_alg=SearchAlgorithm(), - >>> scheduler=MedianStoppingRule(...)) - Returns: - List of Trial objects, holding data for each executed trial. + List of Trial objects. + Raises: + TuneError if any trials failed and `raise_on_failed_trial` is True. + + Examples: + >>> tune.run(mytrainable, scheduler=PopulationBasedTraining()) + + >>> tune.run(mytrainable, num_samples=5, reuse_actors=True) + + >>> tune.run( + "PG", + num_samples=5, + config={ + "env": "CartPole-v0", + "lr": tune.sample_from(lambda _: np.random.rand()) + } + ) """ - # This is important to do this here - # because it schematize the experiments - # and it conducts the implicit registration. - experiments = convert_to_experiment_list(experiments) - checkpoint_dir = _find_checkpoint_dir(experiments) + experiment = run_or_experiment + if not isinstance(run_or_experiment, Experiment): + experiment = Experiment( + name, run_or_experiment, stop, config, resources_per_trial, + num_samples, local_dir, upload_dir, trial_name_creator, loggers, + sync_function, checkpoint_freq, checkpoint_at_end, export_formats, + max_failures, restore) + else: + logger.debug("Ignoring some parameters passed into tune.run.") + + checkpoint_dir = _find_checkpoint_dir(experiment) + should_restore = _prompt_restore(checkpoint_dir, resume) runner = None - restore = False - - if TrialRunner.checkpoint_exists(checkpoint_dir): - if resume == "prompt": - msg = ("Found incomplete experiment at {}. " - "Would you like to resume it?".format(checkpoint_dir)) - restore = click.confirm(msg, default=False) - if restore: - logger.info("Tip: to always resume, " - "pass resume=True to run_experiments()") - else: - logger.info("Tip: to always start a new experiment, " - "pass resume=False to run_experiments()") - elif resume: - restore = True - else: - logger.info( - "Tip: to resume incomplete experiments, " - "pass resume='prompt' or resume=True to run_experiments()") - else: - logger.info( - "Did not find checkpoint file in {}.".format(checkpoint_dir)) - - if restore: - runner = try_restore_runner(checkpoint_dir, search_alg, scheduler, - trial_executor) + if should_restore: + try: + runner = TrialRunner.restore(checkpoint_dir, search_alg, scheduler, + trial_executor) + except Exception: + logger.exception("Runner restore failed. Restarting experiment.") else: logger.info("Starting a new experiment.") if not runner: - if scheduler is None: - scheduler = FIFOScheduler() + scheduler = scheduler or FIFOScheduler() + search_alg = search_alg or BasicVariantGenerator() - if search_alg is None: - search_alg = BasicVariantGenerator() - - search_alg.add_configurations(experiments) + search_alg.add_configurations([experiment]) runner = TrialRunner( search_alg, @@ -197,3 +255,58 @@ def run_experiments(experiments, logger.error("Trials did not complete: %s", errored_trials) return runner.get_trials() + + +def run_experiments(experiments, + search_alg=None, + scheduler=None, + with_server=False, + server_port=TuneServer.DEFAULT_PORT, + verbose=2, + resume=False, + queue_trials=False, + reuse_actors=False, + trial_executor=None, + raise_on_failed_trial=True): + """Runs and blocks until all trials finish. + + Examples: + >>> experiment_spec = Experiment("experiment", my_func) + >>> run_experiments(experiments=experiment_spec) + + >>> experiment_spec = {"experiment": {"run": my_func}} + >>> run_experiments(experiments=experiment_spec) + + >>> run_experiments( + >>> experiments=experiment_spec, + >>> scheduler=MedianStoppingRule(...)) + + >>> run_experiments( + >>> experiments=experiment_spec, + >>> search_alg=SearchAlgorithm(), + >>> scheduler=MedianStoppingRule(...)) + + Returns: + List of Trial objects, holding data for each executed trial. + + """ + # This is important to do this here + # because it schematize the experiments + # and it conducts the implicit registration. + experiments = convert_to_experiment_list(experiments) + + trials = [] + for exp in experiments: + trials += run( + exp, + search_alg=search_alg, + scheduler=scheduler, + with_server=with_server, + server_port=server_port, + verbose=verbose, + resume=resume, + queue_trials=queue_trials, + reuse_actors=reuse_actors, + trial_executor=trial_executor, + raise_on_failed_trial=raise_on_failed_trial) + return trials