feat(peft): support multi-LoRA on grouped MoE expert linears - #23
Conversation
Export contract: identical to the single-adapter pathSince the whole point of reusing Both produce byte-identical names and shapes: So a multi-LoRA slot exported through Worth recording for anyone reading the layout: the per-expert index lives in the tensor's expert axis |
MultiLoRA previously skipped every MoE expert linear, so on an MoE model the
experts — most of the trainable capacity — silently received no adapter.
Add MultiLoRAGroupedExpertLinear for grouped expert linears
(mlp.experts.linear_fc{1,2} of a TEGroupedMLP). Each slot is a
GroupedExpertLinearAdapter, so the packed [num_local_experts, ...] weight layout
that peft_bridge's grouped-expert export and distributed checkpointing already
understand is reused unchanged. It subclasses MultiLoRALinear so isinstance-based
multi-LoRA discovery (slot lifecycle here, per-slot optimizers and adapter-state
zeroing downstream) picks expert layers up with no changes.
The hard part is that inside the experts the row order is the dispatcher's
expert-major permutation of tokens from every EP rank, so tokens_per_adapter no
longer segments it. install_moe_slot_routing registers a forward pre-hook on each
MoE layer's experts module — the only point with both the dispatcher's
permutation metadata and the final row order — which co-permutes an int32
slot-id vector through the same stages the dispatcher applied and stable-sorts by
(slot, local expert) so one grouped GEMM covers every pair. The companion
all-to-all carries one int32 per dispatched token.
Every slot's weights stay in the autograd graph (via the stacked weights, and an
explicit zero term on the empty-batch path) because Megatron's grad buffers
expect one grad hook per trainable parameter; a slot missing from the graph would
leave its bucket incomplete.
Unsupported configurations are rejected at layer construction, not in the hook:
a hook that raised on only some ranks would leave its peers waiting in the
companion all-to-all. Rejected: expert TP > 1, fp8/fp4 experts, permute fusion
(the fused permute records TE's row_id_map rather than a token gather index),
capacity padding, and non-alltoall dispatchers. Sequential (SequentialMLP)
expert linears are still skipped, with the warning rewritten to say so.
reset_adapter now forks the expert RNG tracker instead of the dense one: the
dense seed varies with the dense TP rank, and expert-data-parallel peers differ
in exactly that rank whenever tensor and expert tensor parallel sizes differ, so
replicas of a reused slot would diverge.
f39ebd3 to
f15719c
Compare
|
Pushed 592f5e2: a GLM-5.2_5layer multi-LoRA e2e smoke (the first MLA model through this path — TP=EP=4, ETP=1, SP, thd, dp-attention rollout) caught two defects in the dense
Qwen3-style models never reach either path (all their targets are TP-parallel), which is why the existing e2e missed them. After the fix the GLM-5.2 smoke completes 3 training steps end-to-end (per-step distributed upserts to both dp-attention engines, |
Why
MultiLoRAskipped every MoE expert linear, so on MoE models the experts — most of the trainable capacity — silently got no adapter. This brings the multi-adapter path to parity with single-adapterLoRAfor grouped experts.What
MultiLoRAGroupedExpertLinearwrapsmlp.experts.linear_fc{1,2}of aTEGroupedMLPwith one low-rank pair per (slot, local expert). Each slot is an existingGroupedExpertLinearAdapter, so the packed[num_local_experts, ...]export and checkpoint layout is reused unchanged; subclassingMultiLoRALinearkeepsisinstance-based multi-LoRA discovery working with no downstream changes.tokens_per_adapterno longer segments them.install_moe_slot_routingputs a forward pre-hook on each MoE layer'sexpertsmodule that co-permutes an int32 slot-id vector through the dispatcher's own stages (local permute, EP all-to-all, local-expert sort), then stable-sorts by (slot, expert) so one grouped GEMM covers every pair.Rejected configurations
Raised at layer construction, not in the hook (a hook raising on some ranks would strand peers in the companion all-to-all): expert TP > 1,
moe_permute_fusion(records TE'srow_id_map, not a token gather index), fp8/fp4 experts (input padding desyncs the row order), capacity padding, non-alltoall dispatchers.SequentialMLPexperts stay skipped with a clearer warning;normalize_moe_lora/share_expert_adapters/experts_shared_outer_lorasraiseNotImplementedError.Bug fix
reset_adapterforked the dense RNG tracker, whose seed varies with the dense TP rank — expert-DP peers differ in exactly that rank whenever TP != ETP, so replicas of a reused slot diverged. It now forks the expert tracker.Testing
8x H200:
test_multi_lora_moe.py17 passed; all multi-LoRA tests 70 passed (9 failures elsewhere intests/unit_tests/peft/are pre-existing single-LoRA default mismatches onbridge). Real-model EP=2 integration (Qwen3-MoE-shaped, realMoEAlltoAllTokenDispatcher): all expert adapter params get grads, and routing tokens to different slots gives different losses. Export throughAutoBridge.export_adapter_weightsis byte-identical (names and shapes) to single-adapterLoRA.Merge order
First of three: this → radixark/miles#1794 → sgl-project/sglang#32376.