配置系统:Hydra YAML
8.1 为什么 verl 用 Hydra
verl 一份训练 config 有几百个字段,分散在 actor / critic / rollout / data / algorithm 等 10+ 个子组件。Hydra 解决"把若干 YAML 文件按层级组合"的问题:
- 每个组件一份独立 YAML(actor 一份、critic 一份、rollout 一份、...);
- 顶层 YAML 用
defaults:节把它们组装起来; - 命令行 override 任何字段:
actor_rollout_ref.actor.optim.lr=1e-6。
8.2 配置目录长什么样
verl/trainer/config/
├── ppo_trainer.yaml ← 顶层,组合所有子组件
├── _generated_ppo_trainer.yaml ← Hydra 渲染后的扁平产物
├── _generated_ppo_megatron_trainer.yaml ← Megatron 后端的渲染产物
├── _generated_ppo_torchtitan_trainer.yaml
├── _generated_ppo_veomni_trainer.yaml
├── actor/ ← actor 子组件,按后端分文件
│ ├── dp_actor.yaml ← FSDP actor
│ ├── megatron_actor.yaml ← Megatron actor
│ └── ...
├── critic/ ← critic 子组件
├── data/ ← 数据 pipeline 配置
│ └── legacy_data.yaml
├── algorithm/ ← 算法字段(GAE / GRPO / KL)
├── engine/ ← 训练后端通用字段
├── distillation/ ← 蒸馏配置
├── evaluation.yaml ← 评测配置
└── legacy_reward_impl.yaml ← reward 配置
8.3 顶层 ppo_trainer.yaml 的 defaults 段
从源码 verl/trainer/config/ppo_trainer.yaml 摘出来:
defaults:
- model_engine: dp # 默认走 FSDP 风格
- actor@actor_rollout_ref.actor: ${model_engine}_actor # = dp_actor / megatron_actor
- data@data: legacy_data
- ref@actor_rollout_ref.ref: ${model_engine}_ref
- rollout@actor_rollout_ref.rollout: rollout
- model@actor_rollout_ref.model: hf_model
- critic@critic: ${model_engine}_critic
- model@critic.model: hf_model
- legacy_reward_impl
- reward@reward: reward
- algorithm@algorithm.rollout_correction: rollout_correction
- distillation@distillation: distillation
- _self_
这段读法:
| 语法 | 含义 |
|---|---|
- model_engine: dp | 定义变量 model_engine 默认值 = "dp" |
- actor@actor_rollout_ref.actor: ${model_engine}_actor | 把 actor/${model_engine}_actor.yaml 装载到 actor_rollout_ref.actor 这一层级 |
${model_engine} | 变量插值;命令行改 model_engine 就会切到对应文件 |
- _self_ | 当前 YAML 的字段覆盖前面所有 default |
8.4 命令行 override 全套语法
| 语法 | 含义 |
|---|---|
data.train_files=/path/to/train.parquet | 设置字段 |
actor_rollout_ref.actor.optim.lr=1e-6 | 深层字段 |
+data.new_field=42 | 新增不存在的字段 |
~data.train_files | 删除字段 |
trainer.logger=[wandb,tensorboard] | 列表值 |
algorithm.adv_estimator=grpo | 切换算法(见第 9 章) |
model_engine=megatron | 整组切换到 Megatron 后端 |
8.5 几个最常被改的字段速查
数据 & batch
| 字段 | 含义 |
|---|---|
data.train_files | 训练数据 parquet 路径 |
data.val_files | 验证集 |
data.train_batch_size | 每次 rollout 的 prompt 数 |
data.max_prompt_length | prompt 截断长度 |
data.max_response_length | response 生成长度上限 |
data.filter_overlong_prompts | 过长 prompt 是否丢 |
data.truncation | error / left / right |
actor
| 字段 | 含义 |
|---|---|
actor_rollout_ref.actor.optim.lr | 学习率,PPO/GRPO 通常 1e-6 ~ 5e-7 |
actor_rollout_ref.actor.ppo_mini_batch_size | 训练 actor 的 mini batch |
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu | 单卡 micro batch |
actor_rollout_ref.actor.ppo_epochs | 每批 PPO 迭代轮数 |
actor_rollout_ref.actor.use_kl_loss | 是否在 loss 里加 KL 项 |
actor_rollout_ref.actor.kl_loss_coef | KL 系数 |
actor_rollout_ref.actor.entropy_coef | 熵奖励系数 |
actor_rollout_ref.actor.clip_ratio | PPO clip 阈值,默认 0.2 |
算法
| 字段 | 含义 |
|---|---|
algorithm.adv_estimator | gae / grpo / reinforce_plus_plus / dapo |
algorithm.gamma | 折扣因子 |
algorithm.lam | GAE λ |
algorithm.use_kl_in_reward | 是否把 KL 加到 reward(vs 加到 loss) |
algorithm.kl_penalty | kl / abs / mse / low_var_kl / full |
algorithm.norm_adv_by_std_in_grpo | GRPO 是否用 std 归一 |
训练器
| 字段 | 含义 |
|---|---|
trainer.project_name | WandB / TB 项目名 |
trainer.experiment_name | 实验名 |
trainer.total_epochs | 总训练轮数 |
trainer.save_freq | 多少 step 保存一次 |
trainer.test_freq | 多少 step 跑一次验证 |
trainer.logger | [wandb] / [tensorboard] / [console] |
trainer.n_gpus_per_node | 每节点 GPU 数 |
trainer.nnodes | 节点数 |
trainer.default_local_dir | checkpoint 保存目录 |
8.6 字段查不到怎么办
- 从
_generated_ppo_trainer.yaml(Hydra 渲染产物)grep:所有"扁平"字段名都在这里; - 找不到 → 在
verl/trainer/config/*/*.yaml里 grep; - 还是找不到 → 看
verl/trainer/config/config.py(dataclass 定义); - 都没有 → 拼错了,Hydra 默认会 silently 忽略未知字段,要打开
strict模式:HYDRA_FULL_ERROR=1 python -m verl.trainer.main_ppo ...
8.7 写自己的 YAML 而不是命令行长串
命令行越来越长时,把 override 抽成一份 my_config.yaml:
# my_config.yaml
defaults:
- ppo_trainer # 继承 verl 默认
- _self_
data:
train_files: ${oc.env:HOME}/data/gsm8k/train.parquet
val_files: ${oc.env:HOME}/data/gsm8k/test.parquet
max_response_length: 4096
actor_rollout_ref:
model:
path: Qwen/Qwen3-4B
actor:
optim:
lr: 1.0e-6
ppo_mini_batch_size: 256
rollout:
n: 8
temperature: 1.0
algorithm:
adv_estimator: grpo
trainer:
project_name: my_grpo
experiment_name: qwen3_4b_gsm8k
total_epochs: 10
启动:
python -m verl.trainer.main_ppo \
--config-path . --config-name my_config
8.8 多机配置的 Hydra 风格
trainer.nnodes=2 \
trainer.n_gpus_per_node=8 \
+ray_init.address=10.0.0.1:6379 # ★ 加号表示新增字段
8.9 Hydra 调试技巧
| 命令 | 用途 |
|---|---|
HYDRA_FULL_ERROR=1 python -m verl.trainer.main_ppo ... | 完整堆栈 |
python -m verl.trainer.main_ppo --help | 看顶层选项 |
python -m verl.trainer.main_ppo --cfg job | 打印最终生效的扁平 config(不会真跑训练) |
python -m verl.trainer.main_ppo --info | 看 Hydra 解析结果 |
--cfg job 是调试 Hydra 最有用的开关——所有 override 后的字段一目了然。
8.10 ppo_trainer.yaml 的 defaults 组合顺序
verl 的顶层 config 文件是 verl/trainer/config/ppo_trainer.yaml,开头那段 defaults:(line 8–47)就是 Hydra 的合成总账。读源码定位字段时这块必须先看:
# ppo_trainer.yaml 节选
defaults:
- model_engine: dp # actor / critic / ref 默认走 dp(→ FSDP)
- actor: dp_actor # → trainer/config/actor/dp_actor.yaml
- critic: dp_critic # → critic/dp_critic.yaml
- ref: dp_ref # → ref/dp_ref.yaml
- rollout: vllm # → rollout/vllm.yaml
- reward_model: ~ # 默认无神经 reward
- distillation: ~ # 默认无在线蒸馏
- reward_correction: ~ # 默认无 rollout correction
- data: default
- trainer: default
- algorithm: default
- hydra: default
- _self_ # ★ 当前文件覆盖所有 defaults
两个值得知道的细节:
_self_在最后 = 本文件里的字段优先级最高,default 段先 merge 完再被本文件覆盖;~= "不加载这一组"——所以默认情况下没有 reward_model / distillation;要启用就在命令行reward_model=naive这样切到一个具体 yaml。
8.11 algorithm 段全字段
RL 算法的字段几乎全在 algorithm 这一组里。verl 把它落到 verl/trainer/config/algorithm.py 的 AlgoConfig dataclass,下表是最常碰的:
| 字段 | 含义 / 取值 |
|---|---|
adv_estimator | "gae" / "grpo" / "rloo" / "reinforce_plus_plus" / "remax" / "opo" / "gdpo" / "grpo_passk" / "gpg" / "rloo_vectorized" / "grpo_vectorized" / "optimal_token_baseline" / "tir_optimal_token_baseline"(详见 ch09) |
gamma | 折扣(GAE / R++) |
lam | GAE λ |
use_kl_in_reward | KL 加到 reward 上做 token-level shaping(默认 False) |
kl_penalty | "kl" / "abs" / "mse" / "low_var_kl" / "full" |
kl_ctrl.type | "fixed" / "adaptive" |
kl_ctrl.kl_coef | 固定时的 β |
kl_ctrl.target_kl | adaptive 时的目标 KL |
norm_adv_by_std_in_grpo | True = GRPO;False = Dr.GRPO(不除 std) |
use_pf_ppo | ICML25 Priority Filtering PPO |
filter_groups | DAPO 风格 group-level 过滤(去掉无信号 group) |
rollout_correction | off-policy IS / RS 矫正子段 |
gdpo_reward_keys | GDPO 时各维度 reward 的 key 列表 |
gdpo_reward_weights | 多维 reward 的聚合权重 |
transfer_queue | 非 None 即启用 TransferQueue 异步数据通道 |
8.12 PolicyLossConfig:actor 段最难懂的字段
PPO loss 不是只有一种——actor.policy_loss 子段(来自 workers/config/actor.py:78 的 PolicyLossConfig)选择具体公式:
policy_loss.mode | 背后函数 | 典型场景 |
|---|---|---|
vanilla(默认) | compute_policy_loss_vanilla() | 经典 PPO clip,绝大多数情况 |
clip-cov | compute_policy_loss_clip_cov() | token 重要性加权 clip |
kl-cov | compute_policy_loss_kl_cov() | KL 协方差正则 |
gspo | compute_policy_loss_gspo() | 序列级 IS × token-level ratio(2025-11) |
sapo | compute_policy_loss_sapo() | sigmoid soft-clip(2025-11) |
gpg | compute_policy_loss_gpg() | group policy gradient |
cispo | compute_policy_loss_cispo() | clipped IS PPO(rollout IS 矫正) |
geo_mean | compute_policy_loss_geo_mean() | 序列长度归一的几何均值,数值稳定 |
dppo_tv / dppo_kl | compute_policy_loss_dppo_*() | DPPO 双策略约束 |
bypass_mode | compute_policy_loss_bypass_mode() | off-policy 矫正路径(搭 rollout_correction) |
所有函数都在 verl/trainer/ppo/core_algos.py,通过 POLICY_LOSS_REGISTRY + @register_policy_loss(name) 注册。ch09.10–9.12 会展开公式细节。
8.13 RolloutCorrectionConfig:off-policy 矫正预设
2025-11 加入的 rollout correction 是为了应对"rollout 完后训练阶段策略已经变了"的 off-policy 情况。配置在 algorithm.rollout_correction:
| 字段 | 含义 |
|---|---|
bypass_mode | True = 用 rollout 的 logprob 当 old_logprob(2 策略);False = 重新算 old_logprob(3 策略) |
rollout_is | "token" / "sequence" —— 重要性采样粒度 |
rollout_rs | "token_k1/k2/k3" / "seq_*_k1/k2/k3" —— 拒绝采样阈值 |
仓库给了几个开箱预设(函数式调用而非 YAML 一行字段):
| 预设 | 组合 |
|---|---|
decoupled_token_is() | 3-策略 + token-IS |
decoupled_seq_is() | 3-策略 + sequence-IS |
bypass_ppo_clip_geo_rs() | 2-策略 + PPO clip + 几何 RS |
不熟时先用 bypass_mode=False 跑同步路径(默认),等观察到 IS ratio 显著偏离 1 再开矫正。
8.14 EngineConfig / EngineRouterReplayConfig 速览
这两组字段在 ch06 已经讲过实现——这里给 YAML 写法快查:
actor_rollout_ref:
actor:
strategy: fsdp # or megatron / torchtitan / veomni / automodel / mindspeed
use_dynamic_bsz: true
max_token_len_per_gpu: 16384
infer_max_token_len_per_gpu: 16384
param_offload: false
optimizer_offload: false
grad_offload: false
router_replay:
mode: "R2" # disabled / R2 / R3(MoE 用)
8.15 这章你需要带走什么
- 知道 verl config 是 Hydra 组件式组装,
_self_决定优先级; - 能用命令行 override 任何深层字段;
- 会用
--cfg job看最终生效的 config; - 能写自己的
my_config.yaml取代长 bash 脚本; - 记得
+新增、~删除、${var}插值这套语法; - 看到
algorithm/actor.policy_loss/rollout_correction段时知道每个字段的来源类。