笔记日期: 2026-08-05 作者: Zhongzhu Zhou 阅读论文: AnchorKV: Anchor-Residual KV Cache Compression 论文作者: Malik Khalaf, Yara Shamshoum, Nitzan Hodos, Yuval Sieradzki, Assaf Schuster(以色列理工学院 Technion) arXiv: 2608.02901 状态: Preprint(cs.LG),2026 年 8 月
1. 为什么读这篇论文,它到底在解决什么问题
先给一个让人不太舒服的数字:在 128K 上下文长度下,一个 Llama-3.1-8B 请求的 KV(key-value)缓存在 bf16 精度下要占 16 GiB —— 几乎和模型本身的权重一样大。注意,这不是模型,这是单个请求的对话状态。上下文长度翻倍,缓存也翻倍;再加上并发用户数,缓存(而不是模型)就成了决定一块 GPU 能同时服务多少请求、每个请求推进速度有多快的那个瓶颈。这是本文出发点的核心事实:在长上下文场景下,真正卡脖子的往往是”每一步解码要读取整块缓存所需的内存带宽”和”要同时容纳多少并发请求所需的内存容量”,而不是算力。
这个领域给出的答案,大体分成两大阵营,本文对”为什么这两个阵营都不够好”的论述是它动机部分最干净的地方。
阵营一:驱逐(Eviction)。 给缓存里的每个 token 打个重要性分数(通常用 prompt 末尾一小段”观察查询窗口”对它的注意力大小来衡量),保留分数最高的一个子集,永久丢弃剩下的。这是文献里的主流做法——SnapKV、PyramidKV、H2O、StreamingLLM 的”sink + recency”模式都属于这一类。它受欢迎的原因很直接:被丢弃的 token 不花一个字节,理论上压缩比不封顶,只保留 1% 的 token 差不多就能拿到 100 倍。问题在哪?论文把这一点说得很直白,并在第 4.2 节用硬数据佐证:打分发生在”将来到底哪些查询需要用到这个 token”之前。prompt 早期观察窗口没看重的某个 token,可能恰恰是后面某个解码查询迫切需要的——一旦被驱逐,就再也拿不回来了。论文自己的逐任务分解(下面复现的 Figure 3)显示,这绝不是罕见的边缘情况:在需要”从一堆干扰项中检索出目标”或”聚合整段上下文信息”的 RULER 任务上,驱逐类基线的准确率会掉到未压缩分数的 16% 以下。这不是小幅退化,是近乎彻底的失败——而这类任务恰恰是长上下文服务存在的意义所在。
阵营二:量化(Quantization)。 保留每一个 token——没有任何 token 是不可达的——但把每个 token 用更少的比特存储(旋转类的 INT2/INT4 方案、按通道感知异常值的量化器等等)。这完全避开了驱逐的不可逆问题。它的缺点不是”广度”上的,而是”深度”上的:把比特宽度压得足够低才能拿到真正的压缩收益,而这时准确率会掉,因为每一个 token 的表示现在都被系统性地变粗糙了,不只是不重要的那些。在论文自己的对比中,最强的近期量化基线(TurboQuant,固定 3.5 比特/值)紧跟未压缩缓存,但压缩比封顶在 5 倍附近——比 AnchorKV 想要达到的量级差了整整一个数量级。
于是你有两个阵营,失败的结构性原因正好相反:驱逐无上限但脆弱(猜错一次,一个被需要的 token 就再也找不回来了);量化安全但浅(每个 token 都要交同样的质量税,这个税封住了能压多深)。AnchorKV 的核心想法是:能不能在不丢弃任何 token 的前提下,拿到驱逐才有的激进压缩比?
一句话预览机制:每个注意力头挑一小部分 token 精确存储(锚点,anchor)。把其他每个 token 表示成离它最近的锚点的一个缩放版本——具体就是一个整数索引加一个浮点标量,几个字节,而不是一整个 维向量。剩下的字节预算,花在专门估计出”锚点近似会最大程度损害模型实际输出”的那些具体 token 的量化残差上——注意,不是原始注意力最高的 token,也不是残差数值绝对值最大的 token,而是”如果丢掉它的残差,最终注意力输出会偏移最多”的 token。在 20 倍压缩目标下,这在三个模型规模上都保留了 93%–99% 的未压缩准确率,而每个驱逐基线在一半这个压缩比(10 倍)下的分数都更低。如果你关心 LLM 推理成本、长上下文服务吞吐、或者 KV 缓存工程本身,这篇论文提供了一个和现有两大家族真正不同的设计点,而不是对某一家族的小修小补——支撑它为什么能做到这一点的推导,值得仔细过一遍。
前置知识:读正文前需要知道什么
如果你对自注意力、KV 缓存机制、RoPE,以及 KV 缓存压缩里”驱逐 vs 量化”的常规词汇已经很熟悉,可以直接跳到第 2 节。否则先看这一节。
KV 缓存,具体说是什么。 自回归解码时,Transformer 每生成一个新 token 都要用它的查询向量去和所有历史位置的 key 做注意力计算。如果每一步都重新计算整个前缀的 key、value,代价会高得无法接受,所以模型会把每个历史位置 、每一层、每个注意力头算出来的 key 向量 和 value 向量 缓存下来。对于长度为 的序列、 个 KV 头、每头维度 、 层的模型,用 bf16 存储全部缓存需要 字节(系数 4 来自 key 和 value 各占 2 字节)。这个量随上下文长度线性增长,也随并发请求数线性增长——这正是为什么在长上下文场景下它会先于算力变成瓶颈。
注意力机制,用本文的记号简单过一遍。 对解码时的单个查询向量 ,在某一个头上,针对已缓存的 key 和 value :
就是流入该层后续计算的注意力输出。几乎所有 KV 缓存压缩方法本质上都是在用某种更便宜的 去近似真实的 ,而”这个压缩方法到底行不行”这个问题,归根结底就是:重建后的输出 离真实的 偏了多远?
RoPE(旋转位置编码)。 现代 LLM(Llama、Mistral、Qwen 等)不是把位置信息加到 token 嵌入上,而是在做点积之前,对 key(和 query)向量施加一个与位置相关的旋转 :注意力实际使用的 key 是 ,不是投影矩阵直接输出的原始 。这个细节对 AnchorKV 非常关键——第 3 节会看到,论文对”在流水线的哪个环节应用压缩、相对于这个旋转来说”做了一个具体而不显然的设计选择,一旦选反了,效果差距是决定性的。
Token 驱逐,作为一个方法家族。 给每个缓存位置打分——最常见的是用最近一小段”观察”查询对它的注意力大小(如 SnapKV),保留分数最高的一部分直到达到 token 预算,其余全部丢弃。PyramidKV 和 AdaKV 是让”预算本身”变得自适应的改进:PyramidKV 让每一层保留的 token 数量不同(实证发现越靠后的层需要保留的越少),AdaKV 让每个注意力头保留的 token 数量不同。三者共享同一个不可逆的承诺:一个 token 一旦被丢弃,无论后面哪个查询需要它,都再也拿不回来了。
KV 缓存量化,作为一个方法家族。 不丢 token,而是把每个 token 的 key/value 用更低的比特宽度存储——4 比特、2 比特,有时会先做一个学习到的旋转(比如 Hadamard 变换)把异常值能量均匀摊到各个坐标上再量化,这正是让低比特量化在实践中可行的关键技巧。TurboQuant,本文的量化基线,正是这个家族里一种近期的基于旋转的方案。
锚点与残差,本文需要的核心词汇。 *锚点(anchor)*是一个被精确存储、没有任何近似的 token 的 key/value 向量。*残差(residual)*是把某个其他 token 近似成锚点的缩放版本之后剩下的误差向量——即 ,其中 是 基于锚点的近似。存储一个 token 的残差(即便是低精度的)能把锚点近似丢掉的大部分信息恢复回来;不存储则意味着该 token 只能靠”锚点的缩放拷贝”这一种形式存活下来。
有了这些词汇,第 2 节就是直接走一遍 AnchorKV 具体在构建什么。
2. 架构总览:三步压缩流水线
AnchorKV 只在 prefill 结束时、在一个冻结的模型上跑一次——不需要训练,不需要微调,不需要校准语料库。给定用户设置的固定”保留比例” (唯一控制压缩比的旋钮),每个 KV 头独立地、逐层地做三件事:
- 挑选锚点。 每个头选出 个位置精确存储:最近的 个位置(一个始终原样保留的近期窗口),加上一部分高注意力位置和一部分随机采样的早期位置的混合。
- 把其他所有 token 投影到最近的锚点上。 每个非锚点位置都表示成离它最近的锚点方向上的一个缩放拷贝,代价只是一个锚点索引加一个标量系数(几个字节,而不是一整个 维向量)。
- 花费按字节预算分配的残差。 支付完锚点和投影元数据后剩下的存储预算,会买下固定数量的量化 2 比特残差,这些残差竞争性地——跨该层所有头一起竞争——分配给”锚点近似估计对注意力输出伤害最大”的那些具体 token。

