Repository navigation
[moe] sync and shard expert grads over moe_dp * sp with all_to_all sequence parallelism - #6468
Merged
Merged
Conversation
…quence parallelism With all_to_all sequence parallelism, the ranks of an sp group hold the same experts but run the MoE blocks on different parts of the sequence. Dense grads are synced over (moe_dp, ep, sp), but expert grads were put in a ZeRO store on the moe_dp group only, so each sp rank kept the grad of its own tokens and the expert copies drifted apart (0.8-1.0 relative error against a single-GPU reference with ep=2, sp=2). Add MoeHybridParallelPlugin.mixed_moe_dp_group, a group along the (moe_dp, sp) axes when all_to_all SP is on and moe_dp_group otherwise, and use it for the expert ZeRO store and MoECheckpointIO. The output gather already scales grads by sp_size, so ZeRO's division by the group size gives the correct average. Expert optimizer states are now sharded over sp too. When loading a sharded optimizer, expert states were padded by the global dp size but split by the moe_dp size, while ZeRO pads each param by the size of its own group; pad by the moe_dp size so they agree. Add test_moe_ep_sp_grad_sync.py: ep=2, sp=2, ZeRO-1/2, every grad compared with an fp32 single-GPU reference and across the sp group, plus a sharded model/optimizer checkpoint round trip with sizes that exercise the padding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yuxuan-lou
approved these changes
Oct 19, 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.
Related issue
Fixes #6466
Related to #6464 / #6467 (expert grad scaling) and #6465 (ZeRO grad norm with EP). Those are separate bugs and are not touched here.
Background
With
MoeHybridParallelPlugin, EP > 1 andsequence_parallelism_mode="all_to_all", every sequence is split along the sequence dimension and the MoE blocks run on each rank's part. The ranks of one sp group hold the same experts but see different tokens, so their expert gradients must be summed over the sp group, exactly as for dense parameters.Dense parameters are synced over
mixed_dp_group, which spans the (moe_dp, ep, sp) axes. Expert parameters, however, were put in a ZeRO store onmoe_dp_group, which spans only the moe_dp axis. Each sp rank therefore kept the expert gradient of its own part of the sequence. With ep=2, sp=2 on 4 GPUs, expert grads were 0.8–1.0 off (relative L2) from a single-GPU reference, while the mean of the two sp ranks matched it to 0.007. The expert copies on different sp ranks drift apart over training, and sharded checkpoints keep only the copy from sp rank 0. Reproduction in #6466.The existing
test_shard_mixtral.py/test_shard_deepseek.pyinclude ep=2, sp=2 configs but only compare parameters after training withatol=4e-3, which is larger than the gradients of the tiny test models.Changes
colossalai/booster/plugin/moe_hybrid_parallel_plugin.pymixed_moe_dp_group, the expert counterpart ofmixed_dp_group. With all_to_all SP it is a group along the (moe_dp, sp) axes; otherwise it ismoe_dp_group, so nothing changes without SP.MoeHybridParallelZeroOptimizerandMoECheckpointIOuse it for expert parameters. Expert grads are therefore reduced over moe_dp × sp and divided by its size. The existinggrad_scale=sp_sizeon the output gather makes this the correct average.plugin.moe_dp_groupitself is unchanged; the model code still uses it.colossalai/checkpoint_io/moe_checkpoint.py: when loading a sharded optimizer, expert states were padded by the global dp size before being split by the moe_dp size. LowLevelZero pads each param by the size of the group it is sharded over, so the two disagreed whenever an expert param's numel is not divisible by the global dp size, and the loaded states ended up in the wrong place. They are now padded by the moe_dp size. This mismatch exists on main independently of SP (e.g. ep=2, moe_dp=2, a 30-element expert param); this PR makes it more likely to hit, so it is fixed here.tests/test_moe/test_moe_ep_sp_grad_sync.py(4 GPUs, ep=2, sp=2 all_to_all, bf16):Not in this PR:
zero_stage=0/ DDP path. With EP > 1 it already requires the dp and moe_dp groups to be identical and raises otherwise.test_shard_mixtral.py/test_shard_deepseek.py.User and compatibility impact
MoeHybridParallelPlugin.mixed_moe_dp_group; nothing removed.Test environment
nvidia-smiCUDA Version: 13.2)torch.version.cuda: 12.8, NCCL 2.28.9)nvcc --version): 12.8 (V12.8.61)250c903cCI uses torch 2.13 / CUDA 13.0; CI results will be added below.
Test commands and results
All runs:
FAST_TESTunset, 4 GPUs reserved viagpu-run.python -m pytest -p no:cacheprovider -rA tests/test_moe python -m pytest -p no:cacheprovider -rA tests/test_shardformer/test_model/test_shard_mixtral.py tests/test_shardformer/test_model/test_shard_deepseek.py -k "not 3d"tests/test_moe(this branch)_3dcases)tests/test_moe/test_moe_ep_sp_grad_sync.pyon main250c903c(control)experts.2.w1grad 0.884 off the reference, grads differ across the sp groupPassed:
test_moe_checkpoint::test_mixtral_moe_layer[4],test_moe_ep_sp_grad_sync[4],test_mixtral[4],test_deepseek[4].The 6 skips are pre-existing markers, not touched by this PR: 4 × "tested in corresponding sharderformer" and 2 × "requires MOE_TENSOR_PATH reference tensors".
Logs and JUnit XML are kept on the team cluster in
test-results/sp_ep_grad_sync_fix_20261016/.CI (torch 2.13.0+cu130 image
sha256:a7c63f5a…, headec5f1ed4):8 9,fast_mode=false(run 38770628022, attempt 2)Layer 3 details from the JUnit reports:
test_moe_ep_sp_grad_sync[4]andtest_moe_checkpoint::test_mixtral_moe_layer[4]passed; the MoE skips are the pre-existing markers.test_shard_mixtral::test_mixtral[4]passed.Attempt 1 of the Layer 3 run failed during job setup, before any test ran: the runner timed out downloading
actions/upload-artifactfrom GitHub. It passed on re-run. The Draft-onlycontainer smoke (batch 4a)check shows as failed because it was cancelled when the PR was marked ready; batch 4a ran and passed in Layer 2.Known issues
test_mixtral_3d[8]andtest_deepseek_3d[8](2 passed).test_shard_deepseek.py/test_shard_deepseek_v3.pywithout a local Hub snapshot; coverage comes from the local run above.skip,xfail,--ignoreor loosened tolerance is added. The new test's gradient tolerance (relative L2 < 3e-2) is explained in the file: bf16 noise stays below 2e-2, while the bug is 0.8–1.0 off. The checkpoint check is exact (torch.equal).Risk and rollback
mixed_moe_dp_groupismoe_dp_groupand every call site gets the same group as before. The most likely issue is a code path that relies on expert states being complete on every sp rank. The ZeRO store andMoECheckpointIOare the two consumers and both are updated and covered by the new test.Documentation and release impact
None: no README, installation, API docs, Docker,
version.txt, PyPI or DockerHub changes. A changelog line: "Fix expert gradients not being synchronized across sequence-parallel ranks with all_to_all SP + EP, and the re-sharding of expert optimizer states when loading checkpoints (#6466)."