运行基本调优实验#
使用 Tune 最常见的方式也是最简单的:作为一个并行实验运行器。如果你可以用 Python 函数定义实验试验,你就可以使用 Tune 在集群中运行数百到数千个独立的试验实例。Tune 管理试验执行、状态报告和容错。
并行运行独立调优试验#
作为一个一般示例,让我们考虑使用 Tune 执行 N
次独立的模型训练试验,作为一个简单的网格扫描。每个试验可以根据传入的配置字典执行不同的代码。
步骤 1: 首先,我们定义我们想要运行变体的模型训练函数。该函数接受一个配置字典作为参数,并返回一个简单的字典输出。了解更多关于记录 Tune 结果的信息,请参阅 如何在 Tune 中配置日志记录?。
from ray import tune
import ray
import os
NUM_MODELS = 100
def train_model(config):
score = config["model_id"]
# Import model libraries, etc...
# Load data and train model code here...
# Return final stats. You can also return intermediate progress
# using ray.train.report() if needed.
# To return your model, you could write it to storage and return its
# URI in this dict, or return it as a Tune Checkpoint:
# https://docs.ray.io/en/latest/tune/tutorials/tune-checkpoints.html
return {"score": score, "other_data": ...}
步骤 2: 接下来,定义要运行的试验空间。这里,我们定义了一个简单的网格扫描,从 0..NUM_MODELS
开始,这将生成要传递给每个模型函数的配置字典。了解更多关于 Tune 在定义空间时提供的功能,请参阅 使用 Tune 搜索空间。
# Define trial parameters as a single grid sweep.
trial_space = {
# This is an example parameter. You could replace it with filesystem paths,
# model types, or even full nested Python dicts of model configurations, etc.,
# that enumerate the set of trials to run.
"model_id": tune.grid_search([
"model_{}".format(i)
for i in range(NUM_MODELS)
])
}
步骤 3: 可选地,配置每个试验分配的资源。Tune 使用这些资源分配来控制并行性。例如,如果每个试验配置为使用 4 个 CPU,而集群只有 32 个 CPU,那么 Tune 将限制并发试验的数量为 8,以避免集群过载。更多信息,请参阅 Ray Tune 的并行性和资源指南。
# Can customize resources per trial, here we set 1 CPU each.
train_model = tune.with_resources(train_model, {"cpu": 1})
步骤 4: 使用 Tune 运行试验。Tune 将报告实验状态,实验结束后,您可以检查结果。Tune 可以自动重试失败的试验,以及整个实验;请参阅 tune-停止指南。
# Start a Tune run and print the best result.
tuner = tune.Tuner(train_model, param_space=trial_space)
results = tuner.fit()
# Access individual results.
print(results[0])
print(results[1])
print(results[2])
步骤 5: 检查结果。它们看起来会像这样。Tune 会定期将状态摘要打印到 stdout,显示正在进行的实验状态,直到完成:
== Status ==
Current time: 2022-09-21 10:19:34 (running for 00:00:04.54)
Memory usage on this node: 6.9/31.1 GiB
Using FIFO scheduling algorithm.
Resources requested: 0/8 CPUs, 0/0 GPUs, 0.0/16.13 GiB heap, 0.0/8.06 GiB objects
Result logdir: /home/ubuntu/ray_results/train_model_2022-09-21_10-19-26
Number of trials: 100/100 (100 TERMINATED)
+-------------------------+------------+----------------------+------------+--------+------------------+
| Trial name | status | loc | model_id | iter | total time (s) |
|-------------------------+------------+----------------------+------------+--------+------------------|
| train_model_8d627_00000 | TERMINATED | 192.168.1.67:2381731 | model_0 | 1 | 8.46386e-05 |
| train_model_8d627_00001 | TERMINATED | 192.168.1.67:2381761 | model_1 | 1 | 0.000126362 |
| train_model_8d627_00002 | TERMINATED | 192.168.1.67:2381763 | model_2 | 1 | 0.000112772 |
...
| train_model_8d627_00097 | TERMINATED | 192.168.1.67:2381731 | model_97 | 1 | 5.57899e-05 |
| train_model_8d627_00098 | TERMINATED | 192.168.1.67:2381767 | model_98 | 1 | 6.05583e-05 |
| train_model_8d627_00099 | TERMINATED | 192.168.1.67:2381763 | model_99 | 1 | 6.69956e-05 |
+-------------------------+------------+----------------------+------------+--------+------------------+
2022-09-21 10:19:35,159 INFO tune.py:762 -- Total run time: 5.06 seconds (4.46 seconds for the tuning loop).
最终结果对象包含完成的试验元数据:
Result(metrics={'score': 'model_0', 'other_data': Ellipsis, 'done': True, 'trial_id': '8d627_00000', 'experiment_tag': '0_model_id=model_0'}, error=None, log_dir=PosixPath('/home/ubuntu/ray_results/train_model_2022-09-21_10-19-26/train_model_8d627_00000_0_model_id=model_0_2022-09-21_10-19-30'))
Result(metrics={'score': 'model_1', 'other_data': Ellipsis, 'done': True, 'trial_id': '8d627_00001', 'experiment_tag': '1_model_id=model_1'}, error=None, log_dir=PosixPath('/home/ubuntu/ray_results/train_model_2022-09-21_10-19-26/train_model_8d627_00001_1_model_id=model_1_2022-09-21_10-19-31'))
Result(metrics={'score': 'model_2', 'other_data': Ellipsis, 'done': True, 'trial_id': '8d627_00002', 'experiment_tag': '2_model_id=model_2'}, error=None, log_dir=PosixPath('/home/ubuntu/ray_results/train_model_2022-09-21_10-19-26/train_model_8d627_00002_2_model_id=model_2_2022-09-21_10-19-31'))
Tune 与使用 Ray Core (ray.remote
) 相比如何?#
你可能想知道 Tune 与简单使用 ray-远程函数 进行并行试验执行有何不同。确实,上述示例可以类似地重写为:
remote_train = ray.remote(train_model)
futures = [remote_train.remote({"model_id": i}) for i in range(NUM_MODELS)]
print("Submitting tasks...")
results = ray.get(futures)
print("Trial results", results)
与使用 Ray 任务相比,Tune 提供了以下附加功能:
状态报告和跟踪,包括与常见监控工具的集成和回调。
细粒度容错的试验检查点。
多工作者试验的团伙调度。
简而言之,如果你需要状态跟踪或支持更高级的机器学习工作负载,请考虑使用 Tune。