Figure 1(论文 Fig.1):面板 (a) 展示了 中两个例子 token 的表示方式。锚点 被精确存储。Token 干净地投影到它锚点的方向上,不需要残差()。Token 投影效果差——它丢弃的残差 很大——所以得到一个预算内的 2 比特残差槽位,给出更好的重建 。面板 (b) 展示了为什么这个字节账目很重要:在相同字节预算下,驱逐把全部预算花在一小部分精确存储的 token 上,而 AnchorKV 之所以能负担得起更少的完整锚点,正是因为它剩下的预算能以低成本表示每一个剩余 token,而不是对没保留的 token 完全不存任何信息。
把同样的流水线重画成一个数据流图,把”prefill 末尾只发生一次的事”和”解码时持续发生的事”分开:
flowchart TD
subgraph Prefill["Prefill 结束时(每个请求只跑一次)"]
A["prompt 完整的 K, V 张量,
每层每个 KV 头一份"] --> B["第1步:锚点选择
近期窗口 + 注意力打分 + 随机采样"]
B --> C["第2步:把每个非锚点 token
按方向分配给最近的锚点(式1)"]
C --> D["为每个非锚点 token 计算
投影系数 gamma_i 和残差 r_i(式2)"]
D --> E["第3步:为每个残差打注意力输出
效用分(式6)"]
E --> F["按字节预算分配:效用最高的
N 个残差获得 2 比特槽位(式9)"]
F --> G["丢弃稠密 K, V。
存储:锚点(bf16) + 每 token 索引/系数
+ 稀疏量化残差"]
end
subgraph Decode["每个解码步骤(每生成一个 token 重复一次)"]
H["融合的分块 kernel 按需
逐块重建 key/value"] --> I["重建:锚点投影
+ 若有则加残差(式3)"]
I --> J["对重建出的 key 施加 RoPE 旋转
(key 是在旋转前存储的)"]
J --> K["标准注意力计算;
重建出的张量从不写回
显存中的稠密缓存"]
end
G --> H
在进入数学细节之前,有两点值得先说清楚,因为它们解释了这个设计为什么长这个样子而不是别的样子。第一,压缩只在 prefill 和解码的交界处发生一次——解码过程中不会重新优化,注意力计算本身完全不变;AnchorKV 只改变了”存什么”和”怎么重建”。第二,也是接下来一切的基础:“任何位置都不会离开 softmax”。每个 token,无论是不是锚点,都会参与后续生成过程中的每一次注意力计算。token 与 token 之间的区别只在于”被表示得有多忠实”——精确存储、靠一个残差修补、还是只靠锚点投影——从来不是”是否存在”这个二元问题。正是这一个设计选择,让 AnchorKV 能站在和驱逐完全不同的”压缩比 vs 准确率”权衡曲线上:它永远不会付出驱逐那种最坏情况的代价(一个被需要的 token 根本不存在),因为它从不删除任何东西。
3. 锚点-残差表示,逐步推导
3.1 分配与投影
固定一个 KV 头,用通用向量 来描述这个表示(既适用于 key 也适用于 value,两边独立分配锚点归属和残差预算,但共享同一批锚点位置)。 个位置中的一个子集 ——锚点——被精确存储。每一个其他向量 都会被分配给方向和它最接近的那个锚点,用绝对余弦相似度衡量:
为什么用绝对值而不是普通的余弦相似度?因为后面式(2)真正要做的”投影”关心的是 落在哪个方向上(不区分正负),一个和某个锚点方向几乎相反的 token,用一个负系数照样能被那个锚点很好地表示——所以在分配这一步扔掉符号信息不会有任何损失,反而稍微拓宽了锚点的有效覆盖范围。
把 分配给它的锚点 之后,AnchorKV 计算 在 张成的一维子空间上的正交投影:
把这个推导展开一下,因为这是值得亲眼看一遍的标准线性代数:向量 到单位向量 张成直线上的正交投影是 。这里 不是单位向量,所以要先归一化:令 ,投影就是 ——正好就是带着这个 定义的 。所以 说白了就是” 在锚点方向上走了多远”,而因为 把锚点的范数吸收进了自己,锚点和系数加在一起,携带的信息量和一次原始点积一样多,但被打包成了单个 token 只需一个标量,而不必在每个 token 处都重新用到锚点的完整 维形态。残差 按构造和 正交——它精确捕捉了锚点方向无论怎么缩放都无法表达的那部分成分。
最后是重建规则,这才是解码时真正被使用的东西:
其中 是第 3.3/3.4 节里被授予了存储(量化)残差 的位置子集,不在其中时指示函数为零。每个非锚点 token 的重建最多需要三份信息:它属于哪个锚点、标量 ,以及——如果幸运地在 里——一个量化残差。这就是压缩机制的全部。锚点根本不需要重建(对 直接有 ,论文的实现统一处理成锚点自身的 ,这样每个 token 的数组保持同一种形状,不需要额外的特殊情况分支)。
3.2 锚点选择,以及为什么 key 要在 RoPE 之前投影
锚点选择沿用了 SnapKV 观察窗口的打分机制——论文明确表示复用了这个机制,只是用来做另一个决策。最后 个位置(一个较小的近期窗口,实验里比如 )总是被当作锚点保留;它们身兼三职:既是近期窗口,又是给其余序列打分的”观察查询”,也是不额外占用槛位的锚点方向本身。剩下的 个锚点槛位里,一部分比例 (论文里是 0.7)分给这些观察查询打分最高的位置——沿用 SnapKV 附录 A.2 里的式(14),用小的位置核做平均池化,让一个尖锐的注意力峰值同时也带动它周围的邻居位置,而不只是单独一个峰值 token 得分——剩下的部分从其余上下文里均匀随机采样。
那个随机成分不是可有可无的补充,它解决的是一个真实的覆盖问题。基于注意力的选择能找到观察查询直接用到的位置。但锚点在这个方法里的职责不只是”是不是重要的 token”,而是”能不能为其他很多毫不相关的 token 提供一个好的投影方向”。观察窗口打分很高的 token 不能保证它所在的方向对将被分配给它的众多其他 token 都有用。均匀随机采样的锚点,正是因为它们不集中在注意力已经集中的地方,能改善整个锚点字典的方向覆盖度——后面第 6 节的组件移除消融(“去掉随机锚点”)直接量化了移除它的代价,代价是真实存在的,而且随着压缩比收紧会越来越大。
pre-RoPE(在 RoPE 之前投影)这个决策值得单独仔细展开,因为它正是那种看起来像是无关紧要的实现细节、结果却能把数字拉出决定性差距的选择(Figure 7,第 6.4 节讨论)。RoPE 在注意力点积之前对 key 施加一个与位置相关的旋转 ;关键的是,RoPE 是一个线性映射,所以它能干净地分配到锚点-残差分解上:
这个代数事实给出了一个工程选项:因为旋转能穿过求和运算,AnchorKV 可以在旋转之前存储 key,只在解码时、重建之后再施加旋转——而不需要在 prefill 阶段就存储一个”与旋转兼容”的表示。但为什么要偏好这个选项,而不是直接对已经旋转过的 key 做投影?论文的论证是:RoPE 的旋转是与位置相关的,两个未旋转的 key 方向即使非常相似,各自被施加了各不相同的、依赖自身位置的旋转角之后,方向可能会变得相当不同。在这种依赖位置的”打乱”发生之前分配锚点和计算投影,意味着式(1)里衡量的方向相似度反映的是模型真实的语义内容,而不是两个 token 在序列中恰好相隔多远这个人为产物。Figure 7(第 6.4 节)直接测量并证实了这一点:一个 token 到它最近锚点的余弦相似度中位数,在施加 RoPE 之后要低 0.27——这是一个在论文测试的每一个层分组里都一致成立的、幅度不小的效应。因为投影之后剩下的残差相对范数是 (其中 是到锚点的余弦相似度——这直接来自式(2)那个正交分解应用毕达哥拉斯定理:如果 是到锚点的余弦,投影分量占了范数的 那部分,正交的残差占了 那部分),中位余弦下降 0.27 就直接意味着每一个没有拿到残差修补的 token(也就是没抢到残差槛位的那些),在 RoPE 之后的重建误差会实质性地大于 RoPE 之前。在旋转之前拟合,意味着同一个锚点字典能在不增加任何存储成本的前提下覆盖更多的缓存——一个真正的、一旦注意到就白拿的收益。这一切都不影响 value,因为 RoPE 只施加在 key 和 query 上。
4. 注意力输出感知的残差打分:真正让方法生效的机制
锚点和投影能带来一些压缩收益,但论文明确指出,真正有意思、真正决定 AnchorKV 能否在激进压缩比下和驱逐类方法竞争的部分,是剩下的预算( 支付完锚点和元数据之后没花完的字节)怎么在残差之间分配。这一步做错了,你就只是一个稍微花哨一点的定比特量化器;做对了,你能把剩下的每一个字节都精准花在最能换来准确率的地方。
4.1 为什么注意力分数和残差大小都不是正确的排序依据
论文提出了一个尖锐、几乎像是随口一提但实际上是整个打分机制核心的观察:残差的大小和一个位置获得的原始注意力大小,都不能决定存储这个残差实际能帮上多大忙。 一个几乎不被关注的位置上的大残差,无论锚点近似”看起来”有多”错”,对最终输出的改变都很小。而重注意力落在一个锚点已经代表得不错的位置上,同样是浪费的残差槛位——你会为一个本来没坑的坑去花钱修补。真正应该排序的量,是每个 token 的残差如果被丢弃,对注意力输出造成的一阶效应——这个量依赖于”该 token 拿到的注意力大小”和”该 token 近似引入的真实误差大小”的乘积,而不是单独看任意一个因子。
4.2 从 softmax 雅可比矩阵推导效用分
下面是完整推导,因为它确实很优雅,而论文只用了几行就把它压缩掉了,值得展开来看。
固定一层、一个 KV 头、一个查询 。把精确的 logits、注意力权重、输出写成
用重建后的缓存算出的同样几个量记作 。压缩会把 、 扰动,从而把 logits 扰动为 。
关键一步是围绕未扰动 logits 对 softmax 做一次一阶(线性化)展开。softmax 的雅可比矩阵是标准结果 ( 是克罗内克 delta),所以对 的一阶近似有:
这句话值得停下来体会一下:给每一个logit 都加上同样大小的偏移,softmax 之后的分布完全不变(softmax 平移不变),所以真正影响注意力权重怎么移动的,只有 logit 扰动的离散程度—— 和以注意力权重加权的平均偏移 之间的差异,而不是偏移本身的绝对大小。
现在展开输出扰动。;丢掉二阶交叉项 (这就是线性化,在扰动很小时是合理的),得到 。因为 是精确成立的(softmax 输出永远和为 1,所以任意一个输出的扰动都必须被其他输出补偿,这意味着所有的 加起来必须为零),你就可以自由地用未扰动的输出 去给第一个求和里的每一项 做中心化,而不改变这个求和的值——一个不起眼的代数技巧,但对最终结论很关键。把式(5a)代入并做完这个中心化,得到完整结果:
(中间那一项之所以消失,是因为 ,这直接来自 的定义——同样是那个中心化技巧带来的结果,也是为什么论文的式(5)只保留了两项,尽管推导过程走过了三项。)
这个分解是这篇论文理论部分最有用的一句话:“key 误差通过注意力权重起作用,其大小由 离查询已经形成的输出 有多远来调制;value 误差直接、线性地作用在输出上。“一个 token 的 value 已经和 一致时,它的 key 误差几乎无害——把一个本来就会产生同样输出的 token 往前或往后重新加权,几乎不会改变任何东西。而一个 value 强烈偏离 (即 很大)的 token 上的 key 误差,则能实质性地重新引导输出方向。这正是为什么下面两个通道要用两个结构不同的公式打分,而不是共用一个指标。
4.3 从扰动公式到逐 token、可加的效用分
式(5)是把整个缓存联合定价——它是所有位置贡献的一次性求和。要把它变成能用来给单个 token排序、以适应固定字节预算的量,论文引入了一个不相干性近似(incoherence approximation):假设,作为一个可用的工作模型,各个 token 的误差是零均值、跨 token 独立的(附录 A.3 里给出的理由是残差编解码器在量化前对残差做了一次随机化的 Hadamard 旋转,专门用来把残差能量摊到各个坐标上,使不同 token 的残差方向在高维下近似不相干——这不是巧合,而是有意为之的设计选择,一部分目的就是为了支撑后面这个近似)。在这个模型下,不同 token 之间的交叉项在期望意义上消失,期望平方输出扰动分解为一个干净的、逐 token 相加的求和:
这个”可加性”带来的实际好处是:存储某个 token 的残差只会缩小这个 token 自己那一项,其他每一项都不受影响,而每存一个残差都花同样多的字节(固定的 2 比特量化、固定的维度)。所以在这个可加近似下,在固定残差槛位数量的约束下最小化总期望平方误差,直接等价于”挑最大的那些项”——这是一个简单的贪心 top- 选择,在这个目标下可以证明是最优的,这正是下面第 4.4 节实现的东西。
现在专门看一个没有存储残差的 token :它被丢弃的误差正好是它的完整残差,、(负号是因为 ,不存残差的重建正好从真实向量里去掉了 )。代入式(5b)的两项,并对该 token 所在 KV 头的 个观察窗口查询取平均(用来代替对解码还没发出的那些查询的期望——这是一个近似,但和每一个基于观察窗口的驱逐方法所依赖的近似完全一样),就得到了存储 token 的残差在每一侧的效用:
这个推导过程里干净地掉出了两件事,值得指出来,因为它们解释了为什么这个效用不只是”注意力的平方”或者单独的”残差范数的平方”:(1) 存储一个残差,把被丢弃的误差替换成了量化误差,其相对平方能量是 (2 比特编解码器的一个固定属性,在附录 A.1 里精确定义);因为这个 的恢复能量比例对每个 token 都是一样的,它在排序时会被约掉——它不影响 top- 竞争谁赢谁输,只影响收益的绝对大小——所以式(6)里直接把它丢掉了,不损失任何信息。(2) 锚点和窗口 token 本来就是精确的、误差为零,它们从一开始就不参与这场竞争;只有真正被近似过的、非锚点非窗口的 token 才会竞争残差槛位。
下面的算法 1 把完整的逐层流程写具体(改编自论文的算法 1,并把第 4.4 节的残差分配步骤显式并入):
算法1:AnchorKV prefill 压缩(单层)
输入:每个 KV 头的 K, V;窗口 W;锚点预算 k;保留比例 θ
1: 对每个 KV 头 h:
2: A_h ← (最后 W 个位置) ∪ (按 SnapKV 注意力分数取前 ρ·(k−W) 个,式14)
∪ (剩余槛位,从其余上下文中均匀随机采样)
3: 对每一侧 ∈ {K, V}:
4: 对每个非锚点位置 i:
5: a(i) ← arg max_{a in A_h} |<x_i, x_a>| / (||x_i|| ||x_a||) # 式1
6: gamma_i ← <x_i, x_{a(i)}> / ||x_{a(i)}||^2 # 式2
7: x_tilde_i ← gamma_i * x_{a(i)}
8: r_i ← x_i - x_tilde_i
9: 结束
10: 对每个非锚点位置 i(不在 A_h 中):
11: u_i ← 用式6计算效用分(K侧或V侧公式)
12: 结束
13: 结束
14: 结束
15: N ← max(0, floor((θ * M_full − M_base) / b_res)) # 式9,残差槛位总数
16: N_K ← floor(N/2); N_V ← N − N_K # key 拿到 ⌊N/2⌋,value 拿剩下的
17: R_K ← 该层所有头共同池化竞争,按 u^K 取前 N_K 个位置
18: R_V ← 该层所有头共同池化竞争,按 u^V 取前 N_V 个位置
19: 对每一侧、R_side 中的每个位置:
20: 用 Hadamard 变换 U 旋转残差(式10)
21: 用 4 级 Lloyd–Max 编码本量化,每坐标 2 比特
22: 存储打包后的编码 + 每 token 的 absmax 尺度
23: 结束
24: 丢弃稠密的 K, V 张量
第 17–18 行很容易一扫而过,但其实是一个值得单独点名的设计决策:残差的排序和分配是跨该层所有头一起竞争的,不是每个头固定分到一份配额。这意味着,如果某个头在这个具体请求上系统性地更难被锚点近似(效用普遍偏高),它就能比”锚点已经拟合得不错”的那个头拿到更大份额的该层总残差预算。第 6.2 节的消融实验(“每头单独固定配额”,即强制均分而不是池化竞争)精确量化了池化机制带来的收益,答案是:真实存在的准确率收益,而且预算越紧越明显,因为均分本质上无法追踪哪个头在当前输入下更需要帮助。
4.4 按字节预算分配,用真实数字过一遍
保留比例 (唯一面向用户的参数)确定了一层的总字节预算。锚点和逐 token 记账元数据先被支付——一旦 和 固定下来,这些是不可商量、和数据无关的固定成本——剩下的部分买残差槛位:
其中 是未压缩的层大小, 是存储锚点加上每个非锚点 token 的索引和系数元数据的固定开销(附录 A.1 给出了逐张量的精确字节账目——锚点 key、value、锚点位置 ID、每 token 锚点索引、每 token 系数、残差位掩码、O(1) 查找用的前缀计数,等等), 是一个量化残差的成本。这是一个精确的账目,不是估计——解码 kernel 实际会触碰到的每一个字节都被计入,这也是论文能够声称”压缩后的实际占用永远不会超过请求的预算”的依据——和一些实际内存占用比宣传压缩比更模糊的压缩方案不同。
论文自己给出的算例(附录 A.1)非常有助于建立直觉:在 、、、、锚点预算 时,未压缩层大小 MB。锚点加元数据()只占用 3.39 MB —— 这层的 2.5%——在 (20 倍压缩目标)下,剩下 3.32 MB 的预算能买 个残差,而这一层总共有 个”token-侧”(跨所有非窗口位置和所有头的 key、value 加起来)持有某种分配。换句话说:在 20 倍压缩下,该层大约 17% 的 token-侧仍然在锚点投影之上拿到了一个真正的残差修补——绝不是”20 倍压缩”意味着只有 5% 的 token 有任何表示;每个 token 都有表示,而且相当一部分还拿到了额外的精细修正。
5. 值得质疑的设计选择:为什么这样做、替代方案是什么、边界在哪
为什么每个 token 只投影到唯一一个锚点,而不是多个锚点的加权组合(类似软混合锚点)? 一个显而易见的替代方案——把每个 token 表示成多个锚点的加权和,就像学习到的编码本或者乘积量化器那样——原则上能比任何单个锚点更好地捕捉一个 token 的方向。论文没有直接跑这个消融实验,但它的设计动机在字节账目里就能看出来:单锚点分配的代价恰好是一个索引加一个系数,一个固定、极小、统一的每 token 成本,无论这次分配拟合得好不好。而软多锚点方案需要为每个 token 存储多个索引和系数(或者一个更大的固定大小的稀疏/稠密权重向量),这会成倍增加每 token 元数据成本,直接吃掉式(9)想要最大化的那部分残差预算。这个单锚点选择会失效的地方在于:如果某个 token 恰好落在两个锚点方向的正中间,任何单一锚点都表示不好它,整个正交分量都会变成残差——恰恰是论文的效用打分(式6)被设计来检测并优先处理的情况,但如果字节预算紧到这个 token 没能在 top- 竞争里获胜,它就只能一直忍受一个真正糟糕的近似,直到压缩要求放宽。
为什么用一阶(线性化)近似给残差效用打分,而不是直接测量真实的输出误差? 对每个候选残差分配都精确测量、不经近似的注意力输出误差,需要真的分别跑一遍有和没有这个候选重建的注意力——每个候选的开销是 ,对所有 个位置做穷尽精确打分成本是 ,在本文目标的上下文长度(最高 128K)下完全不可承受。线性化后的效用(式6)在给定已缓存的观察查询注意力权重前提下,每个 token 只需 ,整个打分过程总共只要 。线性化的代价是:只有在扰动确实”足够小”,符合泰勒展开的前提假设时才有效,而在压缩比非常激进、同时丢弃了大量大残差的情况下,支撑式(5b)的”不同被丢弃 token 之间交叉项消失”这一独立性假设可能会不太准。论文很诚实地承认这只是”在一个不相干性模型下”的近似,不是精确结果——附录 D.1 专门(独立于下游准确率)测量了这个排序方法实际引入的输出误差,用来检查这个近似在实践中的代价有多大,这是一个不错的方法论习惯,不过值得注意的是这只在论文测试的模型和上下文长度上验证了这个近似,不是一个放到任何场景下都成立的保证。
为什么残差固定用 2 比特量化、配一个 Hadamard 旋转和 Lloyd–Max 编码本,而不是按残差自适应比特宽度? 按残差自适应比特宽度(更重要的残差多花比特,不重要的少花)听起来应该严格优于固定比特宽度,因为它是一个严格更具表达力的分配空间。论文没有明说,但从式(9)的字节账目怎么运作可以看出隐含的答案:残差选择这一步(决定 个候选里到底哪些 token 能拿到残差槛位)已经在做自适应比特宽度本应做的那部分工作了——一个被判定为低效用的 token,直接拿零比特(完全没有残差),而不是拿几个比特。既然”有比特还是没比特”这个二元决策已经承担了大部分重活,再叠加一个连续的比特宽度维度,会增加实现复杂度(可变长编码会让附录 A.1 那套依赖 O(1) 位置到槛位查找的存储布局变得复杂),而根据组件移除消融(第 6.2 节的”naive 2-bit”,只去掉了 Hadamard 旋转和编码本,没动比特宽度本身)来看,收益反而不如把旋转做对来得大:不带 Hadamard、不带 Lloyd–Max 的朴素 2 比特量化,是四个被消融的组件里损失最大的一个,而且在各个压缩比下几乎保持恒定——因为它对每个存储的残差都均匀地造成损害,而不是改变存了多少个残差,这在结构上是一种和”比特预算更粗”不同、看起来代价更高的损伤方式。
为什么效用排序按侧(key 单独排一个 top-,value 单独排一个 top-)、用固定的 对剩余的划分,而不是把两侧放进一个统一的池子里竞争? 式(5)的推导本身就给出了答案:key 误差和 value 误差是通过结构上不同的机制影响输出的——key 误差的影响被 调制(只有当这个 token 的 value 确实和查询已经形成的期望不一致时才重要),而 value 误差直接、线性地作用。把两者混进一个排序里,需要在这种结构性的不对称之下把两者换算到同一个尺度上,而固定的”key 占一半”的划分是一种简单、稳健地绕开这个比较难题的办法,代价是不能把整个预算随不同输入偏向某一侧。论文没有对这个 50/50 划分本身做消融,这是值得标记出来的一处未经检验的地方:一些工作负载完全可能系统性地把误差更多集中在某一侧,这种情况下固定划分相对一个能自适应划分的方案会损失一部分准确率。
6. 结果复现与点评
6.1 主对比实验:AnchorKV 对阵驱逐类和量化类基线
核心实验(Figure 2)在三个指令微调模型规模——Llama-3.1-8B、Mistral-Small-3.1-24B、Llama-3.1-70B——上,用三个长上下文基准(RULER-32K、RULER-64K、LongBench),在匹配字节预算(不是匹配 token 数——一个重要的方法论细节,因为这意味着驱逐和 AnchorKV 是按对内存受限服务器真正重要的指标来比较的)下,对比 SnapKV、PyramidKV、AdaKV、TurboQuant。

Figure 2(论文 Fig.2):每一行是一个模型规模,每一列是一个基准。这个模式在全部九个面板里是一致的:AnchorKV(红星)随着压缩比收紧,始终紧跟未压缩 FullKV 分数(黑圆点、虚线),而三个驱逐基线(蓝/绿/紫)明显下降——并且 AnchorKV 在20 倍压缩下,始终坐落在每个驱逐基线10 倍压缩时所在的位置之上,意味着用一半的内存就能拿到同样的准确率。TurboQuant(橙色菱形)也紧跟 FullKV,但它固定的 3.5 比特表示把它的压缩比封在了 5 倍附近——它压根没有出现在 AnchorKV 目标的那个区间,因为它结构上到不了那里。具体数字:在 RULER-32K、20 倍压缩下,AnchorKV 在 8B、24B、70B 上分别保留了 93.5%、95.3%、99.3% 的 FullKV 分数,而 70B 上最强的驱逐基线只保留了 86.8%。
论文提炼出的两个二阶观察值得单独复述,因为它们打破了一个自然的假设。第一,压缩的代价随模型规模变大而降低——AnchorKV 在 20 倍压缩下的保留准确率从 8B 到 70B 是变好的(RULER-32K 上从 93.5% 到 99.3%),而这不是”模型越大对任何扰动都越鲁棒”这种泛泛的规律,因为驱逐基线并没有展现出同样干净的规模化趋势(在 Mistral-24B 上,最强的驱逐基线在两个上下文长度上都低于 67%——并不比 8B 时好)。第二,LongBench(真实文档,比 RULER 合成任务的冗余度更低)展现了相同的定性模式:AnchorKV 在 8B 上保留 94.1%,70B 上保留 98.4%,在每个规模上都领先所有基线——所以这个效应不是 RULER 合成结构的产物。
6.2 逐任务分解:驱逐到底在哪里崩掉
聚合分数很容易把灾难性的逐任务失败藏在一个看起来健康的平均值背后,所以论文的逐任务 RULER 分解(Figure 3)信息量往往比总分更大。

Figure 3(论文 Fig.3):AnchorKV(顶行)在 13 个 RULER 任务里的 12 个上匹配或超越最强的驱逐基线,只在第 13 个任务上落后一分——而它在整个任务套件里的下限从未低于 60%。对比一下驱逐基线:SnapKV、AdaKV、PyramidKV 在 single_3 上全都崩到个位数(2%–4%),在 multikey_2 上崩到 40%–55%,PyramidKV 在 cwe 上甚至低到 6%。这个模式正是第 1 节”驱逐脆弱性”论述所预测的:差距最大的地方恰恰是那些需要从干扰项中检索、或需要聚合整段上下文信息的任务——cwe、single_3、multikey_3——AnchorKV 在这三个任务上分别比最强驱逐基线高出 67、88、45 个百分点。这些正是观察窗口打分器(只看最近几个查询)对”后面某个查询究竟需要什么”信息最少的任务——也正是不可逆的驱逐决策最不能承受代价的任务,因为它没法被撤销。
6.3 一个专门用来击穿这个失败模式的压力测试
大海捞针检索(needle-in-a-haystack),特意用了一个很难的 64 位数字密钥(论文指出较短的密钥对它对比的每个方法来说都接近饱和,区分不出差异),把上下文长度从 16K 扫到 128K,把针的深度从 0%(针在最开头,离 prompt 末尾的观察查询最远)扫到 100%(针在最末尾,正好在观察窗口所在的位置)。

Figure 4(论文 Fig.4):在相对温和的 5 倍压缩比下,AnchorKV 平均达到 0.94,对比未压缩缓存的 0.99,几乎在整个”深度 × 长度”网格上都能找回针。AdaKV,最强的驱逐基线,平均只有 0.18,几乎处处失败——除了最底下那一行,而那一行其实不算 AdaKV 方法真正的胜利,只是基准设置的一个人为产物:在 100% 针深度时,针恰好落在每个方法(包括驱逐)都原样保留的近期窗口内,所以每个方法都能轻易做对。一旦针稍微离开 prompt 末尾,驱逐的观察窗口打分就压根不会给它足够高的分数让它存活,它就没了。AnchorKV 自己的弱点也很有信息量:它少数表现打折的格子集中在最长上下文里、最浅的深度——一个放在 128K token prompt 最开头的针,在序列距离上离用来给候选锚点打分的、位于 prompt 末尾的查询最远,所以正是锚点选择这个启发式(复用了 SnapKV 的观察窗口打分)关于”这个位置将来是否重要”信息最少的地方。这是锚点选择启发式一个真实的、被承认的弱点,不是”完美无缺”的宣称——只是这个失败模式,比驱逐几乎全盘崩溃的同一个坐标轴,小得多。
6.4 消融实验:哪些设计选择是承重的

Figure 5(论文 Fig.5)左图,隔离出排序指标本身的效果,保持锚点和总预算不变。除了论文推导出的效用分(式6),还试了按到最近锚点的余弦相似度排序(只看对齐程度)、按残差范数排序(只看近似误差)、按原始注意力分数排序(只看使用程度)、以及随机分配(一个基准下限)。推导出的效用分在每个压缩比下都获胜,而——这是有信息量的一点——在 5 倍压缩比下,选哪种排序几乎无关紧要,因为预算够宽裕,几乎每个 token 都能拿到,可挑选的余地很小;差距只在预算收紧后才打开,在 20 倍压缩比下差距最大,随机分配远远落后于其他所有方法。论文自己给出的解释,为什么每个单因子替代方案都表现更差,值得复述一遍,因为它直接对应式(5)的分解:注意力分数看到 token 被用得多不多,但看不到它的锚点代表得有多差;残差范数正相反;余弦相似度两者都看不到,只看两个向量的夹角。推导出的效用分是这四个指标里唯一同时结合两个因子的,这正是式(6)那个”注意力项的平方”乘以”误差项的平方”在代数上做的事。
右图一次移除一个完整的组件:(i) 去掉随机锚点份额,全部按注意力分数选锚点;(ii) 把跨头池化的残差分配换成每头均等的固定预算;(iii) 完全不存残差,把整个预算都花在更多锚点上;(iv) 朴素量化残差(2 比特,不用 Hadamard 旋转,不用 Lloyd–Max 编码本)。每一种移除都会损失准确率,但损失的形状有实质差异:朴素量化是四者里损失最大的,而且在三个测试的压缩比下几乎恒定不变——一个不会随压缩收紧而变得更差的固定税,因为它对每个存储的残差都造成同样大小的损害,无论存了多少个。完全不存残差在每个压缩比下都损失准确率,即便省下来的字节能买更多锚点——这是一个真正重要的负结果,因为它直接排除了一个诱人的简化方案:“既然要处理残差这么麻烦,为什么不干脆把整个预算花在更多、挑得更好的锚点上?”结果是,更大的锚点字典替代不了残差能修补的东西,两个机制不是可以互相替代的,而是互补的。每头均分预算和只按注意力选锚点在 5 倍压缩比下代价都不大,随着预算收紧代价逐渐增大——和残差排序面板一样的定性模式,论文把这个联系到附录 D.2 的一个发现:不同头实际需要的残差份额高度不均匀,而且随输入变化,所以任何固定的均分启发式在结构上都无法追踪它。

Figure 7(论文 Fig.7)是对第 3.2 节 pre-RoPE 设计论证的实证确认:一个 token 到它最近锚点的余弦相似度中位数,在 RoPE 旋转之后测量,比之前低 0.27,而且这个先后顺序在论文检查的全部八个层分组(连续几层一组)和全部四个 RULER 任务类别上都一致成立——意味着这个效应反映的是旋转本身某种结构性的东西,不是某一个特定层或任务的偶然产物。
6.5 效率:准确率的收益要不要拿吞吐量来换
一个能保持准确率但慢到实际用不了的压缩方法算不上实用的胜利,所以论文对一个融合的 Triton kernel 做了性能画像,这个 kernel 直接从压缩表示逐块(FlashAttention 风格)重建 key 和 value,从不真正实例化稠密缓存。

Figure 6(论文 Fig.6):面板 (a) 展示了稳态解码峰值内存 17–19 倍的下降(对数坐标)——这是”从不把重建后的稠密张量写回全局显存”这个设计带来的机械式收益;运行时的实际占用真的就是压缩后的占用,不是”压缩占用再加上某个临时稠密缓冲区”某些实现可能需要的那种。面板 (b) 是这个故事里比较老实的部分:在短(32K)上下文下,融合重建 kernel 的解码延迟大约是未压缩基线的 1.3 倍,因为重建开销还没被足够大的单步工作量摊平——但到 64K 附近就追平了,到 96K–128K 甚至比未压缩基线快 2%–3%,因为在长上下文下,读取(更大的)未压缩缓存所需的内存带宽占主导,而 AnchorKV 更小的占用即便付出了重建成本,也能赢下这场带宽竞赛。面板 (c) 和 (d) 把内存收益翻译成实际运营中真正重要的东西:在 64K 上下文下,AnchorKV 在触及内存上限之前能撑住最多 6 个并发请求,对比未压缩基线的 3 个——各自跑在自己的内存边界上时,吞吐提升 1.26 倍——而”容量”面板显示,只有 AnchorKV 能服务的那部分并发差距,在更长的上下文(最长到 128K)下还会进一步拉大。
7. 局限性:写出来的与没写出来的
论文对若干局限性相当坦诚。所有实验都在单张 A100-80GB 上以 batch size 1 跑;第 6.5 节的吞吐数字确实延伸到了多请求批处理,但第 2–5 节的主要准确率对比(Figure 2–5)没有报告批处理是否会以任何方式和锚点选择或残差分配产生交互(例如,“跨该层所有头池化残差预算”这个机制,在很多请求共享一块 GPU、竞争同一份总内存池时会不会表现不同)。这个方法按设计是训练自由、无需校准的——这是一个真正的部署优势——但这也意味着它无法从大量请求中学到”某些内容类型(代码、表格、特定语言)系统性地更难被锚点表示好”这件事;每个请求都是从同一套固定超参数出发(、、、),不管 prompt 里实际是什么内容。
锚点选择这个启发式明确来自 SnapKV 的观察窗口打分,这意味着它继承了每一个基于观察窗口的方法都共有的那个根本假设:prompt 最后几百个 token 关注的位置,是将来还没发出的解码查询会需要什么的一个好的代理。第 6.3 节大海捞针实验里那些弱点(浅深度、最长上下文),正是这个假设局限性的一个直接、可见的症状,而论文对把这个具体失败模式归因于这个具体原因这一点是坦率的。论文没有探讨的是,这个同样的假设,在真正的多轮对话场景下会不会进一步退化——在多轮对话里,压缩发生那一刻的”观察窗口”可能反映的是第一轮的内容和需求,而后续很多轮的查询可能有和第一轮观察窗口能预见到的完全不同的信息需求——这个场景对生产服务来说,可能比这里测试的单次长文档基准更真实。
论文也没有报告除了唯一报告的那套配置(、、)之外,对自身固定超参数的敏感性——它被表述为”所有模型、所有任务用同一套配置”,这对简洁性和可复现性是真正的优点,但这也留下了一个开放问题:不针对每个模型家族或每个基准去调这些超参数,到底放弃了多少准确率(或者,较不可能但也有可能,反而是拿到了额外收益),以及这个具体的选择,对报出来的头条数字到底有多敏感。
8. 批判性分析
这篇论文本身的弱点和缺陷。 第一,第 4.2–4.3 节效用分推导所依赖的不相干性近似——假设各 token 误差独立——一部分是靠设计来证成的(Hadamard 旋转正是为了让这个假设更合理才被选用),这作为验证策略有点循环:这个近似之所以更合理,是靠一个”在流水线别处,唯一目的是帮助量化编解码器”的组件来撑起的,而论文并没有独立地检验一下,如果这个近似在更严苛的条件下失效(比如超出测试范围的、超过 20 倍的压缩比,那里同时被丢弃的残差更多,可能比不相干性模型假设的更相关),效用排序的质量会退化多少。第二,式(9)和附录 A.1 里的算例对每一个存储张量都记账得非常细致,这是值得肯定的严谨——但和驱逐基线的对比,是靠一个公式(对保留 个 token 收 字节)把驱逐的”token 预算 + 比特宽度”这两个旋钮换算成一个等价字节预算的,这个换算假设驱逐方法自己的元数据开销(位置记账等等)是可忽略的;如果驱逐方法在实践中每个保留 token 承担的开销比这个理想化的账目假设的更高,那么”匹配字节预算”这个比较可能会系统性地、以一个不大但真实存在的幅度偏向 AnchorKV,论文没有讨论这种可能性。
作者低估或遗漏的局限性。 论文把这个方法描述为完全训练自由、超参数很轻——相对需要校准数据或微调的方法而言这确实是真的——但它低估了有多少个固定超参数(、作为 比例的 、、、2 比特残差精度、具体的 4 级 Lloyd–Max 编码本)是从对测试模型做的单次调参里定下来的,而论文从未展示在一个真正不同的架构家族上会发生什么——比如一个用分组查询注意力、KV 头数量远少于查询头数量的模型,那时式(6)里的""这个观察查询数量,会随具体的 GQA 比例变得很大或很小,对效用估计的噪声程度有不清楚的影响。第二,所有评测任务都是读密集型的检索/问答/摘要类基准;论文没有测试压缩之后有大量写活动进入缓存的场景——比如带很多工具调用往返的长智能体循环,那里新生成的 token(按方法设计从不被压缩,永远精确保存)可能会随着生成推进,逐渐主导一个最初很小的、锚点选择时确定下来的上下文,改变实际达到的有效压缩比,而这个静态的、发生在 prefill 末尾的压缩节点,并没有明显考虑这种动态变化。
具体的改进建议。 (1) 针对第 5 节提到的固定 50/50 key-value 残差划分,至少在一个已知误差分布不对称的工作负载上做消融——比如一个已知 key 误差占主导的任务(softmax 重新归一化很重要的长检索链)对比一个 value 误差占主导的任务,确定一个输入自适应的划分是否真的能明显超过固定划分,而不是把这个留作一个隐含、未经检验的设计选择。(2) 把大海捞针压力测试(Figure 4)扩展到一个真正的多轮场景,让”观察窗口”取自较早的一轮,而把针注入到晚得多的一轮里,直接检验第 7 节标记出来的对话漂移失败模式,而不只是单次版本。(3) 至少对两个最有影响的固定超参数( 和 ),在固定压缩比的前提下,在一个模型上做一次显式的敏感性扫描,让读者能判断头条的 20 倍结果有多依赖这个具体的调参,还是在一个合理的超参数邻域内足够稳健——这是一个相当便宜的实验,加上去能明显加强”一套配置适用一切”这个论断的说服力。(4) 直接测量支撑式(5b)的不相干性近似,在超出 20 倍压缩之后是渐进退化还是急剧崩溃,因为论文自己的表述(“缓存最贵、最值得压的那个区间”)暗示了实践者会想把这个方法推得比这里验证过的范围更远。
9. 可复现性说明
论文表示代码会在录用后发布,同时以对一篇系统论文而言不寻常的粒度记录了实验设置:精确的超参数(、、、、锚点 bf16、残差 2 比特)、精确的硬件(NVIDIA A100-80GB、batch size 1、贪心解码、固定种子 42),以及一份完整的逐张量字节账目表(附录 A.1,Table 1),精确到一个独立的读者能不猜测地照着重新实现整套存储布局。基线是通过 KVPress(一个公开库)跑的,用的是文档记录过的默认设置,这实质性地降低了”不公平或未校准的基线对比”这个 KV 缓存压缩论文里常见的失败模式的风险。RULER、LongBench 和大海捞针的设置都是标准的、公开可用的基准,评测脚本直接引用。截至本文撰写,唯一还开放的复现性缺口是,实际代码(尤其是那个融合的 Triton kernel,它做的真实工程工作超出了正文能完全说清楚的程度)还没有公开,所以第 6.5 节的效率数字目前还不能端到端独立验证,只能验证已经文档化的算法和字节账目所对应的准确率数字。
10. 在更广阔的 KV 缓存效率版图中的位置
如果你一直在关注本博客的 KV 缓存压缩系列,值得把 AnchorKV 明确放到最近几篇覆盖过的工作旁边比一比。DynaCalKV(2607.24331,上一个 Efficient ML 周期覆盖过)通过头分组和自适应秩分配来压缩——一个根本上不同的机制(跨头的低秩结构),攻击的是同一个内存瓶颈。LOCKS(2607.24555)和 KV-Fold(2605.12471)都追求紧凑的按页或循环式摘要,而不是精确锚点加残差的表示。CounterCausalKV(2607.27600)从反事实惊异度的角度来做驱逐式决策,本质上仍然属于 AnchorKV 明确拿来对比的那个”保留一个子集、丢弃其余”的驱逐范式。AnchorKV 相对这些工作最独特的地方,是它坚持永远不把任何一个位置从 softmax 里移除,同时仍然拿到和驱逐相竞争的压缩比——它不太像”一个更好的驱逐打分器”或”一个更好的量化器”,更像是设计空间里一个真正的第三个点,精神上更接近向量量化/编码本方法(论文在相关工作里明确讨论并区分了自己和这类方法,理由是共享质心方法会让多个 token 坍缩到相同的表示上,而 AnchorKV 每个 token 自己的标量系数让即便共享同一个锚点的 token 也保持可区分)。对一个要在内存受限的长上下文服务工作负载上决定先试哪个方法的实践者来说,这篇论文自己的数字建议:当工作负载里包含真正的”从干扰项中检索”或”整段上下文聚合”这类结构的任务时,AnchorKV 是目前能拿到的最强选择——恰恰是驱逐那种二元、不可逆的承诺最暴露弱点的地方。
11. 结语
AnchorKV 的核心赌注是:驱逐 vs 量化这个长期主导 KV 缓存压缩工作的二分法,本身就是一个假的选择——你可以在不付出驱逐的不可逆代价的前提下,拿到驱逐才有的激进压缩比,方法是从不把任何一个 token 从 softmax 里移除,而是改变每个 token 被表示得有多忠实。让这套机制真正生效的每一步都经过了充分论证:为方向覆盖度(而不只是原始重要性)选出的锚点、一个正交残差恰好捕捉锚点方向无法表达的那部分、一个直接从 softmax 雅可比矩阵干净推导出来的注意力输出感知效用分(而不是借用某个代理指标)、以及一套字节精确的账目,把单个用户旋钮变成一个可证明地遵守预算的存储方案。实证结果——20 倍压缩下保留 93%–99% 的未压缩准确率,以一半的压缩比打败每一个驱逐基线,而且准确率差距恰恰在驱逐结构上无法从早期猜错里恢复的那些任务(从干扰项中检索、整段上下文聚合)上朝 AnchorKV 有利的方向拉得更大——对任何要决定生产环境里怎么压缩 KV 缓存的人来说,是一个真正有用的数据点。那些诚实的告诫(一个有点循环论证的不相干性近似、一个继承了观察窗口驱逐自身盲区的锚点选择启发式、代码尚未发布、以及在多轮对话漂移下未经测试的行为)并没有削弱这个核心贡献,但在假设这个 20 倍数字能不变地迁移到一个和这里测试的读密集型、单次长文档基准明显不同的工作负载之前,恰恰应该先去仔细看看这几个地方。