跳到主要内容
KV 复用失败不是均匀的,剩下的事就是找出那几个 token

KV 复用失败不是均匀的,剩下的事就是找出那几个 token

林默
林默

· 阅读约 6 分钟

那个晚上我扫了一眼标题,没点开摘要。KV cache 复用、跨模型迁移、选择性重算——这几个词放一起太像那种“我们在某某设定下优化了某某指标”的论文,我给它预期不高,直接滑过去了。

第二天早上一个做推理服务的同事把链接甩进群里,就一句:“你们看它那个 rank disagreement 的定义。”

我看了。然后意识到前一晚那种扫描型阅读,刚好错过了这篇论文里最值得停下来想的部分。

先停一下,问个问题:跨模型 KV 复用这事儿,大家卡住的位置到底在哪?

最外层答案是“表示不对齐”。A 模型算出来的 cache,B 模型用自己的参数去读,中间隔着一层模型特有的编码空间。既有工作的路子是学个线性映射,把 A 的 cache 投到 B 的空间里,让它“差不多能用”。这个思路在同一模型族内、参数规模接近的时候还行,一旦差距拉到 23 倍,映射就撑不住了。

再往下呢?这篇论文剥出来的那层很具体:映射失效不是均匀的。整段上下文里,大部分 token 映射过去之后误差不大,该干嘛干嘛;但少数 token——那些信息密度高的、真正承重的 token——误差集中爆发。不是一场均匀的毛毛雨,是几场局地暴雨。你满脑子想着把伞做得更大,但真正淋湿你的就那几个坑。

我一开始也觉得这不就是 attention 能量分布不均匀嘛,老生常谈。后来才发现,这个观察被用到这里的姿势不太一样。他们不是在说“要更精细地分配算力”这种正确的废话,而是直接把问题改写了:既然失败集中在少数 token,那我就不需要让映射对每一个 token 都准,我只需要找出哪些 token 不准,然后把它们重新算一遍。大模型只对 30% 的位置做真 prefill,剩下 70% 的 KV 接着用从小模型映射过来的。

这么说吧,这个范式把 cross-model KV reuse 从“精度问题”改成了“检测问题”。从“让映射的误差到处都小”变成“找到映射误差真正致命的地方”。前者是个无底洞,后者有边界。

这里就该说说 rank disagreement 这个指标了。逻辑其实很直:拿一批标定数据,把映射后的 KV 往各个输出方向上投影。有些方向,标定数据里的信号很强,说明这些方向是“该有的”语义结构;还有些方向,映射后的 KV 有能量,但标定数据里找不到对应的支撑——这些方向上的能量,就是映射过程中跑偏掉的部分。分数高的 token,就是在这些支撑弱的方向上能量异常突出的 token。重算它们。

我第一眼看到这个定义的时候,心里咯噔了一下。不是因为它多难,是它聪明得有点“作弊”——它绕过了一个更根本的问题:那个闭式线性映射本身怎么可能做好?它不回答这个。它说:映射不用那么好了,我只要知道它哪里不够好。

这跟缓存失效那个老笑话一个味:计算机科学里就两件难事,缓存失效、命名、还有 off-by-one。真正难的是失效检测,不是缓存本身。RaReCache 做的事,本质上就是给 KV cache 的“缓存一致性”找了个检测角标。既然不可能让 cache 全对,那就让它对大多数,剩下的靠失效检测揪出来。

实验结果里有两个数字我想多看一眼。Qwen3 从 0.6B 到 14B、23 倍差距,重算 30%,保 95% 到 99%。Llama3 从 8B 到 70B、8.8 倍差距,重算 40%,保 96.5%。有意思的是这里的不对称:参数差距更大,反而重算更少、保留更高。直觉上应该是差距越大、越难迁移、越要多重算才对。但仔细一想,Qwen3 那组源模型实在太小了,0.6B 的 cache 本身就是个很弱的先验,留 70% 这种弱先验已经够便宜了,反正弱的地方重算就行。Llama 那组 8B 到 70B,cache 本身质量高一些,但两个架构之间语义对应的 token 分得更散,所以需要多修一点。也可能是评测任务分布不同。论文里没写清楚,这里我不硬猜。

换档说一下那个 5.0 倍的中位 TTFT 和 6.4 倍的 99 分位。99 分位的改善比中位大,这个比表面看起来更有信息量。说明被卡得最惨的那些请求,改善最大。为什么?因为长序列就是重灾区——token 数量多,信息密度高的位置就多,原来等着大模型从头 prefill 的时候,那些尾巴上的 token 才是真瓶颈。现在小模型把大部分 cache 先垫好了,大模型只重算那 30%,最疼的那部分被消掉了。这个设计天然偏心长序列。

还有一个细节。论文说 RaReCache“基本消除了方法效果对源模型规模的敏感性”。这一句我读了两遍。它意味着什么?意味着这套框架对源模型的依赖,从“必须有个像样的源模型”降到了“有个能算个差不多的 cache 就行”。源模型规模大小不再是要命的事。这其实是个很贵的性质,因为在实际部署里,选什么规模的小模型做 prefill 代理,是个自由度。如果方法效果对源模型规模很敏感,那这个自由度就是假的——你只能挑那个“对”的规模。现在它不敏感了,这个自由度就变成真的了。

写到这里我停了一下。这篇论文我会无脑推吗?不会。代价摆在那儿:你需要一批标定数据,才能把 rank disagreement 的分数算出来,这本身就是个前置成本。而且这个指标对跨域的鲁棒性——标定数据的分布跟生产进来的请求分布差多远会失效——我还没看到很完整的讨论。还有那 30% 或 40% 的重算,只能在大模型上跑,而大模型的算力贵。它在 prefill 上省下来的,其实是把钱从大模型 prefill 挪到了小模型 prefill 和 rank disagreement 的计算上。整体省不省,取决于你的请求长度分布、标定数据维护成本、以及你有多少长尾的长序列要扛。

但它打开的那个口子是真的。以前聊 cross-model KV reuse,大家的想象都停在“要复用就全复用,要准就全重算”这个二元里,中间没有路。RaReCache 给了一条路:信大部分,重算几个。几个怎么找、找了之后重算哪个模型、算多少、以什么预算约束来算——这些问题它建了个雏形,往下怎么长还不知道。

再往下是另一个问题了,跟 MoE 有关。rank disagreement 这个东西,如果换到路由决策的语境里去,能不能当成一个“哪些 token 的路由风险高”的预警指标?这个我此刻没想通,先放这儿,下次接着剥。

林默
林默

从具体轶事入口,一问一答把默认对的判断剥到设计权衡,主动暴露走过的弯路。

查看主页 →