Repository navigation
Conversation
Author
|
@mmangkad could you take a look when you have a moment? Quantized DFlash drafts build their layers without the checkpoint prefixes, so the quant config's ignore list never matches and the BF16 q/k/v weights get dropped at load, ending in a device-side assert. Repro and GSM8K numbers are in the description. It would also need |
twu3202
force-pushed
the
fix-dflash-quantized-draft
branch
from
October 4, 2026 01:07
e882ca5 to
867cb10
Compare
…es and refuse tensors the draft would drop
A quantized DFlash draft is built from its quantization config by module
name and loaded by tensor name, and the two did not see the same names:
every non-Nemotron decoder layer had an empty prefix, so name-based ignore
entries never applied and the checkpoint's tensors for those layers were
dropped; fc was a plain nn.Linear that could not take packed tensors.
- A draft whose own config carries a quantization_config (or a Nemotron
draft, as before) builds its layers under layers.{i} and a quantizable
fc. Any other draft, including one that only inherits the target's
--quantization, is built as on main, so its fc stays dense.
- packed_modules_mapping on the draft class, so a shard-only ignore list
reaches the fused qkv_proj / gate_up_proj.
- load_weights refuses a tensor whose module the draft has under a
parameter name that module lacks, and a checkpoint that fills the
layers but never fc; tensors for absent modules and the by-design
extras (biases, rotary caches, activation/KV scales) stay ignored.
twu3202
force-pushed
the
fix-dflash-quantized-draft
branch
from
October 5, 2026 10:19
867cb10 to
685e9b7
Compare
This branch has not been deployed
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.
Motivation
A quantized DFlash draft is built from its quantization config by module name and loaded by tensor name, and the two did not see the same names.
DFlashDraftModelgave every decoder layer an empty prefix unless the checkpoint was a Nemotron 3.5 draft, so quantization matching saw a bareqkv_proj,o_proj,gate_up_proj,down_projfor all layers. Name-basedignoreentries such asre:.*self_attn\.q_proj$never applied, the attention projections were built as quantized layers, andload_weightsthen dropped the checkpoint's BF16q_proj/k_proj/v_projtensors without a word because the fused module had no.weightto put them in. The packed weight went into attention uninitialized. With the W8A16 draft from the recipe below, built from the officialz-lab/Qwen3.8-27B-DFlash2, main hits a device-side assert (srcIndex < srcSelectDimSizeinindexSelectSmallIndex) on the server's own warm-up request, after a startup that only loggedDFLASH fused KV materialization disabled: quantized qkv_proj is not supported for this path.The second half of the same problem is
fc: outside the Nemotron path it was a plainnn.Linear, so a checkpoint that quantizesfc(fc.weight_packed/weight_scale/weight_shape, or FP8fc.weight+weight_scale_inv) had those tensors dropped the same way andfckept its random init, so the draft would propose from a random projection of the target's hidden states. Several of the quantized DFlash2 drafts published on the Hub for Qwen3.8-27B quantizefcas well.Modifications
python/sglang/srt/models/dflash.pyonly:config.jsoncarries aquantization_config, builds every decoder layer withlayers.{i}(or{prefix}.layers.{i}), as Nemotron drafts and drafts whose class setssupports_quantization(LiLiCorr, [Spec] LiLiCorr: named head MLPs and quantized head linears #42057) already do. A quantization config then matches the same names the checkpoint uses.packed_modules_mappingon the draft model class, which the loader already injects into the quant config, so anignorelist that namesq_proj/k_proj/v_projorgate_proj/up_projapplies to the fused module. llm-compressor writes the shard names it was given, as in the recipe below. As for every other model with such a mapping, an ignore list that names a single shard now fails with the fused-layer scheme mismatch error instead of matching nothing.fcas aReplicatedLinearwith the quant config, as those drafts already do. Any other draft is built as on main, with empty layer prefixes and a densenn.Linearfc. That includes an unquantized draft that inherits the target's--quantization: with--quantization fp8its other linears are already quantized online on main, and a quantizablefcwould have become FP8 as well. The check readsconfig.jsononly, so a draft described only byhf_quant_config.jsonis also built as on main, and a quantizedfctensor in it is now refused, as described below, instead of being dropped.fc.weightshape check keeps the packed-aware form andencoder.fc.*vendor names alias tofc.*.load_weightsrefuses a checkpoint that fills the decoder layers but neverfc: a quantizablefcstarts uninitialized, and a dense one left at its random init gave unusable drafts before as well. A projector-only load, as Domino does, is unaffected.load_weightsrefuses a checkpoint tensor whose module the draft has, under a parameter name that module does not have (a dense tensor for a packed layer, or a packed tensor for a dense layer, under the draft's own or the vendorencoder.name), with the module's actual parameter names in the message. Tensors for modules the draft does not have are still ignored, and so are the ones that have no draft parameter by design:.bias, the.inv_freq/.cos_cached/.sin_cachedrotary caches, and the.input_scale/.output_scale/.k_scale/.v_scale/.kv_scaleactivation and KV-cache scales.test/registered/unit/spec/test_dflash_quantized_draft_layout.py: the layer names through the real decoder layer, the shard-only ignore list reaching the fused projection,fcquantized / ignored / dense, an unquantized draft under an inherited online FP8 config keeping main's names and densefc, packed and densefcloading (own and vendor name), the two refusals, and the tensors that must still be ignored. On main 8 of the 13 cases fail.Accuracy Tests
Unit tests (CPU):
test_dflash_quantized_draft_layout.py13 passed (8 fail on main);test_dflash_logits.py,test_dflash_domino.py,test_oot_dflash_hooks.py,test_dflash_extra_buffer_lazy.py,test_dflash_overlap_hostsync.py,model_runner_components/test_spec_aux_hidden_state.py51 passed. The branch is rebased onto 26a1377, after #42057, and these CPU tests were re-run there.The quantized draft is built from the official
z-lab/Qwen3.8-27B-DFlash2with llm-compressor's model-free PTQ: INT8 weight-only for the MLP,o_projandfc, with q/k/v, the candidate selector and the convolution kernels left BF16:The target is
Qwen/Qwen3.8-27Bwith a W4A16 body from the same tool and itslm_headleft BF16, so that this change is tested on its own.Target recipe
One RTX 6000 Ada (48 GB, TP=1),
--speculative-algorithm DFLASH --speculative-dflash-block-size 8 --disable-overlap-schedule. Accuracy is SGLang's own GSM8K eval, one request at a time so completions can be compared across runs:z-lab/Qwen3.8-27B-DFlash2(BF16)z-lab/Qwen3.8-27B-DFlash2(BF16)Accept length is the mean of the scheduler's
accept lenover every decode-log window of the run, bonus token included.With the BF16 draft, this PR's 200 completions are identical to main's. The W8A16 draft's completions match the BF16 draft's on main in 196/200. Against the target decoding on its own, the W8A16 draft matches 165/200 and the BF16 draft 166/200.
An unquantized draft that inherits
--quantizationis built exactly as on main. To check that, the target isQwen/Qwen3.8-27B-FP8served with--quantization fp8, which the BF16 draft inherits as online FP8. An earlier revision of this PR made that draft'sfcFP8 as well:Qwen/Qwen3.8-27B-FP8,--quantization fp8z-lab/Qwen3.8-27B-DFlash2(BF16)Qwen/Qwen3.8-27B-FP8,--quantization fp8z-lab/Qwen3.8-27B-DFlash2(BF16)fcin FP8Qwen/Qwen3.8-27B-FP8,--quantization fp8z-lab/Qwen3.8-27B-DFlash2(BF16)All GPU runs are on 525f140. With #40883 applied as well (measured on the earlier revision, whose quantized-draft path is the same), the same W8A16 draft also serves the target from that PR, whose
lm_headis W8A16 pack-quantized: GSM8K 0.965, accept length 6.025, and 165/200 completions matching that target decoding on its own.Speed Tests and Profiling
No change for unquantized drafts: they are built as on main. The quantized drafts above go from not serving to serving; I have no clean timing for them, since the GPU these runs used was shared with other jobs.
Checklist
CI States
Latest PR Test (Base): ❌ Run #37295821876
Latest PR Test (Extra): ❌ Run #37295821783
Latest PR Test (AMD ROCm 10): ❌ Run #37295821835