Hydra 集成 Nevergrad 实现超参数自动搜索:Nevergrad Sweeper 插件实战指南
2026/9/16 16:30:26 网站建设 项目流程

Hydra 集成 Nevergrad 实现超参数自动搜索:Nevergrad Sweeper 插件实战指南

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

本文基于 Hydra 仓库中的 Nevergrad Sweeper 插件,系统讲解如何借助 Nevergrad 无导数优化平台,在 Hydra 应用中自动搜索与优化实验参数。读完本文,你将掌握该插件的安装与启用方式、optimparams两大配置块的完整字段语义、通过命令行覆盖语法与配置文件两种方式定义搜索空间,以及如何解读优化过程日志与最终的optimization_results.yaml结果文件。

插件是什么

Nevergrad 是一个无导数(derivative-free)优化平台,内置了一批用于超参数搜索的高质量算法。Hydra 的 Nevergrad Sweeper 插件(hydra-nevergrad-sweeper)为 Hydra 应用提供了一套机制,让应用可以使用 Nevergrad 的算法来优化实验/应用参数,实现自动化的超参数搜索(multirun sweep)。

在仓库中,该插件的完整实现位于 plugins/hydra_nevergrad_sweeper,核心代码包括:

  • config.py:结构化配置(dataclass)定义,并注册到 Hydra 的hydra/sweeper配置组;
  • nevergrad_sweeper.py:对外暴露的NevergradSweeper(继承hydra.plugins.sweeper.Sweeper),是插件与 Hydra 的接合点;
  • _impl.py:真正的优化循环实现(NevergradSweeperImpl),包括搜索空间解析、ask/tell 迭代、失败处理与结果序列化。

安装

插件作为独立 Python 包发布,通过 pip 安装并升级即可:

pip install hydra-nevergrad-sweeper --upgrade

从 setup.py 可以看到,该插件的运行依赖为hydra-core>=1.1.0.dev7nevergrad>=1.0.12,安装时会被自动拉取。

启用插件

安装完成后,有两种方式启用 Nevergrad Sweeper。

方式一:命令行指定。在运行 Hydra 应用时追加hydra/sweeper=nevergrad

python your_app.py hydra/sweeper=nevergrad

方式二:在配置中覆盖hydra/sweeper。在应用的defaults列表中加入override条目:

defaults: - override hydra/sweeper: nevergrad

插件示例应用 example/config.yaml 正是采用方式二启用的。

启用后,可随时查看插件的默认配置,检查当前生效的优化器与搜索空间:

# @package hydra.sweeper _target_: hydra_plugins.hydra_nevergrad_sweeper.nevergrad_sweeper.NevergradSweeper optim: optimizer: NGOpt budget: 80 num_workers: 10 noisy: false maximize: false seed: null max_failure_rate: 0.0 parametrization: db: - mnist - cifar lr: init: 0.02 step: 2.0 log: true dropout: lower: 0.0 upper: 1.0 batch_size: lower: 4 upper: 16 integer: true

需要说明的是,上面输出中的parametrization字段是 Hydra 1.2 时代的命名。查看当前仓库源码 config.py,NevergradSweeperConf中该字段已更名为params,原来的parametrization被标记为 deprecated(自 Hydra 1.4 起,预计 1.5 移除,见 news/1890.config 与 _impl.py 中的deprecation_warning)。新应用中应统一使用hydra.sweeper.params定义搜索空间。

优化器与搜索空间配置:optim字段详解

插件在hydra.sweeper.optim下集中管理优化器行为,所有字段的默认值与语义均可在 config.py 的OptimConf中找到:

字段默认值含义
optimizerNGOpt使用的 Nevergrad 优化器名称。示例注释给出几条选型经验:OnePlusOne极其简单稳健,尤其适合低预算,但容易过早收敛;CMA质量很高,但可能需要显著预算(>120);TwoPointsDE在广泛场景下表现良好,适合较大预算(>120);NGOpt会根据你的参数定义自动识别最优优化器(会定期更新)
budget80总共要执行的函数评估(job)次数
num_workers10并行执行评估的 worker 数量
noisyfalse评估是否带噪声(设置为true时,优化器按噪声场景处理)
maximizefalse是否执行最大化而非最小化
seednull优化随机种子,用于复现结果
max_failure_rate0.0单批参数中允许的最大失败率,用于容错(Hydra 1.2.0 起引入,见 NEWS.md)

