[AutoEP]Fix optimizer and replaced MOE parameter mismatch - #8377
Draft
pengdurice wants to merge 4 commits into
Draft
[AutoEP]Fix optimizer and replaced MOE parameter mismatch#8377pengdurice wants to merge 4 commits into
pengdurice wants to merge 4 commits into
Conversation
Signed-off-by: pengdurice <pengduhit@gmail.com>
Signed-off-by: pengdurice <pengduhit@gmail.com>
Signed-off-by: pengdurice <pengduhit@gmail.com>
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.
Fix: AutoEP silently drops expert parameters from a caller-supplied optimizer
Problem
_configure_expert_parallel(engine.py) replaces every MoE module, and it runs before_configure_optimizer. Nothing remaps optimizer param groups in between —set_optimizer_flagsonly sets Muon flags.torch.optim.Optimizer.__init__materialises its argument eagerly (param_groups = list(params)),so an optimizer the caller built from
model.parameters()keeps hard references to the discardedexpert tensors, while the live
GroupedExpertsweights belong to no param group.This is the path HF Trainer and Accelerate take whenever the DeepSpeed config declares no
optimizerblock (transformers/integrations/deepspeed.py:optimizer = trainer.create_optimizer()in the
elsebranch).Two symptoms, same cause:
zero.Init(zero3_init_flag: true)zero.InitAttributeError: 'Parameter' object has no attribute 'partition_numel'from_create_fp16_sub_groups(stage3.py), because the stale params were never ZeRO-converted.Measured on a 2-layer / 4-expert model, ZeRO-3,
autoep_size=2, one step at lr=0.1:The same run with the optimizer declared in
ds_configmoves every tensor by1.005e-01.Fix
_remap_client_optimizer_after_module_replacement, called immediately after_configure_expert_parallel:DummyOptim/ config-built paths return early);so per-group hyper-parameters (e.g. a weight-decay split) survive;
optimizer.stateentries, which Adam keys on the parameter object;assignment would silently mis-apply weight decay.
Tests
New
tests/unit/v1/moe/test_autoep_client_optimizer.py,world_size = 2:test_client_optimizer_covers_replacement_parameters— every trainable param is in a param group;replacement actually happened; no optimized param is detached from the module.
test_client_optimizer_updates_expert_weights— after one real step, no parameter isbit-identical. This is the test that catches the silent freeze.
test_client_optimizer_preserves_param_group_hyperparameters— with decay / no-decay groups,replacement params land in the right group and both
weight_decayvalues survive.3 passed with the fix; 3 failed with
engine.pyreverted and everything else identical.The tests use bf16 rather than the shared
mixed_precision_config()helper: fp16 carries a lossscaler that skips the first optimizer step on overflow, which would leave every parameter
unchanged and make the update test vacuous.
Scope
ZeRO-3 only. A caller-supplied optimizer with MoE on ZeRO-1/2 additionally requires param groups
marked
{"moe": True}(stage_1_and_2.py:780,bf16_optimizer.py:128) — a separate pre-existingrequirement, not addressed here.
Why this was not caught
Every existing AutoEP test supplies the optimizer through
ds_config(
"optimizer": {"type": "Adam"}inmake_autoep_config), so DeepSpeed builds it afterreplacement and the client-optimizer path is never exercised.