[lora] Support GDN in_proj_ba adapters for Qwen3.5 - #30499
Conversation
Qwen3.5's GatedDeltaNet has two packed input projections: in_proj_qkvz (supported) and in_proj_ba (the delta-rule beta/alpha gates). Adapters covering in_proj_ba were silently dropped at load time, so a LoRA trained on the fused megatron in_proj served base weights for the b/a slices (a quiet train/serve mismatch for RL). - qwen3_5.py: add in_proj_ba to supported_lora_modules and get_hidden_dim (out dim = 2 * linear_num_value_heads) - lora/utils.py: register in_proj_ba (known targets, stacked_multiply=2, in_proj_b/in_proj_a name normalization) - lora/lora.py: new _normalize_in_proj_ba (b+a concat / merged repeat); extend _normalize_in_proj_qkvz to accept the 2-way in_proj_qkv + in_proj_z layout emitted by Megatron-Bridge adapter export (repeat the shared A 3x for the q|k|v slices) MergedColumnParallelLinearWithLoRA wraps the base layer generically; no layer/mem-pool changes needed. Validated end-to-end with miles RL on Qwen/Qwen3.5-35B-A3B (8xH200, TP2/EP8, colocate): rollout-vs-train logprob_abs_diff 0.0100 with LoRA on attention + MoE experts + shared expert + GDN in_proj/out_proj; no adapter weights skipped. Signed-off-by: Yusheng Su <yushengsu.thu@gmail.com>
There was a problem hiding this comment.
Code Review
This pull request adds support for normalizing and stacking in_proj_ba weights for GatedDeltaNet (GDN) layers, such as those in Qwen 3.5, and adds support for 2-way split in_proj_qkv and in_proj_z weights. The changes span LoRA weight normalization, utility functions, and the Qwen 3.5 model definition. Feedback suggests updating get_hidden_dim in qwen3_5.py to use config.linear_num_value_heads_cpu when running on CPU to avoid shape mismatches when loading LoRA adapters.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
Additional end-to-end evidence: 10-step LoRA RL run (miles, GRPO/gsm8k) on Qwen/Qwen3.6-35B-A3B (same |
2fdb655
into
sgl-project:sglang-miles
Signed-off-by: Yusheng Su <yushengsu.thu@gmail.com>
Signed-off-by: Yusheng Su <yushengsu.thu@gmail.com>
Signed-off-by: Yusheng Su <yushengsu.thu@gmail.com>
Signed-off-by: Yusheng Su <yushengsu.thu@gmail.com>
Add _normalize_in_proj_ba to LoRAAdapter: split in_proj_b + in_proj_a adapter weights are concatenated into in_proj_ba, and already-merged in_proj_ba adapters get their shared lora_A replicated 2x across the stacked b/a slots. Extend _normalize_in_proj_qkvz with the 2-way Megatron-Bridge split (in_proj_qkv + in_proj_z, qkv A block repeated 3x), register in_proj_ba in the LoRA utils, and wire it into Qwen3_5ForCausalLM. Ported from the sglang-miles branch (original PR sgl-project#30499).
Motivation
Qwen3.5's GatedDeltaNet (GDN) has two packed input projections:
in_proj_qkvz(already LoRA-supported) andin_proj_ba— the delta-rule β/α gate projections. LoRA adapter weights coveringin_proj_baare currently silently dropped at load time (not in_KNOWN_LORA_TARGET_MODULES, warning only). An adapter trained against the fused megatron-sidein_proj(Megatron-Bridge exports it split 4-way asin_proj_qkv/z/b/a) therefore serves base weights for the β/α slices — a quiet train/serve mismatch for RL.Modifications
models/qwen3_5.py: addin_proj_batosupported_lora_modules+get_hidden_dim(2 * linear_num_value_heads)lora/utils.py: registerin_proj_bain_KNOWN_LORA_TARGET_MODULES,get_stacked_multiply(=2), andin_proj_b/in_proj_a→in_proj_baname normalizationlora/lora.py: new_normalize_in_proj_ba(split b+a concat, merged repeat×2); extend_normalize_in_proj_qkvzto accept the 2-wayin_proj_qkv+in_proj_zlayout emitted by Megatron-Bridge adapter export (sharedlora_Arepeated ×3 for the q|k|v slices) — previously only the 4-way PEFT layout and pre-merged form were handledMergedColumnParallelLinearWithLoRAwraps the base layer generically (2-equal-slice path, same as gate_up) — no layer/mem-pool/manager changes needed.Validation
Qwen/Qwen3.5-35B-A3B, 8×H200 TP2/EP8 colocate, LoRA on attention + MoE experts + shared expert + GDNin_proj/out_proj: rollout-vs-trainlogprob_abs_diff = 0.0100, zero adapter weights skippedCI States
Latest PR Test (Base): ❌ Run #28927579933
Latest PR Test (Extra): ❌ Run #28978966210
Model coverage
Also covers Qwen3.6 with no additional changes: Qwen3.6 checkpoints declare the same
Qwen3_5(Moe)ForConditionalGenerationarchitectures /qwen3_5*model_type, so they dispatch toqwen3_5.pyand pick up this LoRA support directly. Verified with a 10-step LoRA RL run onQwen/Qwen3.6-35B-A3B(full target set incl. GDNin_proj_qkvz/in_proj_ba/out_proj): rollout↔trainlogprob_abs_diff0.010 at step 0, KL ≤ 0.004, reward 0.19 → 0.47, zero adapter weights skipped.