Kimi-K3-DSpark — FP8 (per-channel, weight-only)
An 8-bit FP8 quantization of Inferact/Kimi-K3-DSpark,
the MLA-native DSpark draft model for Kimi-K3,
produced with qstream.
The original model card follows in full below.
Size | 4.6 GiB (down from 6.6 GiB BF16, ~69%) |
Per-position agreement
| Variant | pos 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| BF16 | 98.6 | 98.0 | 97.1 | 96.4 | 95.7 | 94.6 | 93.5 |
| FP8 | 98.1 | 96.0 | 94.5 | 92.8 | 92.0 | 90.4 | 89.3 |
| NVFP4 | 91.6 | 83.8 | 78.3 | 71.3 | 67.2 | 64.0 | 59.6 |
| MXFP4 | 89.8 | 82.0 | 76.1 | 69.0 | 65.4 | 60.7 | 57.5 |
Drift-limited acceptance ceiling
Composing the per-position sequential-rollout agreement gives an upper bound on the acceptance length that can survive numerics. This is the number that decides the format:
| Variant | boot |
heavy |
gauss |
|---|---|---|---|
| FP32 reference | 7.00 | 7.00 | 7.00 |
| BF16 (served baseline — the noise floor) | 6.25 | 6.04 | 6.13 |
| FP8 (this model) | 5.70 | 4.91 | 5.04 |
| NVFP4 | 3.29 | 2.34 | 2.25 |
| MXFP4 | 3.08 | 2.28 | 2.15 |
FP8 lands 0.4–1.1 below the BF16 floor depending on regime. Both 4-bit formats lose roughly half the block — about 3 tokens — and are disqualified by a wide margin, consistently across all three regimes.
Why FP8
A speculative draft model is not an ordinary quantization target. Acceptance length is a prefix-run-length statistic — a drafted block is accepted only up to its first mismatch — so per-token error compounds multiplicatively across all 7 positions. Error that would be invisible in a standalone LM turns into lost acceptance, and acceptance is the entire reason the draft model exists.
MXFP4 and NVFP4 builds were produced and measured alongside this one. Both land at 19–20 dB weight SNR, which is perfectly normal for 4-bit — and still not good enough here. FP8 costs 1.0 GiB more than MXFP4, on a model served beside a ~1 T-param target.
What is quantized to what
| Component | Precision | Why |
|---|---|---|
layers.*.mlp.{gate,up,down}_proj |
FP8 | 1.54 B params — the bulk of the draft GEMM work |
layers.*.self_attn.o_proj, q_b_proj |
FP8 | 0.39 B params, no fusion constraint |
context_proj |
FP8 | 0.26 B params; combines the 5 target aux hidden states |
embed_tokens |
BF16 | lookup-only, no GEMM to accelerate — and it is not a copy of the target's embedding (46% relative difference), so it carries trained signal |
markov_head.markov_w{1,2} |
BF16 | rank-256 sequential head; directly shapes the sampled token |
q_a_proj, kv_a_proj_with_mqa |
BF16 | vLLM fuses these into fused_qkv_a_proj; compressed-tensors raises if fused shards disagree on scheme, and this path writes the KV latent shared with the target's page layout |
kv_b_proj |
BF16 | consumed by MLA weight absorption; only 42 M params |
All norms, confidence_head |
BF16 | unchanged |
2.19 B of 3.56 B params quantized (61%). Everything else is bit-identical BF16.
How this was evaluated
Acceptance length cannot be measured without the ~1 T-param Kimi-K3 target in the loop, and the drafter is not a standalone LM — it consumes target hidden states from K3 layers (2, 23, 47, 71, 89). So we measure drift against a high-precision reference: how far a variant's drafted block moves from the reference's, on identical inputs. Every token where they agree is a token whose accept/reject outcome is unchanged by definition.
Three choices make this honest rather than flattering:
- The reference is FP32 weights + FP32 compute, and plain BF16 is measured as a variant. BF16 is what actually ships, so its drift is the numerical noise floor. Quoting a quantized format against BF16 (rather than against exact arithmetic) silently credits it with BF16's own error — an earlier BF16-referenced run of this same checkpoint overstated FP8 by about +0.5 tokens of ceiling.
- Only the unobservable input is shared. Each variant runs its own
context_projand its own context-KV projections, so quantizingcontext_projis measured, not excluded. Only the 5 aux hidden states — which genuinely cannot be obtained — are held common. - The Markov head is rolled out sequentially, feeding each sampled token into
markov_w1to bias the next position, as the real speculator does. Holding that bias fixed hides error compounding through tokens and understates drift.
Real inputs: GSM8K token streams (train+test) tokenized with the actual Kimi-K3 tokenizer,
the actual target lm_head, and the real drafter embedding/Markov/norm weights. The one
input that cannot be obtained — the aux hidden states — is swept over three distributions so
the result can be checked for sensitivity to it: gauss (iid normal), heavy (lognormal
per-channel scales, mimicking massive-activation structure), and boot (the drafter's own 5
layer outputs run causally over the same GSM8K tokens — real LLM hidden states with real
token correlation).
1024 blocks per regime, 7168 drafted tokens, 95% CI ±0.7–0.9 pp.
Full metric set (heavy regime — the pessimistic one)
| Variant | Weight SNR | Greedy agree | Seq rollout | 1−TV | KL mean | KL p99 | Reverse KL | JS | Hidden rel err | Confidence r | Sched flip |
|---|---|---|---|---|---|---|---|---|---|---|---|
| BF16 | exact | 98.41% | 95.20% | 99.13% | 0.00029 | 0.00142 | 0.00029 | 0.00007 | 0.84% | 1.0000 | 7.5% |
| FP8 | 31.5 dB | 96.12% | 88.35% | 97.38% | 0.00275 | 0.01315 | 0.00273 | 0.00068 | 3.12% | 0.9997 | 12.4% |
| NVFP4 | 20.4 dB | 83.80% | 60.04% | 87.79% | 0.06100 | 0.31556 | 0.06192 | 0.01490 | 14.85% | 0.9877 | 46.4% |
| MXFP4 | 19.0 dB | 82.39% | 58.93% | 86.79% | 0.07004 | 0.28368 | 0.06989 | 0.01699 | 16.86% | 0.9898 | 23.1% |
- Greedy agree — top-1 match, teacher-forced on the reference prefix (isolates per-position drift). Seq rollout — free-running, so error compounds through tokens.
- 1−TV = Σ min(p, q), the coupling probability. This is the sampling-aware analogue of
top-1 agreement and is the relevant one under
draft_sample_method: probabilistic. - KL p99 matters more than the mean here: a drafter that is usually right but occasionally confidently wrong is exactly what costs acceptance. FP8's p99 (0.013) stays within an order of magnitude of its mean; both 4-bit formats blow out past 0.28.
- Reverse KL ≈ forward KL for every variant, so no variant is hallucinating confidence in a direction the forward KL would hide.
- Confidence r / sched flip — the
confidence_headdrives resource-aware draft-length scheduling. Flip rate is measured against the per-block median confidence, which is a deliberately knife-edge threshold (half the samples sit near it by construction), so absolute values are pessimistic and only the comparison is meaningful. FP8 roughly matches BF16; NVFP4 flips nearly half of all scheduling decisions.
FP8 tracks the BF16 curve with a roughly constant offset. The 4-bit curves decay steeply — the compounding that the prefix-run-length structure punishes.
Harness: eval_dspark_quant.py (standalone BF16/FP32 reference forward — MLA + SwiGLU +
context-KV cross-attention + sequential Markov head — reimplemented from vLLM's DSpark
backbone).
Limitations
Stated plainly, because this is an unusual evaluation:
- Acceptance length itself is not measured. Everything above is drift against a high-precision reference, not agreement with Kimi-K3. Drift bounds acceptance impact; it does not equal it. In particular the ceiling is conservative — a divergence occurring past the point where the target would have rejected anyway costs nothing in practice.
- The aux hidden states are synthetic. Swept over three distributions, and the ranking is stable across all three, but none of them is Kimi-K3's actual hidden-state distribution.
- Long-context behaviour is untested. A position sweep through the YaRN regime
(
original_max_position_embeddings32768, factor 32) was planned and not completed. The base card reports acceptance holding at ~95 k tokens; whether FP8 holds equally there is unverified. - Not load-tested in vLLM. The build used here has no
K3DSparkModelin its registry. Theconfig.jsonis verified structurally — single config group, 41 ignored modules, no target/ignore overlap, fused-module constraint respected — but has not been through a real loader.
Serving with vLLM
Requires a vLLM build that registers K3DSparkModel (the public vllm/vllm-openai:kimi-k3
image). Serve Kimi-K3 per the official recipe
and point the speculative config at this repo:
--speculative-config '{"method": "dspark", "model": "kodelow/Kimi-K3-DSpark-FP8",
"num_speculative_tokens": 7, "attention_backend": "FLASHINFER_MLA",
"draft_sample_method": "probabilistic", "rejection_sample_method": "block"}'
FP8 weight-only needs no calibration and no activation scales.
How it was made
qstream-quantize \
--model_dir <Inferact/Kimi-K3-DSpark> \
--output_dir ./Kimi-K3-DSpark-FP8 \
--quant_format fp8 \
--include_layers "mlp.gate_proj" "mlp.up_proj" "mlp.down_proj" \
"self_attn.o_proj" "self_attn.q_b_proj" "context_proj" \
--device cuda --workers 1
Built with qstream @fcfe8a2. SmoothQuant is deliberately
off: this FP8 path is weight-only per-output-channel, which is already invariant to
per-column rescaling, so smoothing can only redistribute error, never remove it.
License
Inherits the Kimi-K3 license from the base model. This is a derivative (quantized) work of Inferact/Kimi-K3-DSpark.
Original model card
Model Overview
Inferact/Kimi-K3-DSpark is an MLA-native DSpark draft model that accelerates Kimi-K3 on vLLM, served through vLLM's native dspark speculative method.
The draft trains on target hidden states extracted from vLLM itself — the same engine that serves it, so the numerics it learns from are the numerics it meets at inference. TorchSpec provides the loop, streaming those hidden states from live target inference into concurrent FSDP draft training.
DSpark = a block-diffusion backbone of 5 dense layers with non-causal attention, drafting 7 tokens in a single parallel pass, a low-rank sequential Markov head supplying the intra-block dependency, and a confidence head for resource-aware scheduling. Mirroring Kimi-K3's own MLA attention means draft and target share one KV layout (a compact 576-element latent per token), so the draft's pages unify with the target's cache — KV offloading and P/D disaggregation work with no separate page format.
Performance
Peak bs=1 decode: 464 tok/s
Under low-entropy real reasoning workload — Kimi-K3 + DSpark on vLLM can achieve 464 tok/s using the public vllm/vllm-openai:kimi-k3 image on 4 × GB300 at bs=1 & tensor-parallel-size=16.
Speculator acceptance
Speculators do best on predictable, low-entropy work like the reasoning workload behind the 464 tok/s above, and worst on open-ended, high-entropy generation. So we measured 14 benchmarks spanning math, code synthesis, real-world software engineering, multi-turn chat, RAG and QA, multilingual text, creative writing and long-context generation — all with the Kimi-K3 chat template enabled and production sampling parameters, with temperature=0 reported alongside for reproducibility. Acceptance stays strong at long context too, verified on AA-LCR's ~95k-token multi-document prompts. That is the most comprehensive view we can give of how the speculator performs on real-world workloads.
Acceptance length, with 7 speculative tokens:
| benchmark | temperature=0 |
temperature=1.0, top_p=0.95 |
prompts |
|---|---|---|---|
| GSM8K | 5.64 | 5.44 | 1319 |
| HumanEval | 5.34 | 5.07 | 164 |
| MBPP | 4.44 | 4.31 | 256 |
| SPEED-Bench · coding | 4.38 | 4.22 | 80 |
| SPEED-Bench · multilingual | 4.21 | 4.10 | 80 |
| SPEED-Bench · RAG | 4.11 | 3.97 | 80 |
| MATH-500 | 3.82 | 3.77 | 500 |
| SPEED-Bench · low-entropy, 10k input | 3.72 | 3.66 | 512 |
| SWE-bench Pro | 3.35 | 3.11 | 128 |
| AA-LCR · ~95k input | 3.19 | 3.23 | 100 |
| MT-Bench | 3.14 | 3.06 | 80 |
| SPEED-Bench · QA | 3.07 | 2.98 | 80 |
| SPEED-Bench · writing | 2.79 | 2.69 | 80 |
| AIME 2026 | 2.72 | 2.64 | 30 |
| mean | 3.85 | 3.73 |
Benchmarks: GSM8K, MATH-500, AIME 2026, HumanEval, MBPP, SWE-bench Pro, MT-Bench, AA-LCR — 100 multi-document prompts of 71k–115k tokens — and six splits of NVIDIA SPEED-Bench: its throughput_16k low-entropy split at 10k-token input, plus five qualitative categories.
Training
Data — all responses regenerated on-policy by Kimi-K3 itself, so the draft learns the target's own reasoning traces and chat formatting. Prompts come from public datasets:
lightseekorg/kimi-mtp-dataset— general instruction promptsnvidia/OpenCodeInstruct— single-turn coding- A nine-category prompt pool from the NVIDIA Nemotron SFT/RL collections and
CohereLabs/aya_dataset, spanning chat, code, multilingual, RAG/QA, math, structured output and safety. Evaluation-set prompts are excluded.
Method: DSpark with block_size=7, trained on a combined CE + L1 distribution-distillation objective against the target's post-final-norm hidden state, bf16. The draft consumes Kimi-K3 auxiliary hidden states from target layers (2, 23, 47, 71, 89) of 93. Roughly two epochs in total, on GB300 nodes.
Draft architecture, block size, sequence length, and loss weights are YAML-configurable — see the TorchSpec repo.
Quick Start
Requirements
For serving Kimi-K3 itself — hardware, parallelism and engine flags — follow the official vLLM recipe: recipes.vllm.ai/moonshotai/Kimi-K3.
Enable the draft
Add to your Kimi-K3 vllm serve command:
--speculative-config '{"method": "dspark", "model": "Inferact/Kimi-K3-DSpark", "num_speculative_tokens": 7, "attention_backend": "FLASHINFER_MLA", "draft_sample_method": "probabilistic", "rejection_sample_method": "block"}'
Sampling options
vLLM offers two knobs on top of the defaults, both used in the temperature=1.0 column above:
draft_sample_method—probabilisticsamples the draft from its own distribution instead of taking its argmax. Pair it with a sampling client; usegreedywhen serving attemperature=0so the draft matches the client.rejection_sample_method—blockverifies the drafted block as a unit rather than token by token. It is a no-op under greedy decoding (attemperature=0verification reduces to a deterministic argmax match), so it only applies to the sampling configuration.
The temperature=0 column was produced with {"draft_sample_method": "greedy"} and no rejection_sample_method.
- Downloads last month
- -