1315 words
7 minutes
My first good commit:让 WandB Sweeper 支持 Hydra 参数传递

背景#

ICES 实习过程中,Wandb 是我常用的实验跟踪和可视化工具,而 Hydra 则是我进行配置管理的利器。我经常使用 WandB 的 Sweep 功能扫描超参数。然而,WandB SweeperHydra的支持比较差,无法原生支持向Hydra的参数传递。

NOTE

OmegaConf 是一个灵活的配置库,支持YAML和字典的混合语法。而Hydra 是一个用于多配置应用程序管理的框架。

当时,我希望通过 Wandb Sweeps 来动态调整 Hydra 管理的配置参数。

Hydra 本身支持通过命令行覆盖参数(例如 python train.py ++model.lr=0.01)或者追加新参数(例如 python train.py +model.new_param=true)。但我发现 WandB Sweeper 并没有提供一个直接、优雅的方式来生成这种 +++ 前缀的参数指令,以实现对 Hydra 配置的管理。

在查阅了 Hydra 的文档后,我最初的解决方案是在 sweep.yaml 中手动拼接参数,利用 ${args_no_hyphens} 并为需要覆盖的参数手动添加 ++ 前缀,如下所示:

# sweep.yaml (早期 workaround)
command:
  - ${env}
  - python
  - ${program}
  - ${args_no_hyphens}
  # 手动指定需要覆盖的参数,并加上 ++
  - ++experiment=contras-cat    # experiment 是 Hydra 配置中的一个组
  - ++trainer=gpu               # trainer 也是
method: bayes
metric:
  goal: maximize
  name: test/F1@5
parameters:
  # Sweep 需要优化的参数
  ++model.contrastive_weight:
    distribution: uniform
    max: 20
    min: 0.5
program: train.py

这种方法虽然可行,但显得有些“hacky”,并且容易出错,特别是当需要调整的参数变多时。我意识到这是一个普遍的需求,尤其是对于同时使用 WandB 和 Hydra 的用户。在 Issue #1856 中可以看出,社区中确有用户需要更原生的方式来处理这种情况。

我的解决方案#

引入 args_append_hydra 和 args_override_hydra

基于这个痛点和社区的需求,我想着这是一个提交自己 first good commit 的好机会。我的目标是提供一种更简洁、更符合直觉的方式,让 WandB Sweeper 能够直接生成符合 Hydra 传参语法的参数指令。

经过研究,我向 Wandb 提交了 Pull Request #9657,引入了两个新的魔法变量:

  1. ${args_append_hydra}:这个变量会将 sweep.yamlparameters 部分定义的所有参数,转换为 Hydra 追加参数的格式(即 +key=value)。这对于在 Sweep 时探索全新的参数组合非常有用。
  2. ${args_override_hydra}:这个变量会将 parameters 部分定义的参数转换为 Hydra 覆盖参数的格式(即 ++key=value)。这适用于你想要调整或优化配置文件中已存在的参数值。

这两个变量的工作方式与 Wandb 已有的 ${args}${args_json} 等类似,都是在执行命令时,将 parameters 中的参数动态地格式化并插入到命令行中。

如何使用?#

假设你的 Hydra 基础配置文件 (config.yaml) 如下:

# config.yaml
a: 1
b: 2
c: 3

model:
  lr: 0.01
  optimizer: adam

新参数的追加#

如果你想添加一个新的参数 d,可以使用 ${args_append_hydra}

# sweep_append.yaml
command:
  - ${env}
  - python
  - ${program}
  - ${args_append_hydra} # 使用追加模式
method: grid
metric:
  goal: maximize
  name: validation_accuracy
parameters:
  # Sweep 会追加并优化的参数
  model.lr:
    distribution: uniform
    max: 0.005
    min: 0.001
  d:
    distribution: uniform
    max: 20
    min: 0.5
program: train.py

Wandb Agent 在运行时,会生成类似以下的命令:

python train.py +model.lr=0.001 +d=10.0
python train.py +model.lr=0.0032 +d=5.6
python train.py +model.lr=0.0042 +d=7.2
python train.py +model.lr=0.0039 +d=7.8

最终 Hydra 加载的配置会是原始配置与追加参数的合并结果。

已有参数的覆盖#

如果你只想在 Sweep 时调整 config.yaml已经存在的参数 cmodel.lr,可以使用 ${args_override_hydra}

# sweep_override.yaml
command:
  - ${env}
  - python
  - ${program}
  - ${args_override_hydra} # 使用覆盖模式
method: random
metric:
  goal: minimize
  name: training_loss
parameters:
  # 覆盖 config.yaml 中的 c
  c:
    distribution: int_uniform
    min: 5
    max: 15
  # 覆盖 config.yaml 中的 model.lr
  model.lr:
    distribution: uniform
    min: 0.0001
    max: 0.01
program: train.py

Wandb Agent 会生成类似以下的命令:

python train.py ++c=8 ++model.lr=0.0054
python train.py ++c=12 ++model.lr=0.0012
# ... 等等

这里的参数有 ++ 前缀,符合 Hydra 覆盖现有参数的语法。

NOTE

后来发现其实这玩意也可以用来追加不存在的参数…

行为对照#

为了更清晰地理解这两个新变量与原有变量的区别,可以参考下表:

指令目标语法主要作用典型用例
${args}--key=value传递标准命令行参数适用于 argparse 等标准库
${args_no_hyphens}key=value传递无前缀参数某些特定脚本或 Hydra 覆盖模式的基础
${args_json}'{"key":...}'传递 JSON 字符串需要复杂结构化输入的脚本
${args_override_hydra}++key=value覆盖 Hydra 参数优化 Hydra 配置中已知参数的范围
${args_append_hydra}+key=value追加 Hydra 参数在 Sweep 中探索的 Hydra 参数组合

贡献历程与收获#

这是我第一次向像 Wandb 这样的大型开源项目提交实质性的代码(之前只提过修改错别字的 PR 😄)。从发现问题、思考解决方案、编写代码和测试用例,到最终提交 PR (#9657) 并与维护者交流,整个过程让我受益匪浅。

虽然这个功能的实现本身并不复杂,主要是理解 Wandb Agent 如何处理参数以及 Hydra 的命令行接口,但能够将自己遇到的痛点转化为对社区有用的功能,这种感觉非常棒。

最后,特别感谢 Wandb 团队的帮助以及合并!

My first good commit:让 WandB Sweeper 支持 Hydra 参数传递
https://catwinee.github.io/posts/wandb/
Author
Catwine
Published at
2025-04-02