#KV Cache 压缩与驱逐技术专项强化题库(第十八批:H2O、SnapKV、StreamingLLM 与 Prefix Caching)

前面的题库已经覆盖了 KV cache 的基本概念、GQA/MQA 压缩和 PagedAttention 的分页管理,但随着上下文长度从 4K 扩展到 128K 甚至 1M,KV cache 的显存占用已经成为推理系统的头号瓶颈。这一节专门补齐 KV cache 压缩与动态驱逐 这条知识线,把"知道 KV cache 会爆显存"推进到"能系统讲清各种压缩方法的假设、机制、局限和选型"。

对于推理优化岗和算法岗,这一块是 2025-2026 年面试中极高频的追问方向。

#一、高频问题速览

编号 问题 核心考点
307 KV cache 为什么比模型权重更容易成为显存瓶颈? 线性增长、并发叠加、不可压缩性
308 H2O(Heavy Hitter Oracle)的核心假设是什么?为什么只保留 heavy hitter 就够了? 注意力稀疏性、累积分数、淘汰策略
309 SnapKV 与 H2O 的关键差异是什么?为什么说 SnapKV 更"主动"? 观察窗口、关键 KV 对识别、压缩率
310 StreamingLLM 如何实现"理论上无限长序列"?sink token 是什么? 注意力汇聚、sink tokens、滑动窗口、长度无关
311 Prefix Caching(vLLM 等)的原理是什么?如何复用已计算前缀? 前缀哈希、Page 复用、RadixAttention
312 KV cache 量化与权重量化有何本质不同?为什么 KV cache 量化更难? 动态范围、outlier、per-token 分布
313 KV cache 压缩方法的选型维度有哪些?如何为业务选最合适的方法? 压缩率、质量损失、实现复杂度、兼容性
314 多头注意力下 KV cache 的共享策略(MHA vs GQA vs MQA)如何影响压缩空间? 头数差异、缓存冗余、压缩基线
315 长上下文推理中,KV cache 的带宽瓶颈和显存瓶颈哪个先到来? HBM 容量 vs HBM 带宽、decode 阶段特征
316 如果 KV cache 压缩后模型效果下降,如何定位是"压缩过头"还是"压缩错了位置"? 消融分析、注意力图可视化、分层诊断

#二、逐题详细解答

#307. KV cache 为什么比模型权重更容易成为显存瓶颈?

#知识点

  • KV cache 与序列长度、batch size、层数、head 数成正比
  • 模型权重是常量,KV cache 随生成长度线性增长
  • 并发请求下多个请求的 KV cache 同时驻留
  • decode 阶段的 memory-bound 特性

#详细解答

KV cache 成为显存瓶颈的核心原因是它的增长维度与模型权重完全不同

模型权重的显存占用是固定的。一个 70B 模型的 FP16 权重约占 140GB,无论输入多长、生成多少 token,这个数字不变(量化后会更小)。

KV cache 的显存占用公式为:

KV_cache_size = 2 × batch_size × seq_len × num_layers × num_kv_heads × head_dim × sizeof(dtype)

其中 2 是因为 K 和 V 各一份。关键变量是 seq_len:生成越长,cache 越大;而且它是累加的——每生成一个新 token,就要在已有 cache 后面追加一对 K/V。

假设一个 32B 模型(40 层、8 个 KV head、head_dim=128、FP16),batch_size=1,seq_len=32K:

KV cache = 2 × 1 × 32768 × 40 × 8 × 128 × 2 bytes ≈ 5.4 GB

但如果 seq_len 提升到 128K:

KV cache = 2 × 1 × 131072 × 40 × 8 × 128 × 2 bytes ≈ 21.5 GB

这还只是单个请求。如果并发 8 个请求,每个都生成到 128K,KV cache 总量就是 172 GB——已经超过很多单机的 HBM 容量。

更深层的矛盾在于:decode 阶段是 memory-bound 的。每生成一个 token,GPU 的算力利用率很低,大部分时间花在从 HBM 读取 KV cache 和权重上。这意味着 KV cache 不仅吃显存,还吃带宽。

所以面试里高分回答要同时提到两个维度:

  1. 显存容量维度:KV cache 随长度和并发线性增长,权重固定;
  2. 带宽维度:decode 阶段算力利用率低,KV cache 读取成为带宽瓶颈。

#308. H2O(Heavy Hitter Oracle)的核心假设是什么?为什么只保留 heavy hitter 就够了?

#知识点

  • 注意力分布的稀疏性(sparsity)
  • 累积注意力分数(cumulative attention score)
  • Heavy Hitter:被频繁关注的 KV 对
  • 局部性假设:近期 token 更重要
  • 渐进式淘汰(eviction)策略

#详细解答

H2O 由 Zhang 等人于 2023 年提出,核心假设非常简洁:在自回归生成中,大多数历史 token 对当前 token 的注意力贡献微乎其微,真正"重要"的历史 KV 只占一小部分。

具体而言,H2O 基于两个观察:

观察一:注意力高度稀疏

在标准 attention 中,当前 token 对历史 token 的注意力权重分布通常呈现"少数几个高点 + 大量接近零"的模式。也就是说,当前 token 真正"看"的历史 token 可能只有 5-10%,其余 90% 以上的 KV 对最终输出影响极小。

观察二:重要性可以累积评估

H2O 不只看当前步的注意力分数,而是维护一个累积注意力分数(cumulative attention score):每个历史 token 的 KV 被所有后续生成 token 关注的总得分。得分高的就是 "heavy hitter",得分低的就可以被淘汰。

具体做法

  1. 设定一个缓存预算(budget),例如只保留最近 local 个 token 的 KV + 额外 heavy 个 heavy hitter 的 KV。
  2. 在生成过程中,实时计算每个历史 token 被当前 token 关注的注意力分数,并累加到该 token 的累积分数上。
  3. 当缓存满时,淘汰累积分数最低的 token(在保留的 local 窗口之外)。

为什么只保留 heavy hitter 就够了?

因为注意力机制本身就是一种"选择性读取"。如果某个历史 token 从来没有被后续 token 高概率关注过,说明它的信息要么已经被其他 token 继承,要么对当前任务根本不重要。把这些"冷数据"驱逐掉,模型输出几乎不受影响。

局限性

  • H2O 假设注意力稀疏性在长文本上依然成立,但某些任务(如全局摘要、多文档推理)可能需要真正"看全"所有 token。
  • 累积分数的计算本身需要额外计算和存储开销。
  • 对初始 prompt 中的关键信息(如指令、约束),如果被后续 token 分散注意力,可能误淘汰。

#309. SnapKV 与 H2O 的关键差异是什么?为什么说 SnapKV 更"主动"?

#知识点

  • 观察窗口(observation window)
  • 关键位置识别(key position detection)
  • 压缩策略:逐 token 淘汰 vs 提前筛选
  • 注意力模式聚合
  • 压缩率与质量权衡

#详细解答

H2O 和 SnapKV 的共同目标都是压缩 KV cache,但两者的方法论有本质区别。

H2O 是"被动观察+渐进淘汰"

  • 在生成过程中持续累积注意力分数;
  • 缓存满时淘汰累积分数最低的 token;
  • 是一种在线动态驱逐策略,不预先决定保留哪些 token。

SnapKV 是"主动观察+提前筛选"

  • 用一个观察窗口(通常是 prompt 的最后几个 token 或生成的前几个 token)来"观察"模型会关注哪些历史位置;
  • 基于观察窗口内的注意力模式,一次性识别出对后续生成最关键的历史 KV 对
  • 非关键位置的 KV 直接丢弃,关键位置的 KV 保留,后续不再做动态调整。

为什么说 SnapKV 更"主动"?

SnapKV 不是在生成过程中逐步决定淘汰谁,而是在生成开始前(或生成早期)就通过一小段观察来确定"哪些历史 token 会被后续 token 频繁关注"。这相当于:

"我先看一小段,判断模型会关注哪些位置,然后一次性决定保留策略。"

这种方法有几个优势:

  1. 决策更稳定:一次性筛选后不再变动,避免了 H2O 中频繁淘汰可能带来的抖动;
  2. 压缩率更高:SnapKV 可以达到 50% 甚至更高的压缩率而几乎不掉效果;
  3. 工程实现更简单:不需要维护复杂的累积分数和在线淘汰逻辑。

SnapKV 的具体做法

  1. 取观察窗口中的 token(如 prompt 最后 64 个 token),计算它们对所有历史 token 的注意力分数;
  2. 对每个历史 token,聚合观察窗口中所有 token 对它的注意力分数;
  3. 选择注意力分数最高的 Top-K 个历史 token 保留 KV,其余丢弃;
  4. 后续生成只使用保留的 KV。

关键差异总结

维度 H2O SnapKV
策略类型 在线渐进淘汰 离线一次性筛选
决策时机 生成过程中 生成前/早期
压缩率 中等(通常 20-30%) 更高(可达 50%+)
实现复杂度 需要维护累积分数 只需一次注意力聚合
适用场景 流式生成、动态长度 已知 prompt、批量推理

#310. StreamingLLM 如何实现"理论上无限长序列"?sink token 是什么?

#知识点

  • 注意力汇聚现象(attention sink)
  • Sink tokens(前几个 token 的特殊性)
  • 滑动窗口注意力
  • 长度无关的推理
  • 与 H2O / SnapKV 的互补性

#详细解答

StreamingLLM 由 Xiao 等人于 2023 年提出,核心发现是:在自回归 Transformer 中,模型对序列前几个 token(通常是初始的若干 token,如 BOS 或 system prompt)存在强烈的"注意力汇聚"现象。

注意力汇聚(Attention Sink)现象

研究发现,无论序列多长,模型在生成每个新 token 时,都会把一部分注意力权重"固定"地分配给序列最前面的几个 token。这些 token 被称为 sink tokens(汇点 token)。

为什么?因为在预训练时,模型学到的注意力模式里,前面几个 token(尤其是 BOS、system prompt 的开头)充当了"全局锚点"的角色。它们帮助模型维持对对话主题、任务类型、角色设定等全局信息的感知。如果把它们丢弃,模型的输出会迅速退化。

StreamingLLM 的具体做法

  1. 始终保留 sink tokens:保留序列最前面几个 token(通常 4-8 个)的 KV cache,永远不做淘汰;
  2. 滑动窗口处理其余 token:对 sink tokens 之后的 token,只保留最近 window_size 个 token 的 KV cache,更老的 token 直接丢弃;
  3. 长度无关的推理:因为只保留固定数量的 KV(sink + window),所以无论输入多长,KV cache 的显存占用都是常量。

这意味着 StreamingLLM 可以做到理论上无限长的序列处理——只要滑动窗口够大以覆盖局部上下文,同时 sink tokens 保证全局信息不丢失。

与其他方法的对比

方法 保留策略 是否长度无关 全局信息保障
H2O 累积分数高的 token 否(cache 随长度增长) 依赖分数累积
SnapKV 注意力分数高的 token 否(一次性筛选后固定) 观察窗口决定
StreamingLLM sink tokens + 滑动窗口 (固定大小) sink tokens 保障

局限性

  • 如果任务需要频繁回溯很远的特定信息(如"请总结文档第 3 章的内容"),滑动窗口可能已经把第 3 章的内容淘汰掉了;
  • sink tokens 的数量需要调参,太少可能丢失全局信息,太多浪费显存;
  • 对代码补全等需要精确长距离引用的场景,效果可能不如完整 KV cache。

#311. Prefix Caching(vLLM 等)的原理是什么?如何复用已计算前缀?

#知识点

  • 前缀复用(prefix sharing)
  • Page-based KV cache 管理
  • RadixAttention(vLLM)
  • 计算-显存权衡
  • 多请求共享前缀场景

#详细解答

Prefix Caching 解决的是另一个维度的 KV cache 问题:不是压缩单个请求的 cache,而是在多个请求之间复用公共前缀的 cache

场景:假设一个系统服务中有大量请求共享相同的 system prompt(如 "你是一个专业的编程助手..."),或者多轮对话中历史轮次不变。如果每个请求都独立计算前缀的 KV,就会造成大量重复计算和重复显存占用。

核心思想

把 KV cache 按照 token 序列的前缀组织起来。如果两个请求的 token 序列有公共前缀,那么前缀部分的 KV 只需计算一次,后续请求直接复用。

vLLM 中的 RadixAttention 实现

vLLM 的 PagedAttention 把 KV cache 组织成固定大小的 "page"(块)。RadixAttention 在此基础上增加了一个前缀树(radix tree)索引:

  1. 每个 page 有一个逻辑 token 序列标识
  2. 当新请求到来时,系统先在 radix tree 中查找其 token 序列的最长匹配前缀;
  3. 如果找到匹配前缀,直接引用已计算的 KV page,无需重新计算;
  4. 只有不匹配的后缀部分才需要新计算。

具体例子

  • 请求 A:"你是一个编程助手。请用 Python 写一个排序函数。"
  • 请求 B:"你是一个编程助手。请用 Java 写一个排序函数。"

两个请求的前缀 "你是一个编程助手。" 完全相同。使用 Prefix Caching 后:

  • 请求 A 需要完整计算前缀的 KV;
  • 请求 B 只需复用请求 A 的前缀 KV,只计算 "请用 Java 写一个排序函数。" 这部分。

工程收益

  1. 首 token 延迟大幅降低:如果前缀很长(如 2K tokens 的 system prompt),复用后可省去 prefill 阶段的大量计算;
  2. 显存节省:公共前缀的 KV 只需存一份,多个请求共享;
  3. 吞吐提升:batch 中如果有共享前缀的请求,可以更高效地组织计算。

适用场景

  • 多轮对话(每一轮共享历史对话前缀);
  • RAG 系统(多个 query 共享相同的 retrieved documents 前缀);
  • 批量评测(评测集中大量样本使用相同的 system prompt)。

局限性

  • 只有当请求间确实存在显著公共前缀时才有效,随机独立的请求无收益;
  • radix tree 的查找和维护有一定开销;
  • 需要 KV page 的引用计数管理,避免过早释放还在被引用的前缀。

#312. KV cache 量化与权重量化有何本质不同?为什么 KV cache 量化更难?

#知识点

  • 权重量化:静态、离线、per-tensor/per-channel
  • KV cache 量化:动态、在线、per-token
  • Outlier 问题
  • 动态范围差异
  • 量化粒度选择

#详细解答

权重量化KV cache 量化 虽然都叫"量化",但面对的挑战完全不同。

权重量化的特征

  1. 静态:模型权重在训练后就是固定的,可以离线统计每个 weight tensor 的 min/max,一次性确定缩放因子;
  2. 离线:量化可以在模型加载时完成,不影响推理过程;
  3. per-tensor / per-channel:通常对一个完整的权重矩阵(或每行/每列)做统一缩放;
  4. 数值分布稳定:训练后的权重通常服从较规则的分布(如高斯分布),没有极端 outlier。

KV cache 量化的特征

  1. 动态:KV cache 在推理过程中实时产生,每生成一个 token 就产生新的 K/V 向量,无法离线统计;
  2. 在线:必须在推理过程中实时决定缩放因子,不能预先计算;
  3. per-token:每个 token 的 K/V 向量分布可能差异很大(比如某些 token 是 outlier,某些是常规值);
  4. 数值分布不稳定:不同 token、不同层、不同 head 的 KV 分布差异巨大,某些位置存在强烈的 outlier。

为什么 KV cache 量化更难?

原因一:动态范围不可预测

模型权重的数值范围在训练后基本固定,但 KV cache 中每个 token 的数值范围是实时变化的。某些 token(如标点符号、特殊标记)的 K/V 值可能非常小,而某些关键内容 token 的值可能非常大。如果用全局缩放因子,小值会被淹没;如果用 per-token 缩放,存储开销又太大。

原因二:Outlier 更频繁

在 attention 计算中,某些 token 会被后续大量 token 关注,其 KV 值在传播过程中被不断放大,形成 outlier。这些 outlier 如果用低精度表示(如 int8),会损失关键信息,导致生成质量明显下降。

原因三:精度要求更高

权重量化误差会被 FFN 的后续计算在一定程度上"稀释",但 KV cache 量化误差会直接影响 attention 分数的计算。Attention 分数是 softmax 的输入,如果 KV 值被量化失真,softmax 的分布可能完全改变,导致模型"看错"上下文。

当前主流 KV cache 量化方法

方法 策略 特点
逐 token FP16->INT8 每 token 独立缩放 简单,但 outlier 处理差
KV cache FP8(H100) e4m3 / e5m2 硬件支持,但动态范围有限
混合精度 KV outlier 用 FP16,其余 INT8 精度好,但实现复杂
Layer-wise 动态缩放 每层维护独立缩放因子 平衡精度和开销

#313. KV cache 压缩方法的选型维度有哪些?如何为业务选最合适的方法?

#知识点

  • 压缩率、质量损失、延迟、吞吐
  • 业务场景特征(对话、RAG、代码、摘要)
  • 方法兼容性(是否需改模型)
  • 工程落地难度
  • 组合策略

#详细解答

选型 KV cache 压缩方法时,不能只看"压缩了多少显存",需要从六个维度系统评估。

维度一:压缩率

不同方法的压缩能力差异很大:

  • GQA/MQA:通过减少 KV head 数压缩,压缩率固定(如 GQA 从 32 head 降到 8 head,压缩 4x);
  • H2O:通常压缩 20-30%;
  • SnapKV:可达 50%+;
  • StreamingLLM:长度无关,压缩率随序列长度提升而提升;
  • KV cache 量化:通常 2x(FP16->INT8)或 4x(FP16->INT4)。

维度二:生成质量损失

压缩率越高,质量风险越大。关键是在业务任务上测

  • 对话类任务通常对局部压缩容忍度较高;
  • 代码补全对精确引用容忍度低;
  • 长文档摘要需要全局信息,对淘汰策略敏感。

维度三:延迟与吞吐影响

  • 有些压缩方法本身需要额外计算(如 H2O 的累积分数、SnapKV 的观察窗口),会增加 prefill 延迟;
  • 有些方法(如 StreamingLLM)几乎无额外开销;
  • Prefix Caching 可以显著降低共享前缀场景的首 token 延迟。

维度四:是否需要修改模型

  • 无需改模型:H2O、SnapKV、StreamingLLM、Prefix Caching(纯推理优化);
  • 需要改模型结构:GQA/MQA(需要重新训练或微调);
  • 需要改模型量化:KV cache 量化(需要校准和可能的微调)。

维度五:与现有系统的兼容性

  • vLLM 已经原生支持 PagedAttention + Prefix Caching;
  • StreamingLLM 可以和 PagedAttention 结合;
  • H2O / SnapKV 需要额外集成到推理框架中。

维度六:业务场景的上下文特征

场景 特征 推荐方法
短对话(<4K) KV cache 不大,压缩收益有限 GQA/MQA 基础压缩即可
长文档问答(>32K) 需要保留关键信息 H2O / SnapKV
流式输入(实时转录) 长度无限增长 StreamingLLM
批量 API 服务 大量共享 system prompt Prefix Caching
代码补全 精确引用关键 慎用激进压缩,可尝试 Prefix Caching
多轮对话 历史轮次共享前缀 Prefix Caching + GQA

最佳实践:组合使用

实际系统中往往不是"选一个",而是多层组合

  1. 模型层:用 GQA/MQA 减少基础 KV head 数;
  2. 系统层:用 Prefix Caching 复用公共前缀;
  3. 推理层:根据场景选择 H2O / SnapKV / StreamingLLM 做动态压缩;
  4. 量化层:对保留的 KV 做 INT8/FP8 量化进一步节省显存。