配置示例(沿用插件 example/config.yaml):

hydra: sweeper: optim: # 低预算下表现好的优化器,但可能过早收敛 optimizer: OnePlusOne # 总共执行的函数评估次数 budget: 100 # 并行 worker 数量 num_workers: 10 # maximize: true # 取消注释可改为最大化

从实现看,NevergradSweeper的构造函数直接接收optim: OptimConf参数(nevergrad_sweeper.py),optimizer最终通过ng.optimizers.registryopt实例化(_impl.py),seednoisy则分别作用于parametrization.random_stateparametrization.function.deterministic

完整示例:用 Nevergrad 搜索最小化函数的最优参数

仓库提供了一个开箱即用的示例应用 example/my_app.py。它用@hydra.main()装饰了一个dummy_training(cfg) -> float函数:函数返回值是我们想要最小化的目标,理论最小值是0,在如下参数组合处达到:

db: mnist lr: 0.12 dropout: 0.33 batch_size=4

该目标函数混合了连续参数(lrdropout)、离散整数参数(batch_size)与类别参数(db),很适合演示搜索空间的各种定义方式。注意my_app.py中还支持cfg.error开关,置true时主动抛出RuntimeError,用于验证插件的失败容错逻辑。

运行超参数搜索

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

python example/my_app.py -m

其中-m--multirun,触发 Sweeper 接管执行。示例配置 example/config.yaml 使用OnePlusOne优化器、budget=100num_workers=10,并通过params定义搜索空间:

hydra: sweeper: params: # 二选一的离散类别 db: choice(mnist, cifar) # 对数分布的正标量,平均按 2 的因子演进 lr: {init: 0.02, step: 2.0, log: true} # 0 到 1 之间的线性标量 dropout: interval(0, 1) # 4 到 16 的整数标量(也可提供 init 和 step, # 默认 init 取区间中点、step 取区间六分之一) batch_size: int(interval(4, 16))

在命令行覆盖搜索空间

除了使用配置文件,还可以用 Hydra 的覆盖语法在命令行直接定义/覆盖搜索空间:

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)'

运行日志解读

初始化 sweep 与前 5 次评估(共 100 次)的日志大致如下:

[2020-10-08 20:13:53,592][HYDRA] NevergradSweeper(optimizer=NGOpt, 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

可以看到每轮先打印Launching 10 jobs locally,随后按#0#1……依次输出每批 10 个并行 job 的参数组合与目标值,最后由Best parameters给出优化器推荐的全局最优参数。

sweep 末尾两轮评估大致如下:

[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

结果文件optimization_results.yaml

sweep 结束后,会在 sweep 目录下生成optimization_results.yaml,记录优化器推荐的参数:

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

其中best_evaluated_params是实际评估过的参数中目标值最优的一组,best_evaluated_result为对应目标值。该文件由 _impl.py 在优化循环结束后通过OmegaConf.save写入{hydra.sweep.dir}/optimization_results.yaml。测试 test_nevergrad_sweeper_plugin.py 会校验该文件的存在与字段完整性(name == "nevergrad"、恰好 3 个键)。

定义搜索空间参数

插件支持两类 Nevergrad 参数:Choices(离散选择)与Scalars(标量)。二者既可以在命令行通过 Hydra 覆盖语法定义,也可以在配置文件中定义。

通过命令行覆盖定义

Hydra 自带覆盖解析器,语法相当丰富,建议先阅读 Override 语法基础 与 Override 语法扩展 再继续。

Choices(离散选择)

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

如果给覆盖打上ordered标签,则生成 Nevergrad 的TransitionChoice(带顺序/转移结构的选择):

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

对应实现中(_impl.py):choices 类覆盖被转换为ng.p.Choice,带ordered标签时转换为ng.p.TransitionChoicerangeshuffle(range(...))展开后同样是ng.p.Choice

Scalar(标量)

`key=interval(1,12)` # 默认是浮点区间 `key=int(interval(1,8))` # 将区间边界转为整数标量 `key=tag(log, interval(1,12))` # 打上 log 标签后调用 ng.p.Log

实现上(_impl.py):interval覆盖生成ng.p.Scalar(lower=start, upper=end),带log标签则生成ng.p.Log(...);当区间起点是整数时,自动调用set_integer_casting()转为整数标量。此外,命令行覆盖也可以是字典形式,例如key={init: 0.02, step: 2.0, log: true},此时会走与配置文件相同的标量解析路径。

通过配置文件定义

Choices:配置文件中用列表定义:

db: - mnist - cifar

Scalars:配置文件中用映射定义,支持的字段如下:

字段是否必填含义
init可选初始值
lower可选下界
upper可选上界
log可选设为true表示按对数(log)分布取值
step可选搜索更优参数时的步长:线性模式下是加性步长,对数模式下是乘性步长
integer可选设为true表示整数取值(尽可能优先用浮点数而非整数)

对应的结构化定义见 config.py 的ScalarConfigSpec

默认值规则:如果只提供lowerupper边界,初始值init会自动取区间中点,步长step取区间长度的六分之一;若无界,step默认退化为1

注意:无界标量(缺少upper和/或lower的标量)只能通过配置文件定义,无法用命令行interval覆盖表达——这一点从_impl.py的解析分支可以确认。

底层优化循环与容错机制

理解插件的底层行为有助于正确配置。从 _impl.py 的sweep()可以看出完整流程:

  1. 合并搜索空间:先从配置的params解析出参数与 override 列表,再追加命令行传入的覆盖参数,统一经OverridesParser解析后构造ng.p.Dict参数化对象;
  2. 准备优化器:根据maximize决定方向(direction = -1为最大化,1为最小化),按noisy/seed配置参数化对象,然后实例化注册表中的优化器;
  3. ask/tell 迭代:在budget预算内,每轮ask()取出min(num_workers, remaining_budget)个候选参数,转换为 override 后交给 Launcher 并行启动;所有 job 返回后统一tell()反馈目标值,同时跟踪当前最优;
  4. 失败处理:失败的 job(非COMPLETED状态)回报math.inf并计数,若单批失败比例超过max_failure_rate,则抛出首个失败 job 的原始异常并终止;否则按max_failure_rate容错继续优化;
  5. 输出结果provide_recommendation()得到优化器推荐参数,连同实际评估最优值一起序列化为optimization_results.yaml

测试 test_nevergrad_sweeper_plugin.py 对上述机制做了充分验证:例如test_create_nevergrad_parameter_from_override逐一断言命令行语法到 Nevergrad 参数类型的映射,test_nevergrad_examplebudget=32跑完整例并断言optimization_results.yaml与全部 job 目录(0budget-1)存在,test_failure_rate验证error=true场景下失败率的两种行为(max_failure_rate<1.0时抛异常、=1.0时容错继续)。

常见配置演进与迁移提示

  • params取代parametrization:如前面所述,hydra.sweeper.parametrization已废弃,请改用hydra.sweeper.params。二者不可同时配置——源码中若同时设置会直接抛出ValueError(_impl.py,测试test_params_and_parametrization_are_mutually_exclusive覆盖)。旧式parametrization下 Choices 用列表、Scalars 用{lower, upper, init, step, log, integer}字典,新式params则同时支持这些字典语法与更简洁的字符串语法(如choice(...)interval(...)int(interval(...))tag(log, ...))。
  • 关于配置插件的通用方法:Hydra 插件的配置方式有多种标准模式(命令行覆盖、配置文件覆盖等),详见插件配置指南。
  • 复现与稳定性:正式实验建议显式设置hydra.sweeper.optim.seed,并合理设置num_workers(不超过预算budget),避免测试与线上结果抖动。

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

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

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

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

立即咨询