KV缓存压缩
Transformer 自回归解码需缓存全部历史 token 的 Key-Value 向量,其内存随序列长度线性增长,在长上下文与高并发场景下成为 GPU 显存的首要瓶颈——KV 缓存压缩正是为突破这一瓶颈而生。
Vaswani et al. 在 "Attention Is All You Need" 中提出 Transformer。自回归推理时,为避免对已处理 token 重复计算注意力,每层将 Key 和 Value 向量缓存下来供后续 token 查询,这就是 KV Cache。在当时的短序列场景(几百 token)下,KV 缓存的内存开销微不足道,但它为日后的内存危机埋下了伏笔。
Child et al. (OpenAI, 2019) 提出 Sparse Transformer,用固定稀疏模式(strided + local)将注意力从 $O(n^2)$ 降至 $O(n\sqrt{n})$,首次证明"并非所有 token 对都需要计算注意力"。Wang et al. (2020) 提出 Linformer,通过低秩投影将 K、V 矩阵压缩到固定维度。Katharopoulos et al. (2020) 提出线性注意力,用核函数近似 softmax 使注意力可递推计算。这些工作建立了"注意力天然稀疏、可被压缩"的理论基础,但多数需从头训练,无法直接应用于已有大模型。
Zhang et al. (NeurIPS 2023) 提出 H2O(Heavy-Hitter Oracle),通过统计发现注意力分数高度集中在少数"重击手"token 上,据此设计动态驱逐策略——仅保留累积注意力最高的 token,其余驱逐。同年 Xiao et al. (MIT) 发现"注意力汇聚"(Attention Sink)现象:无论输入内容如何,序列开头几个 token 总获得异常高的注意力权重,这是 softmax 归一化的数学副产物。StreamingLLM 据此提出"保留初始 sink token + 滑动窗口"的极简策略,使 LLM 以恒定内存处理无限长流式输入。这两项工作的核心贡献是证明了 KV 缓存可以在不重新训练的前提下大幅压缩,开启了训练后压缩的实用化浪潮。
这一年涌现出覆盖驱逐、合并、量化、架构四个维度的方案。SnapKV (Li et al.) 通过观察窗口自动识别每个注意力头的关键 token 模式,实现自适应驱逐。PyramidKV 发现底层注意力分散需更多 token、高层注意力集中可大幅压缩,提出金字塔形逐层预算分配。KIVI (Liu et al.) 从量化角度切入,对 Key 按通道、Value 按 token 分别量化到 2-bit,将缓存压缩至 1/8。最具架构影响力的是 DeepSeek-V2 提出的 MLA(Multi-head Latent Attention),将 KV 联合压缩为低维潜在向量,从根本上重新定义了缓存结构。
最新研究(如本日论文 [9])转向端到端学习压缩:训练专用压缩模块将长上下文 KV 缓存映射为紧凑表示,同时通过与语言模型联合优化保持输出质量。这标志着从"手工启发式规则"到"数据驱动学习策略"的范式转变,且开始关注压缩本身的计算效率——压缩一个 100K 上下文不应比推理本身更慢。
标准多头注意力中,KV 缓存的总内存为 $M_{KV} = 2 \times L \times H_{KV} \times n \times d_h \times b$,其中因子 2 对应 K 和 V,$L$ 为层数,$H_{KV}$ 为 KV 头数(GQA 下远小于查询头数),$n$ 为序列长度,$d_h$ 为每头维度,$b$ 为每元素字节数。以 LLaMA-2-70B($L=80, H_{KV}=8, d_h=128$)处理 128K 上下文(FP16)为例,$M_{KV} = 2 \times 80 \times 8 \times 128000 \times 128 \times 2 \approx 40$ GB,已超过单张 A100 的一半显存。驱逐策略的核心是定义 token 重要性。H2O 将 token $i$ 的重要性定义为累积注意力分数:$s_i = \sum_{j > i} A_{j,i}$,其中 $A_{j,i} = \text{softmax}(q_j k_i^T / \sqrt{d_h})$ 是位置 $j$ 对位置 $i$ 的注意力权重。保留 $s_i$ 最大的 $m$ 个 token,压缩比 $r = m/n$。这一定义的合理性在于:累积注意力高的 token 是被后续生成反复"查阅"的信息锚点,丢弃它们会导致信息断裂。量化压缩方面,KIVI 的关键洞察是 K 和 V 的数值分布特征不同,需要不同的量化粒度。Key 参与点积 $q \cdot k$,通道间相对大小决定注意力分数,因此按通道(per-channel)量化:$\hat{k}_{:,c} = \text{round}((k_{:,c} - z_c) / s_c) \cdot s_c + z_c$。Value 参与加权求和 $\sum_i A_i v_i$,token 间相对大小决定输出贡献,因此按 token(per-token)量化:$\hat{v}_{t,:} = \text{round}((v_{t,:} - z_t) / s_t) \cdot s_t + z_t$。MLA 从架构层面压缩:将 $h_t$ 通过下投影矩阵 $W^{DKV} \in \mathbb{R}^{d \times d_c}$ 映射为潜在向量 $c_t = W^{DKV} h_t$($d_c \ll H \cdot d_h$),推理时再通过上投影恢复 $k_t = W^{UK} c_t, v_t = W^{UV} c_t$。缓存中只存 $c_t$,内存从 $O(2 H d_h)$ 降至 $O(d_c)$,DeepSeek-V2 中 $d_c = 512$ 而 $2Hd_h = 32768$,压缩比达 64 倍。
KV 缓存压缩的整体逻辑是:在自回归解码的每一步,对不断增长的 KV 缓存施加压缩操作(驱逐、合并、量化或潜在映射),使其内存占用保持在预算内,同时最大限度保留对后续生成至关重要的上下文信息。
在部署压缩策略前,需在校准数据集上分析模型的注意力分布特征。通过在每层注册 attention hook,收集注意力权重矩阵,统计三个关键特征:(a) Attention Sink 强度——序列首 token 获得的平均注意力占比,通常在 20%-60% 之间;(b) 局部性半径——最近多少个 token 覆盖了 80% 的注意力质量;(c) 层间方差——不同层的注意力集中度差异。这一步之所以必要,是因为不同模型(甚至同一模型的不同层)的注意力模式差异巨大,盲目套用统一策略会导致某些层严重退化。校准通常只需几百条样本,耗时几分钟。
根据校准结果,为每层分配 KV 缓存预算。PyramidKV 的策略最具代表性:将 $L$ 层按注意力集中度排序,底层(注意力分散)分配更多 token 预算,高层(注意力集中)分配更少,总预算满足 $\sum_{l=1}^{L} b_l = B$。具体实现中,可以用注意力熵 $H_l = -\sum_i A_i \log A_i$ 作为分散度指标,熵越高的层分配越多预算。此外,需为 Attention Sink token(通常前 4 个)在所有层预留固定位置,因为驱逐它们会导致注意力分数分布崩溃。关键设计决策:预算分配是静态(部署前确定)还是动态(根据每条输入实时调整)?静态更简单高效,动态更灵活但引入额外计算开销。
在推理过程中,每当 KV 缓存超出预算时触发压缩。三类策略可独立或组合使用:(a) 驱逐(Eviction):丢弃低重要性 token 的 KV 向量。StreamingLLM 的实现最简洁:def evict(kv_cache, new_kv, sink=4, window=1024): kv_cache.append(new_kv) if len(kv_cache) > sink + window: kv_cache = kv_cache[:sink] + kv_cache[-window:] return kv_cache H2O 则维护重要性分数的在线估计,每步更新并驱逐最低分 token。驱逐是不可逆的——一旦丢弃,信息永久丢失。(b) 合并(Merging):将相邻或相似 token 的 KV 向量加权平均。D2O (2024) 按注意力权重加权合并:$\tilde{v} = \frac{s_i v_i + s_j v_j}{s_i + s_j}$。合并比驱逐保留更多信息,但引入近似误差,且合并后的"虚拟 token"不对应真实输入位置,可能影响位置编码(RoPE)的准确性。(c) 量化(Quantization):降低数值精度。KIVI 的实现将缓存分为两个区域:最近 $r$ 个 token 保持 FP16 全精度(它们对当前生成最关键),其余 token 量化到 2-bit。量化操作本身很快(几微秒),且与驱逐正交——可以先驱逐再量化,双重压缩。
压缩后需验证输出质量。离线评估使用困惑度(PPL)、长文本检索(Needle-in-a-Haystack)、下游任务准确率等指标。在线部署中,可以监控生成 token 的 logit 熵:如果压缩后熵突然升高(模型变得"不确定"),说明压缩过于激进,需要回退到更保守的策略。端到端学习压缩方法(论文 [9] 的方向)通过将压缩模块与语言模型联合训练来自动学习最优压缩策略,避免手工调参。训练目标通常是最小化压缩前后输出分布的 KL 散度:$\mathcal{L} = D_{KL}(P_{full} \| P_{compressed})$。
KV 缓存压缩需与推理引擎的内存管理深度集成。vLLM 的 PagedAttention 将 KV 缓存组织为固定大小的物理块(block),压缩策略需在块级别操作——驱逐整个块而非单个 token,以避免内存碎片。TensorRT-LLM 原生支持 INT8/FP8 KV 缓存量化,只需在构建引擎时设置 `kv_cache_type=int8`。SGLang 的 RadixAttention 通过前缀树共享不同请求间的公共 KV 缓存,压缩策略需与共享机制兼容——不能驱逐被多个请求引用的 token。集成时的关键工程决策:(a) 压缩触发时机——每 N 步定期压缩(可预测延迟)还是缓存满时紧急压缩(延迟不可预测);(b) 与连续批处理的兼容——同一批次中不同请求可能处于不同压缩阶段;(c) 压缩操作是否可以与 GPU 计算重叠(pipeline 化)。
KV 缓存压缩直接决定了 LLM 的最大上下文长度和并发服务能力。vLLM PagedAttention、DeepSeek-V2 MLA、TensorRT-LLM INT8 KV Cache 已在工业界大规模部署。对于音视频多模态模型,视觉和音频 token 数量远超文本(一段 10 秒视频可产生数千 token),KV 缓存压缩是实现实时多模态推理的关键使能技术。随着上下文窗口从 128K 向 1M+ 扩展,该技术的重要性只会持续增长。
当前前沿包括:(1) 端到端学习压缩——用可训练模块替代手工规则,自动适应不同任务;(2) 跨层 KV 共享——不同层复用压缩后的潜在表示,进一步减少冗余;(3) 多模态差异化压缩——视觉 token 与文本 token 采用不同压缩策略;(4) 硬件协同设计——针对 FP4/FP6 等新数据类型优化量化方案。核心未解决问题:极端压缩比下如何保持长距离推理链的准确性。