Repository navigation
[megatron] feat: add Muon optimizer support (expose Megatron-Core TensorParallelMuon) - #7120
Merged
Merged
Conversation
ISEEKYAN
requested review from
PeterSH6,
eric-haibin-lin,
tongyx361 and
vermouth1992
as code owners
July 22, 2026 19:04
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
ISEEKYAN
force-pushed
the
megatron-muon-clean
branch
from
July 24, 2026 08:06
434dd82 to
3ef58a3
Compare
โฆsorParallelMuon) verl's native Megatron backend previously supported only AdamW-type optimizers. Megatron-Core ships TensorParallelMuon (via emerging_optimizers), but verl did not expose it. This PR wires it through so verl users on the Megatron engine can select Muon. What it does - McoreOptimizerConfig: expose optimizer=muon/adaptive_muon plus the muon_* hyperparameters, each mirroring the like-named Megatron-Core OptimizerConfig field. - verl/utils/megatron/optimizer.py: forward the muon_* fields to Megatron-Core and route matrix vs non-matrix params (Muon on 2D weights, AdamW fallback on the rest). - LayerWise-DDP-aware wiring + master-weight buffer integration to avoid the double-buffer / extra fp32 master clone that would otherwise inflate memory. - Fail-closed: requesting a muon_* field absent from the installed Megatron-Core build raises instead of silently ignoring it. - CPU wiring/real-config tests + a GPU smoke test. Tested - CPU: optimizer config + wiring unit tests. - GPU (Qwen3-30B-A3B, 2 nodes x 8 GPUs): bitwise-identical parameter updates vs the reference path; Muon peak memory 39.66 GiB < AdamW 46.63 GiB; a short GSM8K RL A/B shows Muon reward increasing and its convergence at least matching AdamW.
ISEEKYAN
force-pushed
the
megatron-muon-clean
branch
2 times, most recently
from
July 24, 2026 18:03
9aacd13 to
3bb6903
Compare
When vanilla_mbridge=False, defer provider DDP wrapping for Muon layer-wise configs and apply wrap_model_chunks_with_layerwise_aware_ddp (matching the legacy mbridge path). Fail closed on Megatron FSDP combo.
ISEEKYAN
force-pushed
the
megatron-muon-clean
branch
from
July 24, 2026 18:12
3bb6903 to
50a5551
Compare
Megatron-Core declares muon_scalar_optimizer with the docstring "one of 'adam' or 'lion'", and this docstring mirrored that wording. In practice no Megatron-Core code path reads the field, so selecting "lion" is a silent no-op; the default "adam" is both the shipped default and the only behaviour Muon is documented against upstream (emerging_optimizers recommends a standard method such as AdamW for the non-matrix params). Describe the field as it actually behaves instead of advertising a choice that does not take effect. No functional change.
Muon's effective step size is lr * muon_extra_scale_factor, and the
Megatron-Core default of 1.0 is not AdamW-comparable: reusing an AdamW
learning rate unchanged gives a ~4.4x larger effective step, a common
cause of unstable or non-converging Muon runs. The previous README text
("Muon does not need a separate learning rate") was misleading on
exactly this point and is replaced.
Add opt-in muon_match_adamw_update_rms, which resolves the factor from
the closed form documented by emerging_optimizers 0.3.0:
muon_extra_scale_factor = sqrt((1 - beta1) / (1 + beta1))
("analytically gives the update RMS norm of AdamW",
get_muon_scale_factor docstring; https://kexue.fm/archives/11267;
https://arxiv.org/abs/2502.16982). At the default beta1=0.9 this is
~0.229416, which is where the commonly quoted ~0.2 comes from -- that is
the value of the factor, not a target for any measured update RMS.
The flag is verl-side only and is not forwarded to Megatron-Core. The
resolved value is logged on rank 0 so it stays reproducible from the
config instead of being pasted in as a constant. Setting the flag
together with an explicit muon_extra_scale_factor raises. The default
stays 1.0, matching Megatron-Core, so no existing config changes
behaviour.
Make explicit that the recommended configuration is muon_match_adamw_update_rms, not a pasted-in muon_extra_scale_factor value. A hand-entered constant is the thing to avoid regardless of how it was derived: a reader can re-derive the switch's value from beta1, but not a literal. The numeric value is kept only as orientation.
This was referenced Jul 30, 2026
wuxibin89
approved these changes
Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add Muon optimizer support to verl's native Megatron backend.
verl's native Megatron backend previously supported only AdamW-type optimizers. Megatron-Core ships
TensorParallelMuon(via theemerging_optimizerspackage), but verl did not expose it. This PR wires it through so verl users on the Megatron engine can select Muon.What it does
McoreOptimizerConfig(verl/workers/config/optimizer.py): exposeoptimizer=muonplus themuon_*hyperparameters, each mirroring the like-named field on Megatron-CoreOptimizerConfig.verl/utils/megatron/optimizer.py: forward themuon_*fields to Megatron-Core and route parameters โ Muon on 2D weight matrices, AdamW fallback on the rest (embeddings, norms, biases).muon_*field that the installed Megatron-Core build does not declare raises, instead of silently ignoring it.muon_match_adamw_update_rms: opt-in flag derivingmuon_extra_scale_factorfromsqrt((1 - beta1) / (1 + beta1)), the closed form that analytically matches AdamW's update RMS norm (emerging_optimizers 0.3.0get_muon_scale_factordocstring; https://kexue.fm/archives/11267; https://arxiv.org/abs/2502.16982). Muon's effective step islr * muon_extra_scale_factorand the Megatron-Core default1.0is not AdamW-comparable, so reusing an AdamWlrunchanged gives a ~4.4x larger effective step. The resolved value is logged on rank 0; the default stays1.0so no existing config changes behaviour.Results
Qwen3-30B-A3B, 2 nodes ร 8 GPUs (TP=2, PP=1, EP=8, DP=1), matched two-arm comparison (same data / seed / schedule; only the optimizer changes):
How these are judged. Two 30-step GSM8K A/Bs were run whose AdamW arms use a
byte-identical configuration, so the pair also measures this workload's run-to-run spread.
Every claim is reported against that spread rather than as a raw difference:
Supported: Muon reduces peak optimizer memory by 18.3%, a gap 49.5x the run-to-run
spread of that metric, while preserving bitwise-correct parameter updates. Its
optimizer-update time is roughly half AdamW's, ~4x the spread โ the portion of the step Muon
actually governs.
Not supported: on reward, Muon and AdamW are level within run-to-run spread. The gaps
match the AdamW-vs-AdamW spread in size and the sign flips between metrics (Muon ahead on
the last-3 mean, AdamW ahead on the last-10). An earlier total-gain difference of +0.032 is
likewise inside the 0.023 spread, so the previous "at least matching AdamW" wording is
withdrawn. No hyperparameter was tuned to produce a difference. End-to-end throughput and step
time are dominated by ~2x run-to-run variation in tokens per step and are not usable here.
Scoped to two 30-step short runs; the n = 2 spread estimate is itself crude.
Compatibility
The optimizer wiring uses Megatron-Core's native
get_megatron_optimizerand is independent of the model-build path. The LayerWise-DDP path is validated on both the vanilla Megatron-bridge and the full Megatron-Bridge (non-vanilla) model builds. On the full Megatron-Bridge path (Qwen3-30B-A3B, 12-step check): Muon peak 39.92 GiB vs AdamW 45.40 GiB (Muon saves ~12%), consistent with the vanilla path (39.64 GiB), confirming the layer-wise wiring holds under Megatron-Bridge.Testing
tests/utils/megatron/,tests/workers/config/).muon_optim_gpu_smoke.pysmoke test.