feat(moe): add Rollout Routing Replay (R3) for MoE RL training - #2797
Merged
Conversation
Adds opt-in router replay so on-policy RL on MoE models reuses the rollout's top-k expert selection during the training forward, removing the rollout/training routing mismatch that destabilizes GRPO/GSPO (mirrors Megatron-LM's moe_enable_routing_replay). What this adds: * components/moe/router_replay.py: a RouterReplay handle (RECORD/REPLAY modes) with a process-global per-layer registry plus record()/replay() context managers. Only the discrete top-k selection is replayed; the router logits and their softmax/sigmoid are recomputed from the live router weights, so the gradient still flows into the router. * MoEConfig.enable_routing_replay flag, default False. * Hooks in the shared Gate (every score_func branch) and the custom Gemma4Gate so both record/replay their final expert selection. With replay disabled each path is a no-op: the re-gather reproduces the original top-k values exactly. * Unit tests at 100% coverage of router_replay.py. Signed-off-by: khazic <khazzz1c@gmail.com>
Contributor
|
/ok to test 20552e3 |
Contributor
|
/claude review |
| @@ -0,0 +1,231 @@ | |||
| # Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. | |||
Contributor
There was a problem hiding this comment.
LGTM — clean design, well-tested, no issues found. The record/replay separation, identity-check optimization, and context-manager cleanup are all correct.
Contributor
|
I tested PR #2797 locally on 8x H100s. Validation done:
|
HuiyingLi
approved these changes
Jun 27, 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.
What
Adds opt-in Rollout Routing Replay (R3) to the MoE subsystem.
In on-policy RL on a Mixture-of-Experts model, the rollout (inference) engine and the training engine compute the router's top-k expert selection independently. Small numerical differences flip a fraction of routing decisions per layer, and that compounds across layers until most tokens train under a different expert assignment than they were rolled out with. The mismatch breaks the importance-sampling assumption behind GRPO/GSPO and destabilizes training.
R3 removes the mismatch: capture the top-k expert selection during the rollout-equivalent forward, then replay that exact selection during the training forward. Only the discrete selection is replayed. The router logits and their softmax/sigmoid are still recomputed from the live router weights, so the gradient continues to flow into the router. This mirrors Megatron-LM's
moe_enable_routing_replay.Changes
components/moe/router_replay.py(new): aRouterReplayhandle (RECORD / REPLAY modes) with a process-global per-layer registry plusrecord()/replay()context managers, and thereplay_selectionhelper.MoEConfig.enable_routing_replayflag (defaultFalse).Gate(everyscore_funcbranch) and the customGemma4Gate, so both record and replay their final expert selection.router_replay.py.Default path stays a no-op
When
enable_routing_replay=False(the default), noRouterReplayhandle is created and the gate forward is unchanged:replay_selectionreturns the original index tensor. The two branches that previously consumedtopk's returned values directly re-gather only when replay actually swapped the selection, guarded by an identity check, so the replay-off and record paths add no extra tensor work.Usage
Testing
Unit tests:
tests/unit_tests/moe/test_router_replay.py, 27 tests, 100% coverage ofrouter_replay.py. They cover the record/replay roundtrip across everyscore_funcbranch, the default-path no-op, gradient flow to the router under replay, multi-layer distribute/collect, error handling, and theGemma4Gatepath.Beyond the unit tests, I exercised the real Automodel model classes on GPU (record on batch A, replay on a different batch B):
Qwen3MoeForCausalLM, 3 MoE layers)Gemma4MoE+Gemma4Gate)Test environment
ruff formatandruff checkcleanAddresses #2454.