Hydra Nevergrad Sweeper 插件实战:用无导数优化算法自动搜索超参数
2026/9/16 19:45:36 网站建设 项目流程

Hydra Nevergrad Sweeper 插件实战:用无导数优化算法自动搜索超参数

【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra

本指南完整讲解 Hydra 生态中的hydra-nevergrad-sweeper插件:它让 Hydra 应用能够调用 Nevergrad 无导数优化平台中一系列先进的超参数搜索算法,在--multirun模式下对实验/应用参数进行智能寻优。读完本文,你将掌握该插件的安装激活方式、完整配置项含义、命令行与配置文件两种搜索空间定义语法,并能结合仓库中的示例应用跑通一次端到端优化、读懂optimization_results.yaml输出结果。

插件概述:从网格/随机搜索到"智能寻优"

Hydra 自身的 Basic Sweeper 提供的是网格与随机组合搜索;而 Nevergrad 则是一个无导数优化平台,内置了大量状态先进的超参数搜索算法(如进化策略、CMA-ES 类算法、差分进化等)。hydra-nevergrad-sweeper将两者衔接起来:

  • Hydra 负责配置组合、任务函数注入与并行调度;
  • Nevergrad 负责"下一个候选参数怎么生成",即根据历史评估结果自适应地选择更优的方向,从而用远少于网格搜索的预算逼近最优参数。

从源码看,插件的核心实现位于 nevergrad_sweeper.py,其NevergradSweeper类实现了 Hydra 的 Sweeper 接口,内部委托给NevergradSweeperImpl(见 _impl.py)完成优化循环:optimizer.ask()取候选参数 →launcher.launch()并行评估 →optimizer.tell()回传损失 → 循环直至预算耗尽 → 输出推荐结果。

安装与激活

安装插件(会同时拉取hydra-corenevergrad依赖,仓库 setup.py 中声明了hydra-core>=1.1.0.dev7nevergrad>=1.0.12,且要求 Python >= 3.10):

pip install hydra-nevergrad-sweeper --upgrade

激活方式有两种,任选其一:

方式一:命令行指定(适合一次性使用)

python your_app.py hydra/sweeper=nevergrad -m

方式二:在配置文件的defaults中声明(适合作为项目的默认寻优方案)

defaults: - hydra/sweeper: nevergrad

激活后,可用下面的命令查看该 sweeper 的默认配置(文档中原样给出的参数即为 config.py 中NevergradSweeperConf的默认值):

# @package hydra.sweeper _target_: hydra_plugins.hydra_nevergrad_sweeper.nevergrad_sweeper.NevergradSweeper optim: optimizer: OnePlusOne budget: 80 num_workers: 10 noisy: false maximize: false seed: null parametrization: {} version: 1

说明:仓库当前源码的默认配置与文档快照略有演进——config.py 中optimizer默认值已改为"NGOpt",并新增了max_failure_rate: 0.0参数,同时用params取代了旧字段parametrization(后者保留但标记为弃用,两者不可同时配置)。下表以当前仓库源码为准。