#314. 多头注意力下 KV cache 的共享策略(MHA vs GQA vs MQA)如何影响压缩空间?

#知识点

  • MHA(Multi-Head Attention):每个 query head 有独立的 K/V head
  • GQA(Grouped-Query Attention):多个 query head 共享一组 K/V head
  • MQA(Multi-Query Attention):所有 query head 共享同一组 K/V
  • 缓存大小与 KV head 数成正比
  • 压缩基线差异

#详细解答

KV cache 压缩的"基线"首先取决于模型用了哪种注意力结构。理解这三种结构的差异,是评估压缩潜力的前提。

MHA(Multi-Head Attention)

  • 每个 query head 对应独立的 K head 和 V head;
  • 如果模型有 32 个 query head,那么就有 32 个 K head 和 32 个 V head;
  • KV cache 最大,但注意力表达能力最强(每个 head 独立看上下文)。

GQA(Grouped-Query Attention)

  • 把 query head 分成若干组,每组共享一组 K/V head;
  • 例如 32 个 query head 分成 8 组,每组 4 个 query head 共享 1 个 K head 和 1 个 V head;
  • KV head 数从 32 降到 8,KV cache 减少 4x。

MQA(Multi-Query Attention)

  • 所有 query head 共享同一组 K/V head;
  • 32 个 query head 共享 1 个 K head 和 1 个 V head;
  • KV cache 最小(相比 MHA 减少 32x),但表达能力损失最大。

如何影响压缩空间?

MHA 模型的 KV cache 最大,因此压缩空间也最大。但要注意:

  1. MHA 的压缩不能损害多头独立性:如果在 MHA 上做 H2O/SnapKV 压缩,需要确保不同 head 的淘汰策略不会过度损害 head 间的表达分工。
  2. GQA 已经做了"结构压缩":GQA 的 KV cache 本身已经比 MHA 小很多,再叠加 H2O/SnapKV 的压缩,边际收益可能递减。
  3. MQA 的缓存已经很小:MQA 模型(如 PaLM、Falcon)的 KV cache 本身就已经被大幅压缩,进一步做 token 级压缩的空间有限。

面试中常被追问的问题

  • "为什么 GQA 是工程上的最佳折中?"
    • 答:MQA 压缩太狠,质量下降明显;MHA 缓存太大;GQA 在 4x 压缩和可接受质量损失之间找到了工程平衡点。LLaMA 2/3、Qwen 等主流模型都选择了 GQA。
  • "如果已经在用 GQA,还有必要做 H2O/SnapKV 吗?"
    • 答:有必要。GQA 压缩的是 head 维度,H2O/SnapKV 压缩的是序列长度维度,两者正交。GQA 后的 KV cache 依然随长度线性增长,长序列下仍需要序列维度的压缩。

#315. 长上下文推理中,KV cache 的带宽瓶颈和显存瓶颈哪个先到来?

#知识点

  • HBM 容量 vs HBM 带宽
  • Prefill 阶段 vs Decode 阶段的不同瓶颈
  • 算力利用率(arithmetic intensity)
  • Roofline 模型视角
  • Batch size 的影响

#详细解答

这个问题没有一个固定答案,取决于推理阶段和并发配置。需要用 Roofline 模型的思路来分析。

Prefill 阶段(处理输入 prompt)

  • 特点是计算密集:一次性处理整个 prompt,矩阵乘法规模大,算力利用率高;
  • 瓶颈通常是 GPU 算力(FLOPs),而不是显存或带宽;
  • KV cache 在此阶段被"写入",但读取压力不大。

Decode 阶段(逐个 token 生成)

  • 特点是内存带宽密集:每步只处理一个新 token,矩阵乘法规模小,大部分时间花在从 HBM 读取 KV cache 和权重;
  • 瓶颈通常是 HBM 带宽
  • KV cache 在此阶段被"读取",而且每步都要读全部历史 KV。

显存瓶颈 vs 带宽瓶颈的顺序

