运行基本 Tune 实验#
使用 Tune 最常见也是最简单的方式是将其用作并行实验运行器。如果你可以在 Python 函数中定义实验 trial,就可以使用 Tune 在集群中运行数百到数千个独立的 trial 实例。Tune 管理 trial 执行、状态报告和容错。
并行运行独立的 Tune Trial#
作为一个通用示例,我们考虑使用 Tune 作为一个简单的网格扫描,执行 N
个独立的模型训练 trial。每个 trial 可以根据传入的配置字典执行不同的代码。
步骤 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.tune.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.rayai.org.cn/en/latest/tune/tutorials/tune-checkpoints.html
return {"score": score, "other_data": ...}
步骤 2: 接下来,定义要运行的 trial 空间。这里,我们定义了一个简单的从 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: (可选)配置分配给每个 trial 的资源。Tune 使用此资源分配来控制并行性。例如,如果每个 trial 配置为使用 4 个 CPU,而集群只有 32 个 CPU,那么 Tune 将把并发 trial 的数量限制为 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 运行 trial。Tune 将报告实验状态,实验完成后,你可以检查结果。Tune 可以自动重试失败的 trial 以及整个实验;请参阅如何定义 Ray 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 会定期向标准输出打印状态摘要,显示正在进行的实验状态,直到实验完成。
== 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).
最终结果对象包含已完成 trial 的元数据
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 与简单地使用 Tasks 进行并行 trial 执行有何不同。实际上,上面的示例可以类似地重写为
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 task 相比,Tune 提供以下附加功能
状态报告和追踪,包括与常用监控工具的集成和回调。
为细粒度容错进行 trial 检查点。
多 worker trial 的组调度。
简而言之,如果你需要状态追踪或支持更高级的机器学习工作负载,请考虑使用 Tune。