mirror of
https://github.com/wassname/ray.git
synced 2026-07-16 11:21:10 +08:00
[tune] Change tune resource request syntax to be less confusing (#1764)
* update * update examples * Wed Mar 21 15:19:56 PDT 2018 * Wed Mar 21 15:21:32 PDT 2018 * Update train_a3c.py * Update train.py * fix resources accounting
This commit is contained in:
committed by
Philipp Moritz
parent
10dabce4d7
commit
72595cca0d
@@ -62,7 +62,7 @@ if __name__ == "__main__":
|
||||
"run": args.run,
|
||||
"checkpoint_freq": args.checkpoint_freq,
|
||||
"local_dir": args.local_dir,
|
||||
"resources": resources_to_json(args.resources),
|
||||
"trial_resources": resources_to_json(args.trial_resources),
|
||||
"stop": args.stop,
|
||||
"config": dict(args.config, env=args.env),
|
||||
"restore": args.restore,
|
||||
|
||||
@@ -4,9 +4,9 @@ cartpole-ppo:
|
||||
stop:
|
||||
episode_reward_mean: 200
|
||||
time_total_s: 180
|
||||
resources:
|
||||
cpu: 3
|
||||
driver_cpu_limit: 1
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
extra_cpu: 1
|
||||
config:
|
||||
num_workers: 2
|
||||
num_sgd_iter:
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
hopper-ppo:
|
||||
env: Hopper-v1
|
||||
run: PPO
|
||||
resources:
|
||||
cpu: 65
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
gpu: 4
|
||||
driver_cpu_limit: 1
|
||||
driver_gpu_limit: 4
|
||||
extra_cpu: 64
|
||||
config: {"gamma": 0.995, "kl_coeff": 1.0, "num_sgd_iter": 20, "sgd_stepsize": .0001, "sgd_batchsize": 32768, "devices": ["/gpu:0", "/gpu:1", "/gpu:2", "/gpu:3"], "tf_session_args": {"device_count": {"GPU": 4}, "log_device_placement": false, "allow_soft_placement": true}, "timesteps_per_batch": 160000, "num_workers": 64}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
humanoid-es:
|
||||
env: Humanoid-v1
|
||||
run: ES
|
||||
resources:
|
||||
cpu: 101
|
||||
driver_cpu_limit: 1
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
extra_cpu: 100
|
||||
stop:
|
||||
episode_reward_mean: 6000
|
||||
config:
|
||||
|
||||
@@ -3,9 +3,9 @@ humanoid-ppo-gae:
|
||||
run: PPO
|
||||
stop:
|
||||
episode_reward_mean: 6000
|
||||
resources:
|
||||
cpu: 65
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
gpu: 4
|
||||
driver_cpu_limit: 1
|
||||
extra_cpu: 64
|
||||
config: {"lambda": 0.95, "clip_param": 0.2, "kl_coeff": 1.0, "num_sgd_iter": 20, "sgd_stepsize": .0001, "sgd_batchsize": 32768, "horizon": 5000, "devices": ["/gpu:0", "/gpu:1", "/gpu:2", "/gpu:3"], "tf_session_args": {"device_count": {"GPU": 4}, "log_device_placement": false, "allow_soft_placement": true}, "timesteps_per_batch": 320000, "num_workers": 64, "model": {"free_log_std": true}, "write_logs": false}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ humanoid-ppo:
|
||||
run: PPO
|
||||
stop:
|
||||
episode_reward_mean: 6000
|
||||
resources:
|
||||
cpu: 65
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
gpu: 4
|
||||
driver_cpu_limit: 1
|
||||
extra_cpu: 64
|
||||
config: {"kl_coeff": 1.0, "num_sgd_iter": 20, "sgd_stepsize": .0001, "sgd_batchsize": 32768, "devices": ["/gpu:0", "/gpu:1", "/gpu:2", "/gpu:3"], "tf_session_args": {"device_count": {"GPU": 4}, "log_device_placement": false, "allow_soft_placement": true}, "timesteps_per_batch": 320000, "num_workers": 64, "model": {"free_log_std": true}, "use_gae": false}
|
||||
|
||||
@@ -5,9 +5,9 @@ cartpole-ppo:
|
||||
stop:
|
||||
episode_reward_mean: 200
|
||||
time_total_s: 180
|
||||
resources:
|
||||
cpu: 2
|
||||
driver_cpu_limit: 1
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
extra_cpu: 1
|
||||
config:
|
||||
num_workers: 1
|
||||
num_sgd_iter:
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
pendulum-ppo:
|
||||
env: Pendulum-v0
|
||||
run: PPO
|
||||
resources:
|
||||
cpu: 5
|
||||
driver_cpu_limit: 1
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
extra_cpu: 4
|
||||
config:
|
||||
timesteps_per_batch: 2048
|
||||
num_workers: 4
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
pong-a3c-pytorch-cnn:
|
||||
env: PongDeterministic-v4
|
||||
run: A3C
|
||||
resources:
|
||||
cpu: 17
|
||||
driver_cpu_limit: 1
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
extra_cpu: 16
|
||||
config:
|
||||
num_workers: 16
|
||||
batch_size: 20
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
pong-a3c:
|
||||
env: PongDeterministic-v4
|
||||
run: A3C
|
||||
resources:
|
||||
cpu: 17
|
||||
driver_cpu_limit: 1
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
extra_cpu: 16
|
||||
config:
|
||||
num_workers: 16
|
||||
batch_size: 20
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
pong-apex:
|
||||
env: PongNoFrameskip-v4
|
||||
run: APEX
|
||||
resources:
|
||||
cpu:
|
||||
eval: 1 + spec.config.num_workers
|
||||
driver_cpu_limit: 1
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
gpu: 1
|
||||
extra_cpu:
|
||||
eval: 4 + spec.config.num_workers
|
||||
config:
|
||||
target_network_update_freq: 50000
|
||||
num_workers: 32
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
pong-deterministic-dqn:
|
||||
env: PongDeterministic-v4
|
||||
run: DQN
|
||||
resources:
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
gpu: 1
|
||||
stop:
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
pong-deterministic-ppo:
|
||||
env: PongDeterministic-v4
|
||||
run: PPO
|
||||
resources:
|
||||
cpu: 5
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
gpu: 1
|
||||
driver_cpu_limit: 1
|
||||
extra_cpu: 4
|
||||
stop:
|
||||
episode_reward_mean: 21
|
||||
config:
|
||||
|
||||
@@ -4,7 +4,7 @@ cartpole-a3c:
|
||||
stop:
|
||||
episode_reward_mean: 200
|
||||
time_total_s: 600
|
||||
resources:
|
||||
trial_resources:
|
||||
cpu: 2
|
||||
config:
|
||||
num_workers: 4
|
||||
|
||||
@@ -4,7 +4,7 @@ cartpole-dqn:
|
||||
stop:
|
||||
episode_reward_mean: 200
|
||||
time_total_s: 600
|
||||
resources:
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
config:
|
||||
n_step: 3
|
||||
|
||||
@@ -4,7 +4,7 @@ cartpole-es:
|
||||
stop:
|
||||
episode_reward_mean: 200
|
||||
time_total_s: 300
|
||||
resources:
|
||||
trial_resources:
|
||||
cpu: 2
|
||||
config:
|
||||
num_workers: 2
|
||||
|
||||
@@ -4,7 +4,7 @@ cartpole-ppo:
|
||||
stop:
|
||||
episode_reward_mean: 200
|
||||
time_total_s: 300
|
||||
resources:
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
config:
|
||||
num_workers: 1
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
walker2d-v1-ppo:
|
||||
env: Walker2d-v1
|
||||
run: PPO
|
||||
resources:
|
||||
cpu: 65
|
||||
trial_resources:
|
||||
cpu: 1
|
||||
gpu: 4
|
||||
driver_cpu_limit: 1
|
||||
extra_cpu: 64
|
||||
config: {"kl_coeff": 1.0, "num_sgd_iter": 20, "sgd_stepsize": .0001, "sgd_batchsize": 32768, "devices": ["/gpu:0", "/gpu:1", "/gpu:2", "/gpu:3"], "tf_session_args": {"device_count": {"GPU": 4}, "log_device_placement": false, "allow_soft_placement": true}, "timesteps_per_batch": 320000, "num_workers": 64}
|
||||
|
||||
@@ -15,24 +15,34 @@ def json_to_resources(data):
|
||||
if type(data) is str:
|
||||
data = json.loads(data)
|
||||
for k in data:
|
||||
if k in ["driver_cpu_limit", "driver_gpu_limit"]:
|
||||
raise TuneError(
|
||||
"The field `{}` is no longer supported. Use `extra_cpu` "
|
||||
"or `extra_gpu` instead.".format(k))
|
||||
if k not in Resources._fields:
|
||||
raise TuneError(
|
||||
"Unknown resource type {}, must be one of {}".format(
|
||||
k, Resources._fields))
|
||||
return Resources(
|
||||
data.get("cpu", 1), data.get("gpu", 0),
|
||||
data.get("driver_cpu_limit"), data.get("driver_gpu_limit"))
|
||||
data.get("extra_cpu", 0), data.get("extra_gpu", 0))
|
||||
|
||||
|
||||
def resources_to_json(resources):
|
||||
if resources is None:
|
||||
resources = Resources(cpu=1, gpu=0)
|
||||
return {
|
||||
"cpu": resources.cpu,
|
||||
"gpu": resources.gpu,
|
||||
"driver_cpu_limit": resources.driver_cpu_limit,
|
||||
"driver_gpu_limit": resources.driver_gpu_limit,
|
||||
"extra_cpu": resources.extra_cpu,
|
||||
"extra_gpu": resources.extra_gpu,
|
||||
}
|
||||
|
||||
|
||||
def _tune_error(msg):
|
||||
raise TuneError(msg)
|
||||
|
||||
|
||||
def make_parser(**kwargs):
|
||||
"""Returns a base argument parser for the ray.tune tool."""
|
||||
|
||||
@@ -56,7 +66,12 @@ def make_parser(**kwargs):
|
||||
help="Algorithm-specific configuration (e.g. env, hyperparams), "
|
||||
"specified in JSON.")
|
||||
parser.add_argument(
|
||||
"--resources", default='{"cpu": 1}', type=json_to_resources,
|
||||
"--resources", help="Deprecated, use --trial-resources.",
|
||||
type=lambda v: _tune_error(
|
||||
"The `resources` argument is no longer supported. "
|
||||
"Use `trial_resources` or --trial-resources instead."))
|
||||
parser.add_argument(
|
||||
"--trial-resources", default='{"cpu": 1}', type=json_to_resources,
|
||||
help="Machine resources to allocate per trial, e.g. "
|
||||
"'{\"cpu\": 64, \"gpu\": 8}'. Note that GPUs will not be assigned "
|
||||
"unless you specify them here.")
|
||||
|
||||
@@ -68,7 +68,7 @@ if __name__ == "__main__":
|
||||
"run": "my_class",
|
||||
"stop": {"training_iteration": 1 if args.smoke_test else 99999},
|
||||
"repeat": 20,
|
||||
"resources": {"cpu": 1, "gpu": 0},
|
||||
"trial_resources": {"cpu": 1, "gpu": 0},
|
||||
"config": {
|
||||
"width": lambda spec: 10 + int(90 * random.random()),
|
||||
"height": lambda spec: int(100 * random.random()),
|
||||
|
||||
@@ -79,7 +79,7 @@ if __name__ == "__main__":
|
||||
"run": "my_class",
|
||||
"stop": {"training_iteration": 2 if args.smoke_test else 99999},
|
||||
"repeat": 10,
|
||||
"resources": {"cpu": 1, "gpu": 0},
|
||||
"trial_resources": {"cpu": 1, "gpu": 0},
|
||||
"config": {
|
||||
"factor_1": 4.0,
|
||||
"factor_2": 1.0,
|
||||
|
||||
@@ -50,7 +50,7 @@ if __name__ == "__main__":
|
||||
"run": "PPO",
|
||||
"env": "Humanoid-v1",
|
||||
"repeat": 8,
|
||||
"resources": {"cpu": 4, "gpu": 1},
|
||||
"trial_resources": {"cpu": 4, "gpu": 1},
|
||||
"config": {
|
||||
"kl_coeff": 1.0,
|
||||
"num_workers": 8,
|
||||
|
||||
@@ -20,7 +20,7 @@ class Experiment(object):
|
||||
empty dict.
|
||||
config (dict): Algorithm-specific configuration
|
||||
(e.g. env, hyperparams). Defaults to empty dict.
|
||||
resources (dict): Machine resources to allocate per trial,
|
||||
trial_resources (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.
|
||||
@@ -36,13 +36,13 @@ class Experiment(object):
|
||||
checkpointing is enabled. Defaults to 3.
|
||||
"""
|
||||
def __init__(self, name, run, stop=None, config=None,
|
||||
resources=None, repeat=1, local_dir=None,
|
||||
trial_resources=None, repeat=1, local_dir=None,
|
||||
upload_dir="", checkpoint_freq=0, max_failures=3):
|
||||
spec = {
|
||||
"run": run,
|
||||
"stop": stop or {},
|
||||
"config": config or {},
|
||||
"resources": resources or {"cpu": 1, "gpu": 0},
|
||||
"trial_resources": trial_resources or {"cpu": 1, "gpu": 0},
|
||||
"repeat": repeat,
|
||||
"local_dir": local_dir or DEFAULT_RESULTS_DIR,
|
||||
"upload_dir": upload_dir,
|
||||
|
||||
@@ -130,7 +130,7 @@ class TrainableFunctionApiTest(unittest.TestCase):
|
||||
def f():
|
||||
run_experiments({"foo": {
|
||||
"run": "PPO",
|
||||
"resources": {"asdf": 1}
|
||||
"trial_resources": {"asdf": 1}
|
||||
}})
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
@@ -453,6 +453,27 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
except Exception as e:
|
||||
self.assertIn("a class", str(e))
|
||||
|
||||
def testExtraResources(self):
|
||||
ray.init(num_cpus=4, num_gpus=2)
|
||||
runner = TrialRunner()
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 1},
|
||||
"resources": Resources(cpu=1, gpu=0, extra_cpu=3, extra_gpu=1),
|
||||
}
|
||||
trials = [
|
||||
Trial("__fake", **kwargs),
|
||||
Trial("__fake", **kwargs)]
|
||||
for t in trials:
|
||||
runner.add_trial(t)
|
||||
|
||||
runner.step()
|
||||
self.assertEqual(trials[0].status, Trial.RUNNING)
|
||||
self.assertEqual(trials[1].status, Trial.PENDING)
|
||||
|
||||
runner.step()
|
||||
self.assertEqual(trials[0].status, Trial.TERMINATED)
|
||||
self.assertEqual(trials[1].status, Trial.PENDING)
|
||||
|
||||
def testResourceScheduler(self):
|
||||
ray.init(num_cpus=4, num_gpus=1)
|
||||
runner = TrialRunner()
|
||||
|
||||
@@ -62,7 +62,7 @@ class TuneServerSuite(unittest.TestCase):
|
||||
spec = {
|
||||
"run": "__fake",
|
||||
"stop": {"training_iteration": 3},
|
||||
"resources": dict(cpu=1, gpu=1),
|
||||
"trial_resources": dict(cpu=1, gpu=1),
|
||||
}
|
||||
client.add_trial("test", spec)
|
||||
runner.step()
|
||||
|
||||
+20
-25
@@ -28,34 +28,32 @@ def date_str():
|
||||
|
||||
|
||||
class Resources(
|
||||
namedtuple("Resources", [
|
||||
"cpu", "gpu", "driver_cpu_limit", "driver_gpu_limit"])):
|
||||
namedtuple("Resources", ["cpu", "gpu", "extra_cpu", "extra_gpu"])):
|
||||
"""Ray resources required to schedule a trial.
|
||||
|
||||
Attributes:
|
||||
cpu (int): Number of CPUs required for the trial total.
|
||||
gpu (int): Number of GPUs required for the trial total.
|
||||
driver_cpu_limit (int): Max CPUs allocated to the driver.
|
||||
Defaults to all of the required CPUs.
|
||||
driver_gpu_limit (int): Max GPUs allocated to the driver.
|
||||
Defaults to all of the required GPUs.
|
||||
cpu (int): Number of CPUs to allocate to the trial.
|
||||
gpu (int): Number of GPUs to allocate to the trial.
|
||||
extra_cpu (int): Extra CPUs to reserve in case the trial needs to
|
||||
launch additional Ray actors that use CPUs.
|
||||
extra_gpu (int): Extra GPUs to reserve in case the trial needs to
|
||||
launch additional Ray actors that use GPUs.
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls, cpu, gpu, driver_cpu_limit=None, driver_gpu_limit=None):
|
||||
if driver_cpu_limit is not None:
|
||||
assert driver_cpu_limit <= cpu
|
||||
else:
|
||||
driver_cpu_limit = cpu
|
||||
if driver_gpu_limit is not None:
|
||||
assert driver_gpu_limit <= gpu
|
||||
else:
|
||||
driver_gpu_limit = gpu
|
||||
def __new__(cls, cpu, gpu, extra_cpu=0, extra_gpu=0):
|
||||
return super(Resources, cls).__new__(
|
||||
cls, cpu, gpu, driver_cpu_limit, driver_gpu_limit)
|
||||
cls, cpu, gpu, extra_cpu, extra_gpu)
|
||||
|
||||
def summary_string(self):
|
||||
return "{} CPUs, {} GPUs".format(self.cpu, self.gpu)
|
||||
return "{} CPUs, {} GPUs".format(
|
||||
self.cpu + self.extra_cpu, self.gpu + self.extra_gpu)
|
||||
|
||||
def cpu_total(self):
|
||||
return self.cpu + self.extra_cpu
|
||||
|
||||
def gpu_total(self):
|
||||
return self.gpu + self.extra_gpu
|
||||
|
||||
|
||||
class Trial(object):
|
||||
@@ -66,9 +64,6 @@ class Trial(object):
|
||||
|
||||
Trials start in the PENDING state, and transition to RUNNING once started.
|
||||
On error it transitions to ERROR, otherwise TERMINATED on success.
|
||||
|
||||
The driver for the trial will be allocated at most `driver_cpu_limit` and
|
||||
`driver_gpu_limit` CPUs and GPUs.
|
||||
"""
|
||||
|
||||
PENDING = "PENDING"
|
||||
@@ -79,7 +74,7 @@ class Trial(object):
|
||||
|
||||
def __init__(
|
||||
self, trainable_name, config=None, local_dir=DEFAULT_RESULTS_DIR,
|
||||
experiment_tag=None, resources=Resources(cpu=1, gpu=0),
|
||||
experiment_tag="", resources=Resources(cpu=1, gpu=0),
|
||||
stopping_criterion=None, checkpoint_freq=0,
|
||||
restore_path=None, upload_dir=None, max_failures=0):
|
||||
"""Initialize a new trial.
|
||||
@@ -347,8 +342,8 @@ class Trial(object):
|
||||
trainable_cls = ray.tune.registry.get_registry().get(
|
||||
ray.tune.registry.TRAINABLE_CLASS, self.trainable_name)
|
||||
cls = ray.remote(
|
||||
num_cpus=self.resources.driver_cpu_limit,
|
||||
num_gpus=self.resources.driver_gpu_limit)(trainable_cls)
|
||||
num_cpus=self.resources.cpu,
|
||||
num_gpus=self.resources.gpu)(trainable_cls)
|
||||
if not self.result_logger:
|
||||
if not os.path.exists(self.local_dir):
|
||||
os.makedirs(self.local_dir)
|
||||
|
||||
@@ -181,7 +181,9 @@ class TrialRunner(object):
|
||||
|
||||
cpu_avail = self._avail_resources.cpu - self._committed_resources.cpu
|
||||
gpu_avail = self._avail_resources.gpu - self._committed_resources.gpu
|
||||
return resources.cpu <= cpu_avail and resources.gpu <= gpu_avail
|
||||
return (
|
||||
resources.cpu_total() <= cpu_avail and
|
||||
resources.gpu_total() <= gpu_avail)
|
||||
|
||||
def _can_launch_more(self):
|
||||
self._update_avail_resources()
|
||||
@@ -265,13 +267,13 @@ class TrialRunner(object):
|
||||
|
||||
def _commit_resources(self, resources):
|
||||
self._committed_resources = Resources(
|
||||
self._committed_resources.cpu + resources.cpu,
|
||||
self._committed_resources.gpu + resources.gpu)
|
||||
self._committed_resources.cpu + resources.cpu_total(),
|
||||
self._committed_resources.gpu + resources.gpu_total())
|
||||
|
||||
def _return_resources(self, resources):
|
||||
self._committed_resources = Resources(
|
||||
self._committed_resources.cpu - resources.cpu,
|
||||
self._committed_resources.gpu - resources.gpu)
|
||||
self._committed_resources.cpu - resources.cpu_total(),
|
||||
self._committed_resources.gpu - resources.gpu_total())
|
||||
assert self._committed_resources.cpu >= 0
|
||||
assert self._committed_resources.gpu >= 0
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ def generate_trials(unresolved_spec, output_path=''):
|
||||
config=spec.get("config", {}),
|
||||
local_dir=os.path.join(args.local_dir, output_path),
|
||||
experiment_tag=experiment_tag,
|
||||
resources=json_to_resources(spec.get("resources", {})),
|
||||
resources=json_to_resources(spec.get("trial_resources", {})),
|
||||
stopping_criterion=spec.get("stop", {}),
|
||||
checkpoint_freq=args.checkpoint_freq,
|
||||
restore_path=spec.get("restore"),
|
||||
@@ -118,7 +118,7 @@ _MAX_RESOLUTION_PASSES = 20
|
||||
def _format_vars(resolved_vars):
|
||||
out = []
|
||||
for path, value in sorted(resolved_vars.items()):
|
||||
if path[0] in ["run", "env", "resources"]:
|
||||
if path[0] in ["run", "env", "trial_resources"]:
|
||||
continue # TrialRunner already has these in the experiment_tag
|
||||
pieces = []
|
||||
last_string = True
|
||||
|
||||
Reference in New Issue
Block a user