Skip to content

DeepSeek-V4: quantized K-cache (--cache-type-k q8_0) produces garbage on all backends — attention rotation forces the model off its sparse attention paths #25382

Description

@Mermiges

Summary

DeepSeek-V4(-Flash) with a quantized K-cache (--cache-type-k q8_0) produces confident gibberish on every backend (CPU and CUDA verified identically) while the server stays healthy — no error, no abort. With --cache-type-k f16 output is correct. Root-caused to the attention-rotation (Hadamard incoherence) path forcing the model off its sparse attention implementation.

Minimal reproduction

Any DeepSeek-V4-Flash GGUF (tested: two independently sha256-verified unsloth quants, UD-Q4_K_XL and Q4_K_M-XL):

llama-server -m DeepSeek-V4-Flash-UD-Q4_K_XL-00001-of-00005.gguf -fa on --cache-type-k q8_0 --cache-type-v q8_0
# CPU-only (-ngl 0) is sufficient to reproduce
curl -s http://127.0.0.1:8080/completion -d '{"prompt":"The capital of France is","n_predict":12,"temperature":0}'

Result: " newcom wrt wrtêu newcom dialog大有lean…" (identical signature on CPU and 8× V100 CUDA).
With --cache-type-k f16 (V may stay q8_0): " Paris." — correct.

Root cause

  1. A quantized K/V cache enables the Hadamard incoherence rotation (attn_rot_k/attn_rot_v), allocating self_k_rot.
  2. DeepSeek-V4's attention dispatch (models/deepseek4.cpp, build_attention) requires self_k_rot == nullptr to take its designed sparse CSA/HCA/lightning-indexer paths; a non-null self_k_rot diverts every affected layer to build_raw_attention.
  3. build_raw_attention applies the rotation with a plain ggml_mul_mat instead of the block-reshaping ggml_mul_mat_aux the standard path uses, and never un-rotates the MLA V-is-a-view-of-K output before the v_mla up-projection (compare build_attn's cur = ggml_mul_mat_aux(cur, v_rot)).

Graph-level, therefore backend-independent — which matches the identical CPU/CUDA corruption. f16-K never enables the rotation, hence unaffected.

Isolation proof (the kernels are fine)

test-backend-ops cases added at hsk=576/hsv=512 (V as a sub-view of K) with q8_0: FLASH_ATTN_EXT 56/56 PASS and SET_ROWS (quantize-on-write) 2/2 PASS, CUDA-vs-CPU — the FA read kernels and the K-cache write kernel are numerically correct in isolation. The defect is purely the rotation path composition in the live graph.

Additional discriminator: disabling all rotations at runtime (patch adding an env override, incl. the forced-indexer rotation) with q8_0 K/V → coherent on CPU.

Live verification of the minimal fix

Disabling the KV Hadamard rotation for LLM_ARCH_DEEPSEEK4 (so quantized K flows through the model's designed sparse attention as plain q8_0) fixes it — full 4-cell K/V ∈ {q8_0,f16}² matrix coherent at temp 0, CPU and CUDA:

K V before after
q8_0 q8_0 garbage " Paris."
q8_0 f16 garbage " Paris."
f16 q8_0 correct correct (unchanged)
f16 f16 correct correct (unchanged)

Suggested fixes (in increasing scope)

a. Disable the KV rotation for DEEPSEEK4 until the sparse paths support it (minimal; what our branch does).
b. Make build_csa_lid_attention / build_hca_attention rotation-aware (apply + undo) instead of asserting self_k_rot == nullptr.
c. Fix build_raw_attention to use ggml_mul_mat_aux and to un-rotate the V output for the MLA V-is-K-view case.

(We have (b)/(c) in progress as well and can PR whichever direction maintainers prefer.)

Provenance

Happy to open a PR for option (a) immediately or (b)/(c) once validated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions