笔记日期: 2026-06-10 笔记作者: Zhongzhu Zhou 论文标题: KeepKV: Achieving Periodic Lossless KV Cache Compression for Efficient LLM Inference 作者: Yuxuan Tian, Zihan Wang, Yebo Peng, Aomufei Yuan, Zhiming Wang, Bairen Yi, Xin Liu, Yong Cui, Tong Yang arXiv: 2504.09936 状态 / Venue: arXiv 预印本,2025 年 4 月(v2 2025 年 11 月)
一句话总结
KeepKV 是一种新型 KV 缓存合并压缩方法,通过「选举票」(Electoral Votes)机制和「零推理扰动合并」(ZIP-Merging)在单步上实现了可证明的无损压缩,彻底解决了此前所有合并方法都存在的「注意力衰落」缺陷,并在只保留 10% KV 缓存的情况下实现了超过 2 倍推理吞吐提升。
前置知识:理解这篇论文需要什么背景
KV 缓存是什么?
基于 Transformer 的大语言模型(LLM)在生成每个新 token 时,需要将当前 query 与所有历史 token 的 key 做内积、再对 value 加权求和,这就是 自注意力机制。如果每步都从头重新计算所有历史 key 和 value,计算量随序列长度平方增长。
为了解决这个问题,LLM 推理时会把每个历史 token 的 key 和 value 向量存起来——这就是 KV 缓存。新 token 只需一次前向传播,利用缓存的 K、V 直接计算注意力,避免重复计算。
但 KV 缓存的内存代价极高。 以 LLaMA-3-70B 为例,batch size=128、上下文长度 8K 时,KV 缓存需要高达 320 GB 显存,远超单卡甚至多卡的承受上限。这就是本文要解决的核心问题。
KV 缓存压缩的三个思路
现有方法大致分三类:
-
量化(Quantization):把 KV 张量存储成低精度格式(4-bit、8-bit)。例子:KVQuant、MiKV、GEAR。与 KeepKV 正交,可以结合使用。
-
驱逐(Eviction):只保留「重要」的 KV 对,其余永久丢弃。重要性通常用注意力分数衡量。例子:H2O(历史注意力分数)、SnapKV(观察窗口内的注意力)、StreamingLLM(开头 + 最近 token)、PyramidKV(底层分配更多 cache)、AdaKV / HeadKV / DuoAttention(按注意力头分配预算)。
-
合并(Merging):不丢弃 KV 对,而是把不重要的 KV 合并进旁边的 KV,保留所有 token 的信息(压缩形式)。例子:CaM(只合并 value)、D2O(按余弦相似度加权合并)、KVMerger(聚类 + 高斯核权重合并)。
KeepKV 的核心发现:现有所有合并方法都存在一个可以数学证明的缺陷——「注意力衰落」(Attention Sag),而 KeepKV 推导出了唯一能消除这一缺陷的合并公式。
注意力计算的基本公式
在理解论文的推导之前,先把注意力计算写清楚。对于单头注意力,解码阶段第 步的输出为:
其中 是非归一化注意力分数(标量), 是归一化权重。输出是 value 向量的加权平均,权重就是注意力分数。
图1:KV 缓存压缩方法全景
graph TD
A[KV 缓存内存瓶颈] --> B[量化 Quantization]
A --> C[驱逐 Eviction]
A --> D[合并 Merging]
B --> B1["降低存储位宽(4-bit, 8-bit)
示例:KVQuant, MiKV
优点:与其他方法正交可叠加
缺点:低位精度下有质量损失"]
C --> C1["只保留重要 KV,其余永久丢弃
示例:H2O, SnapKV, StreamingLLM
优点:简单快速
缺点:不可逆信息丢失
被驱逐的 token 后续可能变重要"]
D --> D1["把不重要的 KV 合并进其他 KV
示例:CaM, D2O, KVMerger
优点:无永久信息损失
缺点:注意力衰落 → 仍有输出扰动"]
D --> D2["KeepKV:选举票 + ZIP-Merging
单步可证明无损(定理 3)
多步有上界(定理 4)"]
style D2 fill:#d4edda,stroke:#28a745
style C1 fill:#fff3cd,stroke:#856404
style D1 fill:#fff3cd,stroke:#856404
核心问题:为什么现有方法都有扰动?
驱逐方法的不可避免误差
设驱逐了 KV 对 ,剩余 对产生的新输出为:
将其用原始输出 表示,整理得:
扰动量 等于零,当且仅当 。 也就是说,只有被驱逐的 token 注意力权重恰好为零时,驱逐才是无损的——这在实践中几乎不可能。PyramidKV 等方法再聪明,也只能减小 ,无法将其归零。
更糟糕的是,论文图 2(b) 表明,在解码过程中,原本被判定为「不重要」的 token 会在后续步骤变得非常重要。驱逐是不可逆的,一旦丢弃就无法找回。
合并方法的「注意力衰落」
合并方法更聪明——把 合并进 ,形成新的 。现有方法普遍用凸组合:
其中 (D2O 用余弦相似度计算权重,KVMerger 用高斯核)。
定理 2(注意力衰落): 对于任意满足 的凸组合,合并后新 KV 对的注意力分数必然满足:
从而 ,输出扰动 。
直觉证明(Jensen 不等式): 指数函数是凸函数,因此:
且当 时不等号严格成立。两个原始 KV 合并成一个向量后,分数「缩水」了——被合并的那两个 token 本来合起来有 的影响力,现在只剩下 。这就是「注意力衰落」,是凸组合合并的数学必然,不是参数选择不当的问题。
图2:注意力衰落的几何直觉
graph LR
subgraph 合并前
E["token e: k_e → 分数 s_e"]
C["token c: k_c → 分数 s_c"]
SUM["合起来的注意力贡献 ∝ s_e + s_c"]
end
subgraph 凸组合合并后
R["merged: k_r = w_e k_e + w_c k_c"]
SCORE["s_r = exp(q·k_r/√d)"]
LOSS["根据 Jensen 不等式:
s_r < w_e·s_e + w_c·s_c ≤ s_e + s_c
→ 注意力衰落!"]
end
E --> R
C --> R
R --> SCORE
SCORE --> LOSS
style LOSS fill:#f8d7da,stroke:#842029
style SUM fill:#d4edda,stroke:#28a745
根本原因:将两个向量线性插值后再取指数,结果比先取指数再加权求和要小。单个合并后的 KV 向量,永远无法「顶替」两个原始 KV 向量合起来的注意力贡献。
KeepKV 的解法:选举票 + ZIP-Merging
思路一:选举票机制——修改注意力计算规则
既然无法用一个向量同时满足分子和分母的约束,KeepKV 就修改注意力计算规则,让每个 KV 对携带一个整数**「票数」** (初始化为 1)。新的输出公式变为:
当把 合并进 时,新 KV 的票数设为:
类比美国选举人制度:票数多的「选举人」代表更多选民。一个票数为 3 的 KV 对,在注意力计算中相当于有 3 个独立的相同 KV 对参与——这就补偿了分数缩水带来的影响力损失。
思路二:ZIP-Merging——唯一正确的合并公式
有了选举票机制,我们可以推导出让输出 精确 不变的合并公式。设合并后 ,要求分子和分母分别相等:
分母条件(保证归一化):
令 ,,则 。
分子条件(保证加权求和):
代入 ,得:
这就是 value 合并公式——以当前注意力分数为权重,对两个 value 向量做加权平均,直觉非常自然。
key 合并公式的推导: 由 ,可得:
我们不知道未来的 ,但知道 和 。利用这两个约束,用 和 的线性组合表示 ,使得等式(11)在点积意义下成立:
定理 3(单步无损): 使用公式(10)和(12)合并,并按公式(6)计算注意力,则:
这是论文最核心的贡献——从数学上严格证明了单步合并的无损性。
图3:选举票 + ZIP-Merging 操作序列
sequenceDiagram
participant Cache as KV 缓存
participant EV as 选举票 p_i
participant ZIP as ZIP-Merging
Note over Cache: 合并前:(k_e,v_e,p_e=1) 和 (k_c,v_c,p_c=2)
Cache->>ZIP: 按 cosim(k_e, k_c) 选取合并候选对
ZIP->>ZIP: 计算权重:w_e = p_e·s_e^t = 1·s_e, w_c = p_c·s_c^t = 2·s_c
ZIP->>ZIP: v_r = (w_e·v_e + w_c·v_c) / (w_e + w_c)
ZIP->>ZIP: k_r = (w_e·k_e + w_c·k_c) · ln((w_e+w_c)/(p_e+p_c))
/ (w_e·ln(s_e) + w_c·ln(s_c))
ZIP->>EV: p_r = p_e + p_c = 3
ZIP->>Cache: 用 (k_r, v_r, p_r=3) 替换原来两个 KV 对
Note over Cache: 新注意力输出:o_t = Σ p_i·s_i·v_i / Σ p_i·s_i
Note over Cache: 定理3 保证:||o_t' - o_t|| = 0 ✓
扩展到多步生成:EMA 注意力分数预测
ZIP-Merging 公式(12)需要当前步的注意力分数 和 。预填充(prefill)阶段这是可以精确获得的。但实际推理中,我们在 prefill 结束时压缩一次缓存,然后跑很多步解码——在后续步骤 ,新 query 与 不同,之前的合并是基于旧分数的,会引入误差。
为了减小这个误差,KeepKV 利用了一个关键的经验观察:注意力分数具有很强的局部性——相邻步骤之间,某个 token 的注意力分数变化平滑(图 2d)。基于此,KeepKV 用指数移动平均(EMA) 预测分数:
其中 是初始化窗口大小, 是衰减系数, 是偏差修正项(防止早期估计偏小)。
定理 4(多步误差上界): 解码步骤 的输出扰动满足:
其中 是 EMA 预测误差, 与 value 向量范数有关。只要注意力分数演化平滑, 就小,误差也就有界。
图4:多步解码流水线
flowchart TD
A["输入序列(长度 L)
运行 prefill → 计算所有 K, V"] --> B
B["将完整 KV 存入外部存储
(CPU 内存 / NVMe)"] --> C
C["将 GPU 缓存压缩到预算 B
应用 ZIP-Merging + 选举票
(在步骤 L 时单步无损)"] --> D
D["解码步骤 t+1:新 query q_{t+1}
用压缩缓存计算注意力
(多步:有界误差)"] --> E
E["EMA 更新:
S^t = α·S^{t-1} + (1-α)·s^t
为下次合并预测分数"] --> F
F{"预算超出?"}
F -- "是:再次压缩" --> G
F -- "否" --> D
G["定期从外部 KV 刷新
重新加载完整缓存,重新压缩
(将累积误差重置为 0)"] --> D
style B fill:#cce5ff,stroke:#004085
style G fill:#d4edda,stroke:#28a745
定期刷新是实现「周期性无损」的关键:将完整 KV 存在外部,每隔 步重新加载并重新压缩,可以将多步累积误差清零。刷新周期 是吞吐量与质量的权衡参数。
合并候选选择:为什么用余弦相似度?
KeepKV 用两个 key 向量间的余弦相似度来选择合并对。论文为此提供了理论依据:从多步误差上界的 Taylor 展开来看,主导项与 成正比。余弦相似度高的两个 key 在向量空间中更接近,合并后引入的 key 空间误差更小。这为 D2O 中「余弦相似度选候选」这一此前只是启发式的做法给出了理论解释。
实现中,选择候选时排除最近的 个 token(最近 token 的时序局部重要性高,不宜过早合并)。
完整算法流程
算法 1:KeepKV 推理
输入:提示词 token x_1,...,x_L;预算 B;EMA 衰减 α;窗口 w;刷新周期 T
输出:生成的 token
=== Prefill 阶段 ===
1. 计算完整 KV 缓存:K_L = X_L W_k,V_L = X_L W_v
2. 初始化所有票数:p_i = 1(i=1,...,L)
3. 将 (K_L, V_L, p_i) 存入外部存储(CPU)
4. 初始化 EMA:S^L = Σ_{k=L-w}^{L} (1-α)α^{L-k} s^k
5. 当 |缓存| > B 时循环:
a. 计算所有候选对的 cosim(k_i, k_j)
b. 选取 (e,c) = argmax cosim(k_e, k_c)(排除最近 R 个 token)
c. 计算权重:w_e = p_e·s_e^L,w_c = p_c·s_c^L
d. 合并 value:v_r = (w_e·v_e + w_c·v_c) / (w_e + w_c)
e. 合并 key:k_r = (w_e·k_e + w_c·k_c) · ln((w_e+w_c)/(p_e+p_c))
/ (w_e·ln(s_e^L) + w_c·ln(s_c^L))
f. 票数累加:p_r = p_e + p_c
g. 从缓存删除 (k_e,v_e,p_e) 和 (k_c,v_c,p_c),添加 (k_r,v_r,p_r)
=== 解码阶段 ===
6. 对每个生成步骤 t = L+1, L+2, ...:
a. 计算新 KV:k_t = x_t W_k,v_t = x_t W_v
b. 添加 (k_t, v_t, p_t=1) 到缓存
c. 计算 query:q_t = x_t W_q
d. 计算注意力输出:o_t = Σ p_i·s_i^t·v_i / Σ p_i·s_i^t
e. 生成 token;更新 EMA:S^t = α·S^{t-1} + (1-α)·s^t
f. 若 |缓存| > B:用 EMA 预测分数再次压缩(ZIP-Merging)
g. 若 (t-L) mod T == 0:[定期刷新]
- 从外部存储加载 (K_L, V_L)
- 用当前 EMA 分数重新执行 ZIP-Merging
7. 返回生成的 token
关键细节:prefill 阶段(步骤 5c)使用精确注意力分数(定理 3 严格成立);解码阶段(步骤 6f)使用 EMA 预测分数(引入有界误差)。定期刷新(步骤 6g)将累积误差清零。
图5:KeepKV 与现有方法对比
graph LR
subgraph 驱逐方法
EV_M["H2O / SnapKV / PyramidKV
不可逆信息丢失
无理论保证
实现简单"]
end
subgraph 现有合并方法
MG_M["CaM / D2O / KVMerger
无永久信息丢失 ✓
仍有注意力衰落 ✗
无理论误差界"]
end
subgraph KeepKV
KK["选举票 + ZIP-Merging
无永久信息丢失 ✓
单步无损(定理3)✓
多步有界(定理4)✓
外部存储支持定期刷新 ✓"]
end
style KK fill:#d4edda,stroke:#28a745
style EV_M fill:#f8d7da,stroke:#842029
style MG_M fill:#fff3cd,stroke:#856404
实验结果
实验设置
模型: LLaMA-3-8B-Instruct、LLaMA-3-70B-Instruct、Mistral-7B-Instruct-v0.2
基准测试:
- LongBench(14 个长上下文任务):多文档问答(HotpotQA、2WikiMQA、MuSiQue)、单文档问答(NarrativeQA、Qasper、MultiFieldQA)、摘要(GovReport、QMSum、MultiNews)、少样本学习、合成任务(段落检索)、代码补全(LCC、RepoBench-P)
- RULER(最长 128K 的长度泛化压力测试)
对比基线: Full KV(上界)、StreamingLLM、H2O、SnapKV、PyramidKV、CaM、D2O、KVMerger
KV 预算比例: 10%、20%、50%
主要结果
LongBench 结果(20% 预算,LLaMA-3-8B):
| 方法 | 问答 | 摘要 | 代码 | 平均 |
|---|---|---|---|---|
| Full KV | 47.1 | 27.3 | 66.2 | 45.8 |
| StreamingLLM | 28.4 | 18.6 | 48.3 | 30.4 |
| H2O | 35.2 | 22.1 | 53.7 | 36.4 |
| SnapKV | 40.6 | 24.8 | 59.4 | 40.7 |
| PyramidKV | 41.3 | 25.3 | 60.1 | 41.5 |
| CaM | 39.8 | 24.2 | 58.2 | 39.8 |
| D2O | 41.7 | 25.0 | 60.8 | 41.9 |
| KVMerger | 42.1 | 25.4 | 61.0 | 42.3 |
| KeepKV | 44.8 | 26.5 | 63.7 | 44.3 |
在 20% 预算下,KeepKV 填补了最优基线(KVMerger)与 Full KV 之间约 70% 的性能差距。
关键发现:
-
极端压缩(10% 预算): KeepKV 仍能保持可用的生成质量,而所有驱逐方法的质量显著崩塌。选举票机制保留了所有 token 的信息(压缩形式),即使在极低预算下也没有 token 被完全遗忘。
-
RULER 长上下文: 在 128K 上下文下,KeepKV 的退化幅度明显小于其他方法,因为 EMA 分数追踪对长范围依赖维持了更准确的重要性估计。
-
吞吐量: 在 10% 预算下,LLaMA-3-70B 的推理吞吐量比 Full KV 提升超过 2.1 倍(以 token/s 计算)——KV 大小减少直接转化为每次注意力计算的内存访问减少。
-
消融实验(LLaMA-3-8B,20% 预算):
| 变体 | LongBench 均分 |
|---|---|
| 无压缩(Full KV) | 45.8 |
| 仅 D2O 合并 | 41.9 |
| 仅选举票(无 ZIP) | 42.6 |
| 仅 ZIP(无选举票) | 43.1 |
| 选举票 + ZIP(KeepKV) | 44.3 |
两个组件各有贡献:单独的选举票部分弥补了注意力衰落;单独的 ZIP 公式在没有票数追踪的情况下无法完整应用;两者结合才实现了无损保证。
图6:不同 KV 预算下的 LongBench 性能
xychart-beta
title "LongBench 平均分 vs KV 预算比例(LLaMA-3-8B)"
x-axis ["10%", "20%", "50%", "100%"]
y-axis "分数" 25 --> 50
line [28.8, 36.4, 41.8, 45.8]
line [32.5, 41.9, 43.5, 45.8]
line [36.7, 44.3, 45.1, 45.8]
(近似值:蓝=H2O,橙=D2O,绿=KeepKV;KeepKV 的优势在低预算时最显著)
理论深入:ZIP-Merging Key 公式的推导
这里我更详细地梳理公式(12)的推导逻辑。
目标: 找到 ,使得 (即 )。
这等价于:
约束: 我们不知道未来的 query ,但有两个已知的点积关系:
构造: 令 ( 是 、 的线性组合),则:
一个自然的选择是令 ,,归一化使等式成立:
代回得 Eq.12:
这个推导的关键假设是:,即内积的线性性(这对任意 都精确成立,不是近似)。精确成立!唯一的「近似」在于多步生成时 会变化,此时用的是 EMA 预测的 。
注意事项:分母为零的边界情况
公式(21)在 时分母为零。这等价于 ,即两个分数的对数加权平均为零,也就是 与两个 key 都正交(,即点积为零)。这是一个极端边界情况,实现时需要加保护(例如加入 epsilon)。
局限性与边界条件
-
多步误差有界但不为零。 单步无损性需要精确的当前步注意力分数;在多步解码中,KeepKV 依赖 EMA 预测,引入有界误差。对于需要极精确远程依赖的任务(如「大海捞针」—— 段落中随机插入的事实检索),仍可能退化。
-
外部 KV 存储的内存代价。 定期无损压缩需要把完整 KV 存到 CPU 内存中。对于 LLaMA-3-70B 在 128K 上下文下,这可能是几十 GB。论文没有量化定期刷新的时延开销。
-
GQA / MLA 兼容性未评估。 现代架构如 LLaMA-3 用了分组查询注意力(GQA),多个 query head 共享同一 KV head。KeepKV 的理论基于标准 MHA;论文没有明确讨论 EV 和 ZIP-Merging 对 GQA 共享 KV 结构的泛化性。
-
注意力局部性假设。 ZIP-Merging 的 key 公式假设未来 query 方向与当前相似。对于有突然话题切换的任务,或一段长上下文中有截然不同段落的情况,这个假设可能失效。
-
未测试批量推理场景。 实验均为单序列推理。在生产中常见的批量服务(如 vLLM 的 continuous batching)下,不同序列有不同的上下文长度和注意力分布,票数和 EMA 状态需要按序列、按 head、按层维护,元数据开销值得关注。
批判性分析:不足与可改进之处
不好的地方和方法层面的缺陷
(a) 吞吐量声称掩盖了真实时延开销。 论文报告的 2× 吞吐量提升只测量注意力计算部分,并未将定期刷新(从 CPU 加载完整 KV 并重新压缩)的 I/O 时延计入。在 NUMA 架构或 NVLink 带宽受限的系统中,这段 I/O 可能非常显著。表现为吞吐量高但端到端时延不降。
(b) 10% 预算实验的上下文窗口较短。 论文 LongBench 实验的上下文在 4K–32K 范围,10% 预算对应 400–3200 个 token,这已经是相当宽裕的缓存了。真正困难的场景是 128K 上下文 + 10% 预算(约 800 个 token),在这种情况下 EMA 局部性假设的成立更难保证。RULER 实验虽然测了 128K,但粒度较粗。
(c) 基线对比不完整。 论文对比了 CaM、D2O、KVMerger,但没有对比 KIVI(量化方案)、GemFilter、或 InfiniGen(利用部分注意力处理长上下文),而论文声称超越「所有现有 KV 缓存压缩方法」,这个说法因遗漏上述方法而显得过于宽泛。
(d) 消融实验粒度不足。 消融只对比了「有/无选举票」和「有/无 ZIP」,但对以下最关键的超参数没有敏感性分析:EMA 衰减系数 、EMA 窗口大小 、刷新周期 ,以及排除最近 个 token 的参数 。这些参数直接影响方法质量,却缺乏系统研究。
(e) 定理 3 的适用范围在正文中表述不够清晰。 论文有时暗示「KeepKV 是无损的」,但严格来说,只有在精确当前分数可用时(prefill 阶段)单步才是无损的。在解码阶段再次压缩(缓存超出预算时),用的是 EMA 预测分数,定理 3 不严格成立。这一区分在正文中不够突出,容易让读者误解「所有步骤都无损」。
作者淡化或回避的局限
(a) 外部存储方案对系统的要求被低估。 论文提到「存入 CPU 内存」,但没有量化对不同规模模型和上下文长度的内存需求,也没有讨论当 CPU 内存也受限时的降级策略。对于实际部署来说,额外几十 GB 的 CPU 内存占用是一个显著的系统约束。
(b) 方法不完全是「训练自由的」。 EMA 衰减 和窗口 的最优值因模型架构而异。论文报告了固定值,但实际部署不同模型时可能需要重新调参,这带来了额外的开发和维护成本。
(c) 与量化方法的联合评估缺失。 论文宣称与量化正交可以结合,但没有给出实验数据。「KeepKV + 4-bit 量化」能达到什么质量水平?这对于实际部署价值最高,却被完全留白。
具体改进建议
-
提供完整时延分解。 在不同刷新周期 下报告端到端时延(不仅是吞吐量),并在评测中包含刷新 I/O 时间,使实验结论与生产环境的预期更一致。
-
在更长上下文(64K–128K)下系统评测 10% 预算场景。 这才是真正考验 EMA 局部性假设的关键场景。
-
超参数敏感性分析。 对 和 进行系统扫描,给出调参建议,降低实际使用门槛。
-
联合量化实验。 给出 KeepKV + 4-bit KVQuant 的组合基准,量化两者结合的收益,这对于极致效率场景(模型部署)非常有价值。
-
GQA/MLA 的理论扩展和实验验证。 明确推导 GQA 下选举票机制的正确性,并在 LLaMA-3(实际使用 GQA)上做针对性验证,因为这是目前最主流的开源模型架构。
-
批量推理实验。 在 vLLM 框架下的 continuous batching 场景中测量元数据开销,并与单序列场景对比,为系统集成提供数据支撑。
可复现性说明
- 代码: 写作时(v2,2025 年 11 月)尚未公开,但论文对算法的描述足以复现。
- 模型: LLaMA-3-8B/70B-Instruct(Meta)、Mistral-7B-Instruct(Mistral AI),均公开可用。
- 基准: LongBench(Hugging Face)、RULER(github.com/hsiehjackson/RULER)。
- 需要调的超参数: 预算比例 、EMA 衰减 、EMA 窗口 、刷新周期 、排除最近 token 数 。
- 实现陷阱: ZIP-Merging key 公式(12)中分母 理论上可能为零(当两个分数均为 1,即 时)——需要加保护 epsilon;另外 中 恒成立(指数函数恒正),所以对数本身不会有未定义问题。
总结与我的看法
KeepKV 对一个经典的工程问题做出了扎实的理论贡献。识别出「注意力衰落」是所有现有合并方法的共同缺陷,并用 Jensen 不等式给出严格证明——这是一个真正的洞见,而不是实验上的改进。选举票 + ZIP-Merging 的组合设计也很优雅:不是修补原有的合并公式,而是改变注意力计算规则本身,从根本上绕过了 Jensen 不等式的限制。
多步扩展不如单步那么干净——它依赖 EMA 预测的局部性假设,只能提供有界误差而非零误差——但通过定期刷新将问题分解为若干个「有保证的单步」,是一个实用且有吸引力的工程选择。
对于实际从业者,三点带走:
- 如果你现在在用 H2O/SnapKV 等驱逐方法,切换到 KeepKV 风格的合并理论上应该有明显的质量提升,且没有额外吞吐代价。
- 定期刷新需要 CPU 内存缓冲完整 KV;对于 CPU 内存也紧张的边缘设备,可以使用无刷新变体(有界误差但不需外部存储)。
- 方法与量化正交——KeepKV + 4-bit 量化是一个值得尝试的组合,论文未覆盖但应该有协同效应。
未来方向: 将理论扩展到 GQA/MLA(DeepSeek-V2 的 MLA 特别有趣);集成到 vLLM/SGLang 的连续批处理中;自适应选择刷新周期(根据实时 EMA 误差动态调整 )。
深度思考:KeepKV 与信息论的联系
从更高层次来思考 KeepKV 为何有效。KV 缓存压缩的根本问题是:保留什么信息,以何种形式保留?
驱逐方法的回答: 保留那些移除后对当前输出影响最小的 KV 对。这是贪心的、短视的——忽略了 token 的重要性会随时间变化。
朴素合并方法的回答: 把不重要的 KV 混合在一起,以免信息丢失。这在原则上更好,但具体的混合公式(凸组合)系统性地低估了合并后 KV 对的贡献(注意力衰落)。
KeepKV 的回答: 改变信息的表示方式。不试图用一个向量近似两个不同的向量,而是追踪已被融合的向量个数(选举票),再推导出唯一正确的 key 向量,使得这个 key 向量加上票数后,产生与原来两个向量完全相同的注意力输出。
类比无损压缩: 这类似于信息论中的无损压缩——无法在减少符号的同时保留每一个可能的输出,但可以改变表示方式,使得解码器(这里是注意力计算)用更少的存储项产生相同的结果,前提是解码器也相应地更新(这里是使用加权注意力公式)。
为什么现有合并方法调参也救不了
自然的问题:CaM、D2O、KVMerger 能不能通过选择更好的权重来避免注意力衰落?答案是不能,定理 2 是证明。
注意力衰落与在凸组合内如何选择 、 无关——它是两个向量的任意凸组合所产生的单个向量,对任意 query 的指数内积,都严格小于各自指数内积之和(由指数函数的严格凸性决定)。无论如何调整 和 ,都无法逃脱 Jensen 不等式。
唯一的出路是改变下游计算的方式——这正是选举票机制所做的事。
选举票的空间开销
选举票机制对每个 KV 条目增加一个整数 。对于有 个活跃条目的缓存,就是 个整数。与 KV 张量本身相比:每个条目是 维 float16 向量(key + value),即 个 float16 = 字节。对于 LLaMA-3 的头维度 ,每个条目 512 字节,而一个 int32 票数 4 字节。额外开销 ——完全可以忽略。
ZIP-Merging 本身的计算量增加也很小(主要是 log 运算),在 GPU 内存带宽瓶颈面前可以忽略不计。
与 SnapKV、H2O 的深度对比
SnapKV 和 H2O 是目前部署最广泛的驱逐方法,值得深入比较 KeepKV 在哪些地方超越了它们。
H2O(重击者预言机): 驱逐历史累积注意力分数最低的 token。核心弱点:用历史注意力分数预测未来重要性,但如图 2(b) 所示,很多被驱逐的 token 后来会变成高注意力的「重击者」。KeepKV 从不驱逐,只压缩,所以历史信息永远不会消失。
SnapKV: 在提示词末尾的观察窗口内用注意力分数决定驱逐哪些 token。这比 H2O 更适合提示词密集型长上下文任务,但仍然永久丢弃 token,且窗口外的重要 token 仍可能被错误丢弃。
KeepKV 在 10% 预算下碾压两者的原因: 在极端压缩(丢弃 90% token)时,驱逐方法只保留分数最高的 10% token,其余 90% 上下文信息永久消失。KeepKV 保留的是所有 token 的压缩表示,没有任何上下文被真正丢弃,只是编码得更紧凑。在 10% 预算下,KeepKV 的 LongBench 分数比 SnapKV 高约 8 分,正是因为合并表示保留了那些本会被驱逐的信息。
与训练方法的对比:DMC 和 Compressive Transformer
Compressive Transformer(Rae 等,2020): 使用学习到的压缩函数将过去的激活值压缩成固定大小的记忆。KeepKV 的定期刷新在概念上与之相关——两者都维护完整上下文的外部记忆。但 Compressive Transformer 需要训练压缩函数,而 KeepKV 是无训练的,使用理论推导的合并公式。
动态记忆压缩(DMC,Nawrot 等,2024): 训练模型决定何时以及如何合并 token。DMC 在某些场景下压缩率更高,但需要重新训练,无法直接用于已有预训练模型。KeepKV 是后训练方法——不需要修改模型,可以直接应用于任何现成的 LLM。
KeepKV 相对于训练方法的核心实践优势:可以立即应用于任何模型,无需微调,在标准基准上表现有竞争力或更优。
图7:与相关工作的全面对比
graph LR
subgraph 无训练方法
E1["StreamingLLM
Initial + recent tokens
最简单,质量最差"]
E2["H2O
历史注意力分数驱逐
快,但历史重要性≠未来重要性"]
E3["SnapKV
观察窗口内驱逐
对提示词重任务更好"]
E4["D2O / KVMerger
余弦相似度 / 高斯核合并
保留信息但有注意力衰落"]
E5["KeepKV
选举票 + ZIP-Merging
单步无损 + 多步有界"]
end
subgraph 需要训练的方法
T1["DMC
训练模型做合并决策
压缩率高但依赖微调"]
T2["Compressive Transformer
学习压缩函数
需要从头训练"]
end
E1 -.->|"更好"| E2
E2 -.->|"更好"| E3
E3 -.->|"更好"| E4
E4 -.->|"理论突破"| E5
style E5 fill:#d4edda,stroke:#28a745
style T1 fill:#cce5ff,stroke:#004085
style T2 fill:#cce5ff,stroke:#004085
实际部署考量
与推理框架的集成
KeepKV 可以集成到任何暴露 KV 缓存的 LLM 推理框架中。关键改动:
- KV 缓存数据结构:在每个 (key, value) 条目旁增加整数
votes字段。 - 注意力计算:修改注意力核,在 softmax 前将每个 token 的注意力 logit 加上 (等价于将非归一化分数乘以 )。
- 压缩步骤:实现 ZIP-Merging 算法(按余弦相似度选候选,计算合并 key/value/votes,更新缓存)。
- EMA 状态:为每个激活的缓存条目维护 EMA 状态 (按层、按头存储)。
候选选择的余弦相似度计算是 ,对大预算可用 LSH 或 FAISS 近邻搜索近似。
数值稳定性
ZIP-Merging key 公式的几个数值陷阱:
-
分数的对数: 就是 softmax 前的原始 logit,直接读取即可,无需先 再 。
-
分母趋近于零: 在两个 logit 均接近零时趋近于零。修复方法:若 ,回退到简单等权平均 。
-
票数增长: 在级联合并下,票数最大值约为 (原始长度除以预算)。对 32K 上下文、10% 预算,最大约 10——用 int8 完全足够。
对注意力机制的更宽视角
KeepKV 的贡献从另一个角度来看,是对注意力机制本身做了一个自然的推广。标准注意力可以写成:
这里的「1」是每个 KV 对的隐式权重——每个 KV 对在注意力中地位平等(都用 参与)。KeepKV 将这个「1」推广为 :
这实际上是一种加权注意力,权重不是人为设置的,而是由合并历史自动维护的。这个推广形式保留了标准注意力的所有性质(正则化、可微等),同时赋予了注意力机制「记忆合并历史」的能力。
从这个角度来说,KeepKV 不仅仅是一种压缩技巧,而是对注意力计算语义的一个有趣扩展:KV 对不再只代表自身对应的 token,而是代表一组被压缩进来的 token 的「代表」,票数就是代表的数量。
结论
KeepKV 是 KV 缓存压缩领域一个扎实的理论贡献。识别出「注意力衰落」是所有现有合并方法的共同缺陷,并用严格的 Jensen 不等式证明——这是真正的洞见,不是实验上的小改进。选举票 + ZIP-Merging 的组合很优雅:不是修补原来的合并公式,而是修改注意力计算规则本身,从根本上绕开了 Jensen 不等式的限制。
多步扩展不如单步干净,但定期刷新将问题分解为若干「有保证的单步」,是工程上实用且有吸引力的设计。
这篇论文对我的主要启示:在优化系统时,有时不应该只在现有约束下做近似,而应该重新审视约束本身是否可以合理放宽。KeepKV 发现的关键点是:注意力机制要求每个 KV 对地位平等(权重都是 1),这个限制是不必要的——只要下游的注意力公式也做相应修改,就可以给不同 KV 对赋予不同的「代表权重」,而这完全不破坏注意力的数学性质。这种「修改游戏规则」的思路值得在其他场景下借鉴。
延伸分析:EMA 衰减参数 的行为
EMA 衰减系数 是 KeepKV 多步扩展中最重要的超参数。理解它的行为有助于判断方法在什么场景下有效、在什么场景下会遇到困难。
高 (如 0.99): EMA 追踪注意力分数的长期平均,变化缓慢。适合注意力模式在长上下文中比较稳定的任务(如摘要任务,文档中的多处内容在整个解码过程中都相关)。不适合注意力快速转移的场景(如在长文档末尾回答一个具体问题,此时只有问题相关的 token 重要)。
低 (如 0.8): EMA 快速响应近期变化,适应哪些 token 重要的突然转变。适合动态上下文,但容易受单步异常值干扰。
偏差修正项 确保解码初期(EMA 尚未「预热」前)估计不偏小。没有修正项时, 步的 EMA 是 ,对 来说只有 ——远低于实际。有修正项则 时 EMA 等于 ,之后逐渐收敛到指数加权平均。
实用调参建议: 论文报告 、窗口 效果不错。一个合理的启发式规则:令有效记忆长度 大约为上下文长度的 10-20%。对 32K 上下文, 对应记忆长度 ,可能过短;(记忆 )可能更合适。
局部性假设为何在经验上成立
KeepKV 多步扩展的核心理论假设是注意力分数在相邻解码步之间平滑变化。这一「局部性」既有直觉支撑,也有实验验证。
直觉: token 在第 步的注意力分数反映了模型当前隐状态 对该 token 的「关注程度」。随着生成推进, 在连续变化(每次只处理一个新 token),因此点积 也连续变化——注意力分数平滑演化,而不是突然跳变。
实验支持: 论文图 2(c) 显示,某个 token 的注意力分数在相邻步的方差(蓝色点)远大于在滑动窗口内的方差(橙色点)。这说明基于窗口内预测(即 EMA 所用的)比跨窗口预测准确得多——正是 KeepKV 所依赖的局部性。
失效场景: 以下情况局部性可能不成立:
- 文档开头和结尾注意力模式截然不同的超长上下文。
- 多轮对话中话题突然切换。
- 投机解码(speculative decoding)场景,草稿模型和目标模型的 query 差异较大。
这些情况下,需要更短的 EMA 窗口或更频繁的定期刷新。
内存和计算开销分析
内存开销:
| 组件 | 每层/头大小 | 说明 |
|---|---|---|
| 票数 | 字节 (int32) | 约占 KV 大小的 0.78% |
| EMA 状态 | 字节 (fp16) | 与 K 缓存大小相同 |
| 外部完整 KV | 字节 | 存放于 CPU 内存 |
以 LLaMA-3-8B(32 层,32 头,)、、(10% 预算)为例:
- 票数: MB(可忽略)
- EMA 状态:与 K 缓存相同 = MB
- 外部完整 KV: GB(CPU 内存)
对于 LLaMA-3-70B(80 层,64 头)在 128K 上下文下,外部 KV 约需 280 GB CPU 内存——需要配置充足的服务器。
计算开销:
主要开销来自候选选择(余弦相似度计算),朴素实现是 。在 时约 1070 万次相似度计算。每 10 步触发一次压缩,每步摊下来约 107 万次——对 GPU 来说可以接受,但对大预算需要考虑 LSH 近似。
未解问题与未来方向
1. 自适应刷新调度。 当前设计使用固定刷新周期 。一个更合理的方案是实时监控 EMA 预测误差,当误差超过阈值时触发刷新——注意力模式稳定时省去不必要的 I/O,不稳定时更频繁刷新。
2. 层间协同合并。 不同层的注意力模式差异很大。协调跨层的合并决策(如只在多层都认为某 token 不重要时才合并)可能进一步提升质量,但会显著增加复杂度。
3. 与前缀缓存的结合。 现代推理框架(vLLM、SGLang)对系统提示词的 KV 做前缀缓存,跨请求复用。KeepKV 的外部 KV 存储与前缀缓存兼容——系统提示词的完整 KV 可以存一份共享,每个请求只需对动态部分维护自己的压缩 KV。
4. 与 GQA/MLA 的理论整合。 LLaMA-3 用了 GQA(多个 query head 共享一个 KV head),DeepSeek-V2/V3 用了 MLA(低秩 KV 压缩)。KeepKV 的理论基于标准 MHA,如何将选举票和 ZIP-Merging 推广到这些共享/低秩 KV 结构,是重要的开放问题。
5. 学习型合并调度策略。 ZIP-Merging 公式在给定当前 query 时是理论最优的,但何时触发合并(哪一步压缩)和选择哪对候选(余弦相似度是否最优)仍有改进空间。一个轻量级学习策略,基于历史合并质量来决策,值得探索。