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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions