AI Pulse

KV cache 量化实测:8-bit 基本免费,4-bit 代价随上下文增大

KV cache 量化在这个社区里已经是一个争论已久的话题,观点从“绝不量化”到“4-bit 没问题”应有尽有。由于轶事证据相当不一致,我决定通过 KLD(KL 散度)来量化 KV cache 量化对模型的影响程度。KLD 是衡量模型输出分布变化程度的一个良好指标。

我在 llama.cpp、exllamav3 和 vLLM 上测试了 35 种权重量化,每种都搭配 16-bit、8-bit 以及(在支持的情况下)4-bit 的 KV cache。这次我还重新设计了 KLD 数据集和测量方法,加入了混合的智能体工具调用(由全精度 Qwen3.8 27B 生成)以及不同领域的多种文本。当前数据集每行最长覆盖 64k token 的上下文。

TLDR;

* 8-bit KV cache 效果很好。在 llama.cpp 和 ExLlamaV3 中缓存大小减半,KLD 几乎没有差异。在 vLLM 中略有差异,但对显存不足的人来说仍然是一个很好的权衡。
* 4-bit 缓存有可测的影响。对 ~Q8 权重来说打击很大,但如果你用的是 Q3 或更低,可能还值得一试。不过仍然不推荐。
* 只有 4-bit 缓存的影响会随上下文增长。在测试的长文档中,16-32k token 处新增的 KL 比前 4k 高出约 15-30%。

> 如果你感兴趣,博客文章中有包含所有数据和结果的交互式图表和表格。

如何解读这些数字

每个量化版本都与模型的未量化 bf16 副本逐 token 进行比较。KL 散度衡量量化版本的下一个 token 概率分布偏离原始版本的程度(0 表示无变化)。Top-1 一致率是两个模型选出同一个最可能 token 的频率。

作为参照,我在 llama.cpp 和 vLLM 中都运行了未量化模型并进行比较。两者的 KL 差异为 0.0004,这基本上相当于两个(正确的)实现以不同的顺序进行浮点运算。因此,你可以放心比较这里不同引擎下量化版本的 KLD 数据。

> 注意:exllamav3 的量化版本是使用 llama.cpp 的 bf16 参考 logits 进行测量的。

权重量化

KL 散度 vs. 加载的权重大小,16-bit KV cache:

博客文章中有交互式图表和完整的 KLD 数据表格,感兴趣的话可以去看看。

vLLM 检查点仍然表现更差

与 Qwen3.6 基准测试一样,每个 vLLM 检查点都被一个更小的 GGUF 或 EXL3 量化版本击败。部分原因在于激活量化。NVIDIA 和 Unsloth 的 NVFP4 检查点将 MLP 层以 W4A4(4-bit 激活)运行,注意力以 FP8 W8A8 运行,而 Qwen 的 FP8 全程为 W8A8。我通过 vLLM 的 Marlin 内核重新运行了 NVIDIA 的 NVFP4 和 Qwen 的 FP8,这些内核使用相同的量化权重,但将激活保持在 bf16:

* NVFP4:KL 从 0.0256 降至 0.0117,降低了一半以上。
* FP8:KL 从 0.0039 降至 0.0028,减少了约 30%。即便如此,它也只是与 UD-Q5_K_XL 打平,而后者还小 8.7 GiB。

KV cache 量化

KV cache每 token64k 上下文时相比 16-bit 节省
f16 / bf1664 KiB4.00 GiB\-
8-bit:llama.cpp q8_0、EXL3 8-bit34 KiB2.13 GiB1.9 GiB
8-bit:vLLM fp8_e4m332 KiB2.00 GiB2.0 GiB
4-bit:llama.cpp q4_0、EXL3 4-bit18 KiB1.13 GiB2.9 GiB

下面的每个点代表一个搭配 8-bit 或 4-bit KV cache 的权重量化版本。点的高度表示与同样权重在 16-bit 缓存上相比的 KL 增加量。零表示无变化;点越高表示增加越大。我是在相同 token 上按 prompt 逐一计算差异的。

KV cache 惩罚 vs. 加载的权重大小:

空心标记表示 8-bit KV;实心标记表示 4-bit KV。颜色区分引擎。虚线标出两个 bf16 参考引擎之间的 KL 差异。vLLM 没有 4-bit KV cache 选项。

在 llama.cpp 和 ExLlamaV3 中,8-bit 基本上是免费的。对于每个量化版本,q8_0 和 ExLlamaV3 的 8-bit 缓存使 KL 朝任一方向的移动都小于 0.00012。有些量化版本在启用后甚至测得略微更好,这表明它处于数值噪声范围内。

vLLM 的 FP8 缓存显示出轻微的 KLD 增加。它在每个检查点上都增加了 0.0004 到 0.0006,仍然极其微小,但几乎在每个 prompt 上都会出现。这可能是由于开启 FP8 缓存会将 vLLM 的注意力后端从 FlashAttention 切换到 FlashInfer。

llama.cpp 的 4-bit 缓存增加了一个大致固定的惩罚。在测试的 GGUF 量化版本中,q4_0 增加了 +0.0016 到 +0.0027 的 KL。这一增量对更精确的权重具有更大的相对影响:

* 在 Q3 级量化版本上(KL 约 0.025-0.035),增加了 6-10%。
* 在 UD-Q4_K_XL 上增加了约 39%。
* 在 Q8_0 上增加了约 390%。KL 从 0.0007 上升到 0.0034,比使用 16-bit 缓存的 UD-Q5_K_XL 还要差。

EXL3 的 4-bit 缓存惩罚似乎取决于权重精度。在 2.0-2.5 bpw 权重下仅增加 +0.0002 到 +0.0004 的 KL,在 3.0 bpw 权重下增加约 +0.0021 到 +0.0022,在 4.0 bpw 权重下增加 +0.0026 到 +0.0028。在 6.0 和 8.0 bpw 时,惩罚达到约 +0.0032 到 +0.0033。因此,不同 EXL3 量化版本之间的增量并不恒定。

4-bit 缓存惩罚随上下文增长

数据集中的 12 篇长文档在每个位置都进行了评分,因此我可以检查 4-bit 惩罚是否取决于 token 在上下文中的深度。事实上它确实会略微增长。

位置Q8_0 权重 + q4_0 KVUD-Q5_K_XL 权重 + q4_0 KVEXL3 8.0 BPW 权重 + 4-bit KV
前 4k token+0.0035+0.0030+0.0041
4k–8k+0.0037+0.0033+0.0044
8k–16k+0.0040+0.0036+0.0047
16k–32k+0.0041+0.0038+0.0050
32k–64k(一篇文档)+0.0044+0.0038+0.0053

在 16-32k 上下文处的损害比前 4k token 高约 15-30%。相同量化版本搭配 16-bit 缓存在这些上下文长度上不受影响,因此这相当清楚地表明了 4-bit 缓存的损害。我预计 128k+ 上下文长度会更严重。

> 模型量化 KLD vs KV cache KLD:
> 与权重量化产生的 KLD 相比,KV cache 量化产生的 KLD 可能具有不同的质量权衡。轶事证据表明,在长上下文下,KV cache 量化对模型性能的损害可能比权重量化更大,因此使用 4-bit KV cache 时要保持警惕。

最终图表:权重 + 64k KV cache vs. KL 散度:

线条标出推荐的量化版本,排除 4-bit KV。菱形表示 8-bit KV;淡化的圆形和三角形表示 16-bit 和 4-bit KV。内存包括权重和 64k 上下文下的 KV cache。

方法

每个量化版本都以未量化的 bf16 参考为基准进行评分:

* GGUF 和 EXL3 量化版本以 llama.cpp 中 Bartowski 的 bf16 GGUF 为基准。
* vLLM 检查点以 vLLM 中官方的 bf16 safetensors 为基准。

KL 的计算方式与我之前的基准测试相同。在每个位置从两个模型中取前 200 个 log 概率,并计算全词表 D_KL(P_reference ‖ P_quant) 的下界。平均而言,参考模型将其 0.0036 的概率分配给了前 200 个 token 之外。KV 增量是配对的,也就是说它们是在相同 token 上按 prompt 逐一计算差异得出的。

检查点按其声明的设置运行,包括任何激活量化(除了 Unsloth 的 NVFP4 和官方 FP8 量化这两个仅权重、无激活量化的重新运行)。

―― 高票评论(帖子共 82 条讨论)――

[+41] u/MindfulMan1984:干得漂亮,结论:KV Q8_0/Q8_0 量化在只用一半内存的情况下几乎无损。

[+41] u/Dathide:只测到 64K 上下文限制了数据的实用性。很多人用的都超过这个长度。

[+13] u/BringTea_666:根据我的经验,KV cache 量化的影响被严重夸大了,人们对 4/4 或 6/4 的恐惧远超实际。

1. 只要模型发生分叉,会话运行得越长(也就是上下文越多),它就会偏离原始模型越多。
2. 上面这点只是选词不同的效应,而选词不同会导致整个逻辑链改变。逻辑链越长,上下文就越大。这与“蝴蝶效应”是同样的原理。
3. 上面这一点并不意味着逻辑链是错的,只是不同而已。
4. KV cache 量化在聊天时代的 LLM 中很重要。那个时代要求 LLM 在单次回复中产出完美代码。这种情况在 harness 时代发生了变化,模型可以犯错并纠正这些错误。

这同样适用于 Top1% 和 KL。这些衡量的不是模型质量,而是与基础模型的差异程度。

唯一重要的是模型得出正确答案的能力是否受损。模型改变句子中的一个词没有实质性差异,但把 - 改成 + 就有。

根据我运行数千个编码工作函数的经验,只要基础模型够好,4/4、6/4、6/6 或 8/8 并不会产生任何有意义的差异。

真正产生差异的地方是超低量化模型,这类模型的基础能力已经因为量化而摇摇欲坠,量化可能让模型能力从可用跨越到不可用。

这是我在自己开发推理引擎的过程中学到的。我甚至内置了针对 BF16 Qwen 3.8 的评分。

[+9] u/kevin_1994:这家伙在这个话题上有一些非常好的文章 https://anbeeld.com/articles

特别是:https://anbeeld.com/articles/kv-cache-quantization-benchmarks-for-long-context

tl;dr:q8 几乎无法区分,q5_0 / q4_1 也能应付,但在我看来,我不会用比这更低的了。

[+7] u/pseudobacon:我们经常看到建议说用 K8 和 V4,如果能测一下会很有意思。

[+8] u/Interesting-Ad689:q8_0/q4_0 呢?

[+6] u/llama-impersonator:一旦你来到 220k+ token,量化的 KV cache 就开始累积影响,而通常正是在这个位置你不希望注意力出现漏洞,这样压缩才能成功总结上下文窗口。

[+3] u/fgk55555:从个人经验来说,如果资源受限,我发现 Q5_1 KV 是 q8 和 q4 之间的一个很好的中间点。在我会注意到 q4 出问题的地方,q5_1 往往表现更好。

[+2] u/unjustifiably_angry:看到更多证据表明 8-bit kv-cache 基本上无损,这让人放心,但我仍然没见过有人在真正的长上下文(比如 256K,更不用说 512K 或 1M)下做这类测试。我尤其担心 kv-cache 量化与超出原生上下文长度的扩展相结合可能会造成叠加损害。

我不知道为什么大家的测试似乎都在 64K 或 128K 封顶。那并不是真正关键的地方,我们早就知道低深度下 8-bit kv-cache 是无害的。

[+2] u/Tormeister:请注意:

* 64k 上下文真的非常小,无法代表/无法与更长上下文(128k、256k、512k...)中累积的错误相比。
* 这里得出的结论仅适用于 Qwen3.8-27B,完全不适用于其他模型。

[+1] u/challis88ocarina:其实争论的范围一直延伸到“2-bit 没问题”,在 ~700B 参数以上时基本确实如此,仅供参考。

[+1] u/DasGamerlein:这场争论在我看来真的很有趣,因为我显然只是非常幸运。我整天跑 Q2 量化搭配 4/4 KV cache,老实说从没遇到过问题。我想唯一能让我往上升级的原因,是等我抽出时间来给引擎加入自适应 KV cache 流式处理的时候。

阅读原文
📚 相关主题 大模型量化性能测试

订阅 AI Pulse

每天 08:00 · 12:30 · 18:30 · 23:50 更新