Skip to content

deepseek4: NextN/MTP speculative decoding (converter + graph)#25642

Closed
bownux wants to merge 3 commits into
ggml-org:masterfrom
rogerai-fyi:dsv4-mtp-upstream
Closed

deepseek4: NextN/MTP speculative decoding (converter + graph)#25642
bownux wants to merge 3 commits into
ggml-org:masterfrom
rogerai-fyi:dsv4-mtp-upstream

Conversation

@bownux

@bownux bownux commented Jul 14, 2026

Copy link
Copy Markdown

Implements DeepSeek-V4's multi-token-prediction (NextN/MTP) head end to end for the deepseek4 arch, so V4 GGUFs can use --spec-type draft-mtp like qwen35/step35/hy_v3.

What

  • Converter (conversion/deepseek.py): keep the mtp.* tensors (currently dropped) by rekeying them as trailing layer n_layer at ingestion, so the existing FP8/MXFP4/expert-merge machinery applies unchanged. DeepSeek's e_proj/h_proj glue folds into the existing nextn.eh_proj convention via e_proj(e) + h_proj(x) = eh_proj(concat[e; x]). Writes block_count = n_layer + nextn and nextn_predict_layers, matching the step35 convention.
  • Graph (src/models/deepseek4.cpp): graph_mtp for the NextN block - enorm/hnorm -> eh_proj -> a full V4 layer (MLA attention in pure-SWA mode, 256-expert MoE, hyper-connection mixers) -> shared head. V4's hyper-connections make the drafter's hidden-state input hc_mult * n_embd (4x) wide; plumbed as n_embd_nextn() through llama-context and the speculative driver.
  • Registers DEEPSEEK4 in the MTP-context KV filter alongside STEP35/HY_V3.

Included fixes (hit while testing; can split into separate PRs on request)

  • llama-quant.cpp: pass integer tensors (I8/I16/I32/I64) through unquantized - deepseek4's tid2eid routing tables crashed llama-quantize.
  • llama-kv-cache.cpp: guard build_input_k_rot against n_embd_head_k_all == 0 (SIGFPE / infinite loop when a KV-cache filter leaves a cache empty).

Testing

On 4x RTX PRO 4500 + Threadripper 9970X, DeepSeek-V4-Flash converted with this branch (Q8/native-MXFP4 and Q3_K_M requant, both public at https://huggingface.co/rogerai-fyi/DeepSeek-V4-Flash-MTP-GGUF):

  • glue tensors verified against DeepSeek's reference MTPBlock (inference/model.py)
  • coherent output, deterministic across runs at temp 0
  • --spec-draft-n-max 1: 100% draft acceptance, 23.5 -> 30.5 tok/s (+30%) on the Q3 quant with 10 expert layers on CPU; acceptance 93%/78% at n_max 2/3 as expected
  • with experts partially in system RAM, batch-verify costs ~N x the host expert reads, so short drafts win; the speedup grows as more of the model is GPU-resident (2.2x measured on a fully-resident Qwen3.6-27B control)
  • served in production for a full day (concurrent slots, 64K ctx/slot, tool calling, long context) without errors

Draft-quality note: models converted with earlier mainline (which dropped mtp.*) simply have no NextN tensors and are unaffected; this only adds capability for newly-converted GGUFs.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RNCV8Tr1GAhQMomrZdS676

bownux and others added 3 commits July 13, 2026 17:35
- gguf-py: add NEXTN_HC_HEAD_{FN,BASE,SCALE} tensor types; register the 7
  nextn tensors in DEEPSEEK4's MODEL_TENSORS
- conversion/deepseek.py: rekey mtp.{i}.* -> layers.{n_layer+i}.* at init so
  FP8 pairing / expert merge / MXFP4 repack apply unchanged; map the glue
  tensors; concat e_proj+h_proj -> nextn.eh_proj {2*n_embd, n_embd}
  (eh_proj(concat[e;x]) == e_proj(e) + h_proj(x)); include NextN layers in
  the MXFP4 expert loop

Verified: 1,359-tensor GGUF with all 7 glue tensors + full blk.43 layer.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RNCV8Tr1GAhQMomrZdS676
First working DeepSeek-V4 MTP on llama.cpp. The NextN block is a full V4
layer (pure-SWA MLA, MoE, hyper-connections) per the reference MTPBlock:

  logits = shared_head(shared_head_norm(hc_head(V4Block(
               eh_proj(concat[enorm(embed(tok)); hnorm(h)])))))

- deepseek4: read nextn_predict_layers FIRST (n_layer() sizes hparam arrays);
  mark nextn layers SWA + compress_ratio 0; n_embd_nextn = hc_mult*n_embd
  (the MTP block exchanges the full hyper-connection state)
- loader: extend the per-layer loop to n_layer_all; load the 7 nextn glue
  tensors (eh_proj packs e_proj|h_proj via the concat identity)
- graph_mtp: qwen35-pattern draft graph reusing the trunk's hc/attention/moe
  builders at il = n_layer; t_h_nextn = raw hc state for chaining
- trunk graph: expose t_h_nextn (pre-hc_head, all tokens)
- hparams/context: n_embd_nextn() accessor; nextn buffer sizing/indexing and
  MTP batch-embd width honor the wider rows
- speculative: draft-mtp width from llama_model_n_embd_nextn (new ext API);
  verify_h row stats at trace verbosity
- kv-cache: deepseek4 MTP-context layer filter (STEP35 pattern); fix SIGFPE
  in build_input_k_rot when a layer filter selects no layers
- arch: NEXTN_HC_HEAD_{FN,BASE,SCALE} tensor types
- DSV4_MTP_DEBUG=1 env for load/graph debug logging

Validated: 44-layer MTP GGUF loads; trunk unchanged; draft-mtp generates
coherent output with 82% draft acceptance (172 drafted / 141 accepted),
deterministic across runs.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RNCV8Tr1GAhQMomrZdS676
…les)

llama-quantize aborted on GGML_TYPE_I32 tensors ('cannot dequantize/convert
tensor type i32') because the Q-mix tried to requantize them. Integer tensors
are lookup/routing tables, never weights - skip them.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RNCV8Tr1GAhQMomrZdS676
@bownux bownux requested review from a team, CISC and ggerganov as code owners July 14, 2026 00:50
@github-actions github-actions Bot added documentation Improvements or additions to documentation model Model specific conversion labels Jul 14, 2026
@ggml-gh-bot

ggml-gh-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

Hi @bownux, thanks for your contribution!

Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:

  • PR Template not respected: Please respect the template when creating a new pull request. Make sure to fill out all required sections.

  • AI-generated content: This project does not accept PRs, descriptions or commit messages that are fully or predominantly AI-generated. If you have used AI to assist you in writing code, please make sure to disclose that explicitly.


Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below.

@am17an

am17an commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

I will be adding this in the future. It requires some more changes

@am17an am17an closed this Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conversion documentation Improvements or additions to documentation model Model specific

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants