feat(models): add Qwen3-Next-80B-A3B support - #212
Open
akushonkamen wants to merge 2 commits into
Open
Conversation
Register Qwen3NextForCausalLM in the qwen3_5_moe runtime (same GDN + MoE family: flat model.layers.* weights, per-expert gate/up/down). The work is in telling the two checkpoint dialects apart at load time and in the router, not in the runtime itself: - parse_config: probe the first shard to tell modelopt NVFP4 (two-level scales, bf16 shared_expert, bf16 attention) from compressed-tensors packed FP4; detect the pre-fused GDN in_proj layout. parse_config now takes an optional model_path; every model config gets the extra parameter. - weight loading: de-interleave the per-k-head-group qkvz/ba rows into the contiguous [q|k|v|z]/[b|a] split the GDN expects, including the per-128-row fp8 scale blocks that alias head_dim. Loading the interleaved layout as-is scrambles q/k/v/z silently -- output is fluent garbage and decode speed looks normal, so a unit test covers the permutation. - router: thread sigmoid scoring through fused_topk. Qwen3-Next scores with sigmoid and top-10, which has no triton_kernels counterpart; take the torch path for it. - bench_decode_moe: --extra-args passthrough to the spawned server. Measured on a single RTX 4090 (greedy, warmup + 3 runs, median): Qwen3-Next-80B-A3B NVFP4 offload 96.9 tok/s, hybrid 33.2, FP8 offload 40.6; Qwen3.6-35B-A3B bf16 offload 34.9. docs/models.md: list the Qwen3-Next checkpoints. Signed-off-by: akushonkamen <akushonkamen@163.com>
Original HF Qwen3-Next-80B-A3B checkpoints store each routed expert as separate gate/up/down tensors under model.layers.N.mlp.experts.E.<proj>. The dense loader binned every .mlp.experts.<int>. key as an NVFP4 offload tensor, so a bf16 checkpoint fed zero expert layers to the bank builder and died with "Missing MoE expert source layers". Only skip a per-expert tensor when it actually carries modelopt scales; plain bf16 parts are now fused gate|up, stacked per layer into [E, 2*I, H] / [E, H, I] and yielded as the whole-layer gate_up_proj / down_proj sources stream_moe_expert_sources expects. Signed-off-by: akushonkamen <akushonkamen@163.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.
feat(models): add Qwen3-Next support
Summary
This PR adds serving support for Qwen3-Next-80B-A3B (
Qwen3NextForCausalLM) on top of the existingqwen3_5_moeruntime — same GDN + MoE family, flatmodel.layers.*weights, per-expert gate/up/down. The work is in telling the two checkpoint dialects apart at load time and in the router, not in the runtime itself.What's included
Qwen3NextForCausalLMis registered inmodels/register.pyand reuses theqwen3_5_moepackage (Qwen3_5MoEForCausalLM).qwen3_5_moe/config.py):_is_ct_storage()probes the first shard to tell modelopt NVFP4 (two-level scalesweight_scale/weight_scale_2, bf16shared_expert, bf16 attention) from compressed-tensors packed FP4 (weight_packed/weight_global_scale)._gdn_split_layout()detects the pre-fused GDNin_projlayout (in_proj_qkvz/in_proj_bavs the four unfused parts)._shared_expert_quant()probes whether the MoEshared_expertis packed NVFP4 or left bf16 (modeloptignorelist excludes it; the previous unconditionaldense_quant = "nvfp4"brokeload_state_dictwith a missingweight_scaleKeyError on those checkpoints).parse_confignow takes an optionalmodel_pathso every model config gets the extra parameter; the engine passesself.model_paththrough.qwen3_5_moe/weight.py):_gdn_split_reorder()de-interleaves the per-k-head-groupqkvz/barows into the contiguous[q|k|v|z]/[b|a]split the GDN expects, including the per-128-row fp8 scale blocks that aliashead_dim. Loading the interleaved layout as-is scrambles q/k/v/z silently — output is fluent garbage and decode speed looks normal.model.layers.*(nolanguage_model.prefix, which Qwen3-Next checkpoints don't carry).iter_weightsis split into a thin reorder/dispatch shell over a new_iter_weights_flatcore, so the de-interleave runs once per loaded tensor without duplicating the shard-walk.moe/fused.py,layers/moe.py):fused_topk/_torch_fused_topkgain ascoringparameter ("softmax"default,"sigmoid"for Qwen3-Next). Sigmoid scoring has notriton_kernelscounterpart and Qwen3-Next's top-10 is not a power of 2, so both cases take the pure-torch path; the layer passesconfig.moe_scoring_functhrough.qwen3_5_moe/gdn.py,model.py): the pre-fused split layout (in_proj_split) takes the same two-GEMM path as fp8, but with a bf16in_proj_qkvzGEMM.--extra-argspassthrough to the spawned server.qwen3_5_moe/weight.py): original (un-quantized) Qwen3-Next checkpoints store each routed expert as separategate_proj/up_proj/down_projtensors per expert. The dense loader previously binned every.mlp.experts.<int>.key as an NVFP4 offload tensor, so a bf16 checkpoint fed zero expert layers to the bank builder and died with "Missing MoE expert source layers". A_Bf16ExpertPackernow fusesgate|up, stacks allnum_expertsper layer into[E, 2*I, H]/[E, H, I], and yields the whole-layerexperts.gate_up_proj/experts.down_projsources the bank builder expects — in both the serial and parallel reader paths.tests/models/test_qwen3_next_weights.pycovers thein_proj_qkvz/in_proj_bade-interleave round-trip (bf16 weights + the per-128-row fp8 scale blocks that aliashead_dim), pass-through of unrelated names, rejection of ambiguous row counts, and sigmoid-router correctness against a hand-rolled reference.What's NOT included
prometheus.NCCLWrapper→freetoken.NCCLWrapperFFI rename is a local artifact; upstream FreeToken already has the correct name).benchbwfallback path.ds_fp4machinery (separate baseline, not FreeToken).Test hardware
nvidia-smi)Checkpoints
Qwen/Qwen3-Next-80B-A3B-InstructQwen/Qwen3-Next-80B-A3B-Instruct-FP8nvidia/Qwen3-Next-80B-A3B-Instruct-NVFP4Run command
Performance
Single RTX 4090, greedy decode, warmup + 3 runs (median):
Commit breakdown
feat(models): add Qwen3-Next support— registration, dialect probing, de-interleave, sigmoid router, bench passthrough, docs, tests.feat(models): bf16 per-expert offload banks for qwen3_5_moe—_Bf16ExpertPackerfor plain-bf16 per-expert checkpoints in serial + parallel reader paths.