优化器配置项(hydra.sweeper.optim.*

配置项类型默认值含义
optimizerstr"NGOpt"(旧文档快照为"OnePlusOne"Nevergrad 优化器名称,可从ng.optimizers.registry中取值
budgetint80函数评估(job 运行)的总预算次数
num_workersint10并行 worker 数,控制每批同时评估多少个参数组合
noisyboolfalse目标函数是否含噪声(含噪时优化器会采用抗噪策略)
maximizeboolfalsetrue时执行最大化而非最小化
seedOptional[int]null随机种子,用于结果复现
max_failure_ratefloat0.0一批参数中允许的最大失败率;超过则抛错终止

config.py 的注释给出了常用优化器的选型建议:

  • OnePlusOne:极其简单且稳健,尤其适合低预算,但容易过早收敛;
  • CMA:非常优秀的算法,但通常需要较显著预算(如 > 120 次评估);
  • TwoPointsDE:在广泛场景下表现良好,适合较大预算(> 120 次评估);
  • NGOpt:旨在根据你的参数定义自动挑选最优优化器(会定期更新),适合不确定选型的场景。

搜索空间配置项(hydra.sweeper.params.*

配置项说明
params当前推荐的搜索空间配置方式,使用 Hydra 的 override 语法描述参数(如db: choice(mnist, cifar)
parametrization旧字段,已弃用(Hydra 1.4 起告警、1.5 移除),仅接受列表(Choice)或 Scalar 字典两种结构,不可与params同时使用

示例:用 Nevergrad 做一次端到端超参寻优

仓库在 plugins/hydra_nevergrad_sweeper/example 下提供了一个可直接运行的示例。目标函数由 my_app.py 定义,是一个最小值为 0 的"伪训练"函数(真实值为 0,最优解位于db=mnist, lr=0.12, dropout=0.33, batch_size=4):

@hydra.main(config_path=".", config_name="config") def dummy_training(cfg: DictConfig) -> float: do = cfg.dropout bs = cfg.batch_size out = float( abs(do - 0.33) + int(cfg.db == "mnist") + abs(cfg.lr - 0.12) + abs(bs - 4) ) log.info(f"dummy_training(dropout={do:.3f}, lr={cfg.lr:.3f}, db={cfg.db}, batch_size={bs}) = {out:.3f}") if cfg.error: raise RuntimeError("cfg.error is True") return out

注意三点:① 被@hydra.main()装饰的函数必须返回一个 float,作为优化目标值(越小越好,除非设置maximize: true);②cfg.error字段用于演示"job 失败"场景;③ 示例配置 config.yaml 通过- override hydra/sweeper: nevergrad预置了 sweeper,并给出了 4 个参数的搜索空间定义。

克隆仓库代码后,在plugins/hydra_nevergrad_sweeper目录下执行:

python example/my_app.py -m

启动后日志会依次展示初始化信息、并行批次与最终最优参数(文档给出的真实运行片段,预算 100、10 个并行 worker):

[2020-10-08 20:13:53,592][HYDRA] NevergradSweeper(optimizer=OnePlusOne, budget=100, num_workers=10) minimization [2020-10-08 20:13:53,593][HYDRA] with parametrization Dict(batch_size=Choice(choices=Tuple(4,8,16),weights=Array{(1,3)}),db=Choice(choices=Tuple(mnist,cifar),weights=Array{(1,2)}),dropout=Scalar{Cl(0,1,b)}[sigma=Log{exp=2.0}],lr=Log{exp=3.162277660168379,Cl(0.001,1,b)}):{'db': 'mnist', 'lr': 0.03162277660168379, 'dropout': 0.5, 'batch_size': 8} [2020-10-08 20:13:53,593][HYDRA] Sweep output dir: multirun/2020-10-08/20-13-53 [2020-10-08 20:13:55,023][HYDRA] Launching 10 jobs locally [2020-10-08 20:13:55,023][HYDRA] #0 : db=mnist lr=0.03162277660168379 dropout=0.5 batch_size=16 [2020-10-08 20:13:55,217][__main__][INFO] - dummy_training(dropout=0.500, lr=0.032, db=mnist, batch_size=16) = 13.258 [2020-10-08 20:13:55,218][HYDRA] #1 : db=cifar lr=0.018178519762066934 dropout=0.5061074452336254 batch_size=4 [2020-10-08 20:13:55,408][__main__][INFO] - dummy_training(dropout=0.506, lr=0.018, db=cifar, batch_size=4) = 0.278 [2020-10-08 20:13:55,409][HYDRA] #2 : db=cifar lr=0.10056825918734161 dropout=0.6399687427725211 batch_size=4 [2020-10-08 20:13:55,595][__main__][INFO] - dummy_training(dropout=0.640, lr=0.101, db=cifar, batch_size=4) = 0.329 [2020-10-08 20:13:55,596][HYDRA] #3 : db=mnist lr=0.06617542958182834 dropout=0.5059497416026679 batch_size=8 [2020-10-08 20:13:55,812][__main__][INFO] - dummy_training(dropout=0.506, lr=0.066, db=mnist, batch_size=8) = 5.230 [2020-10-08 20:13:55,813][HYDRA] #4 : db=mnist lr=0.16717013388679514 dropout=0.6519070394318255 batch_size=4 ... [2020-10-08 20:14:27,988][HYDRA] Best parameters: db=cifar lr=0.11961221693764439 dropout=0.37285878409770895 batch_size=4

优化后期收敛(日志展示最后 2 次评估,此时已围绕最优解微调):

[HYDRA] #8 : db=mnist batch_size=4 lr=0.094 dropout=0.381 [__main__][INFO] - my_app.py(dropout=0.381, lr=0.094, db=mnist, batch_size=4) = 1.077 [HYDRA] #9 : db=mnist batch_size=4 lr=0.094 dropout=0.381 [__main__][INFO] - my_app.py(dropout=0.381, lr=0.094, db=mnist, batch_size=4) = 1.077 [HYDRA] Best parameters: db=mnist batch_size=4 lr=0.094 dropout=0.381

用命令行覆盖搜索空间

不修改任何配置文件,直接通过 Hydra 的 override 语法重定义搜索空间(示例中dbbatch_size用列表/范围表达离散选项,lr用对数区间、dropout用线性区间):

python example/my_app.py --multirun db=mnist,cifar batch_size=4,8,16 \ 'lr=tag(log, interval(0.001, 1))' 'dropout=interval(0,1)'

理解optimization_results.yaml

优化结束后,sweep 目录下会生成optimization_results.yaml,记录优化器给出的最终推荐(由 _impl.py 写入):

best_evaluated_result: 0.381 best_evaluated_params: batch_size: 4 db: mnist dropout: 0.381 lr: 0.094 name: nevergrad

字段含义:best_evaluated_result为历史评估中最优的目标函数值(注意maximize: true时此处为去符号后的原始值,见源码中direction * best[0]的处理);best_evaluated_params为该最优结果对应的参数组合;name固定为"nevergrad"便于下游脚本识别。测试 test_nevergrad_sweeper_plugin.py 会校验该文件的name == "nevergrad"、包含 3 个键,且best_evaluated_params.batch_size收敛到 4。

定义搜索空间:两种方式详解

插件支持两类 Nevergrad 参数:ng.p.Choice)。

方式一:命令行 override

这依赖 Hydra 自带的富语法 override 解析器,建议先阅读 Override 语法基础 与 Override 语法扩展。

Choices(离散选项)

'key=1,5' 'key=shuffle(range(1, 8))' 'key=range(1,5)'

ordered标签可将选择转换为ng.p.TransitionChoice(有序传递选择,适合相邻取值有相似性的场景,如学习率档位):

`key=tag(ordered, choice(1,2,3))`

Scalar(连续标量)

`key=interval(1,12)` # Interval 默认是 float `key=int(interval(1,8))` # 将 Scalar 边界转为 int(整数离散化) `key=tag(log, interval(1,12))` # 带 log 标签则调用 ng.p.Log(对数分布)

这些语法与解析器的对应关系,可以在插件的参数映射函数create_nevergrad_parameter_from_override(_impl.py)与单元测试用例(test_nevergrad_sweeper_plugin.py)中找到一一验证:如key=choice(1,2)ng.p.Choice([1,2])key=tag(ordered, choice(1,2,3))ng.p.TransitionChoice([1,2,3])key=range(1,5)ng.p.Choice([1,2,3,4])key=int(interval(1,12))→ 整数化的ng.p.Scalarkey=tag(log, interval(1,12))ng.p.Log

方式二:配置文件

Choices(离散选项):直接写一个 YAML 列表即可:

db: - mnist - cifar

Scalar(连续标量):用一个字典描述,可用的字段如下(对应 config.py 中的ScalarConfigSpec):

字段说明
init可选,初始值
lower可选,下界
upper可选,上界
logtrue时采用对数分布取值
step可选,搜索步长:线性模式下为加性步长,对数模式下为乘性步长
integertrue时取值限定为整数(优先使用 float 而非整数)

关键行为:只提供lowerupper时,init自动取区间中点,step自动取区间长度的六分之一。另外注意:无界标量(缺省lower和/或upper)只能通过配置文件定义,命令行 override 无法表达无界区间。

示例配置中三种标量的实际写法(config.yaml):

# 一个对数分布的连续正标量,平均按 2 倍因子演变 lr: {init: 0.02, step: 2.0, log: true} # 一个 0 到 1 之间线性分布的标量 dropout: interval(0, 1) # 一个 4 到 16 的整数标量,init 与 step 缺省时取中点与区间六分之一 batch_size: int(interval(4, 16))

配置式与命令行式的混合能力也在测试中被覆盖:params中值为字符串(如"choice(1,2,3)")的项会被当作 override 追加解析,值为字典/列表的项则直接转成 Nevergrad 参数(见 _impl.py 与 test_nevergrad_sweeper_plugin.py)。

优化循环的内部机制与失败处理

从源码看,一次完整的寻优由NevergradSweeperImpl.sweep()(_impl.py)驱动,核心流程如下:

  1. 合并params/parametrization配置与命令行 overrides,逐一映射为 Nevergrad 参数,组装成ng.p.Dict参数化对象;
  2. 依据noisyseed设置parametrization.function.deterministic与随机种子,实现可复现实验;
  3. ng.optimizers.registry[optimizer]实例化优化器,传入budgetnum_workers
  4. 循环:每轮optimizer.ask()生成min(num_workers, 剩余预算)个候选,转成 override 后交给 Hydra Launcher 并行启动 job,收集返回的目标值(失败 job 计为inf),调用optimizer.tell()反馈损失并维护历史最优;
  5. 若一批任务中失败率超过max_failure_rate,直接抛出异常终止(缺省 0.0 即不允许失败);
  6. 预算耗尽后,optimizer.provide_recommendation()给出最终推荐,写入optimization_results.yaml并打印Best parameters:

失败处理的行为差异有测试专门验证(test_nevergrad_sweeper_plugin.py):当max_failure_rate=1.0时,失败 job 只记录日志("Returning infinity for failed experiment")不中断优化;低于 1.0 时则重新抛出真实异常栈。这为真实训练中偶发的 job 崩溃提供了容错选项。

小结与最佳实践

  • 无导数优化 ≠ 网格搜索:在评估代价高昂的场景(真实训练、仿真)下,用budget控制评估次数、num_workers控制并行度,通常能以远小于全网格搜索的开销逼近最优参数。
  • 预算紧张优先选OnePlusOne,预算充足可尝试CMA/TwoPointsDE/NGOptseed固定后结果可复现。
  • 参数类型上优先使用 float 而非 integer(整数离散化会限制优化器效率);无界标量只能写在配置文件里。
  • 若任务可能失败,适当调高max_failure_rate让优化继续而非整体中断。
  • 更多验证与进阶用法可直接阅读仓库的 测试用例 与插件 源码,以及示例应用 config.yaml。

【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询