[Model] Qwen4-Exp: honour per-layer FP8 for the PLE table and the MTP experts in ModelOpt mixed checkpoints - #38727
Conversation
…derive the block size ModelOptMixedPrecisionConfig had a Linear entry for `FP8_PB_WO` but no FusedMoE entry for any block-FP8 algorithm, and it hard-coded the block size to 128. `nvidia/Qwen3.8-Flash-Next-NVFP4` lists its MTP experts as block-FP8 (`FP8_BLOCK_SCALES` in hf_quant_config.json, the canonical `FP8_PB_WO` in the inline config.json copy), so those experts resolved to "unquantized": the loader cast the fp8 values to bf16 without their block scales and silently skipped `weight_scale_inv`. The server ran, but the draft was numerically wrong (MTP accept length 1.6 instead of ~3). - Treat `FP8_PB_WO` and `FP8_BLOCK_SCALES` as one block-FP8 family for both Linear and FusedMoE, dispatching FusedMoE to `Fp8MoEMethod` with the shared block-FP8 sub-config (mirrors vllm-project/vllm#55513). - Build that sub-config's block size from the checkpoint's `group_size` (default 128) and reject a MIXED_PRECISION map whose block-FP8 layers disagree on it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… experts in ModelOpt mixed checkpoints `nvidia/Qwen3.8-Flash-Next-NVFP4` is a three-precision MIXED_PRECISION checkpoint: NVFP4 routed experts, an FP8 PLE n-gram table (F8_E4M3 shards + a scalar weight_scale) and block-FP8 MTP experts. Two decisions were made before consulting the checkpoint's per-layer `quantized_layers` map and broke on it: 1. PLE table dtype. The table's storage dtype is fixed at construction and only switched to fp8 for a whole-checkpoint `fp8` quant name or an explicit `text_config.ple_embedding_dtype`. This checkpoint sets neither (RadixArk's NVFP4 repo sets the config key; nvidia's does not), so the table was built bf16, moved into pinned host memory by the default `--ple-offload-embedding`, and load_weights then raised "fp8 PLE auto-switch is unsupported with ple_offload_embedding". `_ple_table_is_fp8()` now also asks the mixed config's `_resolve_quant_algo()` for the embedding's own prefix, so the table is fp8 from the start (mirrors vllm-project/vllm#54882). 2. Draft quantization. `_mtp_quant_config` dropped the quant config for every modelopt_mixed checkpoint on the assumption that embedded MTP weights are bf16. Here `mtp.layers.0.mlp.experts` is block-FP8, so the draft's FusedMoE was built unquantized, the fp8 expert values were cast to bf16 without their scales and `weight_scale_inv` was silently skipped: the server ran and target accuracy was fine, but MTP accept length fell to ~1.6. The draft now keeps the quant config when the map lists any `mtp.` layer; every other draft module still resolves to bf16 through the same per-layer lookup. Verified on 4x B300 with the user launch command (TP4, NEXTN 3/1/4, no --quantization flag): loads, sgl-eval GSM8K (200, thinking) 0.97, accept length ~3.2-3.6, draft weights 1.53 GB (was 2.47 GB bf16). Depends on the block-FP8 FusedMoE dispatch in ModelOptMixedPrecisionConfig. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
69d28b8 to
e7dfaeb
Compare
|
We independently reproduced and isolated the PLE startup failure addressed by this PR. Configuration:
On the unpatched image, the FP8 PLE n-gram table was constructed with the wrong storage dtype and startup failed with:
We then applied only #38727’s runtime changes to
With #38727 alone, the PLE failure disappeared. SGLang detected the NVFP4 checkpoint, proceeded past PLE construction, and loaded all checkpoint shards. This directly confirms that resolving the PLE table dtype from the per-layer The run subsequently failed later, in NVFP4 expert post-processing, because the unresolved default remained:
The later failure was:
That is the separate failure addressed by #38728. Therefore, our result is:
We also tested the complete #38726/#38727/#38728 stack with the same model and TP4 shape on arm64 GB300 and GB200. In that configuration, the backend resolved to |
Motivation
nvidia/Qwen3.8-Flash-Next-NVFP4is a three-precision ModelOpt MIXED_PRECISION checkpoint: NVFP4 routed experts, an FP8 PLE n-gram table (128 F8_E4M3 shards + a scalarweight_scale), and block-FP8 MTP experts. Two decisions in the Qwen4-Exp model code were made before consulting the checkpoint's per-layerquantized_layersmap, and both break on it.RadixArk/Qwen3.8-Flash-Next-NVFP4(plain NVFP4, PLE declared viatext_config.ple_embedding_dtype, bf16 MTP) andQwen/Qwen3.8-Flash-Next-FP8(fp8) are on other code paths and are not affected.1. Startup failure. The PLE table's storage dtype is fixed at construction and only became fp8 for a whole-checkpoint
fp8quant name or an explicittext_config.ple_embedding_dtype. This checkpoint sets neither, so the table was built bf16, moved into pinned host memory by the default--ple-offload-embedding, andload_weightsthen hit2. Silent draft corruption.
_mtp_quant_configdropped the quant config for everymodelopt_mixedcheckpoint, assuming embedded MTP weights are bf16. Heremtp.layers.0.mlp.expertsis block-FP8, so the draft's FusedMoE was built unquantized: fp8 expert values were cast to bf16 without their scales andweight_scale_invwas skipped by the loader's ignore list. The server ran and target accuracy was fine, but MTP accept length fell to ~1.6.Mirrors vllm-project/vllm#54882 (PLE) and #55513 (MTP).
Modifications
models/qwen4_exp.py:_ple_table_is_fp8()additionally asks the mixed config's_resolve_quant_algo()for the embedding's own prefix (...ple.ple_embedding.ngram_embedding), so the table is fp8 from construction; the prefix is threaded throughQwen4ExpPLELayer→Qwen4ExpNGramEmbedding.models/qwen3_5_mtp.py: formodelopt_mixed, keep the quant config whenquantized_layerslists anymtp.layer. Every other draft module still resolves to bf16 through the same per-layer lookup (verified: qkv_proj, shared_expert, gate, lm_head all resolve to None).Depends on #38726 (block-FP8 FusedMoE dispatch in
ModelOptMixedPrecisionConfig). This branch is stacked on that one, so its diff also shows #38726's commit until it merges; please review only the second commit here.Test
test/registered/unit/models/test_qwen4_exp_mixed_precision.py(cpu): PLE fp8 detection from the per-layer map incl. thelanguage_model.model.prefix drift; draft keeps / drops the quant config depending onmtp.entries. Verified red on the pre-change code.--quantizationflag): loads; sgl-eval GSM8K 200 examples, thinking, max_tokens 16384: 0.97 (CI threshold 0.94), stop_rate 1.0; accept length 3.2–3.6 (was 1.6); draft weights 1.53 GB (was 2.47 GB bf16).Checklist
🤖 Generated with Claude Code
CI States
Latest PR Test (Base): ❌ Run #34413177538
Latest PR Test (Extra): ❌ Run #34413177514
Latest PR Test (AMD ROCm 10): ❌ Run #34413177487