mirror of
https://github.com/wassname/ray.git
synced 2026-07-14 11:17:54 +08:00
Lint Python files with Yapf (#1872)
This commit is contained in:
committed by
Robert Nishihara
parent
a3ddde398c
commit
74162d1492
@@ -9,14 +9,7 @@ from ray.tune.result import TrainingResult
|
||||
from ray.tune.trainable import Trainable
|
||||
from ray.tune.variant_generator import grid_search
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Trainable",
|
||||
"TrainingResult",
|
||||
"TuneError",
|
||||
"grid_search",
|
||||
"register_env",
|
||||
"register_trainable",
|
||||
"run_experiments",
|
||||
"Experiment"
|
||||
"Trainable", "TrainingResult", "TuneError", "grid_search", "register_env",
|
||||
"register_trainable", "run_experiments", "Experiment"
|
||||
]
|
||||
|
||||
@@ -35,10 +35,13 @@ class AsyncHyperBandScheduler(FIFOScheduler):
|
||||
halving rate, specified by the reduction factor.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, time_attr='training_iteration',
|
||||
reward_attr='episode_reward_mean', max_t=100,
|
||||
grace_period=10, reduction_factor=3, brackets=3):
|
||||
def __init__(self,
|
||||
time_attr='training_iteration',
|
||||
reward_attr='episode_reward_mean',
|
||||
max_t=100,
|
||||
grace_period=10,
|
||||
reduction_factor=3,
|
||||
brackets=3):
|
||||
assert max_t > 0, "Max (time_attr) not valid!"
|
||||
assert max_t >= grace_period, "grace_period must be <= max_t!"
|
||||
assert grace_period > 0, "grace_period must be positive!"
|
||||
@@ -51,8 +54,10 @@ class AsyncHyperBandScheduler(FIFOScheduler):
|
||||
self._trial_info = {} # Stores Trial -> Bracket
|
||||
|
||||
# Tracks state for new trial add
|
||||
self._brackets = [_Bracket(
|
||||
grace_period, max_t, reduction_factor, s) for s in range(brackets)]
|
||||
self._brackets = [
|
||||
_Bracket(grace_period, max_t, reduction_factor, s)
|
||||
for s in range(brackets)
|
||||
]
|
||||
self._counter = 0 # for
|
||||
self._num_stopped = 0
|
||||
self._reward_attr = reward_attr
|
||||
@@ -60,7 +65,7 @@ class AsyncHyperBandScheduler(FIFOScheduler):
|
||||
|
||||
def on_trial_add(self, trial_runner, trial):
|
||||
sizes = np.array([len(b._rungs) for b in self._brackets])
|
||||
probs = np.e ** (sizes - sizes.max())
|
||||
probs = np.e**(sizes - sizes.max())
|
||||
normalized = probs / probs.sum()
|
||||
idx = np.random.choice(len(self._brackets), p=normalized)
|
||||
self._trial_info[trial.trial_id] = self._brackets[idx]
|
||||
@@ -71,28 +76,23 @@ class AsyncHyperBandScheduler(FIFOScheduler):
|
||||
action = TrialScheduler.STOP
|
||||
else:
|
||||
bracket = self._trial_info[trial.trial_id]
|
||||
action = bracket.on_result(
|
||||
trial,
|
||||
getattr(result, self._time_attr),
|
||||
getattr(result, self._reward_attr))
|
||||
action = bracket.on_result(trial, getattr(result, self._time_attr),
|
||||
getattr(result, self._reward_attr))
|
||||
if action == TrialScheduler.STOP:
|
||||
self._num_stopped += 1
|
||||
return action
|
||||
|
||||
def on_trial_complete(self, trial_runner, trial, result):
|
||||
bracket = self._trial_info[trial.trial_id]
|
||||
bracket.on_result(
|
||||
trial,
|
||||
getattr(result, self._time_attr),
|
||||
getattr(result, self._reward_attr))
|
||||
bracket.on_result(trial, getattr(result, self._time_attr),
|
||||
getattr(result, self._reward_attr))
|
||||
del self._trial_info[trial.trial_id]
|
||||
|
||||
def on_trial_remove(self, trial_runner, trial):
|
||||
del self._trial_info[trial.trial_id]
|
||||
|
||||
def debug_string(self):
|
||||
out = "Using AsyncHyperBand: num_stopped={}".format(
|
||||
self._num_stopped)
|
||||
out = "Using AsyncHyperBand: num_stopped={}".format(self._num_stopped)
|
||||
out += "\n" + "\n".join([b.debug_str() for b in self._brackets])
|
||||
return out
|
||||
|
||||
@@ -111,6 +111,7 @@ class _Bracket():
|
||||
>>> b.on_result(trial3, 1, 1) # STOP
|
||||
>>> b.cutoff(b._rungs[0][1]) == 2.0
|
||||
"""
|
||||
|
||||
def __init__(self, min_t, max_t, reduction_factor, s):
|
||||
self.rf = reduction_factor
|
||||
MAX_RUNGS = int(np.log(max_t / min_t) / np.log(self.rf) - s + 1)
|
||||
@@ -140,9 +141,10 @@ class _Bracket():
|
||||
return action
|
||||
|
||||
def debug_str(self):
|
||||
iters = " | ".join(
|
||||
["Iter {:.3f}: {}".format(milestone, self.cutoff(recorded))
|
||||
for milestone, recorded in self._rungs])
|
||||
iters = " | ".join([
|
||||
"Iter {:.3f}: {}".format(milestone, self.cutoff(recorded))
|
||||
for milestone, recorded in self._rungs
|
||||
])
|
||||
return "Bracket: " + iters
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
import argparse
|
||||
import json
|
||||
|
||||
@@ -24,8 +23,8 @@ def json_to_resources(data):
|
||||
"Unknown resource type {}, must be one of {}".format(
|
||||
k, Resources._fields))
|
||||
return Resources(
|
||||
data.get("cpu", 1), data.get("gpu", 0),
|
||||
data.get("extra_cpu", 0), data.get("extra_gpu", 0))
|
||||
data.get("cpu", 1), data.get("gpu", 0), data.get("extra_cpu", 0),
|
||||
data.get("extra_gpu", 0))
|
||||
|
||||
|
||||
def resources_to_json(resources):
|
||||
@@ -50,59 +49,85 @@ def make_parser(**kwargs):
|
||||
|
||||
# Note: keep this in sync with rllib/train.py
|
||||
parser.add_argument(
|
||||
"--run", default=None, type=str,
|
||||
"--run",
|
||||
default=None,
|
||||
type=str,
|
||||
help="The algorithm or model to train. This may refer to the name "
|
||||
"of a built-on algorithm (e.g. RLLib's DQN or PPO), or a "
|
||||
"user-defined trainable function or class registered in the "
|
||||
"tune registry.")
|
||||
parser.add_argument(
|
||||
"--stop", default="{}", type=json.loads,
|
||||
"--stop",
|
||||
default="{}",
|
||||
type=json.loads,
|
||||
help="The stopping criteria, specified in JSON. The keys may be any "
|
||||
"field in TrainingResult, e.g. "
|
||||
"'{\"time_total_s\": 600, \"timesteps_total\": 100000}' to stop "
|
||||
"after 600 seconds or 100k timesteps, whichever is reached first.")
|
||||
parser.add_argument(
|
||||
"--config", default="{}", type=json.loads,
|
||||
"--config",
|
||||
default="{}",
|
||||
type=json.loads,
|
||||
help="Algorithm-specific configuration (e.g. env, hyperparams), "
|
||||
"specified in JSON.")
|
||||
parser.add_argument(
|
||||
"--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."))
|
||||
"--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,
|
||||
"--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.")
|
||||
parser.add_argument(
|
||||
"--repeat", default=1, type=int,
|
||||
"--repeat",
|
||||
default=1,
|
||||
type=int,
|
||||
help="Number of times to repeat each trial.")
|
||||
parser.add_argument(
|
||||
"--local-dir", default=DEFAULT_RESULTS_DIR, type=str,
|
||||
"--local-dir",
|
||||
default=DEFAULT_RESULTS_DIR,
|
||||
type=str,
|
||||
help="Local dir to save training results to. Defaults to '{}'.".format(
|
||||
DEFAULT_RESULTS_DIR))
|
||||
parser.add_argument(
|
||||
"--upload-dir", default="", type=str,
|
||||
"--upload-dir",
|
||||
default="",
|
||||
type=str,
|
||||
help="Optional URI to sync training results to (e.g. s3://bucket).")
|
||||
parser.add_argument(
|
||||
"--checkpoint-freq", default=0, type=int,
|
||||
"--checkpoint-freq",
|
||||
default=0,
|
||||
type=int,
|
||||
help="How many training iterations between checkpoints. "
|
||||
"A value of 0 (default) disables checkpointing.")
|
||||
parser.add_argument(
|
||||
"--max-failures", default=3, type=int,
|
||||
"--max-failures",
|
||||
default=3,
|
||||
type=int,
|
||||
help="Try to recover a trial from its last checkpoint at least this "
|
||||
"many times. Only applies if checkpointing is enabled.")
|
||||
parser.add_argument(
|
||||
"--scheduler", default="FIFO", type=str,
|
||||
"--scheduler",
|
||||
default="FIFO",
|
||||
type=str,
|
||||
help="FIFO (default), MedianStopping, AsyncHyperBand,"
|
||||
"HyperBand, or HyperOpt.")
|
||||
"HyperBand, or HyperOpt.")
|
||||
parser.add_argument(
|
||||
"--scheduler-config", default="{}", type=json.loads,
|
||||
"--scheduler-config",
|
||||
default="{}",
|
||||
type=json.loads,
|
||||
help="Config options to pass to the scheduler.")
|
||||
|
||||
# Note: this currently only makes sense when running a single trial
|
||||
parser.add_argument("--restore", default=None, type=str,
|
||||
help="If specified, restore from this checkpoint.")
|
||||
parser.add_argument(
|
||||
"--restore",
|
||||
default=None,
|
||||
type=str,
|
||||
help="If specified, restore from this checkpoint.")
|
||||
|
||||
return parser
|
||||
|
||||
@@ -60,18 +60,27 @@ if __name__ == "__main__":
|
||||
# `episode_reward_mean` as the
|
||||
# objective and `timesteps_total` as the time unit.
|
||||
ahb = AsyncHyperBandScheduler(
|
||||
time_attr="timesteps_total", reward_attr="episode_reward_mean",
|
||||
grace_period=5, max_t=100)
|
||||
time_attr="timesteps_total",
|
||||
reward_attr="episode_reward_mean",
|
||||
grace_period=5,
|
||||
max_t=100)
|
||||
|
||||
run_experiments({
|
||||
"asynchyperband_test": {
|
||||
"run": "my_class",
|
||||
"stop": {"training_iteration": 1 if args.smoke_test else 99999},
|
||||
"repeat": 20,
|
||||
"trial_resources": {"cpu": 1, "gpu": 0},
|
||||
"config": {
|
||||
"width": lambda spec: 10 + int(90 * random.random()),
|
||||
"height": lambda spec: int(100 * random.random()),
|
||||
},
|
||||
}
|
||||
}, scheduler=ahb)
|
||||
run_experiments(
|
||||
{
|
||||
"asynchyperband_test": {
|
||||
"run": "my_class",
|
||||
"stop": {
|
||||
"training_iteration": 1 if args.smoke_test else 99999
|
||||
},
|
||||
"repeat": 20,
|
||||
"trial_resources": {
|
||||
"cpu": 1,
|
||||
"gpu": 0
|
||||
},
|
||||
"config": {
|
||||
"width": lambda spec: 10 + int(90 * random.random()),
|
||||
"height": lambda spec: int(100 * random.random()),
|
||||
},
|
||||
}
|
||||
},
|
||||
scheduler=ahb)
|
||||
|
||||
@@ -59,7 +59,8 @@ if __name__ == "__main__":
|
||||
# Hyperband early stopping, configured with `episode_reward_mean` as the
|
||||
# objective and `timesteps_total` as the time unit.
|
||||
hyperband = HyperBandScheduler(
|
||||
time_attr="timesteps_total", reward_attr="episode_reward_mean",
|
||||
time_attr="timesteps_total",
|
||||
reward_attr="episode_reward_mean",
|
||||
max_t=100)
|
||||
|
||||
exp = Experiment(
|
||||
|
||||
@@ -12,8 +12,8 @@ def easy_objective(config, reporter):
|
||||
time.sleep(0.2)
|
||||
reporter(
|
||||
timesteps_total=1,
|
||||
episode_reward_mean=-((config["height"]-14) ** 2
|
||||
+ abs(config["width"]-3)))
|
||||
episode_reward_mean=-(
|
||||
(config["height"] - 14)**2 + abs(config["width"] - 3)))
|
||||
time.sleep(0.2)
|
||||
|
||||
|
||||
@@ -34,12 +34,18 @@ if __name__ == '__main__':
|
||||
'height': hp.uniform('height', -100, 100),
|
||||
}
|
||||
|
||||
config = {"my_exp": {
|
||||
config = {
|
||||
"my_exp": {
|
||||
"run": "exp",
|
||||
"repeat": 5 if args.smoke_test else 1000,
|
||||
"stop": {"training_iteration": 1},
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"config": {
|
||||
"space": space}}}
|
||||
"space": space
|
||||
}
|
||||
}
|
||||
}
|
||||
hpo_sched = HyperOptScheduler()
|
||||
|
||||
run_experiments(config, verbose=False, scheduler=hpo_sched)
|
||||
|
||||
@@ -42,8 +42,11 @@ class MyTrainableClass(Trainable):
|
||||
def _save(self, checkpoint_dir):
|
||||
path = os.path.join(checkpoint_dir, "checkpoint")
|
||||
with open(path, "w") as f:
|
||||
f.write(json.dumps(
|
||||
{"timestep": self.timestep, "value": self.current_value}))
|
||||
f.write(
|
||||
json.dumps({
|
||||
"timestep": self.timestep,
|
||||
"value": self.current_value
|
||||
}))
|
||||
return path
|
||||
|
||||
def _restore(self, checkpoint_path):
|
||||
@@ -63,7 +66,8 @@ if __name__ == "__main__":
|
||||
ray.init()
|
||||
|
||||
pbt = PopulationBasedTraining(
|
||||
time_attr="training_iteration", reward_attr="episode_reward_mean",
|
||||
time_attr="training_iteration",
|
||||
reward_attr="episode_reward_mean",
|
||||
perturbation_interval=10,
|
||||
hyperparam_mutations={
|
||||
# Allow for scaling-based perturbations, with a uniform backing
|
||||
@@ -74,15 +78,23 @@ if __name__ == "__main__":
|
||||
})
|
||||
|
||||
# Try to find the best factor 1 and factor 2
|
||||
run_experiments({
|
||||
"pbt_test": {
|
||||
"run": "my_class",
|
||||
"stop": {"training_iteration": 2 if args.smoke_test else 99999},
|
||||
"repeat": 10,
|
||||
"trial_resources": {"cpu": 1, "gpu": 0},
|
||||
"config": {
|
||||
"factor_1": 4.0,
|
||||
"factor_2": 1.0,
|
||||
},
|
||||
}
|
||||
}, scheduler=pbt, verbose=False)
|
||||
run_experiments(
|
||||
{
|
||||
"pbt_test": {
|
||||
"run": "my_class",
|
||||
"stop": {
|
||||
"training_iteration": 2 if args.smoke_test else 99999
|
||||
},
|
||||
"repeat": 10,
|
||||
"trial_resources": {
|
||||
"cpu": 1,
|
||||
"gpu": 0
|
||||
},
|
||||
"config": {
|
||||
"factor_1": 4.0,
|
||||
"factor_2": 1.0,
|
||||
},
|
||||
}
|
||||
},
|
||||
scheduler=pbt,
|
||||
verbose=False)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""Example of using PBT with RLlib.
|
||||
|
||||
Note that this requires a cluster with at least 8 GPUs in order for all trials
|
||||
@@ -30,7 +29,8 @@ if __name__ == "__main__":
|
||||
return config
|
||||
|
||||
pbt = PopulationBasedTraining(
|
||||
time_attr="time_total_s", reward_attr="episode_reward_mean",
|
||||
time_attr="time_total_s",
|
||||
reward_attr="episode_reward_mean",
|
||||
perturbation_interval=120,
|
||||
resample_probability=0.25,
|
||||
# Specifies the mutations of these hyperparams
|
||||
@@ -45,26 +45,40 @@ if __name__ == "__main__":
|
||||
custom_explore_fn=explore)
|
||||
|
||||
ray.init()
|
||||
run_experiments({
|
||||
"pbt_humanoid_test": {
|
||||
"run": "PPO",
|
||||
"env": "Humanoid-v1",
|
||||
"repeat": 8,
|
||||
"trial_resources": {"cpu": 4, "gpu": 1},
|
||||
"config": {
|
||||
"kl_coeff": 1.0,
|
||||
"num_workers": 8,
|
||||
"devices": ["/gpu:0"],
|
||||
"model": {"free_log_std": True},
|
||||
# These params are tuned from a fixed starting value.
|
||||
"lambda": 0.95,
|
||||
"clip_param": 0.2,
|
||||
"sgd_stepsize": 1e-4,
|
||||
# These params start off randomly drawn from a set.
|
||||
"num_sgd_iter": lambda spec: random.choice([10, 20, 30]),
|
||||
"sgd_batchsize": lambda spec: random.choice([128, 512, 2048]),
|
||||
"timesteps_per_batch":
|
||||
run_experiments(
|
||||
{
|
||||
"pbt_humanoid_test": {
|
||||
"run": "PPO",
|
||||
"env": "Humanoid-v1",
|
||||
"repeat": 8,
|
||||
"trial_resources": {
|
||||
"cpu": 4,
|
||||
"gpu": 1
|
||||
},
|
||||
"config": {
|
||||
"kl_coeff":
|
||||
1.0,
|
||||
"num_workers":
|
||||
8,
|
||||
"devices": ["/gpu:0"],
|
||||
"model": {
|
||||
"free_log_std": True
|
||||
},
|
||||
# These params are tuned from a fixed starting value.
|
||||
"lambda":
|
||||
0.95,
|
||||
"clip_param":
|
||||
0.2,
|
||||
"sgd_stepsize":
|
||||
1e-4,
|
||||
# These params start off randomly drawn from a set.
|
||||
"num_sgd_iter":
|
||||
lambda spec: random.choice([10, 20, 30]),
|
||||
"sgd_batchsize":
|
||||
lambda spec: random.choice([128, 512, 2048]),
|
||||
"timesteps_per_batch":
|
||||
lambda spec: random.choice([10000, 20000, 40000])
|
||||
},
|
||||
},
|
||||
},
|
||||
}, scheduler=pbt)
|
||||
scheduler=pbt)
|
||||
|
||||
@@ -29,12 +29,10 @@ from ray.tune import Trainable
|
||||
from ray.tune import TrainingResult
|
||||
from ray.tune.pbt import PopulationBasedTraining
|
||||
|
||||
|
||||
num_classes = 10
|
||||
|
||||
|
||||
class Cifar10Model(Trainable):
|
||||
|
||||
def _read_data(self):
|
||||
# The data, split between train and test sets:
|
||||
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
|
||||
@@ -54,27 +52,51 @@ class Cifar10Model(Trainable):
|
||||
x = Input(shape=(32, 32, 3))
|
||||
y = x
|
||||
y = Convolution2D(
|
||||
filters=64, kernel_size=3, strides=1, padding="same",
|
||||
activation="relu", kernel_initializer="he_normal")(y)
|
||||
filters=64,
|
||||
kernel_size=3,
|
||||
strides=1,
|
||||
padding="same",
|
||||
activation="relu",
|
||||
kernel_initializer="he_normal")(y)
|
||||
y = Convolution2D(
|
||||
filters=64, kernel_size=3, strides=1, padding="same",
|
||||
activation="relu", kernel_initializer="he_normal")(y)
|
||||
filters=64,
|
||||
kernel_size=3,
|
||||
strides=1,
|
||||
padding="same",
|
||||
activation="relu",
|
||||
kernel_initializer="he_normal")(y)
|
||||
y = MaxPooling2D(pool_size=2, strides=2, padding="same")(y)
|
||||
|
||||
y = Convolution2D(
|
||||
filters=128, kernel_size=3, strides=1, padding="same",
|
||||
activation="relu", kernel_initializer="he_normal")(y)
|
||||
filters=128,
|
||||
kernel_size=3,
|
||||
strides=1,
|
||||
padding="same",
|
||||
activation="relu",
|
||||
kernel_initializer="he_normal")(y)
|
||||
y = Convolution2D(
|
||||
filters=128, kernel_size=3, strides=1, padding="same",
|
||||
activation="relu", kernel_initializer="he_normal")(y)
|
||||
filters=128,
|
||||
kernel_size=3,
|
||||
strides=1,
|
||||
padding="same",
|
||||
activation="relu",
|
||||
kernel_initializer="he_normal")(y)
|
||||
y = MaxPooling2D(pool_size=2, strides=2, padding="same")(y)
|
||||
|
||||
y = Convolution2D(
|
||||
filters=256, kernel_size=3, strides=1, padding="same",
|
||||
activation="relu", kernel_initializer="he_normal")(y)
|
||||
filters=256,
|
||||
kernel_size=3,
|
||||
strides=1,
|
||||
padding="same",
|
||||
activation="relu",
|
||||
kernel_initializer="he_normal")(y)
|
||||
y = Convolution2D(
|
||||
filters=256, kernel_size=3, strides=1, padding="same",
|
||||
activation="relu", kernel_initializer="he_normal")(y)
|
||||
filters=256,
|
||||
kernel_size=3,
|
||||
strides=1,
|
||||
padding="same",
|
||||
activation="relu",
|
||||
kernel_initializer="he_normal")(y)
|
||||
y = MaxPooling2D(pool_size=2, strides=2, padding="same")(y)
|
||||
|
||||
y = Flatten()(y)
|
||||
@@ -91,9 +113,10 @@ class Cifar10Model(Trainable):
|
||||
model = self._build_model(x_train.shape[1:])
|
||||
|
||||
opt = tf.keras.optimizers.Adadelta()
|
||||
model.compile(loss="categorical_crossentropy",
|
||||
optimizer=opt,
|
||||
metrics=["accuracy"])
|
||||
model.compile(
|
||||
loss="categorical_crossentropy",
|
||||
optimizer=opt,
|
||||
metrics=["accuracy"])
|
||||
self.model = model
|
||||
|
||||
def _train(self):
|
||||
@@ -134,8 +157,7 @@ class Cifar10Model(Trainable):
|
||||
|
||||
# loss, accuracy
|
||||
_, accuracy = self.model.evaluate(x_test, y_test, verbose=0)
|
||||
return TrainingResult(timesteps_this_iter=10,
|
||||
mean_accuracy=accuracy)
|
||||
return TrainingResult(timesteps_this_iter=10, mean_accuracy=accuracy)
|
||||
|
||||
def _save(self, checkpoint_dir):
|
||||
file_path = checkpoint_dir + "/model"
|
||||
@@ -154,15 +176,17 @@ class Cifar10Model(Trainable):
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--smoke-test",
|
||||
action="store_true",
|
||||
help="Finish quickly for testing")
|
||||
parser.add_argument(
|
||||
"--smoke-test", action="store_true", help="Finish quickly for testing")
|
||||
args, _ = parser.parse_known_args()
|
||||
|
||||
register_trainable("train_cifar10", Cifar10Model)
|
||||
train_spec = {
|
||||
"run": "train_cifar10",
|
||||
"trial_resources": {"cpu": 1, "gpu": 1},
|
||||
"trial_resources": {
|
||||
"cpu": 1,
|
||||
"gpu": 1
|
||||
},
|
||||
"stop": {
|
||||
"mean_accuracy": 0.80,
|
||||
"timesteps_total": 300,
|
||||
@@ -170,7 +194,7 @@ if __name__ == "__main__":
|
||||
"config": {
|
||||
"epochs": 1,
|
||||
"batch_size": 64,
|
||||
"lr": grid_search([10 ** -4, 10 ** -5]),
|
||||
"lr": grid_search([10**-4, 10**-5]),
|
||||
"decay": lambda spec: spec.config.lr / 100.0,
|
||||
"dropout": grid_search([0.25, 0.5]),
|
||||
},
|
||||
@@ -178,17 +202,17 @@ if __name__ == "__main__":
|
||||
}
|
||||
|
||||
if args.smoke_test:
|
||||
train_spec["config"]["lr"] = 10 ** -4
|
||||
train_spec["config"]["lr"] = 10**-4
|
||||
train_spec["config"]["dropout"] = 0.5
|
||||
|
||||
ray.init()
|
||||
|
||||
pbt = PopulationBasedTraining(
|
||||
time_attr="timesteps_total", reward_attr="mean_accuracy",
|
||||
time_attr="timesteps_total",
|
||||
reward_attr="mean_accuracy",
|
||||
perturbation_interval=10,
|
||||
hyperparam_mutations={
|
||||
"dropout": lambda _: np.random.uniform(0, 1),
|
||||
})
|
||||
|
||||
run_experiments({"pbt_cifar10": train_spec},
|
||||
scheduler=pbt)
|
||||
run_experiments({"pbt_cifar10": train_spec}, scheduler=pbt)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
"""A deep MNIST classifier using convolutional layers.
|
||||
|
||||
See extensive documentation at
|
||||
@@ -42,7 +41,7 @@ import tensorflow as tf
|
||||
|
||||
FLAGS = None
|
||||
status_reporter = None # used to report training status back to Ray
|
||||
activation_fn = None # e.g. tf.nn.relu
|
||||
activation_fn = None # e.g. tf.nn.relu
|
||||
|
||||
|
||||
def deepnn(x):
|
||||
@@ -90,7 +89,7 @@ def deepnn(x):
|
||||
W_fc1 = weight_variable([7 * 7 * 64, 1024])
|
||||
b_fc1 = bias_variable([1024])
|
||||
|
||||
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
|
||||
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
|
||||
h_fc1 = activation_fn(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
|
||||
|
||||
# Dropout - controls the complexity of the model, prevents co-adaptation of
|
||||
@@ -173,7 +172,10 @@ def main(_):
|
||||
batch = mnist.train.next_batch(50)
|
||||
if i % 10 == 0:
|
||||
train_accuracy = accuracy.eval(feed_dict={
|
||||
x: batch[0], y_: batch[1], keep_prob: 1.0})
|
||||
x: batch[0],
|
||||
y_: batch[1],
|
||||
keep_prob: 1.0
|
||||
})
|
||||
|
||||
# !!! Report status to ray.tune !!!
|
||||
if status_reporter:
|
||||
@@ -181,11 +183,17 @@ def main(_):
|
||||
timesteps_total=i, mean_accuracy=train_accuracy)
|
||||
|
||||
print('step %d, training accuracy %g' % (i, train_accuracy))
|
||||
train_step.run(
|
||||
feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
|
||||
train_step.run(feed_dict={
|
||||
x: batch[0],
|
||||
y_: batch[1],
|
||||
keep_prob: 0.5
|
||||
})
|
||||
|
||||
print('test accuracy %g' % accuracy.eval(feed_dict={
|
||||
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
|
||||
x: mnist.test.images,
|
||||
y_: mnist.test.labels,
|
||||
keep_prob: 1.0
|
||||
}))
|
||||
|
||||
|
||||
# !!! Entrypoint for ray.tune !!!
|
||||
@@ -195,7 +203,9 @@ def train(config={'activation': 'relu'}, reporter=None):
|
||||
activation_fn = getattr(tf.nn, config['activation'])
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'--data_dir', type=str, default='/tmp/tensorflow/mnist/input_data',
|
||||
'--data_dir',
|
||||
type=str,
|
||||
default='/tmp/tensorflow/mnist/input_data',
|
||||
help='Directory for storing input data')
|
||||
FLAGS, unparsed = parser.parse_known_args()
|
||||
tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
|
||||
@@ -213,8 +223,8 @@ if __name__ == '__main__':
|
||||
'run': 'train_mnist',
|
||||
'repeat': 10,
|
||||
'stop': {
|
||||
'mean_accuracy': 0.99,
|
||||
'timesteps_total': 600,
|
||||
'mean_accuracy': 0.99,
|
||||
'timesteps_total': 600,
|
||||
},
|
||||
'config': {
|
||||
'activation': grid_search(['relu', 'elu', 'tanh']),
|
||||
@@ -228,8 +238,12 @@ if __name__ == '__main__':
|
||||
ray.init()
|
||||
|
||||
from ray.tune.async_hyperband import AsyncHyperBandScheduler
|
||||
run_experiments({'tune_mnist_test': mnist_spec},
|
||||
scheduler=AsyncHyperBandScheduler(
|
||||
time_attr="timesteps_total",
|
||||
reward_attr="mean_accuracy",
|
||||
max_t=600,))
|
||||
run_experiments(
|
||||
{
|
||||
'tune_mnist_test': mnist_spec
|
||||
},
|
||||
scheduler=AsyncHyperBandScheduler(
|
||||
time_attr="timesteps_total",
|
||||
reward_attr="mean_accuracy",
|
||||
max_t=600,
|
||||
))
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
"""A deep MNIST classifier using convolutional layers.
|
||||
|
||||
See extensive documentation at
|
||||
@@ -42,7 +41,7 @@ import tensorflow as tf
|
||||
|
||||
FLAGS = None
|
||||
status_reporter = None # used to report training status back to Ray
|
||||
activation_fn = None # e.g. tf.nn.relu
|
||||
activation_fn = None # e.g. tf.nn.relu
|
||||
|
||||
|
||||
def deepnn(x):
|
||||
@@ -90,7 +89,7 @@ def deepnn(x):
|
||||
W_fc1 = weight_variable([7 * 7 * 64, 1024])
|
||||
b_fc1 = bias_variable([1024])
|
||||
|
||||
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
|
||||
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
|
||||
h_fc1 = activation_fn(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
|
||||
|
||||
# Dropout - controls the complexity of the model, prevents co-adaptation of
|
||||
@@ -173,7 +172,10 @@ def main(_):
|
||||
batch = mnist.train.next_batch(50)
|
||||
if i % 10 == 0:
|
||||
train_accuracy = accuracy.eval(feed_dict={
|
||||
x: batch[0], y_: batch[1], keep_prob: 1.0})
|
||||
x: batch[0],
|
||||
y_: batch[1],
|
||||
keep_prob: 1.0
|
||||
})
|
||||
|
||||
# !!! Report status to ray.tune !!!
|
||||
if status_reporter:
|
||||
@@ -181,11 +183,17 @@ def main(_):
|
||||
timesteps_total=i, mean_accuracy=train_accuracy)
|
||||
|
||||
print('step %d, training accuracy %g' % (i, train_accuracy))
|
||||
train_step.run(
|
||||
feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
|
||||
train_step.run(feed_dict={
|
||||
x: batch[0],
|
||||
y_: batch[1],
|
||||
keep_prob: 0.5
|
||||
})
|
||||
|
||||
print('test accuracy %g' % accuracy.eval(feed_dict={
|
||||
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
|
||||
x: mnist.test.images,
|
||||
y_: mnist.test.labels,
|
||||
keep_prob: 1.0
|
||||
}))
|
||||
|
||||
|
||||
# !!! Entrypoint for ray.tune !!!
|
||||
@@ -195,7 +203,9 @@ def train(config={'activation': 'relu'}, reporter=None):
|
||||
activation_fn = getattr(tf.nn, config['activation'])
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'--data_dir', type=str, default='/tmp/tensorflow/mnist/input_data',
|
||||
'--data_dir',
|
||||
type=str,
|
||||
default='/tmp/tensorflow/mnist/input_data',
|
||||
help='Directory for storing input data')
|
||||
FLAGS, unparsed = parser.parse_known_args()
|
||||
tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
|
||||
@@ -212,8 +222,8 @@ if __name__ == '__main__':
|
||||
mnist_spec = {
|
||||
'run': 'train_mnist',
|
||||
'stop': {
|
||||
'mean_accuracy': 0.99,
|
||||
'time_total_s': 600,
|
||||
'mean_accuracy': 0.99,
|
||||
'time_total_s': 600,
|
||||
},
|
||||
'config': {
|
||||
'activation': grid_search(['relu', 'elu', 'tanh']),
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
|
||||
"""A deep MNIST classifier using convolutional layers.
|
||||
See extensive documentation at
|
||||
https://www.tensorflow.org/get_started/mnist/pros
|
||||
@@ -39,7 +38,7 @@ from tensorflow.examples.tutorials.mnist import input_data
|
||||
import tensorflow as tf
|
||||
import numpy as np
|
||||
|
||||
activation_fn = None # e.g. tf.nn.relu
|
||||
activation_fn = None # e.g. tf.nn.relu
|
||||
|
||||
|
||||
def setupCNN(x):
|
||||
@@ -85,7 +84,7 @@ def setupCNN(x):
|
||||
W_fc1 = weight_variable([7 * 7 * 64, 1024])
|
||||
b_fc1 = bias_variable([1024])
|
||||
|
||||
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
|
||||
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
|
||||
h_fc1 = activation_fn(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
|
||||
|
||||
# Dropout - controls the complexity of the model, prevents co-adaptation of
|
||||
@@ -182,14 +181,18 @@ class TrainMNIST(Trainable):
|
||||
self.sess.run(
|
||||
self.train_step,
|
||||
feed_dict={
|
||||
self.x: batch[0], self.y_: batch[1], self.keep_prob: 0.5
|
||||
self.x: batch[0],
|
||||
self.y_: batch[1],
|
||||
self.keep_prob: 0.5
|
||||
})
|
||||
|
||||
batch = self.mnist.train.next_batch(50)
|
||||
train_accuracy = self.sess.run(
|
||||
self.accuracy,
|
||||
feed_dict={
|
||||
self.x: batch[0], self.y_: batch[1], self.keep_prob: 1.0
|
||||
self.x: batch[0],
|
||||
self.y_: batch[1],
|
||||
self.keep_prob: 1.0
|
||||
})
|
||||
|
||||
self.iterations += 1
|
||||
@@ -215,11 +218,11 @@ if __name__ == '__main__':
|
||||
mnist_spec = {
|
||||
'run': 'my_class',
|
||||
'stop': {
|
||||
'mean_accuracy': 0.99,
|
||||
'time_total_s': 600,
|
||||
'mean_accuracy': 0.99,
|
||||
'time_total_s': 600,
|
||||
},
|
||||
'config': {
|
||||
'learning_rate': lambda spec: 10 ** np.random.uniform(-5, -3),
|
||||
'learning_rate': lambda spec: 10**np.random.uniform(-5, -3),
|
||||
'activation': grid_search(['relu', 'elu', 'tanh']),
|
||||
},
|
||||
"repeat": 10,
|
||||
@@ -231,8 +234,6 @@ if __name__ == '__main__':
|
||||
|
||||
ray.init()
|
||||
hyperband = HyperBandScheduler(
|
||||
time_attr="timesteps_total", reward_attr="mean_accuracy",
|
||||
max_t=100)
|
||||
time_attr="timesteps_total", reward_attr="mean_accuracy", max_t=100)
|
||||
|
||||
run_experiments(
|
||||
{'mnist_hyperband_test': mnist_spec}, scheduler=hyperband)
|
||||
run_experiments({'mnist_hyperband_test': mnist_spec}, scheduler=hyperband)
|
||||
|
||||
@@ -35,14 +35,26 @@ class Experiment(object):
|
||||
checkpoint at least this many times. Only applies if
|
||||
checkpointing is enabled. Defaults to 3.
|
||||
"""
|
||||
def __init__(self, name, run, stop=None, config=None,
|
||||
trial_resources=None, repeat=1, local_dir=None,
|
||||
upload_dir="", checkpoint_freq=0, max_failures=3):
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
run,
|
||||
stop=None,
|
||||
config=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 {},
|
||||
"trial_resources": trial_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,
|
||||
|
||||
@@ -91,8 +91,8 @@ class FunctionRunner(Trainable):
|
||||
for k in self._default_config:
|
||||
if k in scrubbed_config:
|
||||
del scrubbed_config[k]
|
||||
self._runner = _RunnerThread(
|
||||
entrypoint, scrubbed_config, self._status_reporter)
|
||||
self._runner = _RunnerThread(entrypoint, scrubbed_config,
|
||||
self._status_reporter)
|
||||
self._start_time = time.time()
|
||||
self._last_reported_timestep = 0
|
||||
self._runner.start()
|
||||
@@ -104,9 +104,8 @@ class FunctionRunner(Trainable):
|
||||
|
||||
def _train(self):
|
||||
time.sleep(
|
||||
self.config.get(
|
||||
"script_min_iter_time_s",
|
||||
self._default_config["script_min_iter_time_s"]))
|
||||
self.config.get("script_min_iter_time_s",
|
||||
self._default_config["script_min_iter_time_s"]))
|
||||
result = self._status_reporter._get_and_clear_status()
|
||||
while result is None:
|
||||
time.sleep(1)
|
||||
|
||||
@@ -102,9 +102,8 @@ class HyperOptScheduler(FIFOScheduler):
|
||||
self._hpopt_trials.refresh()
|
||||
|
||||
# Get new suggestion from
|
||||
new_trials = self.algo(
|
||||
new_ids, self.domain, self._hpopt_trials,
|
||||
self.rstate.randint(2 ** 31 - 1))
|
||||
new_trials = self.algo(new_ids, self.domain, self._hpopt_trials,
|
||||
self.rstate.randint(2**31 - 1))
|
||||
self._hpopt_trials.insert_trial_docs(new_trials)
|
||||
self._hpopt_trials.refresh()
|
||||
new_trial = new_trials[0]
|
||||
@@ -112,8 +111,11 @@ class HyperOptScheduler(FIFOScheduler):
|
||||
suggested_config = hpo.base.spec_from_misc(new_trial["misc"])
|
||||
new_cfg.update(suggested_config)
|
||||
|
||||
kv_str = "_".join(["{}={}".format(k, str(v)[:5])
|
||||
for k, v in sorted(suggested_config.items())])
|
||||
kv_str = "_".join([
|
||||
"{}={}".format(k,
|
||||
str(v)[:5])
|
||||
for k, v in sorted(suggested_config.items())
|
||||
])
|
||||
experiment_tag = "{}_{}".format(new_trial_id, kv_str)
|
||||
|
||||
# Keep this consistent with tune.variant_generator
|
||||
@@ -166,8 +168,7 @@ class HyperOptScheduler(FIFOScheduler):
|
||||
del self._tune_to_hp[trial]
|
||||
|
||||
def _to_hyperopt_result(self, result):
|
||||
return {"loss": -getattr(result, self._reward_attr),
|
||||
"status": "ok"}
|
||||
return {"loss": -getattr(result, self._reward_attr), "status": "ok"}
|
||||
|
||||
def _get_hyperopt_trial(self, tid):
|
||||
return [t for t in self._hpopt_trials.trials if t["tid"] == tid][0]
|
||||
@@ -183,8 +184,9 @@ class HyperOptScheduler(FIFOScheduler):
|
||||
experiments and trials left to run. If self._max_concurrent is None,
|
||||
scheduler will add new trial if there is none that are pending.
|
||||
"""
|
||||
pending = [t for t in trial_runner.get_trials()
|
||||
if t.status == Trial.PENDING]
|
||||
pending = [
|
||||
t for t in trial_runner.get_trials() if t.status == Trial.PENDING
|
||||
]
|
||||
if self._num_trials_left <= 0:
|
||||
return
|
||||
if self._max_concurrent is None:
|
||||
|
||||
@@ -66,9 +66,10 @@ class HyperBandScheduler(FIFOScheduler):
|
||||
mentioned in the original HyperBand paper.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, time_attr='training_iteration',
|
||||
reward_attr='episode_reward_mean', max_t=81):
|
||||
def __init__(self,
|
||||
time_attr='training_iteration',
|
||||
reward_attr='episode_reward_mean',
|
||||
max_t=81):
|
||||
assert max_t > 0, "Max (time_attr) not valid!"
|
||||
FIFOScheduler.__init__(self)
|
||||
self._eta = 3
|
||||
@@ -78,13 +79,12 @@ class HyperBandScheduler(FIFOScheduler):
|
||||
self._get_n0 = lambda s: int(
|
||||
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._get_r0 = lambda s: int((max_t * self._eta**(-s)))
|
||||
self._hyperbands = [[]] # list of hyperband iterations
|
||||
self._trial_info = {} # Stores Trial -> Bracket, Band Iteration
|
||||
|
||||
# Tracks state for new trial add
|
||||
self._state = {"bracket": None,
|
||||
"band_idx": 0}
|
||||
self._state = {"bracket": None, "band_idx": 0}
|
||||
self._num_stopped = 0
|
||||
self._reward_attr = reward_attr
|
||||
self._time_attr = time_attr
|
||||
@@ -116,9 +116,9 @@ class HyperBandScheduler(FIFOScheduler):
|
||||
cur_bracket = None
|
||||
else:
|
||||
retry = False
|
||||
cur_bracket = Bracket(
|
||||
self._time_attr, self._get_n0(s), self._get_r0(s),
|
||||
self._max_t_attr, self._eta, s)
|
||||
cur_bracket = Bracket(self._time_attr, self._get_n0(s),
|
||||
self._get_r0(s), self._max_t_attr,
|
||||
self._eta, s)
|
||||
cur_band.append(cur_bracket)
|
||||
self._state["bracket"] = cur_bracket
|
||||
|
||||
@@ -217,11 +217,11 @@ class HyperBandScheduler(FIFOScheduler):
|
||||
"""
|
||||
|
||||
for hyperband in self._hyperbands:
|
||||
for bracket in sorted(hyperband,
|
||||
key=lambda b: b.completion_percentage()):
|
||||
for bracket in sorted(
|
||||
hyperband, key=lambda b: b.completion_percentage()):
|
||||
for trial in bracket.current_trials():
|
||||
if (trial.status == Trial.PENDING and
|
||||
trial_runner.has_resources(trial.resources)):
|
||||
if (trial.status == Trial.PENDING
|
||||
and trial_runner.has_resources(trial.resources)):
|
||||
return trial
|
||||
return None
|
||||
|
||||
@@ -258,6 +258,7 @@ class Bracket():
|
||||
|
||||
Also keeps track of progress to ensure good scheduling.
|
||||
"""
|
||||
|
||||
def __init__(self, time_attr, max_trials, init_t_attr, max_t_attr, eta, s):
|
||||
self._live_trials = {} # maps trial -> current result
|
||||
self._all_trials = []
|
||||
@@ -287,8 +288,9 @@ class Bracket():
|
||||
"""Checks if all iterations have completed.
|
||||
|
||||
TODO(rliaw): also check that `t.iterations == self._r`"""
|
||||
return all(self._get_result_time(result) >= self._cumul_r
|
||||
for result in self._live_trials.values())
|
||||
return all(
|
||||
self._get_result_time(result) >= self._cumul_r
|
||||
for result in self._live_trials.values())
|
||||
|
||||
def finished(self):
|
||||
return self._halves == 0 and self.cur_iter_done()
|
||||
@@ -379,7 +381,7 @@ class Bracket():
|
||||
def _calculate_total_work(self, n, r, s):
|
||||
work = 0
|
||||
cumulative_r = r
|
||||
for i in range(s+1):
|
||||
for i in range(s + 1):
|
||||
work += int(n) * int(r)
|
||||
n /= self._eta
|
||||
n = int(np.ceil(n))
|
||||
@@ -389,11 +391,11 @@ class Bracket():
|
||||
|
||||
def __repr__(self):
|
||||
status = ", ".join([
|
||||
"Max Size (n)={}".format(self._n),
|
||||
"Milestone (r)={}".format(self._cumul_r),
|
||||
"completed={:.1%}".format(self.completion_percentage())
|
||||
])
|
||||
"Max Size (n)={}".format(self._n), "Milestone (r)={}".format(
|
||||
self._cumul_r), "completed={:.1%}".format(
|
||||
self.completion_percentage())
|
||||
])
|
||||
counts = collections.Counter([t.status for t in self._all_trials])
|
||||
trial_statuses = ", ".join(sorted(
|
||||
["{}: {}".format(k, v) for k, v in counts.items()]))
|
||||
trial_statuses = ", ".join(
|
||||
sorted(["{}: {}".format(k, v) for k, v in counts.items()]))
|
||||
return "Bracket({}): {{{}}} ".format(status, trial_statuses)
|
||||
|
||||
@@ -13,7 +13,6 @@ from ray.tune.cluster_info import get_ssh_key, get_ssh_user
|
||||
from ray.tune.error import TuneError
|
||||
from ray.tune.result import DEFAULT_RESULTS_DIR
|
||||
|
||||
|
||||
# Map from (logdir, remote_dir) -> syncer
|
||||
_syncers = {}
|
||||
|
||||
@@ -69,9 +68,8 @@ class _LogSyncer(object):
|
||||
def sync_now(self, force=False):
|
||||
self.last_sync_time = time.time()
|
||||
if not self.worker_ip:
|
||||
print(
|
||||
"Worker ip unknown, skipping log sync for {}".format(
|
||||
self.local_dir))
|
||||
print("Worker ip unknown, skipping log sync for {}".format(
|
||||
self.local_dir))
|
||||
return
|
||||
|
||||
if self.worker_ip == self.local_ip:
|
||||
@@ -80,23 +78,21 @@ class _LogSyncer(object):
|
||||
ssh_key = get_ssh_key()
|
||||
ssh_user = get_ssh_user()
|
||||
if ssh_key is None or ssh_user is None:
|
||||
print(
|
||||
"Error: log sync requires cluster to be setup with "
|
||||
"`ray create_or_update`.")
|
||||
print("Error: log sync requires cluster to be setup with "
|
||||
"`ray create_or_update`.")
|
||||
return
|
||||
if not distutils.spawn.find_executable("rsync"):
|
||||
print("Error: log sync requires rsync to be installed.")
|
||||
return
|
||||
worker_to_local_sync_cmd = (
|
||||
("""rsync -avz -e "ssh -i '{}' -o ConnectTimeout=120s """
|
||||
"""-o StrictHostKeyChecking=no" '{}@{}:{}/' '{}/'""").format(
|
||||
worker_to_local_sync_cmd = ((
|
||||
"""rsync -avz -e "ssh -i '{}' -o ConnectTimeout=120s """
|
||||
"""-o StrictHostKeyChecking=no" '{}@{}:{}/' '{}/'""").format(
|
||||
ssh_key, ssh_user, self.worker_ip,
|
||||
pipes.quote(self.local_dir), pipes.quote(self.local_dir)))
|
||||
|
||||
if self.remote_dir:
|
||||
local_to_remote_sync_cmd = (
|
||||
"aws s3 sync '{}' '{}'".format(
|
||||
pipes.quote(self.local_dir), pipes.quote(self.remote_dir)))
|
||||
local_to_remote_sync_cmd = ("aws s3 sync '{}' '{}'".format(
|
||||
pipes.quote(self.local_dir), pipes.quote(self.remote_dir)))
|
||||
else:
|
||||
local_to_remote_sync_cmd = None
|
||||
|
||||
|
||||
@@ -110,9 +110,9 @@ def to_tf_values(result, path):
|
||||
for attr, value in result.items():
|
||||
if value is not None:
|
||||
if type(value) in [int, float]:
|
||||
values.append(tf.Summary.Value(
|
||||
tag="/".join(path + [attr]),
|
||||
simple_value=value))
|
||||
values.append(
|
||||
tf.Summary.Value(
|
||||
tag="/".join(path + [attr]), simple_value=value))
|
||||
elif type(value) is dict:
|
||||
values.extend(to_tf_values(value, path + [attr]))
|
||||
return values
|
||||
@@ -125,8 +125,8 @@ class _TFLogger(Logger):
|
||||
def on_result(self, result):
|
||||
tmp = result._asdict()
|
||||
for k in [
|
||||
"config", "pid", "timestamp", "time_total_s",
|
||||
"timesteps_total"]:
|
||||
"config", "pid", "timestamp", "time_total_s", "timesteps_total"
|
||||
]:
|
||||
del tmp[k] # not useful to tf log these
|
||||
values = to_tf_values(tmp, ["ray", "tune"])
|
||||
train_stats = tf.Summary(value=values)
|
||||
@@ -165,9 +165,9 @@ class _CustomEncoder(json.JSONEncoder):
|
||||
return repr(o) if not np.isnan(o) else nan_str
|
||||
|
||||
_iterencode = json.encoder._make_iterencode(
|
||||
None, self.default, _encoder, self.indent, floatstr,
|
||||
self.key_separator, self.item_separator, self.sort_keys,
|
||||
self.skipkeys, _one_shot)
|
||||
None, self.default, _encoder, self.indent, floatstr,
|
||||
self.key_separator, self.item_separator, self.sort_keys,
|
||||
self.skipkeys, _one_shot)
|
||||
return _iterencode(o, 0)
|
||||
|
||||
def default(self, value):
|
||||
|
||||
@@ -32,10 +32,13 @@ class MedianStoppingRule(FIFOScheduler):
|
||||
time a trial reports. Defaults to True.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, time_attr="time_total_s", reward_attr="episode_reward_mean",
|
||||
grace_period=60.0, min_samples_required=3,
|
||||
hard_stop=True, verbose=True):
|
||||
def __init__(self,
|
||||
time_attr="time_total_s",
|
||||
reward_attr="episode_reward_mean",
|
||||
grace_period=60.0,
|
||||
min_samples_required=3,
|
||||
hard_stop=True,
|
||||
verbose=True):
|
||||
FIFOScheduler.__init__(self)
|
||||
self._stopped_trials = set()
|
||||
self._completed_trials = set()
|
||||
@@ -103,9 +106,10 @@ 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(
|
||||
[getattr(r, self._reward_attr)
|
||||
for r in results if getattr(r, self._time_attr) <= t_max])
|
||||
return np.mean([
|
||||
getattr(r, self._reward_attr) for r in results
|
||||
if getattr(r, self._time_attr) <= t_max
|
||||
])
|
||||
|
||||
def _best_result(self, trial):
|
||||
results = self._results[trial]
|
||||
|
||||
+26
-26
@@ -11,7 +11,6 @@ from ray.tune.trial import Trial
|
||||
from ray.tune.trial_scheduler import FIFOScheduler, TrialScheduler
|
||||
from ray.tune.variant_generator import _format_vars
|
||||
|
||||
|
||||
# Parameters are transferred from the top PBT_QUANTILE fraction of trials to
|
||||
# the bottom PBT_QUANTILE fraction.
|
||||
PBT_QUANTILE = 0.25
|
||||
@@ -27,9 +26,8 @@ class PBTTrialState(object):
|
||||
self.last_perturbation_time = 0
|
||||
|
||||
def __repr__(self):
|
||||
return str((
|
||||
self.last_score, self.last_checkpoint,
|
||||
self.last_perturbation_time))
|
||||
return str((self.last_score, self.last_checkpoint,
|
||||
self.last_perturbation_time))
|
||||
|
||||
|
||||
def explore(config, mutations, resample_probability, custom_explore_fn):
|
||||
@@ -51,12 +49,13 @@ def explore(config, mutations, resample_probability, custom_explore_fn):
|
||||
config[key] not in distribution:
|
||||
new_config[key] = random.choice(distribution)
|
||||
elif random.random() > 0.5:
|
||||
new_config[key] = distribution[
|
||||
max(0, distribution.index(config[key]) - 1)]
|
||||
new_config[key] = distribution[max(
|
||||
0,
|
||||
distribution.index(config[key]) - 1)]
|
||||
else:
|
||||
new_config[key] = distribution[
|
||||
min(len(distribution) - 1,
|
||||
distribution.index(config[key]) + 1)]
|
||||
new_config[key] = distribution[min(
|
||||
len(distribution) - 1,
|
||||
distribution.index(config[key]) + 1)]
|
||||
else:
|
||||
if random.random() < resample_probability:
|
||||
new_config[key] = distribution()
|
||||
@@ -70,8 +69,8 @@ def explore(config, mutations, resample_probability, custom_explore_fn):
|
||||
new_config = custom_explore_fn(new_config)
|
||||
assert new_config is not None, \
|
||||
"Custom explore fn failed to return new config"
|
||||
print(
|
||||
"[explore] perturbed config from {} -> {}".format(config, new_config))
|
||||
print("[explore] perturbed config from {} -> {}".format(
|
||||
config, new_config))
|
||||
return new_config
|
||||
|
||||
|
||||
@@ -148,10 +147,13 @@ class PopulationBasedTraining(FIFOScheduler):
|
||||
>>> run_experiments({...}, scheduler=pbt)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, time_attr="time_total_s", reward_attr="episode_reward_mean",
|
||||
perturbation_interval=60.0, hyperparam_mutations={},
|
||||
resample_probability=0.25, custom_explore_fn=None):
|
||||
def __init__(self,
|
||||
time_attr="time_total_s",
|
||||
reward_attr="episode_reward_mean",
|
||||
perturbation_interval=60.0,
|
||||
hyperparam_mutations={},
|
||||
resample_probability=0.25,
|
||||
custom_explore_fn=None):
|
||||
if not hyperparam_mutations and not custom_explore_fn:
|
||||
raise TuneError(
|
||||
"You must specify at least one of `hyperparam_mutations` or "
|
||||
@@ -209,14 +211,13 @@ class PopulationBasedTraining(FIFOScheduler):
|
||||
if not new_state.last_checkpoint:
|
||||
print("[pbt] warn: no checkpoint for trial, skip exploit", trial)
|
||||
return
|
||||
new_config = explore(
|
||||
trial_to_clone.config, self._hyperparam_mutations,
|
||||
self._resample_probability, self._custom_explore_fn)
|
||||
print(
|
||||
"[exploit] transferring weights from trial "
|
||||
"{} (score {}) -> {} (score {})".format(
|
||||
trial_to_clone, new_state.last_score, trial,
|
||||
trial_state.last_score))
|
||||
new_config = explore(trial_to_clone.config, self._hyperparam_mutations,
|
||||
self._resample_probability,
|
||||
self._custom_explore_fn)
|
||||
print("[exploit] transferring weights from trial "
|
||||
"{} (score {}) -> {} (score {})".format(
|
||||
trial_to_clone, new_state.last_score, trial,
|
||||
trial_state.last_score))
|
||||
# TODO(ekl) restarting the trial is expensive. We should implement a
|
||||
# lighter way reset() method that can alter the trial config.
|
||||
trial.stop(stop_logger=False)
|
||||
@@ -242,9 +243,8 @@ class PopulationBasedTraining(FIFOScheduler):
|
||||
if len(trials) <= 1:
|
||||
return [], []
|
||||
else:
|
||||
return (
|
||||
trials[:int(math.ceil(len(trials)*PBT_QUANTILE))],
|
||||
trials[int(math.floor(-len(trials)*PBT_QUANTILE)):])
|
||||
return (trials[:int(math.ceil(len(trials) * PBT_QUANTILE))],
|
||||
trials[int(math.floor(-len(trials) * PBT_QUANTILE)):])
|
||||
|
||||
def choose_trial_to_run(self, trial_runner):
|
||||
"""Ensures all trials get fair share of time (as defined by time_attr).
|
||||
|
||||
@@ -14,7 +14,8 @@ ENV_CREATOR = "env_creator"
|
||||
RLLIB_MODEL = "rllib_model"
|
||||
RLLIB_PREPROCESSOR = "rllib_preprocessor"
|
||||
KNOWN_CATEGORIES = [
|
||||
TRAINABLE_CLASS, ENV_CREATOR, RLLIB_MODEL, RLLIB_PREPROCESSOR]
|
||||
TRAINABLE_CLASS, ENV_CREATOR, RLLIB_MODEL, RLLIB_PREPROCESSOR
|
||||
]
|
||||
|
||||
|
||||
def register_trainable(name, trainable):
|
||||
@@ -32,8 +33,8 @@ def register_trainable(name, trainable):
|
||||
if isinstance(trainable, FunctionType):
|
||||
trainable = wrap_function(trainable)
|
||||
if not issubclass(trainable, Trainable):
|
||||
raise TypeError(
|
||||
"Second argument must be convertable to Trainable", trainable)
|
||||
raise TypeError("Second argument must be convertable to Trainable",
|
||||
trainable)
|
||||
_default_registry.register(TRAINABLE_CLASS, name, trainable)
|
||||
|
||||
|
||||
@@ -46,8 +47,7 @@ def register_env(name, env_creator):
|
||||
"""
|
||||
|
||||
if not isinstance(env_creator, FunctionType):
|
||||
raise TypeError(
|
||||
"Second argument must be a function.", env_creator)
|
||||
raise TypeError("Second argument must be a function.", env_creator)
|
||||
_default_registry.register(ENV_CREATOR, name, env_creator)
|
||||
|
||||
|
||||
|
||||
+54
-51
@@ -4,8 +4,6 @@ from __future__ import print_function
|
||||
|
||||
from collections import namedtuple
|
||||
import os
|
||||
|
||||
|
||||
"""
|
||||
When using ray.tune with custom training scripts, you must periodically report
|
||||
training status back to Ray by calling reporter(result).
|
||||
@@ -18,73 +16,78 @@ In RLlib, the supplied algorithms fill in TrainingResult for you.
|
||||
# Where ray.tune writes result files by default
|
||||
DEFAULT_RESULTS_DIR = os.path.expanduser("~/ray_results")
|
||||
|
||||
TrainingResult = namedtuple(
|
||||
"TrainingResult",
|
||||
[
|
||||
# (Required) Accumulated timesteps for this entire experiment.
|
||||
"timesteps_total",
|
||||
|
||||
TrainingResult = namedtuple("TrainingResult", [
|
||||
# (Required) Accumulated timesteps for this entire experiment.
|
||||
"timesteps_total",
|
||||
# (Optional) If training is terminated.
|
||||
"done",
|
||||
|
||||
# (Optional) If training is terminated.
|
||||
"done",
|
||||
# (Optional) Custom metadata to report for this iteration.
|
||||
"info",
|
||||
|
||||
# (Optional) Custom metadata to report for this iteration.
|
||||
"info",
|
||||
# (Optional) The mean episode reward if applicable.
|
||||
"episode_reward_mean",
|
||||
|
||||
# (Optional) The mean episode reward if applicable.
|
||||
"episode_reward_mean",
|
||||
# (Optional) The mean episode length if applicable.
|
||||
"episode_len_mean",
|
||||
|
||||
# (Optional) The mean episode length if applicable.
|
||||
"episode_len_mean",
|
||||
# (Optional) The number of episodes total.
|
||||
"episodes_total",
|
||||
|
||||
# (Optional) The number of episodes total.
|
||||
"episodes_total",
|
||||
# (Optional) The current training accuracy if applicable.
|
||||
"mean_accuracy",
|
||||
|
||||
# (Optional) The current training accuracy if applicable.
|
||||
"mean_accuracy",
|
||||
# (Optional) The current validation accuracy if applicable.
|
||||
"mean_validation_accuracy",
|
||||
|
||||
# (Optional) The current validation accuracy if applicable.
|
||||
"mean_validation_accuracy",
|
||||
# (Optional) The current training loss if applicable.
|
||||
"mean_loss",
|
||||
|
||||
# (Optional) The current training loss if applicable.
|
||||
"mean_loss",
|
||||
# (Auto-filled) The negated current training loss.
|
||||
"neg_mean_loss",
|
||||
|
||||
# (Auto-filled) The negated current training loss.
|
||||
"neg_mean_loss",
|
||||
# (Auto-filled) Unique string identifier for this experiment.
|
||||
# This id is preserved across checkpoint / restore calls.
|
||||
"experiment_id",
|
||||
|
||||
# (Auto-filled) Unique string identifier for this experiment. This id is
|
||||
# preserved across checkpoint / restore calls.
|
||||
"experiment_id",
|
||||
# (Auto-filled) The index of this training iteration,
|
||||
# e.g. call to train().
|
||||
"training_iteration",
|
||||
|
||||
# (Auto-filled) The index of this training iteration, e.g. call to train().
|
||||
"training_iteration",
|
||||
# (Auto-filled) Number of timesteps in the simulator
|
||||
# in this iteration.
|
||||
"timesteps_this_iter",
|
||||
|
||||
# (Auto-filled) Number of timesteps in the simulator in this iteration.
|
||||
"timesteps_this_iter",
|
||||
# (Auto-filled) Time in seconds this iteration took to run. This may
|
||||
# be overriden in order to override the system-computed
|
||||
# time difference.
|
||||
"time_this_iter_s",
|
||||
|
||||
# (Auto-filled) Time in seconds this iteration took to run. This may be
|
||||
# overriden in order to override the system-computed time difference.
|
||||
"time_this_iter_s",
|
||||
# (Auto-filled) Accumulated time in seconds for this entire experiment.
|
||||
"time_total_s",
|
||||
|
||||
# (Auto-filled) Accumulated time in seconds for this entire experiment.
|
||||
"time_total_s",
|
||||
# (Auto-filled) The pid of the training process.
|
||||
"pid",
|
||||
|
||||
# (Auto-filled) The pid of the training process.
|
||||
"pid",
|
||||
# (Auto-filled) A formatted date of when the result was processed.
|
||||
"date",
|
||||
|
||||
# (Auto-filled) A formatted date of when the result was processed.
|
||||
"date",
|
||||
# (Auto-filled) A UNIX timestamp of when the result was processed.
|
||||
"timestamp",
|
||||
|
||||
# (Auto-filled) A UNIX timestamp of when the result was processed.
|
||||
"timestamp",
|
||||
# (Auto-filled) The hostname of the machine hosting the
|
||||
# training process.
|
||||
"hostname",
|
||||
|
||||
# (Auto-filled) The hostname of the machine hosting the training process.
|
||||
"hostname",
|
||||
# (Auto-filled) The node ip of the machine hosting the
|
||||
# training process.
|
||||
"node_ip",
|
||||
|
||||
# (Auto-filled) The node ip of the machine hosting the training process.
|
||||
"node_ip",
|
||||
# (Auto=filled) The current hyperparameter configuration.
|
||||
"config",
|
||||
])
|
||||
|
||||
# (Auto=filled) The current hyperparameter configuration.
|
||||
"config",
|
||||
])
|
||||
|
||||
|
||||
TrainingResult.__new__.__defaults__ = (None,) * len(TrainingResult._fields)
|
||||
TrainingResult.__new__.__defaults__ = (None, ) * len(TrainingResult._fields)
|
||||
|
||||
@@ -20,7 +20,9 @@ if __name__ == "__main__":
|
||||
run_experiments({
|
||||
"test": {
|
||||
"run": "my_class",
|
||||
"stop": {"training_iteration": 1}
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
}
|
||||
}
|
||||
})
|
||||
assert 'ray.rllib' not in sys.modules, "RLlib should not be imported"
|
||||
|
||||
@@ -60,163 +60,209 @@ class TrainableFunctionApiTest(unittest.TestCase):
|
||||
def testRewriteEnv(self):
|
||||
def train(config, reporter):
|
||||
reporter(timesteps_total=1)
|
||||
|
||||
register_trainable("f1", train)
|
||||
|
||||
[trial] = run_experiments({"foo": {
|
||||
"run": "f1",
|
||||
"env": "CartPole-v0",
|
||||
}})
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"env": "CartPole-v0",
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.config["env"], "CartPole-v0")
|
||||
|
||||
def testConfigPurity(self):
|
||||
def train(config, reporter):
|
||||
assert config == {"a": "b"}, config
|
||||
reporter(timesteps_total=1)
|
||||
|
||||
register_trainable("f1", train)
|
||||
run_experiments({"foo": {
|
||||
"run": "f1",
|
||||
"config": {"a": "b"},
|
||||
}})
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"a": "b"
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
def testLogdir(self):
|
||||
def train(config, reporter):
|
||||
assert "/tmp/logdir/foo" in os.getcwd(), os.getcwd()
|
||||
reporter(timesteps_total=1)
|
||||
|
||||
register_trainable("f1", train)
|
||||
run_experiments({"foo": {
|
||||
"run": "f1",
|
||||
"local_dir": "/tmp/logdir",
|
||||
"config": {"a": "b"},
|
||||
}})
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"local_dir": "/tmp/logdir",
|
||||
"config": {
|
||||
"a": "b"
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
def testLongFilename(self):
|
||||
def train(config, reporter):
|
||||
assert "/tmp/logdir/foo" in os.getcwd(), os.getcwd()
|
||||
reporter(timesteps_total=1)
|
||||
|
||||
register_trainable("f1", train)
|
||||
run_experiments({"foo": {
|
||||
"run": "f1",
|
||||
"local_dir": "/tmp/logdir",
|
||||
"config": {
|
||||
"a" * 50: lambda spec: 5.0 / 7,
|
||||
"b" * 50: lambda spec: "long" * 40},
|
||||
}})
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"local_dir": "/tmp/logdir",
|
||||
"config": {
|
||||
"a" * 50: lambda spec: 5.0 / 7,
|
||||
"b" * 50: lambda spec: "long" * 40
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
def testBadParams(self):
|
||||
def f():
|
||||
run_experiments({"foo": {}})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams2(self):
|
||||
def f():
|
||||
run_experiments({"foo": {
|
||||
"run": "asdf",
|
||||
"bah": "this param is not allowed",
|
||||
}})
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "asdf",
|
||||
"bah": "this param is not allowed",
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams3(self):
|
||||
def f():
|
||||
run_experiments({"foo": {
|
||||
"run": grid_search("invalid grid search"),
|
||||
}})
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": grid_search("invalid grid search"),
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams4(self):
|
||||
def f():
|
||||
run_experiments({"foo": {
|
||||
"run": "asdf",
|
||||
}})
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "asdf",
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams5(self):
|
||||
def f():
|
||||
run_experiments({"foo": {
|
||||
"run": "PPO",
|
||||
"stop": {"asdf": 1}
|
||||
}})
|
||||
run_experiments({"foo": {"run": "PPO", "stop": {"asdf": 1}}})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams6(self):
|
||||
def f():
|
||||
run_experiments({"foo": {
|
||||
"run": "PPO",
|
||||
"trial_resources": {"asdf": 1}
|
||||
}})
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "PPO",
|
||||
"trial_resources": {
|
||||
"asdf": 1
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadReturn(self):
|
||||
def train(config, reporter):
|
||||
reporter()
|
||||
|
||||
register_trainable("f1", train)
|
||||
|
||||
def f():
|
||||
run_experiments({"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}})
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testEarlyReturn(self):
|
||||
def train(config, reporter):
|
||||
reporter(timesteps_total=100, done=True)
|
||||
time.sleep(99999)
|
||||
|
||||
register_trainable("f1", train)
|
||||
[trial] = run_experiments({"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}})
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result.timesteps_total, 100)
|
||||
|
||||
def testAbruptReturn(self):
|
||||
def train(config, reporter):
|
||||
reporter(timesteps_total=100)
|
||||
|
||||
register_trainable("f1", train)
|
||||
[trial] = run_experiments({"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}})
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result.timesteps_total, 100)
|
||||
|
||||
def testErrorReturn(self):
|
||||
def train(config, reporter):
|
||||
raise Exception("uh oh")
|
||||
|
||||
register_trainable("f1", train)
|
||||
|
||||
def f():
|
||||
run_experiments({"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}})
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testSuccess(self):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(timesteps_total=i)
|
||||
|
||||
register_trainable("f1", train)
|
||||
[trial] = run_experiments({"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}})
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0,
|
||||
},
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result.timesteps_total, 99)
|
||||
|
||||
|
||||
class RunExperimentTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
ray.init()
|
||||
|
||||
@@ -228,6 +274,7 @@ class RunExperimentTest(unittest.TestCase):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(timesteps_total=i)
|
||||
|
||||
register_trainable("f1", train)
|
||||
trials = run_experiments({
|
||||
"foo": {
|
||||
@@ -251,13 +298,14 @@ class RunExperimentTest(unittest.TestCase):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(timesteps_total=i)
|
||||
|
||||
register_trainable("f1", train)
|
||||
exp1 = Experiment(**{
|
||||
"name": "foo",
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0
|
||||
}
|
||||
"name": "foo",
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0
|
||||
}
|
||||
})
|
||||
[trial] = run_experiments(exp1)
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
@@ -267,20 +315,21 @@ class RunExperimentTest(unittest.TestCase):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(timesteps_total=i)
|
||||
|
||||
register_trainable("f1", train)
|
||||
exp1 = Experiment(**{
|
||||
"name": "foo",
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0
|
||||
}
|
||||
"name": "foo",
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0
|
||||
}
|
||||
})
|
||||
exp2 = Experiment(**{
|
||||
"name": "bar",
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0
|
||||
}
|
||||
"name": "bar",
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"script_min_iter_time_s": 0
|
||||
}
|
||||
})
|
||||
trials = run_experiments([exp1, exp2])
|
||||
for trial in trials:
|
||||
@@ -306,9 +355,8 @@ class VariantGeneratorTest(unittest.TestCase):
|
||||
self.assertEqual(trials[0].trainable_name, "PPO")
|
||||
self.assertEqual(trials[0].experiment_tag, "0")
|
||||
self.assertEqual(trials[0].max_failures, 5)
|
||||
self.assertEqual(
|
||||
trials[0].local_dir,
|
||||
os.path.join(DEFAULT_RESULTS_DIR, "tune-pong"))
|
||||
self.assertEqual(trials[0].local_dir,
|
||||
os.path.join(DEFAULT_RESULTS_DIR, "tune-pong"))
|
||||
self.assertEqual(trials[1].experiment_tag, "1")
|
||||
|
||||
def testEval(self):
|
||||
@@ -392,11 +440,13 @@ class VariantGeneratorTest(unittest.TestCase):
|
||||
trials = generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"x": grid_search([
|
||||
"x":
|
||||
grid_search([
|
||||
lambda spec: spec.config.y * 100,
|
||||
lambda spec: spec.config.y * 200
|
||||
]),
|
||||
"y": lambda spec: 1,
|
||||
"y":
|
||||
lambda spec: 1,
|
||||
},
|
||||
})
|
||||
trials = list(trials)
|
||||
@@ -406,12 +456,13 @@ class VariantGeneratorTest(unittest.TestCase):
|
||||
|
||||
def testRecursiveDep(self):
|
||||
try:
|
||||
list(generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"foo": lambda spec: spec.config.foo,
|
||||
},
|
||||
}))
|
||||
list(
|
||||
generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"foo": lambda spec: spec.config.foo,
|
||||
},
|
||||
}))
|
||||
except RecursiveDependencyError as e:
|
||||
assert "`foo` recursively depends on" in str(e), e
|
||||
else:
|
||||
@@ -442,12 +493,15 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
|
||||
register_trainable("f1", train)
|
||||
|
||||
experiments = {"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"a" * 50: lambda spec: 5.0 / 7,
|
||||
"b" * 50: lambda spec: "long" * 40},
|
||||
}}
|
||||
experiments = {
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"a" * 50: lambda spec: 5.0 / 7,
|
||||
"b" * 50: lambda spec: "long" * 40
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for name, spec in experiments.items():
|
||||
for trial in generate_trials(spec, name):
|
||||
@@ -468,12 +522,12 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
ray.init(num_cpus=4, num_gpus=2)
|
||||
runner = TrialRunner()
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 1},
|
||||
"stopping_criterion": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"resources": Resources(cpu=1, gpu=0, extra_cpu=3, extra_gpu=1),
|
||||
}
|
||||
trials = [
|
||||
Trial("__fake", **kwargs),
|
||||
Trial("__fake", **kwargs)]
|
||||
trials = [Trial("__fake", **kwargs), Trial("__fake", **kwargs)]
|
||||
for t in trials:
|
||||
runner.add_trial(t)
|
||||
|
||||
@@ -489,12 +543,12 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
ray.init(num_cpus=4, num_gpus=1)
|
||||
runner = TrialRunner()
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 1},
|
||||
"stopping_criterion": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"resources": Resources(cpu=1, gpu=1),
|
||||
}
|
||||
trials = [
|
||||
Trial("__fake", **kwargs),
|
||||
Trial("__fake", **kwargs)]
|
||||
trials = [Trial("__fake", **kwargs), Trial("__fake", **kwargs)]
|
||||
for t in trials:
|
||||
runner.add_trial(t)
|
||||
|
||||
@@ -518,12 +572,12 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
ray.init(num_cpus=4, num_gpus=2)
|
||||
runner = TrialRunner()
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 5},
|
||||
"stopping_criterion": {
|
||||
"training_iteration": 5
|
||||
},
|
||||
"resources": Resources(cpu=1, gpu=1),
|
||||
}
|
||||
trials = [
|
||||
Trial("__fake", **kwargs),
|
||||
Trial("__fake", **kwargs)]
|
||||
trials = [Trial("__fake", **kwargs), Trial("__fake", **kwargs)]
|
||||
for t in trials:
|
||||
runner.add_trial(t)
|
||||
|
||||
@@ -547,13 +601,13 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
ray.init(num_cpus=4, num_gpus=2)
|
||||
runner = TrialRunner()
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 1},
|
||||
"stopping_criterion": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"resources": Resources(cpu=1, gpu=1),
|
||||
}
|
||||
_default_registry.register(TRAINABLE_CLASS, "asdf", None)
|
||||
trials = [
|
||||
Trial("asdf", **kwargs),
|
||||
Trial("__fake", **kwargs)]
|
||||
trials = [Trial("asdf", **kwargs), Trial("__fake", **kwargs)]
|
||||
for t in trials:
|
||||
runner.add_trial(t)
|
||||
|
||||
@@ -644,7 +698,9 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
ray.init(num_cpus=1, num_gpus=1)
|
||||
runner = TrialRunner()
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 1},
|
||||
"stopping_criterion": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"resources": Resources(cpu=1, gpu=1),
|
||||
}
|
||||
runner.add_trial(Trial("__fake", **kwargs))
|
||||
@@ -675,7 +731,9 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
ray.init(num_cpus=1, num_gpus=1)
|
||||
runner = TrialRunner()
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 2},
|
||||
"stopping_criterion": {
|
||||
"training_iteration": 2
|
||||
},
|
||||
"resources": Resources(cpu=1, gpu=1),
|
||||
}
|
||||
runner.add_trial(Trial("__fake", **kwargs))
|
||||
@@ -692,7 +750,9 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
ray.init(num_cpus=1, num_gpus=1)
|
||||
runner = TrialRunner()
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 2},
|
||||
"stopping_criterion": {
|
||||
"training_iteration": 2
|
||||
},
|
||||
"resources": Resources(cpu=1, gpu=1),
|
||||
}
|
||||
runner.add_trial(Trial("__fake", **kwargs))
|
||||
@@ -721,14 +781,17 @@ class TrialRunnerTest(unittest.TestCase):
|
||||
ray.init(num_cpus=4, num_gpus=2)
|
||||
runner = TrialRunner()
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 5},
|
||||
"stopping_criterion": {
|
||||
"training_iteration": 5
|
||||
},
|
||||
"resources": Resources(cpu=1, gpu=1),
|
||||
}
|
||||
trials = [
|
||||
Trial("__fake", **kwargs),
|
||||
Trial("__fake", **kwargs),
|
||||
Trial("__fake", **kwargs),
|
||||
Trial("__fake", **kwargs)]
|
||||
Trial("__fake", **kwargs)
|
||||
]
|
||||
for t in trials:
|
||||
runner.add_trial(t)
|
||||
runner.step()
|
||||
|
||||
@@ -19,9 +19,8 @@ _register_all()
|
||||
|
||||
|
||||
def result(t, rew):
|
||||
return TrainingResult(time_total_s=t,
|
||||
episode_reward_mean=rew,
|
||||
training_iteration=int(t))
|
||||
return TrainingResult(
|
||||
time_total_s=t, episode_reward_mean=rew, training_iteration=int(t))
|
||||
|
||||
|
||||
class EarlyStoppingSuite(unittest.TestCase):
|
||||
@@ -76,8 +75,7 @@ class EarlyStoppingSuite(unittest.TestCase):
|
||||
rule.on_trial_result(None, t3, result(2, 10)),
|
||||
TrialScheduler.CONTINUE)
|
||||
self.assertEqual(
|
||||
rule.on_trial_result(None, t3, result(3, 10)),
|
||||
TrialScheduler.STOP)
|
||||
rule.on_trial_result(None, t3, result(3, 10)), TrialScheduler.STOP)
|
||||
|
||||
def testMedianStoppingMinSamples(self):
|
||||
rule = MedianStoppingRule(grace_period=0, min_samples_required=2)
|
||||
@@ -89,8 +87,7 @@ class EarlyStoppingSuite(unittest.TestCase):
|
||||
TrialScheduler.CONTINUE)
|
||||
rule.on_trial_complete(None, t2, result(10, 1000))
|
||||
self.assertEqual(
|
||||
rule.on_trial_result(None, t3, result(3, 10)),
|
||||
TrialScheduler.STOP)
|
||||
rule.on_trial_result(None, t3, result(3, 10)), TrialScheduler.STOP)
|
||||
|
||||
def testMedianStoppingUsesMedian(self):
|
||||
rule = MedianStoppingRule(grace_period=0, min_samples_required=1)
|
||||
@@ -124,8 +121,10 @@ class EarlyStoppingSuite(unittest.TestCase):
|
||||
return TrainingResult(training_iteration=t, neg_mean_loss=rew)
|
||||
|
||||
rule = MedianStoppingRule(
|
||||
grace_period=0, min_samples_required=1,
|
||||
time_attr='training_iteration', reward_attr='neg_mean_loss')
|
||||
grace_period=0,
|
||||
min_samples_required=1,
|
||||
time_attr='training_iteration',
|
||||
reward_attr='neg_mean_loss')
|
||||
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):
|
||||
@@ -185,7 +184,6 @@ class _MockTrialRunner():
|
||||
|
||||
|
||||
class HyperbandSuite(unittest.TestCase):
|
||||
|
||||
def schedulerSetup(self, num_trials):
|
||||
"""Setup a scheduler and Runner with max Iter = 9
|
||||
|
||||
@@ -206,7 +204,10 @@ class HyperbandSuite(unittest.TestCase):
|
||||
"""Default statistics for HyperBand"""
|
||||
sched = HyperBandScheduler()
|
||||
res = {
|
||||
str(s): {"n": sched._get_n0(s), "r": sched._get_r0(s)}
|
||||
str(s): {
|
||||
"n": sched._get_n0(s),
|
||||
"r": sched._get_r0(s)
|
||||
}
|
||||
for s in range(sched._s_max_1)
|
||||
}
|
||||
res["max_trials"] = sum(v["n"] for v in res.values())
|
||||
@@ -298,8 +299,8 @@ class HyperbandSuite(unittest.TestCase):
|
||||
|
||||
# Provides results from 0 to 8 in order, keeping last one running
|
||||
for i, trl in enumerate(trials):
|
||||
action = sched.on_trial_result(
|
||||
mock_runner, trl, result(cur_units, i))
|
||||
action = sched.on_trial_result(mock_runner, trl,
|
||||
result(cur_units, i))
|
||||
if i < current_length - 1:
|
||||
self.assertEqual(action, TrialScheduler.PAUSE)
|
||||
mock_runner.process_action(trl, action)
|
||||
@@ -321,8 +322,8 @@ class HyperbandSuite(unittest.TestCase):
|
||||
# # Provides result in reverse order, killing the last one
|
||||
cur_units = stats[str(1)]["r"]
|
||||
for i, trl in reversed(list(enumerate(big_bracket.current_trials()))):
|
||||
action = sched.on_trial_result(
|
||||
mock_runner, trl, result(cur_units, i))
|
||||
action = sched.on_trial_result(mock_runner, trl,
|
||||
result(cur_units, i))
|
||||
mock_runner.process_action(trl, action)
|
||||
|
||||
self.assertEqual(action, TrialScheduler.STOP)
|
||||
@@ -338,8 +339,8 @@ class HyperbandSuite(unittest.TestCase):
|
||||
# # Provides result in reverse order, killing the last one
|
||||
cur_units = stats[str(0)]["r"]
|
||||
for i, trl in enumerate(big_bracket.current_trials()):
|
||||
action = sched.on_trial_result(
|
||||
mock_runner, trl, result(cur_units, i))
|
||||
action = sched.on_trial_result(mock_runner, trl,
|
||||
result(cur_units, i))
|
||||
mock_runner.process_action(trl, action)
|
||||
|
||||
self.assertEqual(action, TrialScheduler.STOP)
|
||||
@@ -354,14 +355,12 @@ class HyperbandSuite(unittest.TestCase):
|
||||
mock_runner._launch_trial(t)
|
||||
|
||||
sched.on_trial_error(mock_runner, t3)
|
||||
self.assertEqual(
|
||||
TrialScheduler.PAUSE,
|
||||
sched.on_trial_result(
|
||||
mock_runner, t1, result(stats[str(1)]["r"], 10)))
|
||||
self.assertEqual(
|
||||
TrialScheduler.CONTINUE,
|
||||
sched.on_trial_result(
|
||||
mock_runner, t2, result(stats[str(1)]["r"], 10)))
|
||||
self.assertEqual(TrialScheduler.PAUSE,
|
||||
sched.on_trial_result(mock_runner, t1,
|
||||
result(stats[str(1)]["r"], 10)))
|
||||
self.assertEqual(TrialScheduler.CONTINUE,
|
||||
sched.on_trial_result(mock_runner, t2,
|
||||
result(stats[str(1)]["r"], 10)))
|
||||
|
||||
def testTrialErrored2(self):
|
||||
"""Check successive halving happened even when last trial failed"""
|
||||
@@ -371,13 +370,14 @@ class HyperbandSuite(unittest.TestCase):
|
||||
trials = sched._state["bracket"].current_trials()
|
||||
for t in trials[:-1]:
|
||||
mock_runner._launch_trial(t)
|
||||
sched.on_trial_result(
|
||||
mock_runner, t, result(stats[str(1)]["r"], 10))
|
||||
sched.on_trial_result(mock_runner, t, result(
|
||||
stats[str(1)]["r"], 10))
|
||||
|
||||
mock_runner._launch_trial(trials[-1])
|
||||
sched.on_trial_error(mock_runner, trials[-1])
|
||||
self.assertEqual(len(sched._state["bracket"].current_trials()),
|
||||
self.downscale(stats[str(1)]["n"], sched))
|
||||
self.assertEqual(
|
||||
len(sched._state["bracket"].current_trials()),
|
||||
self.downscale(stats[str(1)]["n"], sched))
|
||||
|
||||
def testTrialEndedEarly(self):
|
||||
"""Check successive halving happened even when one trial failed"""
|
||||
@@ -390,14 +390,12 @@ class HyperbandSuite(unittest.TestCase):
|
||||
mock_runner._launch_trial(t)
|
||||
|
||||
sched.on_trial_complete(mock_runner, t3, result(1, 12))
|
||||
self.assertEqual(
|
||||
TrialScheduler.PAUSE,
|
||||
sched.on_trial_result(
|
||||
mock_runner, t1, result(stats[str(1)]["r"], 10)))
|
||||
self.assertEqual(
|
||||
TrialScheduler.CONTINUE,
|
||||
sched.on_trial_result(
|
||||
mock_runner, t2, result(stats[str(1)]["r"], 10)))
|
||||
self.assertEqual(TrialScheduler.PAUSE,
|
||||
sched.on_trial_result(mock_runner, t1,
|
||||
result(stats[str(1)]["r"], 10)))
|
||||
self.assertEqual(TrialScheduler.CONTINUE,
|
||||
sched.on_trial_result(mock_runner, t2,
|
||||
result(stats[str(1)]["r"], 10)))
|
||||
|
||||
def testTrialEndedEarly2(self):
|
||||
"""Check successive halving happened even when last trial failed"""
|
||||
@@ -407,13 +405,14 @@ class HyperbandSuite(unittest.TestCase):
|
||||
trials = sched._state["bracket"].current_trials()
|
||||
for t in trials[:-1]:
|
||||
mock_runner._launch_trial(t)
|
||||
sched.on_trial_result(
|
||||
mock_runner, t, result(stats[str(1)]["r"], 10))
|
||||
sched.on_trial_result(mock_runner, t, result(
|
||||
stats[str(1)]["r"], 10))
|
||||
|
||||
mock_runner._launch_trial(trials[-1])
|
||||
sched.on_trial_complete(mock_runner, trials[-1], result(100, 12))
|
||||
self.assertEqual(len(sched._state["bracket"].current_trials()),
|
||||
self.downscale(stats[str(1)]["n"], sched))
|
||||
self.assertEqual(
|
||||
len(sched._state["bracket"].current_trials()),
|
||||
self.downscale(stats[str(1)]["n"], sched))
|
||||
|
||||
def testAddAfterHalving(self):
|
||||
stats = self.default_statistics()
|
||||
@@ -426,8 +425,8 @@ class HyperbandSuite(unittest.TestCase):
|
||||
mock_runner._launch_trial(t)
|
||||
|
||||
for i, t in enumerate(bracket_trials):
|
||||
action = sched.on_trial_result(
|
||||
mock_runner, t, result(init_units, i))
|
||||
action = sched.on_trial_result(mock_runner, t, result(
|
||||
init_units, i))
|
||||
self.assertEqual(action, TrialScheduler.CONTINUE)
|
||||
t = Trial("__fake")
|
||||
sched.on_trial_add(None, t)
|
||||
@@ -435,13 +434,13 @@ class HyperbandSuite(unittest.TestCase):
|
||||
self.assertEqual(len(sched._state["bracket"].current_trials()), 2)
|
||||
|
||||
# Make sure that newly added trial gets fair computation (not just 1)
|
||||
self.assertEqual(
|
||||
TrialScheduler.CONTINUE,
|
||||
sched.on_trial_result(mock_runner, t, result(init_units, 12)))
|
||||
self.assertEqual(TrialScheduler.CONTINUE,
|
||||
sched.on_trial_result(mock_runner, t,
|
||||
result(init_units, 12)))
|
||||
new_units = init_units + int(init_units * sched._eta)
|
||||
self.assertEqual(
|
||||
TrialScheduler.PAUSE,
|
||||
sched.on_trial_result(mock_runner, t, result(new_units, 12)))
|
||||
self.assertEqual(TrialScheduler.PAUSE,
|
||||
sched.on_trial_result(mock_runner, t,
|
||||
result(new_units, 12)))
|
||||
|
||||
def testAlternateMetrics(self):
|
||||
"""Checking that alternate metrics will pass."""
|
||||
@@ -539,7 +538,6 @@ class _MockTrial(Trial):
|
||||
|
||||
|
||||
class PopulationBasedTestingSuite(unittest.TestCase):
|
||||
|
||||
def basicSetup(self, resample_prob=0.0, explore=None):
|
||||
pbt = PopulationBasedTraining(
|
||||
time_attr="training_iteration",
|
||||
@@ -554,9 +552,12 @@ class PopulationBasedTestingSuite(unittest.TestCase):
|
||||
runner = _MockTrialRunner(pbt)
|
||||
for i in range(5):
|
||||
trial = _MockTrial(
|
||||
i,
|
||||
{"id_factor": i, "float_factor": 2.0, "const_factor": 3,
|
||||
"int_factor": 10})
|
||||
i, {
|
||||
"id_factor": i,
|
||||
"float_factor": 2.0,
|
||||
"const_factor": 3,
|
||||
"int_factor": 10
|
||||
})
|
||||
runner.add_trial(trial)
|
||||
trial.status = Trial.RUNNING
|
||||
self.assertEqual(
|
||||
@@ -570,27 +571,23 @@ class PopulationBasedTestingSuite(unittest.TestCase):
|
||||
trials = runner.get_trials()
|
||||
|
||||
# no checkpoint: haven't hit next perturbation interval yet
|
||||
self.assertEqual(
|
||||
pbt.last_scores(trials), [0, 50, 100, 150, 200])
|
||||
self.assertEqual(pbt.last_scores(trials), [0, 50, 100, 150, 200])
|
||||
self.assertEqual(
|
||||
pbt.on_trial_result(runner, trials[0], result(15, 200)),
|
||||
TrialScheduler.CONTINUE)
|
||||
self.assertEqual(
|
||||
pbt.last_scores(trials), [0, 50, 100, 150, 200])
|
||||
self.assertEqual(pbt.last_scores(trials), [0, 50, 100, 150, 200])
|
||||
self.assertEqual(pbt._num_checkpoints, 0)
|
||||
|
||||
# checkpoint: both past interval and upper quantile
|
||||
self.assertEqual(
|
||||
pbt.on_trial_result(runner, trials[0], result(20, 200)),
|
||||
TrialScheduler.CONTINUE)
|
||||
self.assertEqual(
|
||||
pbt.last_scores(trials), [200, 50, 100, 150, 200])
|
||||
self.assertEqual(pbt.last_scores(trials), [200, 50, 100, 150, 200])
|
||||
self.assertEqual(pbt._num_checkpoints, 1)
|
||||
self.assertEqual(
|
||||
pbt.on_trial_result(runner, trials[1], result(30, 201)),
|
||||
TrialScheduler.CONTINUE)
|
||||
self.assertEqual(
|
||||
pbt.last_scores(trials), [200, 201, 100, 150, 200])
|
||||
self.assertEqual(pbt.last_scores(trials), [200, 201, 100, 150, 200])
|
||||
self.assertEqual(pbt._num_checkpoints, 2)
|
||||
|
||||
# not upper quantile any more
|
||||
@@ -608,8 +605,7 @@ class PopulationBasedTestingSuite(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
pbt.on_trial_result(runner, trials[0], result(15, -100)),
|
||||
TrialScheduler.CONTINUE)
|
||||
self.assertEqual(
|
||||
pbt.last_scores(trials), [0, 50, 100, 150, 200])
|
||||
self.assertEqual(pbt.last_scores(trials), [0, 50, 100, 150, 200])
|
||||
self.assertTrue("@perturbed" not in trials[0].experiment_tag)
|
||||
self.assertEqual(pbt._num_perturbations, 0)
|
||||
|
||||
@@ -617,8 +613,7 @@ class PopulationBasedTestingSuite(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
pbt.on_trial_result(runner, trials[0], result(20, -100)),
|
||||
TrialScheduler.CONTINUE)
|
||||
self.assertEqual(
|
||||
pbt.last_scores(trials), [-100, 50, 100, 150, 200])
|
||||
self.assertEqual(pbt.last_scores(trials), [-100, 50, 100, 150, 200])
|
||||
self.assertTrue("@perturbed" in trials[0].experiment_tag)
|
||||
self.assertIn(trials[0].restored_checkpoint, ["trial_3", "trial_4"])
|
||||
self.assertEqual(pbt._num_perturbations, 1)
|
||||
@@ -627,8 +622,7 @@ class PopulationBasedTestingSuite(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
pbt.on_trial_result(runner, trials[2], result(20, 40)),
|
||||
TrialScheduler.CONTINUE)
|
||||
self.assertEqual(
|
||||
pbt.last_scores(trials), [-100, 50, 40, 150, 200])
|
||||
self.assertEqual(pbt.last_scores(trials), [-100, 50, 40, 150, 200])
|
||||
self.assertEqual(pbt._num_perturbations, 2)
|
||||
self.assertIn(trials[0].restored_checkpoint, ["trial_3", "trial_4"])
|
||||
self.assertTrue("@perturbed" in trials[2].experiment_tag)
|
||||
@@ -662,7 +656,6 @@ class PopulationBasedTestingSuite(unittest.TestCase):
|
||||
self.assertEqual(trials[0].config["const_factor"], 3)
|
||||
|
||||
def testPerturbationValues(self):
|
||||
|
||||
def assertProduces(fn, values):
|
||||
random.seed(0)
|
||||
seen = set()
|
||||
@@ -712,8 +705,7 @@ class PopulationBasedTestingSuite(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
pbt.on_trial_result(runner, trials[1], result(20, 1000)),
|
||||
TrialScheduler.PAUSE)
|
||||
self.assertEqual(
|
||||
pbt.last_scores(trials), [0, 1000, 100, 150, 200])
|
||||
self.assertEqual(pbt.last_scores(trials), [0, 1000, 100, 150, 200])
|
||||
self.assertEqual(pbt.choose_trial_to_run(runner), trials[0])
|
||||
|
||||
def testSchedulesMostBehindTrialToRun(self):
|
||||
@@ -748,6 +740,7 @@ class PopulationBasedTestingSuite(unittest.TestCase):
|
||||
new_config["id_factor"] = 42
|
||||
new_config["float_factor"] = 43
|
||||
return new_config
|
||||
|
||||
pbt, runner = self.basicSetup(resample_prob=0.0, explore=explore)
|
||||
trials = runner.get_trials()
|
||||
self.assertEqual(
|
||||
@@ -774,8 +767,7 @@ class AsyncHyperBandSuite(unittest.TestCase):
|
||||
return t1, t2
|
||||
|
||||
def testAsyncHBOnComplete(self):
|
||||
scheduler = AsyncHyperBandScheduler(
|
||||
max_t=10, brackets=1)
|
||||
scheduler = AsyncHyperBandScheduler(max_t=10, brackets=1)
|
||||
t1, t2 = self.basicSetup(scheduler)
|
||||
t3 = Trial("PPO")
|
||||
scheduler.on_trial_add(None, t3)
|
||||
@@ -803,8 +795,7 @@ class AsyncHyperBandSuite(unittest.TestCase):
|
||||
TrialScheduler.STOP)
|
||||
|
||||
def testAsyncHBAllCompletes(self):
|
||||
scheduler = AsyncHyperBandScheduler(
|
||||
max_t=10, brackets=10)
|
||||
scheduler = AsyncHyperBandScheduler(max_t=10, brackets=10)
|
||||
trials = [Trial("PPO") for i in range(10)]
|
||||
for t in trials:
|
||||
scheduler.on_trial_add(None, t)
|
||||
@@ -834,8 +825,10 @@ class AsyncHyperBandSuite(unittest.TestCase):
|
||||
return TrainingResult(training_iteration=t, neg_mean_loss=rew)
|
||||
|
||||
scheduler = AsyncHyperBandScheduler(
|
||||
grace_period=1, time_attr='training_iteration',
|
||||
reward_attr='neg_mean_loss', brackets=1)
|
||||
grace_period=1,
|
||||
time_attr='training_iteration',
|
||||
reward_attr='neg_mean_loss',
|
||||
brackets=1)
|
||||
t1 = Trial("PPO") # mean is 450, max 900, t_max=10
|
||||
t2 = Trial("PPO") # mean is 450, max 450, t_max=5
|
||||
scheduler.on_trial_add(None, t1)
|
||||
|
||||
@@ -30,16 +30,15 @@ class TuneServerSuite(unittest.TestCase):
|
||||
def basicSetup(self):
|
||||
ray.init(num_cpus=4, num_gpus=1)
|
||||
port = get_valid_port()
|
||||
self.runner = TrialRunner(
|
||||
launch_web_server=True, server_port=port)
|
||||
self.runner = TrialRunner(launch_web_server=True, server_port=port)
|
||||
runner = self.runner
|
||||
kwargs = {
|
||||
"stopping_criterion": {"training_iteration": 3},
|
||||
"stopping_criterion": {
|
||||
"training_iteration": 3
|
||||
},
|
||||
"resources": Resources(cpu=1, gpu=1),
|
||||
}
|
||||
trials = [
|
||||
Trial("__fake", **kwargs),
|
||||
Trial("__fake", **kwargs)]
|
||||
trials = [Trial("__fake", **kwargs), Trial("__fake", **kwargs)]
|
||||
for t in trials:
|
||||
runner.add_trial(t)
|
||||
client = TuneClient("localhost:{}".format(port))
|
||||
@@ -61,7 +60,9 @@ class TuneServerSuite(unittest.TestCase):
|
||||
runner.step()
|
||||
spec = {
|
||||
"run": "__fake",
|
||||
"stop": {"training_iteration": 3},
|
||||
"stop": {
|
||||
"training_iteration": 3
|
||||
},
|
||||
"trial_resources": dict(cpu=1, gpu=1),
|
||||
}
|
||||
client.add_trial("test", spec)
|
||||
|
||||
@@ -114,8 +114,8 @@ class Trainable(object):
|
||||
time_this_iter = time.time() - start
|
||||
|
||||
if result.timesteps_this_iter is None:
|
||||
raise TuneError(
|
||||
"Must specify timesteps_this_iter in result", result)
|
||||
raise TuneError("Must specify timesteps_this_iter in result",
|
||||
result)
|
||||
|
||||
self._time_total += time_this_iter
|
||||
self._timesteps_total += result.timesteps_this_iter
|
||||
@@ -159,10 +159,10 @@ class Trainable(object):
|
||||
"""
|
||||
|
||||
checkpoint_path = self._save(checkpoint_dir or self.logdir)
|
||||
pickle.dump(
|
||||
[self._experiment_id, self._iteration, self._timesteps_total,
|
||||
self._time_total],
|
||||
open(checkpoint_path + ".tune_metadata", "wb"))
|
||||
pickle.dump([
|
||||
self._experiment_id, self._iteration, self._timesteps_total,
|
||||
self._time_total
|
||||
], open(checkpoint_path + ".tune_metadata", "wb"))
|
||||
return checkpoint_path
|
||||
|
||||
def save_to_object(self):
|
||||
@@ -186,8 +186,10 @@ class Trainable(object):
|
||||
out = io.BytesIO()
|
||||
with gzip.GzipFile(fileobj=out, mode="wb") as f:
|
||||
compressed = pickle.dumps({
|
||||
"checkpoint_name": os.path.basename(checkpoint_prefix),
|
||||
"data": data,
|
||||
"checkpoint_name":
|
||||
os.path.basename(checkpoint_prefix),
|
||||
"data":
|
||||
data,
|
||||
})
|
||||
if len(compressed) > 10e6: # getting pretty large
|
||||
print("Checkpoint size is {} bytes".format(len(compressed)))
|
||||
|
||||
+37
-32
@@ -42,12 +42,12 @@ class Resources(
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls, cpu, gpu, extra_cpu=0, extra_gpu=0):
|
||||
return super(Resources, cls).__new__(
|
||||
cls, cpu, gpu, extra_cpu, extra_gpu)
|
||||
return super(Resources, cls).__new__(cls, cpu, gpu, extra_cpu,
|
||||
extra_gpu)
|
||||
|
||||
def summary_string(self):
|
||||
return "{} CPUs, {} GPUs".format(
|
||||
self.cpu + self.extra_cpu, self.gpu + self.extra_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
|
||||
@@ -77,11 +77,17 @@ class Trial(object):
|
||||
TERMINATED = "TERMINATED"
|
||||
ERROR = "ERROR"
|
||||
|
||||
def __init__(
|
||||
self, trainable_name, config=None, local_dir=DEFAULT_RESULTS_DIR,
|
||||
experiment_tag="", resources=Resources(cpu=1, gpu=0),
|
||||
stopping_criterion=None, checkpoint_freq=0,
|
||||
restore_path=None, upload_dir=None, max_failures=0):
|
||||
def __init__(self,
|
||||
trainable_name,
|
||||
config=None,
|
||||
local_dir=DEFAULT_RESULTS_DIR,
|
||||
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.
|
||||
|
||||
The args here take the same meaning as the command line flags defined
|
||||
@@ -166,19 +172,20 @@ class Trial(object):
|
||||
try:
|
||||
if error_msg and self.logdir:
|
||||
self.num_failures += 1
|
||||
error_file = os.path.join(
|
||||
self.logdir, "error_{}.txt".format(date_str()))
|
||||
error_file = os.path.join(self.logdir, "error_{}.txt".format(
|
||||
date_str()))
|
||||
with open(error_file, "w") as f:
|
||||
f.write(error_msg)
|
||||
self.error_file = error_file
|
||||
if self.runner:
|
||||
stop_tasks = []
|
||||
stop_tasks.append(self.runner.stop.remote())
|
||||
stop_tasks.append(self.runner.__ray_terminate__.remote(
|
||||
self.runner._ray_actor_id.id()))
|
||||
stop_tasks.append(
|
||||
self.runner.__ray_terminate__.remote(
|
||||
self.runner._ray_actor_id.id()))
|
||||
# TODO(ekl) seems like wait hangs when killing actors
|
||||
_, unfinished = ray.wait(
|
||||
stop_tasks, num_returns=2, timeout=250)
|
||||
stop_tasks, num_returns=2, timeout=250)
|
||||
except Exception:
|
||||
print("Error stopping runner:", traceback.format_exc())
|
||||
self.status = Trial.ERROR
|
||||
@@ -252,12 +259,12 @@ class Trial(object):
|
||||
return '{} pid={}'.format(hostname, pid)
|
||||
|
||||
pieces = [
|
||||
'{} [{}]'.format(
|
||||
self._status_string(),
|
||||
location_string(
|
||||
self.last_result.hostname, self.last_result.pid)),
|
||||
'{} s'.format(int(self.last_result.time_total_s)),
|
||||
'{} ts'.format(int(self.last_result.timesteps_total))]
|
||||
'{} [{}]'.format(self._status_string(),
|
||||
location_string(self.last_result.hostname,
|
||||
self.last_result.pid)),
|
||||
'{} s'.format(int(self.last_result.time_total_s)), '{} ts'.format(
|
||||
int(self.last_result.timesteps_total))
|
||||
]
|
||||
|
||||
if self.last_result.episode_reward_mean is not None:
|
||||
pieces.append('{} rew'.format(
|
||||
@@ -274,10 +281,8 @@ class Trial(object):
|
||||
return ', '.join(pieces)
|
||||
|
||||
def _status_string(self):
|
||||
return "{}{}".format(
|
||||
self.status,
|
||||
", {} failures: {}".format(self.num_failures, self.error_file)
|
||||
if self.error_file else "")
|
||||
return "{}{}".format(self.status, ", {} failures: {}".format(
|
||||
self.num_failures, self.error_file) if self.error_file else "")
|
||||
|
||||
def has_checkpoint(self):
|
||||
return self._checkpoint_path is not None or \
|
||||
@@ -335,9 +340,8 @@ class Trial(object):
|
||||
def update_last_result(self, result, terminate=False):
|
||||
if terminate:
|
||||
result = result._replace(done=True)
|
||||
if self.verbose and (
|
||||
terminate or
|
||||
time.time() - self.last_debug > DEBUG_PRINT_INTERVAL):
|
||||
if self.verbose and (terminate or time.time() - self.last_debug >
|
||||
DEBUG_PRINT_INTERVAL):
|
||||
print("TrainingResult for {}:".format(self))
|
||||
print(" {}".format(pretty_print(result).replace("\n", "\n ")))
|
||||
self.last_debug = time.time()
|
||||
@@ -358,8 +362,8 @@ class Trial(object):
|
||||
prefix="{}_{}".format(
|
||||
str(self)[:MAX_LEN_IDENTIFIER], date_str()),
|
||||
dir=self.local_dir)
|
||||
self.result_logger = UnifiedLogger(
|
||||
self.config, self.logdir, self.upload_dir)
|
||||
self.result_logger = UnifiedLogger(self.config, self.logdir,
|
||||
self.upload_dir)
|
||||
remote_logdir = self.logdir
|
||||
|
||||
def logger_creator(config):
|
||||
@@ -372,7 +376,8 @@ class Trial(object):
|
||||
# Logging for trials is handled centrally by TrialRunner, so
|
||||
# configure the remote runner to use a noop-logger.
|
||||
self.runner = cls.remote(
|
||||
config=self.config, registry=ray.tune.registry.get_registry(),
|
||||
config=self.config,
|
||||
registry=ray.tune.registry.get_registry(),
|
||||
logger_creator=logger_creator)
|
||||
|
||||
def set_verbose(self, verbose):
|
||||
@@ -387,8 +392,8 @@ class Trial(object):
|
||||
def __str__(self):
|
||||
"""Combines ``env`` with ``trainable_name`` and ``experiment_tag``."""
|
||||
if "env" in self.config:
|
||||
identifier = "{}_{}".format(
|
||||
self.trainable_name, self.config["env"])
|
||||
identifier = "{}_{}".format(self.trainable_name,
|
||||
self.config["env"])
|
||||
else:
|
||||
identifier = self.trainable_name
|
||||
if self.experiment_tag:
|
||||
|
||||
@@ -13,7 +13,6 @@ from ray.tune.web_server import TuneServer
|
||||
from ray.tune.trial import Trial, Resources
|
||||
from ray.tune.trial_scheduler import FIFOScheduler, TrialScheduler
|
||||
|
||||
|
||||
MAX_DEBUG_TRIALS = 20
|
||||
|
||||
|
||||
@@ -39,8 +38,11 @@ class TrialRunner(object):
|
||||
misleading benchmark results.
|
||||
"""
|
||||
|
||||
def __init__(self, scheduler=None, launch_web_server=False,
|
||||
server_port=TuneServer.DEFAULT_PORT, verbose=True):
|
||||
def __init__(self,
|
||||
scheduler=None,
|
||||
launch_web_server=False,
|
||||
server_port=TuneServer.DEFAULT_PORT,
|
||||
verbose=True):
|
||||
"""Initializes a new TrialRunner.
|
||||
|
||||
Args:
|
||||
@@ -73,9 +75,8 @@ class TrialRunner(object):
|
||||
"""Returns whether all trials have finished running."""
|
||||
|
||||
if self._total_time > self._global_time_limit:
|
||||
print(
|
||||
"Exceeded global time limit {} / {}".format(
|
||||
self._total_time, self._global_time_limit))
|
||||
print("Exceeded global time limit {} / {}".format(
|
||||
self._total_time, self._global_time_limit))
|
||||
return True
|
||||
|
||||
for t in self._trials:
|
||||
@@ -98,12 +99,12 @@ class TrialRunner(object):
|
||||
for trial in self._trials:
|
||||
if trial.status == Trial.PENDING:
|
||||
if not self.has_resources(trial.resources):
|
||||
raise TuneError((
|
||||
"Insufficient cluster resources to launch trial: "
|
||||
"trial requested {} but the cluster only has {} "
|
||||
"available.").format(
|
||||
trial.resources.summary_string(),
|
||||
self._avail_resources.summary_string()))
|
||||
raise TuneError(
|
||||
("Insufficient cluster resources to launch trial: "
|
||||
"trial requested {} but the cluster only has {} "
|
||||
"available.").format(
|
||||
trial.resources.summary_string(),
|
||||
self._avail_resources.summary_string()))
|
||||
elif trial.status == Trial.PAUSED:
|
||||
raise TuneError(
|
||||
"There are paused trials, but no more pending "
|
||||
@@ -165,24 +166,20 @@ class TrialRunner(object):
|
||||
for state, trials in sorted(states.items()):
|
||||
limit = limit_per_state[state]
|
||||
messages.append("{} trials:".format(state))
|
||||
for t in sorted(
|
||||
trials, key=lambda t: t.experiment_tag)[:limit]:
|
||||
for t in sorted(trials, key=lambda t: t.experiment_tag)[:limit]:
|
||||
messages.append(" - {}:\t{}".format(t, t.progress_string()))
|
||||
if len(trials) > limit:
|
||||
messages.append(" ... {} more not shown".format(
|
||||
len(trials) - limit))
|
||||
messages.append(
|
||||
" ... {} more not shown".format(len(trials) - limit))
|
||||
return "\n".join(messages) + "\n"
|
||||
|
||||
def _debug_messages(self):
|
||||
messages = ["== Status =="]
|
||||
messages.append(self._scheduler_alg.debug_string())
|
||||
if self._resources_initialized:
|
||||
messages.append(
|
||||
"Resources used: {}/{} CPUs, {}/{} GPUs".format(
|
||||
self._committed_resources.cpu,
|
||||
self._avail_resources.cpu,
|
||||
self._committed_resources.gpu,
|
||||
self._avail_resources.gpu))
|
||||
messages.append("Resources used: {}/{} CPUs, {}/{} GPUs".format(
|
||||
self._committed_resources.cpu, self._avail_resources.cpu,
|
||||
self._committed_resources.gpu, self._avail_resources.gpu))
|
||||
return messages
|
||||
|
||||
def has_resources(self, resources):
|
||||
@@ -190,9 +187,8 @@ 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_total() <= cpu_avail and
|
||||
resources.gpu_total() <= gpu_avail)
|
||||
return (resources.cpu_total() <= cpu_avail
|
||||
and resources.gpu_total() <= gpu_avail)
|
||||
|
||||
def _get_next_trial(self):
|
||||
self._update_avail_resources()
|
||||
@@ -307,8 +303,9 @@ class TrialRunner(object):
|
||||
self._scheduler_alg.on_trial_remove(self, trial)
|
||||
elif trial.status is Trial.RUNNING:
|
||||
# NOTE: There should only be one...
|
||||
result_id = [rid for rid, t in self._running.items()
|
||||
if t is trial][0]
|
||||
result_id = [
|
||||
rid for rid, t in self._running.items() if t is trial
|
||||
][0]
|
||||
self._running.pop(result_id)
|
||||
try:
|
||||
result = ray.get(result_id)
|
||||
@@ -339,9 +336,8 @@ class TrialRunner(object):
|
||||
def _update_avail_resources(self):
|
||||
clients = ray.global_state.client_table()
|
||||
local_schedulers = [
|
||||
entry for client in clients.values() for entry in client
|
||||
if (entry['ClientType'] == 'local_scheduler' and not
|
||||
entry['Deleted'])
|
||||
entry for client in clients.values() for entry in client if
|
||||
(entry['ClientType'] == 'local_scheduler' and not entry['Deleted'])
|
||||
]
|
||||
num_cpus = sum(ls['CPU'] for ls in local_schedulers)
|
||||
num_gpus = sum(ls.get('GPU', 0) for ls in local_schedulers)
|
||||
|
||||
@@ -99,12 +99,12 @@ class FIFOScheduler(TrialScheduler):
|
||||
|
||||
def choose_trial_to_run(self, trial_runner):
|
||||
for trial in trial_runner.get_trials():
|
||||
if (trial.status == Trial.PENDING and
|
||||
trial_runner.has_resources(trial.resources)):
|
||||
if (trial.status == Trial.PENDING
|
||||
and trial_runner.has_resources(trial.resources)):
|
||||
return trial
|
||||
for trial in trial_runner.get_trials():
|
||||
if (trial.status == Trial.PAUSED and
|
||||
trial_runner.has_resources(trial.resources)):
|
||||
if (trial.status == Trial.PAUSED
|
||||
and trial_runner.has_resources(trial.resources)):
|
||||
return trial
|
||||
return None
|
||||
|
||||
|
||||
+16
-11
@@ -16,7 +16,6 @@ from ray.tune.trial_scheduler import FIFOScheduler
|
||||
from ray.tune.web_server import TuneServer
|
||||
from ray.tune.experiment import Experiment
|
||||
|
||||
|
||||
_SCHEDULERS = {
|
||||
"FIFO": FIFOScheduler,
|
||||
"MedianStopping": MedianStoppingRule,
|
||||
@@ -30,13 +29,15 @@ def _make_scheduler(args):
|
||||
if args.scheduler in _SCHEDULERS:
|
||||
return _SCHEDULERS[args.scheduler](**args.scheduler_config)
|
||||
else:
|
||||
raise TuneError(
|
||||
"Unknown scheduler: {}, should be one of {}".format(
|
||||
args.scheduler, _SCHEDULERS.keys()))
|
||||
raise TuneError("Unknown scheduler: {}, should be one of {}".format(
|
||||
args.scheduler, _SCHEDULERS.keys()))
|
||||
|
||||
|
||||
def run_experiments(experiments, scheduler=None, with_server=False,
|
||||
server_port=TuneServer.DEFAULT_PORT, verbose=True):
|
||||
def run_experiments(experiments,
|
||||
scheduler=None,
|
||||
with_server=False,
|
||||
server_port=TuneServer.DEFAULT_PORT,
|
||||
verbose=True):
|
||||
"""Tunes experiments.
|
||||
|
||||
Args:
|
||||
@@ -54,17 +55,21 @@ def run_experiments(experiments, scheduler=None, with_server=False,
|
||||
scheduler = FIFOScheduler()
|
||||
|
||||
runner = TrialRunner(
|
||||
scheduler, launch_web_server=with_server, server_port=server_port,
|
||||
scheduler,
|
||||
launch_web_server=with_server,
|
||||
server_port=server_port,
|
||||
verbose=verbose)
|
||||
exp_list = experiments
|
||||
if isinstance(experiments, Experiment):
|
||||
exp_list = [experiments]
|
||||
elif type(experiments) is dict:
|
||||
exp_list = [Experiment.from_json(name, spec)
|
||||
for name, spec in experiments.items()]
|
||||
exp_list = [
|
||||
Experiment.from_json(name, spec)
|
||||
for name, spec in experiments.items()
|
||||
]
|
||||
|
||||
if (type(exp_list) is list and
|
||||
all(isinstance(exp, Experiment) for exp in exp_list)):
|
||||
if (type(exp_list) is list
|
||||
and all(isinstance(exp, Experiment) for exp in exp_list)):
|
||||
for experiment in exp_list:
|
||||
scheduler.add_experiment(experiment, runner)
|
||||
else:
|
||||
|
||||
@@ -7,7 +7,6 @@ import base64
|
||||
import ray
|
||||
from ray.tune.registry import _to_pinnable, _from_pinnable
|
||||
|
||||
|
||||
_pinned_objects = []
|
||||
PINNED_OBJECT_PREFIX = "ray.tune.PinnedObject:"
|
||||
|
||||
@@ -15,14 +14,15 @@ PINNED_OBJECT_PREFIX = "ray.tune.PinnedObject:"
|
||||
def pin_in_object_store(obj):
|
||||
obj_id = ray.put(_to_pinnable(obj))
|
||||
_pinned_objects.append(ray.get(obj_id))
|
||||
return "{}{}".format(
|
||||
PINNED_OBJECT_PREFIX, base64.b64encode(obj_id.id()).decode("utf-8"))
|
||||
return "{}{}".format(PINNED_OBJECT_PREFIX,
|
||||
base64.b64encode(obj_id.id()).decode("utf-8"))
|
||||
|
||||
|
||||
def get_pinned_object(pinned_id):
|
||||
from ray.local_scheduler import ObjectID
|
||||
return _from_pinnable(ray.get(ObjectID(
|
||||
base64.b64decode(pinned_id[len(PINNED_OBJECT_PREFIX):]))))
|
||||
return _from_pinnable(
|
||||
ray.get(
|
||||
ObjectID(base64.b64decode(pinned_id[len(PINNED_OBJECT_PREFIX):]))))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -163,8 +163,8 @@ def _generate_variants(spec):
|
||||
for path, value in grid_vars:
|
||||
resolved_vars[path] = _get_value(spec, path)
|
||||
for k, v in resolved.items():
|
||||
if (k in resolved_vars and v != resolved_vars[k] and
|
||||
_is_resolved(resolved_vars[k])):
|
||||
if (k in resolved_vars and v != resolved_vars[k]
|
||||
and _is_resolved(resolved_vars[k])):
|
||||
raise ValueError(
|
||||
"The variable `{}` could not be unambiguously "
|
||||
"resolved to a single value. Consider simplifying "
|
||||
@@ -262,16 +262,16 @@ def _unresolved_values(spec):
|
||||
for k, v in spec.items():
|
||||
resolved, v = _try_resolve(v)
|
||||
if not resolved:
|
||||
found[(k,)] = v
|
||||
found[(k, )] = v
|
||||
elif isinstance(v, dict):
|
||||
# Recurse into a dict
|
||||
for (path, value) in _unresolved_values(v).items():
|
||||
found[(k,) + path] = value
|
||||
found[(k, ) + path] = value
|
||||
elif isinstance(v, list):
|
||||
# Recurse into a list
|
||||
for i, elem in enumerate(v):
|
||||
for (path, value) in _unresolved_values({i: elem}).items():
|
||||
found[(k,) + path] = value
|
||||
found[(k, ) + path] = value
|
||||
return found
|
||||
|
||||
|
||||
|
||||
@@ -61,8 +61,10 @@ def _resolve(directory, result_fname):
|
||||
|
||||
|
||||
def load_results_to_df(directory, result_name="result.json"):
|
||||
exp_directories = [dirpath for dirpath, dirs, files in os.walk(directory)
|
||||
for f in files if f == result_name]
|
||||
exp_directories = [
|
||||
dirpath for dirpath, dirs, files in os.walk(directory) for f in files
|
||||
if f == result_name
|
||||
]
|
||||
data = [_resolve(d, result_name) for d in exp_directories]
|
||||
data = [d for d in data if d]
|
||||
return pd.DataFrame(data)
|
||||
@@ -76,8 +78,9 @@ def generate_plotly_dim_dict(df, field):
|
||||
dim_dict["values"] = column
|
||||
elif is_string_dtype(column):
|
||||
texts = column.unique()
|
||||
dim_dict["values"] = [np.argwhere(texts == x).flatten()[0]
|
||||
for x in column]
|
||||
dim_dict["values"] = [
|
||||
np.argwhere(texts == x).flatten()[0] for x in column
|
||||
]
|
||||
dim_dict["tickvals"] = list(range(len(texts)))
|
||||
dim_dict["ticktext"] = texts
|
||||
else:
|
||||
|
||||
@@ -39,28 +39,30 @@ class TuneClient(object):
|
||||
|
||||
def get_all_trials(self):
|
||||
"""Returns a list of all trials (trial_id, config, status)."""
|
||||
return self._get_response(
|
||||
{"command": TuneClient.GET_LIST})
|
||||
return self._get_response({"command": TuneClient.GET_LIST})
|
||||
|
||||
def get_trial(self, trial_id):
|
||||
"""Returns the last result for queried trial."""
|
||||
return self._get_response(
|
||||
{"command": TuneClient.GET_TRIAL,
|
||||
"trial_id": trial_id})
|
||||
return self._get_response({
|
||||
"command": TuneClient.GET_TRIAL,
|
||||
"trial_id": trial_id
|
||||
})
|
||||
|
||||
def add_trial(self, name, trial_spec):
|
||||
"""Adds a trial of `name` with configurations."""
|
||||
# TODO(rliaw): have better way of specifying a new trial
|
||||
return self._get_response(
|
||||
{"command": TuneClient.ADD,
|
||||
"name": name,
|
||||
"spec": trial_spec})
|
||||
return self._get_response({
|
||||
"command": TuneClient.ADD,
|
||||
"name": name,
|
||||
"spec": trial_spec
|
||||
})
|
||||
|
||||
def stop_trial(self, trial_id):
|
||||
"""Requests to stop trial."""
|
||||
return self._get_response(
|
||||
{"command": TuneClient.STOP,
|
||||
"trial_id": trial_id})
|
||||
return self._get_response({
|
||||
"command": TuneClient.STOP,
|
||||
"trial_id": trial_id
|
||||
})
|
||||
|
||||
def _get_response(self, data):
|
||||
payload = json.dumps(data).encode()
|
||||
@@ -71,7 +73,6 @@ class TuneClient(object):
|
||||
|
||||
def RunnerHandler(runner):
|
||||
class Handler(SimpleHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
content_len = int(self.headers.get('Content-Length'), 0)
|
||||
raw_body = self.rfile.read(content_len)
|
||||
@@ -82,8 +83,7 @@ def RunnerHandler(runner):
|
||||
else:
|
||||
self.send_response(400)
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(
|
||||
response).encode())
|
||||
self.wfile.write(json.dumps(response).encode())
|
||||
|
||||
def trial_info(self, trial):
|
||||
if trial.last_result:
|
||||
@@ -112,8 +112,9 @@ def RunnerHandler(runner):
|
||||
response = {}
|
||||
try:
|
||||
if command == TuneClient.GET_LIST:
|
||||
response["trials"] = [self.trial_info(t)
|
||||
for t in runner.get_trials()]
|
||||
response["trials"] = [
|
||||
self.trial_info(t) for t in runner.get_trials()
|
||||
]
|
||||
elif command == TuneClient.GET_TRIAL:
|
||||
trial = get_trial()
|
||||
response["trial_info"] = self.trial_info(trial)
|
||||
@@ -147,8 +148,7 @@ class TuneServer(threading.Thread):
|
||||
self._port = port if port else self.DEFAULT_PORT
|
||||
address = ('localhost', self._port)
|
||||
print("Starting Tune Server...")
|
||||
self._server = HTTPServer(
|
||||
address, RunnerHandler(runner))
|
||||
self._server = HTTPServer(address, RunnerHandler(runner))
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
|
||||
Reference in New Issue
Block a user