场景 先到的瓶颈 原因
单请求、短序列(<8K) 通常都不是瓶颈 算力足够,cache 很小
单请求、长序列(>64K) 显存容量先爆 KV cache 超出 HBM
多并发、中等序列 显存容量先爆 多个 cache 叠加
单请求、超长序列但显存放得下 带宽先饱和 每步读取大量 KV,GPU 算力利用率极低
Batch size 极大 显存容量先爆 batch 维度放大 cache

关键洞察

  1. 显存容量瓶颈通常在"能不能放下"这个门槛上出现。一旦超过 HBM 容量,推理直接失败(OOM)。
  2. 带宽瓶颈通常在"已经能放下但很慢"的场景出现。即使显存够,如果每步读取的 KV 太多,decode 速度会急剧下降。
  3. Batch size 是显存瓶颈的放大器:batch_size=1 时 128K 序列可能刚好放下;batch_size=8 时同样的序列直接 OOM。

工程判断方法

  • 如果推理报 OOM,先到的瓶颈是显存容量——需要压缩 KV cache 或减少 batch size;
  • 如果推理能跑但 decode 速度极慢(如每 token >1s),先到的瓶颈是带宽——需要减少 KV 读取量(压缩、GQA、StreamingLLM)或提升带宽利用率(量化降低数据量)。

#316. 如果 KV cache 压缩后模型效果下降,如何定位是"压缩过头"还是"压缩错了位置"?

#知识点

  • 消融实验设计
  • 注意力图可视化
  • 分层诊断(layer-wise)
  • Head-wise 分析
  • 压缩粒度控制

#详细解答

KV cache 压缩导致效果下降时,需要系统化诊断,而不是盲目调参。

第一步:确认下降是"压缩"导致的

  • 先用无压缩版本跑相同输入,确认 baseline 效果正常;
  • 排除其他因素(如量化、温度参数、随机性)的干扰。

第二步:分层诊断——是哪一层/哪一类 token 出了问题

诊断手段 做法 能发现什么
逐层压缩对比 只在某些层做压缩,其他层保留完整 KV 如果是某几层对压缩特别敏感,说明这些层负责的关键信息被压缩掉了
按 token 类型分析 分别测试 system prompt、user query、retrieved documents、model response 的压缩敏感度 如果是 retrieved documents 被压缩后效果下降,说明 RAG 场景需要更保守的文档 KV 保留策略
按序列位置分析 分别测试压缩前半段、后半段、中间段 如果压缩后半段(最新信息)导致效果下降,说明滑动窗口设太小了

第三步:注意力图可视化

  • 提取压缩前后的 attention map 对比;
  • 如果发现某些关键 token(如问题中的实体词、RAG 中的证据句)在压缩后的 attention 分数大幅下降,说明压缩错了位置——这些 token 的 KV 不该被淘汰;
  • 如果 attention map 整体变"模糊"(熵增加),说明压缩过头了——保留的 KV 不足以支撑精确的注意力分布。

第四步:调整策略

根因 解决方案
压缩错了位置 改用更智能的保留策略(如 SnapKV 替代随机淘汰)、增加 protected tokens(如 BOS、system prompt、检索结果)
压缩过头了 增加保留预算(budget)、减少压缩率、对某些层禁用压缩
某些层特别敏感 对敏感层保留完整 KV,只压缩其他层
某些 head 特别敏感 对敏感 head 保留完整 KV(MHA 场景)

第五步:定量评估

  • 在压缩后的模型上跑标准评测(perplexity、下游任务 accuracy);
  • 比较不同压缩率下的"压缩率-质量"曲线,找到"拐点"(sweet spot);
  • 记录不同场景(对话、RAG、代码)的最佳压缩配置。

面试中的高分表达

不要只说"压缩后效果差了我就减少压缩率",而是要展示结构化诊断思路

  1. 先确认因果(确实是压缩导致);
  2. 再定位层次(哪层、哪类 token、哪个位置);
  3. 然后分析注意力图验证假设;
  4. 最后针对性调整策略,而不是盲目全局放松。