verl Megatron 后端启用 Muon 优化器实战指南:从 Hydra 配置到学习率换算
【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl
导读
本文讲解如何在 verl(HybridFlow)框架的 Megatron 后端中启用 Muon(一种对 2D 权重矩阵做正交化更新的新兴优化器,源自 Megatron-Core 的emerging_optimizers模块),包括完整的 Hydra 覆盖参数、muon_*各配置项的含义与推荐值,以及最容易踩坑的"学习率换算"问题——AdamW 的学习率不能直接搬到 Muon 上使用。读完本文,你将掌握在 verl 的任意 Megatron GRPO/PPO 训练脚本中一键切换到 Muon、并正确设置其有效步长的完整方法,同时理解 verl 底层如何把配置安全地桥接到 Megatron-Core。
Muon 在 verl 中的定位:矩阵用 Muon,其余参数保持 AdamW
Muon(Momentumized, Orthogonalized, Unit-scaled)优化器对二维权重矩阵应用基于正交化(通过 Newton–Schulz 迭代近似矩阵符号函数)的更新,而对所有非矩阵参数——包括 embeddings、norms、biases、router 和 lm_head——继续使用 AdamW 更新。这种"矩阵走 Muon、其余走 AdamW"的混合策略是 emerging_optimizers 系列算法的标准用法。
在 verl 中,这一能力是通过把 Megatron-Core 的TensorParallelMuon暴露到 verl 原生的 Megatron 训练路径上实现的,入口说明见 examples/muon/README.md。verl 本身不重新实现优化器,而是:
- 在配置层(
McoreOptimizerConfig)声明muon_*系列字段,默认值与 Megatron-Core 保持一致; - 在构建优化器时把选中的字段"透传"给 Megatron-Core 的
OptimizerConfig(见下文源码解析); - 最终由 Megatron-Core 的
get_megatron_optimizer创建 tensor-parallel 感知的 Muon 优化器。
如何启用:一行 Hydra 覆盖即可
在 verl 中,任何 Megatron 后端的 GRPO/PPO 训练脚本(例如 examples/grpo_trainer 下的*_megatron.sh)都可以通过追加 Hydra 命令行覆盖来切换到 Muon。以下是一组推荐起点参数,其取值镜像了 verl/trainer/config/optim/megatron.yaml 中记录的所有 Muon 默认值:
python3 -m verl.trainer.main_ppo \ ... \ actor_rollout_ref.actor.optim.optimizer=muon \ actor_rollout_ref.actor.optim.use_layer_wise_distributed_optimizer=True \ actor_rollout_ref.actor.optim.muon_momentum=0.95 \ actor_rollout_ref.actor.optim.muon_nesterov=False \ actor_rollout_ref.actor.optim.muon_split_qkv=True \ actor_rollout_ref.actor.optim.muon_scale_mode=spectral \ actor_rollout_ref.actor.optim.muon_coefficient_type=quintic \ actor_rollout_ref.actor.optim.muon_num_ns_steps=5 \ actor_rollout_ref.actor.optim.muon_tp_mode=blockwise \ actor_rollout_ref.actor.optim.muon_match_adamw_update_rms=True其中...表示原有脚本中已有的训练配置(数据路径、模型路径、rollout 配置等),只需在原有参数基础上追加上述覆盖即可。
关键配置项(Key knobs)逐项解析
下表整理了文档中的核心参数及其推荐值与含义,同时补充了megatron.yaml中声明但 README 未展开的字段:
| 字段 | 推荐值 | 含义 |
|---|---|---|
optimizer | muon | 选择 Muon(新兴)优化器;矩阵参数用 Muon,其余参数回退到 AdamW。verl 中adam/sgd走 Megatron 经典优化器路径,muon走 emerging_optimizers 路径(见 verl/trainer/config/optim/megatron.yaml) |
use_layer_wise_distributed_optimizer | True | 构建 Megatron 的 LayerWise 分布式优化器路径,使 Muon 的逐层 buffer 被分布式切分(避免额外的 fp32 master 克隆,使显存占用低于 AdamW) |
use_layer_wise_param_layout | 自动True | 为 LayerWise buffer 使用 Megatron 的 padded shard-aligned DDP 布局(master 权重内嵌在参数 buffer 中)。默认None,在 Muon+LayerWise 组合下自动置True |
muon_momentum | 0.95 | Muon 内部 SGD 的动量;调参收益通常很小 |
muon_nesterov | False | 是否对 Muon 更新使用 Nesterov 动量 |
muon_split_qkv | True | 是否将融合的 QKV 参数按头独立进行正交化 |
muon_scale_mode | spectral | Muon 更新的缩放模式(如spectral/unit_rms_norm),按参数形状做归一化 |
muon_coefficient_type | quintic | Newton–Schulz 迭代的系数类型(合法取值由已安装的emerging_optimizers包决定) |
muon_num_ns_steps | 5 | 正交化使用的 Newton–Schulz 迭代步数 |
muon_tp_mode | blockwise | 张量并行权重上 Newton–Schulz 计算的切分方式(如blockwise/duplicated/allgather) |
muon_fp32_matmul_prec | medium | Muon fp32 matmul 的精度设置 |
muon_extra_scale_factor | 1.0 | 施加到 Muon 更新的额外缩放因子;Muon 的有效步长为lr × muon_extra_scale_factor |
muon_scalar_optimizer | adam | 非矩阵("scalar")参数使用的优化器。注意:Megatron-Core 声明了该字段但当前没有代码路径读取它,因此实际生效(也是唯一推荐)行为是默认值adam,设置其他值目前是静默 no-op(见 verl/workers/config/optimizer.py) |
muon_match_adamw_update_rms | True | verl 侧便利开关(不转发给 Megatron):从betas[0]推导muon_extra_scale_factor,使 Muon 的更新 RMS 与 AdamW 匹配——详见下一节 |
完整字段默认值见 verl/trainer/config/optim/megatron.yaml 与 verl/workers/config/optimizer.py 中的McoreOptimizerConfig。
容错行为:未知字段直接报错,绝不静默忽略
一个值得注意的工程细节:如果安装的 Megatron-Core 版本未声明某个muon_*字段,verl 会在构建期直接抛出异常,而不是静默忽略该参数——这避免了"配置写了但实际没生效"的隐蔽陷阱。对应逻辑在verl/utils/megatron/optimizer.py的_add_muon_args中:verl 只转发已安装OptimizerConfig实际声明的字段;若请求了 Muon 算法但已安装的 Megatron 一个 Muon 字段都没有,则直接raise ValueError,明确提示需要支持emerging_optimizers的 Megatron-Core 构建,拒绝悄悄回退到 Adam(见 verl/utils/megatron/optimizer.py)。
学习率换算:Muon 的有效步长是lr × muon_extra_scale_factor
这是启用 Muon 时最关键的坑:AdamW 的学习率不能直接复用。
Megatron-Core 的默认muon_extra_scale_factor = 1.0与 AdamW 并不可比。如果你把 AdamW 的lr原封不动搬过来,Muon 的有效步长会被放大约4.4 倍(即1 / 0.2294),这正是 Muon 训练不稳定或不收敛的常见原因。
匹配 AdamW 更新 RMS 的闭式解
emerging_optimizers提供了使 Muon 更新 RMS 范数与 AdamW 匹配的缩放因子闭式解:
muon_extra_scale_factor = sqrt((1 - beta1) / (1 + beta1))其中beta1是 AdamW 的一阶矩(动量 EMA)系数。在默认beta1 = 0.9时,该因子约为0.229416(仅供数量级参考)。
正确姿势:配置开关,而不是硬编码数值
文档给出的核心建议是:配置开关,而不是配置数字。即设置:
actor_rollout_ref.actor.optim.muon_match_adamw_update_rms=Trueverl 会自动从你的optim.betas[0]推导该因子,并在 rank 0 上打印解析出的数值(见 verl/utils/megatron/optimizer.py)。这样做的理由很实际:
- 任何阅读配置的人都可以从
beta1重新推导出开关对应的值,但无法从一段粘贴的字面量看出它的来历; - 同时设置
muon_match_adamw_update_rms=True和显式muon_extra_scale_factor会直接报错(ValueError,提示只能二选一),从机制上杜绝了歧义。
muon_scale_mode与muon_extra_scale_factor是正交的
两者都重要但作用不同:muon_scale_mode按参数的形状(shape)做归一化,而muon_extra_scale_factor处理的是动量/EMA层面的缩放。两者的依据来自 emerging_optimizers 0.3.0 的orthogonalized_optimizers/muon.py::get_muon_scale_factor文档字符串及公开论文资料。另外需要澄清一个常见误解:常被引用的0.2是beta1 = 0.9时该因子本身的值,而不是任何实测更新 RMS 的目标值。
源码级解析:verl 如何把 Muon 配置桥接到 Megatron-Core
配置类:McoreOptimizerConfig
所有 Muon 字段定义在 verl/workers/config/optimizer.py 的McoreOptimizerConfig中。该类继承了基础的OptimizerConfig(lr、betas、weight_decay、clip_grad等),并扩展出optimizer(默认"adam")与全部muon_*字段。muon_match_adamw_update_rms被明确注释为 "verl-side convenience, not forwarded to Megatron",即它是 verl 独有的便利开关,不会作为参数传给 Megatron。
桥接函数:init_megatron_optim_config
核心桥接逻辑在 verl/utils/megatron/optimizer.py 的init_megatron_optim_config:
- 当
optimizer是muon时调用_add_muon_args,把_MUON_PASSTHROUGH_FIELDS中列出的字段(use_layer_wise_distributed_optimizer、muon_momentum、muon_nesterov、muon_split_qkv、muon_scale_mode、muon_coefficient_type、muon_num_ns_steps、muon_tp_mode、muon_fp32_matmul_prec、muon_extra_scale_factor、muon_scalar_optimizer等)转发到 Megatron 的OptimizerConfig; - 若开启了
muon_match_adamw_update_rms,调用adamw_rms_match_scale_factor(beta1)计算sqrt((1 - beta1) / (1 + beta1))并写入muon_extra_scale_factor(verl/utils/megatron/optimizer.py); - 当 Muon + LayerWise 组合时,自动补上
use_layer_wise_param_layout=True(前提是已安装 Megatron 支持该字段),让 master 权重整合进参数 buffer,避免额外的 fp32 克隆; - 根据
fp16/bf16设定params_dtype等参数,最后允许override_optimizer_config覆盖任意项。
调用链与测试佐证
在训练引擎侧,verl/workers/engine/megatron/transformer_impl.py 的_build_optimizer会依次调用init_megatron_optim_config与get_megatron_optimizer完成优化器构建。
仓库中还提供了覆盖多层面的测试,可作为配置正确性的参考:
- tests/utils/megatron/test_muon_optim_wiring_on_cpu.py:验证
init_megatron_optim_config在optimizer=muon时正确转发muon_*超参、只转发已安装 Megatron 支持的字段、Adam 路径不转发 Muon 字段、以及无 Muon 支持时"响亮失败"; - tests/utils/megatron/test_muon_optim_realcfg_on_cpu.py:用真实
megatron.core构建OptimizerConfig,验证转发的字段名/类型能被已安装的 Megatron 接受; - tests/utils/megatron/muon_optim_gpu_smoke.py:GPU 冒烟测试,
torchrun --nproc_per_node=1运行,验证真实构建 Muon 优化器; - tests/utils/megatron/test_muon_layerwise_bridge_ddp_on_cpu.py:验证 LayerWise Muon 场景下 Megatron bridge 对 DDP 的处理(对应 verl/utils/megatron_utils.py 的
_assert_muon_layer_wise_ddp_supported); - tests/workers/config/test_optim_config_on_cpu.py:验证
McoreOptimizerConfig的 Muon 默认值与 Megatron 对齐、覆盖项能正确传递。
使用前提与注意事项
- 需要支持
emerging_optimizers的 Megatron-Core 构建。如果缺失,verl 会在构建期报错而不是静默回退到 Adam(见上文容错行为一节)。 use_layer_wise_distributed_optimizer=True是显存控制的关键:它让 Muon 的逐层 buffer 被分布式切分。在 30B 规模下,这是让 Muon 的峰值优化器显存低于 AdamW的原因;如果不开启,逐层 buffer 不会被切分,显存优势也就不存在。muon_*字段只在optimizer选择 Muon 算法时生效,选择 Adam/SGD 时会被忽略。muon_match_adamw_update_rms是 verl 侧开关,不会转发给 Megatron;它与显式muon_extra_scale_factor互斥,同时设置会报错。- 若需要进一步微调其他优化器行为,
optim.lr、optim.betas、optim.weight_decay、optim.clip_grad等基础项仍沿用 Megatron 的McoreOptimizerConfig语义,配置位置见 verl/trainer/config/optim/megatron.yaml。
快速自查清单
在运行 Muon 训练前,建议按以下清单核对:
- 是否已设置
actor_rollout_ref.actor.optim.optimizer=muon? - 是否设置了
use_layer_wise_distributed_optimizer=True(显存与 LayerWise buffer 切分)? - 是否设置了
muon_match_adamw_update_rms=True且没有同时显式设置muon_extra_scale_factor(学习率换算)? - 是否已确认安装的 Megatron-Core 支持
emerging_optimizers(否则构建期会报错)? - 启动后是否在 rank 0 日志中看到解析出的
muon_extra_scale_factor(默认beta1=0.9时应约为0.2294)?
按此配置,即可在 verl 的 Megatron 后端稳定启用 Muon,并获得低于 AdamW 的优化器显存占用。
【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考