- 人工智能
- 分布式训练
- 强化学习
- 任务调度
- 模型推理服务
【免费下载链接】ray
Ray is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.
Ray Tune 是 Ray 生态中面向超参数调优(Hyperparameter Tuning, HPO)的核心库。本文以 doc/source/tune/faq.rst 为主体,系统整理 Ray Tune 使用过程中最高频的疑问:如何区分超参数与模型参数、如何按问题规模选择搜索算法与调度器、如何设计搜索空间(含嵌套与条件空间)、如何控制资源、如何复现实验、如何避免性能瓶颈,以及在本地开发测试 Tune 的完整方法。读完本文,你将掌握一套可直接落地的 Ray Tune 调参方法论,并能在实际集群环境中独立排查调度与性能问题。
什么是超参数?它与模型参数有何区别?
在监督学习中,我们用带标签的数据训练模型,使模型能够正确识别新的数据。模型的一切都由一组参数定义,例如线性回归中的权重——这些是模型参数(model parameters),它们是在训练过程中被学习出来的。
与之相对,超参数(hyperparameters)定义的是模型本身的结构性细节:我们使用的是线性回归还是分类器、神经网络采用什么架构、多少层、什么类型的滤波器等。这些都是在训练之前定义的,而不是通过学习得到的。此外,学习率(learning rate)、折扣因子(discount rate)等训练过程的配置也被视为超参数。如果希望训练过程和最终模型表现良好,首先需要确定一组最优或接近最优的超参数。
确定最优超参数最直接的方法是执行一个循环:从一组合理且尽可能包容的候选值中挑选一组配置,训练模型,与前几次循环的结果比较,最后挑选表现最佳的一组。这个过程称为超参数调优或优化(HPO)。超参数在一个受限且明确的搜索空间中取值,这个搜索空间由config字典中为每个超参数定义的采样分布共同构成。Ray Tune 的搜索空间 API 详见 Search Space API 相关章节。
如何选择搜索算法与调度器?
Ray Tune 提供 多种搜索算法 和 多种调度器。选择主要取决于你的问题特征:
- 问题规模是大还是小(训练耗时多久?资源如 GPU 成本多高?能否并行运行大量 trial?)
- 需要调优多少个超参数
- 超参数的合法取值范围是什么
模型能返回增量结果:优先考虑早停
如果模型逐步返回增量结果(如深度学习按 epoch 返回结果、GBDT 每添加一棵树返回结果等),使用早停(early stopping)通常能让你采样更多配置,因为表现不佳的 trial 会在跑完全程之前被提前剪枝(prune)。请注意,并非所有搜索算法都能利用被剪枝 trial 的信息。早停的前提是必须有增量结果——在使用函数式 API 时,这意味着tune.report()必须在循环中被调用多次,而不是只调用一次。
小模型:随机搜索 + ASHA
如果模型较小,通常可以并行尝试大量不同配置。随机搜索(random search)可以用来生成配置,也可以对部分取值做网格搜索(grid search)。如果问题支持早停,建议同时使用 ASHA 调度器(Async Successive Halving Algorithm,异步连续减半算法)提前终止表现差的 trial。
大模型:贝叶斯优化或 PBT
如果模型较大,可以尝试基于贝叶斯优化的搜索算法,例如 BayesOpt,在少量 trial 之后就能得到较优的配置;Ax 与之类似但对噪声数据更稳健。请注意这些算法只适合少量超参数。另一种选择是 Population Based Training(PBT),它在 trial 数量很少(例如 8 个甚至 4 个)时也能工作得很好。不过 PBT 输出的是一个超参数训练计划(schedule),而不是一组固定的超参数。
按超参数特征选择
- 超参数数量少:贝叶斯优化方法效果好。可参考 BOHB(贝叶斯优化 + Hyperband 的结合体)或 Optuna 配合 ASHA 调度器,把贝叶斯优化的采样能力与早停的加速能力结合起来。
- 只有连续取值:与大多数贝叶斯优化方法配合良好。离散或分类变量也能工作,但随着类别数量增加效果会变差。
- 大量分类取值:考虑使用随机搜索,或基于 TPE 的贝叶斯优化算法,如 Optuna 或 HyperOpt。
Ray Tune 的推荐默认组合
官方 FAQ 的"go-to"方案总结如下:
| 问题场景 | 推荐方案 |
|---|---|
| 较小问题 | 随机搜索 + ASHA 早停 |
| 较大问题、超参数数量少 | BOHB |
| 较大问题、超参数数量多、且可接受学习计划 | PBT |
如何选择超参数的取值范围?
一个好的出发点是:查阅提出该算法的论文,以及看看其他人都在用什么值。大多数算法对其部分参数也有合理的默认值。例如 XGBoost 的参数文档建议决策树最大深度max_depth=6,这里 2 到 10 之间的任何值都可能合理(当然取决于你的问题)。
Ray Tune 官方 FAQ 给出以下经验性建议:
| 超参数 | 建议取值范围/分布 |
|---|---|
| 学习率 | 使用1e-5到1e-1之间的loguniform 分布:tune.loguniform(1e-5, 1e-1) |
| 批大小 | 尝试2 的幂:2、4、8、16、32、64、128、256 等。数据多而问题简单时用较大的批大小,数据少而问题难时用较小的批大小 |
| 网络层大小 | 同样尝试2 的幂。小问题(如 Cartpole)用小层尺寸,大问题用大层尺寸 |
| 强化学习折扣因子 | 在 0.9 到 1.0 之间均匀采样。视问题而定,更严格的范围(如高于 0.97 甚至 0.99,例如 Atari)也可能合理 |
如何使用嵌套/条件搜索空间?
有时需要定义取值依赖于其他参数值的参数。Ray Tune 提供了多种方式。
嵌套空间(Nested spaces)
可以将超参数定义嵌套在子字典中(示例来自 doc_code/faq.py):
config = {"a": {"x": tune.uniform(0, 10)}, "b": tune.choice([1, 2, 3])}trial 的 config 会与输入的 config 完全一致地嵌套展开。
条件空间(Conditional spaces)
自定义与条件搜索空间的详细说明见 Search Space API 文档。简而言之,可以向tune.sample_from()传入自定义函数,函数可以返回依赖其他取值的结果。在源码 python/ray/tune/search/sample.py 中,sample_from(func)接收一个可调用对象并返回Function域,该可调用对象会收到当前 trial 已采样好的config字典,因此可以实现条件采样:
config = { "a": tune.randint(5, 10), "b": tune.sample_from(lambda config: np.random.randint(0, config["a"])), }条件网格搜索(Conditional grid search)
如果想对两个相互依赖的参数做网格搜索,直接使用tune.sample_from是行不通的,因为它不支持网格搜索。解决方案是借助辅助函数生成一组合法元组,例如:假设a在 5 到 10 之间、b在 0 到a之间:
def _iter(): for a in range(5, 10): for b in range(a): yield a, b config = { "ab": tune.grid_search(list(_iter())), }然后你的 trainable 中可以用a, b = config["ab"]解包这两个变量并使用。
早停(如 Hyperband/ASHA)是如何工作的?
早停算法会查看中间报告的值(即每个训练 epoch 之后通过tune.report()报告的值)。经过一定步数后,它们会移除表现最差的 trial,只保留表现最好的 trial。trial 的好坏通过目标指标排序来确定,例如 accuracy 或 loss。
在 ASHA 中,你可以控制被提前终止的 trial 数量。reduction_factor=4意味着每次缩减时只保留全部 trial 的25%。通过grace_period=n可以强制 ASHA 让每个 trial 至少训练n个 epoch。
为什么所有 trial 都返回 "1" iteration?
这个情况最常出现在 Tune 的函数式 API 中。Ray Tune 在内部每次调用tune.report()时递增迭代计数器。如果只在训练结束时调用一次tune.report(),计数器就只递增了一次。如果使用类 API,计数器会在每次调用step()之后递增。
注意,多报告几次指标可能更有意义。例如,如果你的算法训练 1000 个时间步,考虑每 100 步报告一次中间性能值。这样 Hyperband/ASHA 等调度器就能尽早终止表现差的 trial。
那些额外的输出是什么?
你会发现 Ray Tune 不仅报告超参数(来自config)和指标(传给tune.report()的),还会输出一些其他信息:
Result for easy_objective_c64c9112: date: 2020-10-07_13-29-18 done: false experiment_id: 6edc31257b564bf8985afeec1df618ee experiment_tag: 7_activation=tanh,height=-53.116,steps=100,width=13.885 hostname: ubuntu iterations: 0 iterations_since_restore: 1 mean_loss: 4.688385317424468 neg_mean_loss: -4.688385317424468 node_ip: 192.168.1.115 pid: 5973 time_since_restore: 7.605552673339844e-05 time_this_iter_s: 7.605552673339844e-05 time_total_s: 7.605552673339844e-05 timestamp: 1602102558 timesteps_since_restore: 0 training_iteration: 1 trial_id: c64c9112这些自动填充字段的完整词汇表参见 tune-autofilled-metrics 章节。
如何设置资源?
如果想为某个 trial 分配特定资源,可以使用tune.with_resources,把它包裹在 trainable 外层,并传入一个字典或 PlacementGroupFactory 对象:
tuner = tune.Tuner( tune.with_resources( train_fn, resources={"cpu": 2, "gpu": 0.5, "custom_resources": {"hdd": 80}} ), ) tuner.fit()从源码 python/ray/tune/trainable/util.py 可以看到,with_resources会将资源请求以_resources属性注解在 trainable 上(函数式 API),或通过default_resource_request()类方法暴露(类 API),由 Tune 在调度时解析。上面的示例展示了三个要点:
cpu和gpu选项分别设置每个 trial 可用的 CPU 与 GPU 数量。trial 不能请求超过这些数量的资源(例外见第 3 点)。- 可以请求分数 GPU。值为 0.5 意味着 GPU 一半的显存可供该 trial 使用。你需要自行确保模型能放进这部分的显存中。
- 可以请求启动集群时提供给 Ray 的自定义资源。trial 只会被调度到能够提供你请求的全部资源的单个节点上。
一个重要的注意点:每个 Ray worker(因此每个 Ray Tune trial)只会被调度到一台机器上。这意味着如果你为一个 trial 请求 2 个 GPU,但集群由 4 台各带 1 个 GPU 的机器组成,该 trial 永远不会被调度。换句话说,你必须确保 Ray 集群中存在能够满足资源请求的机器。
使用 Placement Group 请求额外资源
在某些情况下,trainable 可能需要启动其他远程 actor,例如通过 Ray Train 进行分布式训练时。此时可以使用 placement groups 请求额外资源:
tuner = tune.Tuner( tune.with_resources( train_fn, resources=tune.PlacementGroupFactory( [ {"CPU": 2, "GPU": 0.5, "hdd": 80}, {"CPU": 1}, {"CPU": 1}, ], strategy="PACK", ), ) ) tuner.fit()这里为远程任务额外请求了 2 个 CPU。这两个额外 actor 不必与主 trainable 在同一节点上。实际上,你可以通过strategy参数控制这一点。在这个例子中,PACK会尽量将 actor 调度到同一节点,但也允许调度到其他节点。更多 placement 策略请参阅 placement groups 文档。
基于自定义规则分配资源(lambda 函数)
还可以基于自定义规则为 trial 分配特定资源。例如根据参数空间中的某个设置来决定是否分配 GPU:
tuner = tune.Tuner( tune.with_resources( train_fn, resources=lambda config: {"GPU": 1} if config["use_gpu"] else {"GPU": 0}, ), param_space={ "use_gpu": True, }, ) tuner.fit()with_resources接受字典、PlacementGroupFactory或可调用对象三种形式(见 util.py),lambda 形式会在每个 trial 解析资源时以该 trial 的 config 为参数被调用。
训练卡住且 Ray 报告 pending actor 或 task 无法调度?
这通常是因为 trainable 启动了 Ray actor 或 task,但没有为它们计入资源,导致死锁。也可能是 trainable 内部使用了基于 Ray 的其他库(如 Modin)"隐蔽地"造成的。解决办法是使用上文介绍的 placement groups 为 trial 请求额外资源。
例如,如果 trainable 使用 Modin dataframe,对其的操作会生成 Ray task。通过为 trial 额外分配一个 CPU bundle,这些 task 就能正常运行而不至于资源匮乏:
def train_fn(config): # some Modin operations here # import modin.pandas as pd tune.report({"metric": metric}) tuner = tune.Tuner( tune.with_resources( train_fn, resources=tune.PlacementGroupFactory( [ {"CPU": 1}, # this bundle will be used by the trainable itself {"CPU": 1}, # this bundle will be used by Modin ], strategy="PACK", ), ) ) tuner.fit()如何向 trainable 传递额外参数?
Ray Tune 期望你的 trainable 函数最多只接受两个参数:config和checkpoint_dir。但有时你想传入常量参数,比如运行的 epoch 数或训练用的数据集。Ray Tune 提供了包装函数 tune.with_parameters():
from ray import tune import numpy as np def train_func(config, num_epochs=5, data=None): for i in range(num_epochs): for sample in data: # ... train on sample pass # Some huge dataset data = np.random.random(size=100000000) tuner = tune.Tuner(tune.with_parameters(train_func, num_epochs=5, data=data)) tuner.fit()这个函数的工作方式类似于functools.partial,但它会把参数**直接存放到 Ray 对象存储(object store)**中。这意味着即使传入数据集这样的大对象,Ray 也能保证它们在集群各机器上被高效存储与检索。从源码看,with_parameters通过内部_ParameterRegistry将每个 kwargs 对象put到对象存储,并在函数 trainable 每次被调用时按 key 取回注入(util.py)。
tune.with_parameters()也适用于类 trainable:对于类 API,参数会作为 kwargs 传给Trainable.setup()方法。
如何复现实验?
复现实验意味着重复运行时得到完全一致的结果。要做到这一点,每次运行实验的条件必须完全相同。在 ML 训练与调优中,这主要涉及训练和调优生命周期中各处用于采样的随机数生成器(RNG)。
随机数生成器用于产生随机性,例如为你定义的参数采样超参数值。计算中没有真正的随机,而是存在能生成看似随机、满足随机分布所有性质的复杂算法。这些算法可以**播种(seeded)**一个初始状态,此后生成的随机数序列就总是相同的。
各环节的播种方法
训练与调优过程中多处存在随机性,需要在所有这些地方引入种子:
- 搜索算法(Search algorithm):搜索算法需要播种,以便每次运行生成相同的超参数配置。某些搜索算法可以在构造函数中显式指定随机种子(寻找构造器中的
seed参数);对于其他算法,尝试使用下面的代码块。 - 调度器(Schedulers):像 PBT 这样的调度器依赖重新采样部分参数,这需要随机性。使用下面的代码块设置初始种子。
- 训练函数(Training function):除了初始化配置,训练函数本身也必须使用种子,例如数据切分。你应该在训练函数开头设置种子。
# This should suffice to initialize the RNGs for most Python-based libraries import random import numpy as np random.seed(1234) np.random.seed(5678)PyTorch 和 TensorFlow 使用各自的 RNG,也需要初始化:
import torch torch.manual_seed(0) import tensorflow as tf tf.random.set_seed(0)因此你应当同时播种 Ray Tune 的调度器、搜索算法和训练代码。调度器与搜索算法应该始终使用同一个种子。训练代码也是如此,但通常让不同训练运行之间的种子互不相同更有益。
可复现实验的完整蓝图
import random import numpy as np from ray import tune def trainable(config): # config["seed"] is set deterministically, but differs between training runs random.seed(config["seed"]) np.random.seed(config["seed"]) # torch.manual_seed(config["seed"]) # ... training code config = { "seed": tune.randint(0, 10000), # ... } if __name__ == "__main__": # Set seed for the search algorithms/schedulers random.seed(1234) np.random.seed(1234) # Don't forget to check if the search alg has a `seed` parameter tuner = tune.Tuner(trainable, param_space=config) tuner.fit()一个最小的可复现示例(来自 doc_code/faq.py):
import numpy as np from ray import tune def train_func(config): # Set seed for trainable random result. # If you remove this line, you will get different results # each time you run the trial, even if the configuration # is the same. np.random.seed(config["seed"]) random_result = np.random.uniform(0, 100, size=1).item() tune.report({"result": random_result}) # Set seed for Ray Tune's random search. # If you remove this line, you will get different configurations # each time you run the script. np.random.seed(1234) tuner = tune.Tuner( train_func, tune_config=tune.TuneConfig( num_samples=10, search_alg=tune.search.BasicVariantGenerator(), ), param_space={"seed": tune.randint(0, 1000)}, ) tuner.fit()请注意,并非总能控制所有非确定性来源。例如使用 ASHA 或 PBT 时,某些 trial 可能比其他 trial 更早结束,从而影响调度器行为;哪个 trial 先结束又可能取决于系统负载、网络通信等我们无法用随机种子控制的环境因素。这对贝叶斯优化等搜索算法同样成立,因为它们会在采样新配置时参考先前结果。可以通过使用 PBT 和 Hyperband 的同步模式来缓解:这些模式下,调度器会等待所有 trial 完成一个 epoch 后再决定提升哪些 trial。
官方建议:在依赖复现结果进行大规模实验之前,先在较小的玩具问题上尝试复现。
如何避免性能瓶颈?
有时你会遇到类似这样的消息:
The `experiment_checkpoint` operation took 2.43 seconds to complete, which may be a performance bottleneck最常见的是experiment_checkpoint操作抛出此警告,但也可能是别的操作,比如process_trial_result。这些操作通常应在 500ms 内完成;当它持续更长时间时,可能意味着存在问题或低效之处。要消除这条消息,需要理解它从何而来。主要原因如下:
trial config 非常大
例如试图通过config参数传入数据集或其他大对象。这种情况下,数据集在实验检查点(experiment checkpointing)期间被反复序列化并写入磁盘,耗时很长。
解决方案:使用 tune.with_parameters 通过对象存储向函数 trainable 传递大对象。对于类 trainable,可以手动通过ray.put()和ray.get()完成。如果需要传递类定义,考虑传递一个指示符(如字符串)而让 trainable 自行选择类。一般来说,config 字典应该只包含数字、字符串等原始类型。
trial result 非常大
例如在类 trainable 的step()返回值或函数 trainable 的tune.report()中返回对象、数据或其他大对象。效果同上:结果被反复序列化并写入磁盘,耗时很长。
解决方案:改用检查点(checkpoint),把数据写入 trainable 的当前工作目录。根据使用的是类 API 还是函数 API,有多种实现方式。
在集群上训练大量 trial,或保存了巨大的检查点
解决方案:使用 cloud checkpointing 将日志和检查点保存到指定的storage_path。这是处理该问题的首选方式:所有节点都能访问云存储,同步工作会自动完成;同时结果也是安全的,即使你在可抢占实例上工作也不会丢失数据。
报告结果过于频繁
每个结果都会被搜索算法、trial 调度器和回调(包括 logger 与 trial syncer)处理。如果每个 trial 报告的结果数量很大(例如每秒多个结果),会非常耗时。
解决方案:减少报告频率。在类 trainable 中,step()也许应该处理更大块的数据;在函数 trainable 中,可以只在训练循环的每 n 次迭代报告一次。平衡好真正需要用于调度与搜索决策的结果数量。如果需要更细粒度的指标做日志或追踪,考虑使用独立的日志机制,而不是 Ray Tune 自带的结果进度日志。
如何配置搜索空间?
可以通过传给Tuner(param_space=...)的字典指定网格搜索或采样分布:
parameters = { "qux": tune.sample_from(lambda spec: 2 + 2), "bar": tune.grid_search([True, False]), "foo": tune.grid_search([1, 2, 3]), "baz": "asd", # a constant value } tuner = tune.Tuner(train_fn, param_space=parameters) tuner.fit()默认情况下,每个随机变量和网格搜索点各采样一次。要多次随机采样,可在实验配置中添加num_samples: N。如果提供了grid_search参数,网格会重复num_samples次。例如下面这个配置会产生总共 90 个 trial(3×3 的网格被num_samples=10重复 10 次):
# num_samples=10 repeats the 3x3 grid search 10 times, for a total of 90 trials tuner = tune.Tuner( train_fn, run_config=tune.RunConfig(name="my_trainable"), param_space={ "alpha": tune.uniform(100, 200), "beta": tune.sample_from(lambda config: config["alpha"] * np.random.normal()), "nn_layers": [ tune.grid_search([16, 64, 256]), tune.grid_search([16, 64, 256]), ], }, tune_config=tune.TuneConfig(num_samples=10), )注意,搜索空间在不同的搜索算法之间可能不互通。例如,对很多搜索算法而言,你无法使用grid_search或sample_from参数。详细说明参见 Search Space API 文档页。
如何在训练函数中访问相对文件路径?
假设你在~/code目录下用my_script.py启动一个 Tune 实验。默认情况下,Tune 会把每个 worker 的工作目录切换到其对应的 trial 目录(例如~/ray_results/exp_name/trial_0000x)。这保证了每个 worker 进程有独立的工作目录,避免保存 trial 专属输出时的冲突。
可以通过设置环境变量RAY_CHDIR_TO_TRIAL_DIR=0来配置:这明确告诉 Tune不把工作目录切换到 trial 目录,从而可以访问相对原始工作目录的路径。一个注意事项是:工作目录此时在 worker 之间是共享的,因此应该使用 tune.get_context().get_trial_dir() API 获取保存 trial 专属输出的路径:
def train_func(config): # Read from relative paths print(open("./read.txt").read()) # The working directory shouldn't have changed from the original # NOTE: The `TUNE_ORIG_WORKING_DIR` environment variable is deprecated. assert os.getcwd() == os.environ["TUNE_ORIG_WORKING_DIR"] # Write to the Tune trial directory, not the shared working dir tune_trial_dir = Path(ray.tune.get_context().get_trial_dir()) with open(tune_trial_dir / "write.txt", "w") as f: f.write("trial saved artifact") os.environ["RAY_CHDIR_TO_TRIAL_DIR"] = "0" tuner = tune.Tuner(train_func) tuner.fit()警告:
TUNE_ORIG_WORKING_DIR环境变量是访问相对原始工作目录路径的旧方案,已被弃用,应改用上述RAY_CHDIR_TO_TRIAL_DIR环境变量。
如何在同一个集群上同时运行多个 Ray Tune 任务(多租户)?
在同一个集群上同时运行多个 Ray Tune 任务并不被官方支持。我们不会测试这一工作流,建议每个调优任务使用独立的集群。原因如下:
- 多个 Ray Tune 任务同时运行时,它们会竞争资源。一个任务可能同时运行其全部 trial,而另一个任务要等很长时间才能获得运行第一个 trial 的资源。
- 如果在你的基础设施上很容易启动一个新的 Ray 集群,运行一个大集群通常并不比运行多个小集群更省钱。例如,运行一个 32 实例的集群与运行 4 个各 8 实例的集群成本几乎相同。
- 并发任务更难调试。如果任务 A 的某个 trial 写满了磁盘,同一节点上任务 B 的 trial 也会受影响。实践中,出问题时很难仅凭日志推断出这些情况。
此前,Ray Tune 的一些内部实现假设同一时刻只运行一个任务。一个症状是任务 A 的 trial 使用了任务 B 中指定的参数,导致意外结果。
如何基于已完成实验继续训练(迭代式实验)?
假设有一个已完成的 Tune 实验,配置如下:
def trainable(config): for epoch in range(1, config["num_epochs"]): # Do some training... with tempfile.TemporaryDirectory() as tempdir: torch.save( {"model_state_dict": {"x": 1}}, os.path.join(tempdir, "model.pt") ) tune.report( {"score": random.random()}, checkpoint=Checkpoint.from_directory(tempdir), ) tuner = tune.Tuner( trainable, param_space={"num_epochs": 10, "hyperparam": tune.grid_search([1, 2, 3])}, tune_config=tune.TuneConfig(metric="score", mode="max"), ) result_grid = tuner.fit() best_result = result_grid.get_best_result() best_checkpoint = best_result.checkpoint现在你想从上一个实验生成的检查点(例如最佳的那个)继续训练,并在新的搜索空间上再搜索 10 个 epoch。
容错恢复文档 说明,Tuner.restore() 的用途是恢复一个中断的未完成实验,且必须使用初始训练时提供的完全相同的配置。因此Tuner.restore并不适合我们想要的行为。这种"迭代式实验"应该通过新建 Tune 实验来完成,而不是反复恢复同一个实验并修改实验规格。
下面是一个基于旧实验创建新实验的示例:
def trainable(config): # Add logic to handle the initial checkpoint. checkpoint: Checkpoint = config["start_from_checkpoint"] with checkpoint.as_directory() as checkpoint_dir: model_state_dict = torch.load(os.path.join(checkpoint_dir, "model.pt")) # Initialize a model from the checkpoint... # model = ... # model.load_state_dict(model_state_dict) for epoch in range(1, config["num_epochs"]): # Do some more training... ... tune.report({"score": random.random()}) new_tuner = tune.Tuner( trainable, param_space={ "num_epochs": 10, "hyperparam": tune.grid_search([4, 5, 6]), "start_from_checkpoint": best_checkpoint, }, tune_config=tune.TuneConfig(metric="score", mode="max"), ) result_grid = new_tuner.fit()核心思路是:把上一个实验的最佳检查点作为best_checkpoint取出,作为新实验参数空间中的一个值传入(本例中为start_from_checkpoint),然后在新搜索空间上重新调优。
如何将 Tune 结果上传到云存储 / 在 Kubernetes、Docker 中使用 Tune?
云存储上传:参见 cloud checkpointing 文档。请确保 worker 节点对云存储有写入权限,否则会出现类似Error message (1): fatal error: Unable to locate credentials的错误。对于 AWS 环境,需要为 worker 节点配置 IamInstanceProfile。
Kubernetes 与 Docker:在这两种环境中使用 Tune,都应配置共享存储,参见 存储选项用户指南。
如何在本地开发和测试 Tune?
首先,按照 python-develop 指南 在不编译 Ray 的情况下搭建 Tune 开发环境。Ray 就绪后,运行以下命令安装 Tune 开发所需的全部包:
pip install -r ray/python/ray/tune/requirements-dev.txt然后运行全部 Tune 测试:
pytest ray/python/ray/tune/tests/如果要提交 Pull Request,建议先在本机运行单元测试以加速评审流程。虽然我们有钩子会对每个 PR 自动运行单元测试,但通常先在本地跑一遍更快,可以避免明显的错误。
如何开始为 Tune 贡献代码?
我们使用 GitHub 跟踪 issue、功能请求和 bug。可以从标记为 "good first issue" 和 "help wanted" 的 issue 入手,并寻找标题中带 "[tune]" 的 issue。
注意:如果提交与 Tune 相关的 issue 或 PR,请务必在标题中包含 "[tune]" 并为该 issue/PR 添加
tune标签。
总结
Ray Tune FAQ 覆盖了从概念(超参数 vs 模型参数)、策略(搜索算法 + 调度器组合)、搜索空间设计(嵌套/条件/网格)、资源管理(with_resources与 Placement Group)、性能优化(避免 checkpoint 瓶颈)到工程实践(可复现性、迭代式实验、本地开发测试)的完整链路。结合 FAQ 示例代码 与核心实现(trainable/util.py、search/sample.py、execution/placement_groups.py),你可以快速定位实际调参任务中的常见问题,并把本文中的方案直接应用到自己的 Ray Tune 工作流中。
- 人工智能
- 分布式训练
- 强化学习
- 任务调度
- 模型推理服务
【免费下载链接】ray
Ray is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.
相关推荐
Ray Tune HyperBand 示例实战:用 HyperBandScheduler 实现超参数搜索的早停调度
Ray Tune HyperBand 示例实战:用 HyperBandScheduler 实现超参数搜索的早停调度 本文围绕 Ray 仓库中的 HyperBan
人工智能分布式训练强化学习任务调度模型推理服务Ray Tune 实战:使用 HyperBandScheduler 对 Trainable 函数做超参搜索与早停
Ray Tune 实战:使用 HyperBandScheduler 对 Trainable 函数做超参搜索与早停 本文以 Ray Tune 官方示例 hyper
人工智能分布式训练强化学习任务调度模型推理服务Ray Train V2 与 Ray Tune 联合调优:分布式超参数搜索的完整实战指南
Ray Train V2 与 Ray Tune 联合调优:分布式超参数搜索的完整实战指南 导读 本文基于 Ray 官方文档 hyperparameter opt
人工智能分布式训练强化学习任务调度模型推理服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考