NVIDIA 让 KV Cache 跨模型搬家:一次矩阵求解,32K 预填充从 6975 毫秒压到 278 毫秒
一次模型切换要付两遍钱,这是很多团队上线多模型编排之后才发现的账。用户的对话已经跑了三十轮,上下文攒到两万多个 token,路由层决定把这一轮交给更大的模型处理。大模型接手之后的第一件事不是生成回答,而是把这两万多个 token 从头到尾再读一遍。这一遍读取叫预填充,它的唯一产物就是 KV Cache,而这份缓存小模型刚刚才算过一次。
NVIDIA 的 Taekyung Heo、Rasoul Shafipour 和 Bita Darvish Rouhani 等人在 8 月 4 日挂到 arXiv 上的论文,把这笔重复开销当成了一个可以直接省掉的目标。编号是 2608.03893,标题叫跨模型 KV Cache 迁移,副标题点明了方法的性质,一个用于预填充复用的闭式线性映射。闭式这个词是整篇论文最值得注意的地方,它意味着不需要反向传播,不需要训练循环,一次矩阵求解就拿到映射器。他们在 Qwen3 的 14B 到 32B 这一对上,把 32K 上下文的预填充从 6975 毫秒压到了 278 毫秒。
这个结论听起来太便宜了,所以值得先把它的边界说清楚。论文只做族内迁移,也就是同一个模型家族里不同尺寸之间的搬运,跨家族的情况明确列进了未来工作。六对模型里只有四对拿到了可用的精度,另外两对垮得非常彻底。速度收益和精度损失是同一件事的两面,谁都不能单独拿出来说。把这两面一起读完,才能判断这套方法能不能进你的推理栈。
预填充这笔钱为什么一直在重复付
要理解论文省掉的是什么,先要看清预填充在推理链路里的位置。模型生成回答分成两个阶段,预填充负责把输入的每一个 token 过一遍全部网络层,算出每层每个注意力头的键和值,堆起来就是 KV Cache。解码阶段每生成一个新 token,只需要读这份缓存再追加一行,不必重算历史。所以预填充是一次性的重活,它的成本随模型规模和提示长度一起涨。
业界对这笔成本早有对策,叫前缀缓存。同一个模型收到共享前缀的多个请求时,把前缀那段 KV 存下来复用,后面的请求直接从断点接着算。这套机制在单模型内部工作得很好,vLLM 和 SGLang 都做了成熟实现,系统提示词和长文档这类固定前缀能省下大量重复计算。它的前提是接收方和生产方是同一个模型,缓存的数值格式完全对得上。一旦换了模型,这个前提立刻失效,前缀缓存就退化成一份没人能读的字节。
问题恰好出在这个前提上。生产部署已经不是单模型系统了,论文点出三种常见形态:成本质量级联、对话中途切换、以及请求路由。这三种形态的共同点是同一个会话会在不同尺寸的模型之间来回跳,而每一次跳跃都让接收方从第零个 token 开始重算。长会话让提示越来越长,多模型编排让切换越来越频繁,两个趋势叠在一起,预填充的账就滚了起来。
直觉上这件事似乎无解,因为源模型和目标模型的层数不同、隐藏维度不同、注意力头配置也可能不同。一份 14B 模型算出来的缓存,直接倒给 32B 模型,形状根本对不上,张量维度在第一步就会报错。既然对不上,就只能重算,这是过去默认的结论,也是各家推理框架当前的实际行为。论文的切入点正是这里,它没有接受这个结论,而是把问题重新定义了一次。
既然预填充的唯一产物就是 KV Cache,那么跳过预填充就不是一个计算问题,而是一个表示问题。手上已经有一份缓存,需要的是把它翻译成接收方期待的格式。翻译的可行性取决于两份缓存之间有没有可利用的结构,而不取决于两个模型的层数是否相等。这个转向让整件事从不可能变成了可测量。
已有的路都要训练,这条不用
跨模型复用 KV 并不是全新的想法,论文在相关工作里列了四条已有路线。C2C 为每一对模型训练神经融合器,LatentAlign 学习把每个模型映射进共享潜空间的适配器,IAM 替换的是小模型的注意力模式而不是 KV 数值,DroidSpeak 则要求两个模型架构完全一致。四条路各有各的适用面,但它们共享同一个成本。
这个共同成本就是梯度训练或者强架构假设。要么你得为每一对模型跑一轮反向传播,要么你得接受两个模型必须长得几乎一样。前者意味着上线一个新尺寸就要重训一次映射,训练数据、显卡和调参时间都得重新排期;后者意味着方法在真实的家族内部就用不上,因为同一家族的不同尺寸本来就有架构差异。论文明确说,据他们所知,没有工作研究过跨模型 KV 的关系是否简单到可以用闭式映射解决。这句话既是他们的贡献声明,也是一个被长期忽略的检查项。
这个问题问得很朴素,也正是全文的支点。如果关系足够线性,那么最小二乘就够了,不需要神经网络,不需要优化器,也不需要学习率。线性方法有一个工程上的巨大好处,就是可以用一次矩阵求解拿到解析解,成本可预测,行为可解释。于是论文先不设计模型,而是去测量这个关系到底有多线性。
还有一条容易混淆的边界需要提前划清。模型内部的跨层 KV 共享,研究的是同一个模型里不同层之间的冗余;前缀缓存研究的是同一个模型不同请求之间的复用。这篇论文做的是把缓存数值从一个模型搬到另一个模型,三者是正交的,可以叠加使用。搞清这一点,才不会把它误当成又一个层间共享的变体。
先量线性,再谈方法
论文没有一上手就设计映射器,而是先做了一件更基础的事:用最笨的办法测量两个模型的 KV 之间有多少线性关系。做法是取源模型的某一层、目标模型的某一层、某一个注意力头,在 token 级别拟合一个最普通的最小二乘回归,看能解释掉多少方差。这个探针不带任何技巧,只有一个源层,没有正则化,就是教科书上的普通线性回归。先测量再设计,这个顺序让后面每一个组件都有实测依据,而不是凭直觉堆上去的。
结果比预期的强。在 Qwen3 的 14B 到 32B 上,单个源层就解释了目标键的 56% 方差,值的 32%。这已经不是噪声水平了,说明两个模型对同一段文本算出的中间表示,共享着相当大的一块结构。当他们把多个源层的信息一起用上时,键的解释度升到 79%,值升到 65%。最好的单个源层与目标层组合,在去掉位置编码之后达到了 0.81。
把所有层组合画成热力图之后,四个规律很清楚。第一,线性拟合度沿对角线明显高于零,也就是源模型的浅层对应目标模型的浅层,深层对应深层,这个直觉是对的。第二,两个模型越接近,对角线越锐利,架构和深度差距越大,这条亮带就越弥散。第三,旋转位置编码会污染拟合,把它剥掉之后对角线普遍变得更清晰。第四,键比值更好预测,两者的解释度通常差着约 0.2。
第四条规律其实有直观解释。键参与的是注意力打分,它的作用是决定"看哪里",这个决策在同一家族的模型之间高度一致;值承载的是"看到了什么内容",随着模型容量增加,内容表示的丰富度差异更大,所以更难线性预测。这个差异后面还会再出现一次,在最终的精度结果里,键的映射质量对下游表现的影响远大于值。换句话说,这套方法的成败主要押在键上,值的误差有更大的容忍空间。
还有一个关键的测量结论决定了方法的形状。既然单个源层只解释了 56%,那多少个源层才够?论文用贪心前向选择做了实验,每一步加入能让解释度提升最多的那个源层。答案是信息确实分散在多个源层里:只用一个源层,键只能拿到全部源层版本的 66%,值只有 42%。从一层加到四层收益最大,加到六层左右就基本饱和了。
三个组件,一次矩阵求解
基于上面的测量,论文的映射器由三个组件拼成,每一个都直接对应一条实测规律。第一个是逐头岭回归,为目标模型的每一个层、每一个注意力头单独拟合一个独立的线性映射。这样做绕开了源和目标在层数、头维度、头数量上的全部不匹配问题,因为映射是按目标侧的结构来组织的。目标要多少层多少头,就拟合多少个独立的小回归,源侧的形状差异被吸收进了输入维度里。
第二个组件是跨层源选择,对应"信息分散在多层"这条发现。对每个目标层,按解释度挑出最有预测力的前 k 个源层,把它们的键值特征拼接起来当作输入。同一个目标层内的所有头共享同一组选中的源层,这允许跨头的信息流动。k 是唯一需要按模型对扫描的超参数,论文在 1 到全部之间扫了十一个取值。
第三个组件是内容空间映射,处理位置编码的污染问题。旋转位置编码给键施加了一个依赖位置的旋转,缓存里存的是旋转之后的键。论文的做法是先用逆旋转把位置信息剥掉,在无位置的内容空间里做映射,映射完再用接收方的位置编码重新旋转回去。因为旋转矩阵是正交的,逆运算精确且几乎不花成本。
这一步的价值不在短文本上的精度,而在长度泛化。论文坦白说,直接在带位置编码的键上拟合,在短上下文基准上的表现落在噪声范围内,看不出差别。但那样拟合出来的权重被绑死在校准时见过的 1024 token 位置分布上,换到 32K 就不成立了。解耦之后的公式天然支持任意长度,这才是服务长提示的前提。
拟合本身简单到有点反直觉。校准数据只用 500 条 FineWeb-Edu 序列,每条 1024 个 token,按步长 4 下采样,每个目标头拿到约 12.8 万个 token 的观测。然后解一个带 Tikhonov 正则的正规方程,正则系数取 0.01。用正则而不是纯最小二乘的原因很实际:k 较大时特征维度能到几万,而被选中的源层本来就相关,矩阵接近奇异,加一点对角项能稳定求逆而几乎不引入偏差。
整个拟合在单个八卡 H100 节点上耗时约 47 到 87 分钟,全程没有梯度训练。有意思的是拟合时间随目标头数的增长是次线性的,因为主导时间的那个矩阵乘积只需按目标层算一次,该层的所有头共享。最终映射器的参数量在 10.1 亿到 33.6 亿之间,存储 4 到 12 GB。这个体积不算小,但相对于它服务的模型规模是可以接受的。
把这套流程跑一遍
论文的三个组件描述得很清楚,但读公式和跑代码是两件事。下面这段程序把完整链路实现了一遍:构造一个人造的两模型家族,其中目标模型的 KV 是若干源层的线性读出加噪声,然后用论文的三个组件去恢复它,并和缺少组件的朴素版本对照。它只依赖 NumPy,可以直接运行。我先把它跑通再写进文章,紧接着给出的输出是本机真实运行结果。
#!/usr/bin/env python3
"""Cross-model KV transfer: closed-form ridge mapper (arXiv 2608.03893 core path)."""
import numpy as np
D_H, N_KV, T_CAL, LAM = 64, 4, 4096, 0.01
rng = np.random.default_rng(0)
def rope_matrix(pos, d_h, base=10000.0):
"""Build per-position rotation angles for half the head dim."""
inv = base ** (-np.arange(0, d_h // 2) / (d_h // 2))
ang = np.outer(pos, inv)
return np.cos(ang), np.sin(ang)
def rope_apply(k, cos, sin, inverse=False):
"""Rotate (or un-rotate) keys in place of the model's own RoPE."""
a, b = k[..., ::2], k[..., 1::2]
s = -sin if inverse else sin
out = np.empty_like(k)
out[..., ::2] = a * cos - b * s
out[..., 1::2] = a * s + b * cos
return out
def fit_ridge(x, y, lam=LAM):
"""Closed-form W = (XtX + lam I)^-1 XtY on centered features."""
xm, ym = x.mean(0), y.mean(0)
xc, yc = x - xm, y - ym
g = xc.T @ xc + lam * np.eye(xc.shape[1])
w = np.linalg.solve(g, xc.T @ yc)
return w, ym - xm @ w
def r2(y, yh):
ss_res = ((y - yh) ** 2).sum()
ss_tot = ((y - y.mean(0)) ** 2).sum()
return 1.0 - ss_res / ss_tot
def select_top_k(src_layers, tgt, k):
"""Rank source layers by single-layer R2, keep the k most predictive."""
scored = []
for i, s in enumerate(src_layers):
w, b = fit_ridge(s, tgt)
scored.append((r2(tgt, s @ w + b), i))
scored.sort(reverse=True)
return [i for _, i in scored[:k]]
def attn_output(q, k, v):
"""Single-head attention output, used as the fidelity diagnostic."""
logits = q @ k.T / np.sqrt(q.shape[-1])
logits -= logits.max(-1, keepdims=True)
p = np.exp(logits)
return (p / p.sum(-1, keepdims=True)) @ v
# --- synthetic two-model family: target KV is a linear+noise read of source layers ---
n_src = 6
src = [rng.normal(size=(T_CAL, D_H)) for _ in range(n_src)]
truth = {i: rng.normal(size=(D_H, D_H)) * 0.3 for i in (1, 3, 4)}
tgt_content = sum(src[i] @ w for i, w in truth.items()) + 0.05 * rng.normal(size=(T_CAL, D_H))
pos = np.arange(T_CAL)
cos, sin = rope_matrix(pos, D_H)
src_roped = [rope_apply(s, cos, sin) for s in src] # what the cache actually stores
tgt_roped = rope_apply(tgt_content, cos, sin)
# 1) strip RoPE so the fit is position-free
src_stripped = [rope_apply(s, cos, sin, inverse=True) for s in src_roped]
tgt_stripped = rope_apply(tgt_roped, cos, sin, inverse=True)
# 2) cross-layer selection, then one ridge solve on the concatenated features
picked = select_top_k(src_stripped, tgt_stripped, k=3)
X = np.concatenate([src_stripped[i] for i in picked], axis=1)
W, B = fit_ridge(X, tgt_stripped)
# 3) map, then re-encode with the receiver's RoPE
pred_stripped = X @ W + B
pred_roped = rope_apply(pred_stripped, cos, sin)
# baseline: no RoPE stripping, single source layer
w1, b1 = fit_ridge(src_roped[picked[0]], tgt_roped)
naive = src_roped[picked[0]] @ w1 + b1
q = rng.normal(size=(256, D_H))
cs = lambda a, b: float((a * b).sum() / (np.linalg.norm(a) * np.linalg.norm(b)))
gt_out = attn_output(q, tgt_roped, tgt_content)
print("selected source layers:", sorted(picked), "| ground truth:", sorted(truth))
print(f"R2 full pipeline : {r2(tgt_roped, pred_roped):.4f}")
print(f"R2 naive (k=1,+RoPE): {r2(tgt_roped, naive):.4f}")
print(f"attn-output cosine full : {cs(gt_out, attn_output(q, pred_roped, tgt_content)):.4f}")
print(f"attn-output cosine naive: {cs(gt_out, attn_output(q, naive, tgt_content)):.4f}")
在我本机跑出来的结果是这样的:跨层选择准确挑回了真实的源层 1、3、4,完整流程的解释度 0.9999,注意力输出余弦 0.9997;而砍掉位置解耦和跨层选择的朴素版本,解释度掉到 0.0341,余弦只有 0.1835。人造数据当然比真实模型友好,这里要看的不是绝对数值,而是三个组件缺失时的塌陷幅度,它和论文消融实验的方向完全一致。论文在真实模型上的对应数字是键解释度从 0.79 掉到 0.56,量级比这里温和得多,但方向一样。差别在于真实模型的 KV 关系只是接近线性,而这段代码里的关系本来就是线性构造出来的。
这段代码里有两处细节值得单独指出。一个是 rope_apply 用同一个函数处理正向和逆向旋转,只翻转正弦项的符号,这利用了旋转矩阵正交的性质,也是论文说逆运算精确且几乎免费的原因。另一个是 fit_ridge 先对特征和响应做中心化再求解,截距通过均值反推回来,这样正则项只作用在斜率上,不会把截距一起压向零。这两处都不是性能优化,而是保证数学上成立的必要步骤,去掉任何一个结果都会偏。
四对能用,两对报废

论文在三个家族的六对模型上做了评测,全部是键值匹配的对,也就是源和目标共享注意力头数和每头维度。评测基准有五个:ARC-Challenge、HellaSwag、WinoGrande、五样本 MMLU、以及八样本带思维链的 GSM8K,另外用 WikiText-2 困惑度和 CoQA 多轮对话做补充。保留率的定义很直接,用迁移之后的准确率除以目标模型自己预填充的准确率。这个比值等于一百,说明映射缓存和真实缓存对下游任务是等价的。所有六对都在各自扫出来的最优源层数下评测,用的是完整流程,没有为某一对单独调整方法。
结果的分化程度超出了论文自己的预期,六对里的平均保留率从 42% 一直铺到 98%。这不是一条平滑的曲线,而是清晰的两档。四对落在可用区间,两对彻底垮掉,中间几乎没有过渡带。下面这张表是论文的头条数字,我按平均保留率从高到低排列。第四列的地板归一化是把随机猜测校正掉之后的结果,它比原始保留率更能反映真实水平,后面会单独解释这个校正为什么必须做。
| 家族 | 模型对 | 平均保留率 | 地板归一化 | HellaSwag | MMLU | GSM8K |
|---|---|---|---|---|---|---|
| Qwen3 | 14B→32B | 97.6% | 96.3% | 97.6% | 95.0% | 95.6% |
| Qwen3 | 8B→32B | 87.5% | 80.7% | 95.2% | 88.5% | 68.8% |
| Ministral 3 | 3B→8B | 76.2% | 65.9% | 93.3% | 69.4% | 36.6% |
| Llama 3.1 | 8B→70B | 72.8% | 62.9% | 94.4% | 73.3% | 18.2% |
| Ministral 3 | 3B→14B | 44.2% | 14.7% | 68.0% | 32.0% | 3.2% |
| Ministral 3 | 8B→14B | 41.6% | 11.1% | 58.7% | 32.7% | 1.6% |
先看最好的那一行。Qwen3 从 14B 迁到 32B,平均保留 97.6%,ARC-Challenge 甚至到了 101%,就是说迁移之后比目标模型自己算的还高了一点点,这属于评测噪声范围内的正常波动。这一对的两个模型架构最接近,深度差距最小,正好对应前面热力图里对角线最锐利的情况。它是这套方法的最佳案例,也是论文用来做全部消融实验的对象。
地板归一化这一列必须一起读,否则会高估效果。多选题基准都有随机猜测的基线,四选一是 25%,二选一的 WinoGrande 是 50%。如果不减掉这个地板,一个完全失效的映射器在 WinoGrande 上也能拿到接近 70% 的保留率,看起来还行,实际上什么都没保住。归一化之后把随机猜测放到 0%、目标模型自己放到 100%,数字才诚实。
做了这个校正,Llama 3.1 的 8B 到 70B 就从 72.8% 掉到 62.9%,WinoGrande 单项从 87.1% 直接掉到 58.5%。这一对是全部评测里参数比例最悬殊的,从 80 亿搬到 700 亿,能保住六成多已经算不错。真正刺眼的是它的 GSM8K,只有 18.2%,而目标模型自己能做到 81.12%。也就是说这个映射器保住了模型的常识判断,却几乎废掉了它的数学推理能力。这两种能力在同一份缓存上的存活率差了四倍多,这个落差本身就值得注意。
GSM8K 是这张表里最残酷的一列,因为它的地板本来就接近零,归一化前后数字不变,没有虚高的空间。它考的是八样本带思维链的数学推理,要求模型沿着多步链条一路推下去,中间任何一步的表示失真都会让整条链断掉。所以这一列可以当成一个压力测试:多选题看的是模型能不能选对,数学题看的是模型的内部状态还够不够干净。四对可用的模型里,只有 Qwen3 14B 到 32B 的 GSM8K 站住了 95.6%,其余三对分别是 68.8%、36.6% 和 18.2%,衰减非常陡。
两个垮掉的对都来自 Ministral 3,目标都是 14B。8B 到 14B 归一化后只剩 11.1%,3B 到 14B 是 14.7%,GSM8K 分别是 1.6% 和 3.2%,基本等于完全不会做题了。这两对的键值配置是匹配的,走的是同一套流程,同样的校准数据,同样的超参扫描,结果就是不行。论文没有掩饰这个结果,反而把它当成最重要的线索:键值匹配与迁移成功相关,但不构成保证。
为什么同样的拟合质量会有不同结局
到这里出现了一个真正有意思的谜题。工程上最自然的做法是拿拟合的解释度当筛选指标,如果它能预测下游表现,那么部署前只要看一眼拟合质量就能决定这对模型能不能用。论文测了这个想法,答案是不行。这个否定结论比一个正面指标更有价值,因为它拦掉了一条看起来顺理成章、实际会误导部署决策的捷径。如果按拟合质量来筛选,你会把一对废掉的组合放进生产环境。
反例给得很干净。Llama 3.1 的 8B 到 70B,校准集上键的解释度是 0.84,小到大方向 HellaSwag 保住 94%,可反过来大到小只剩 37%。Ministral 3B 到 8B 拟合出来的解释度一模一样也是 0.84,两个方向都稳定保住 93%。同样的拟合质量,下游结局差了一倍多。解释度在单对模型内部仍然有用,比如用来挑源层,但跨对比较时它给不出答案。同一个方向上的两对模型可以有相同的拟合质量和完全不同的结局,这说明缺失的信息不在回归本身里。
论文找到的答案在于误差落在哪里,而不是误差有多大。解释度衡量的是每个通道的重建精度,所有维度一视同仁;但注意力不是这样工作的,它拿键去和查询打分,再用得到的注意力权重给值加权。真正决定下游行为的,是目标模型最终会算出来的那个注意力输出。于是他们直接测量这个量,比较用映射缓存和用真实缓存算出的注意力输出之间的余弦相似度。
这个指标的预测力明显更好。在三个家族的十二次配对评测里,注意力输出余弦与 HellaSwag 保留率的皮尔逊相关系数是 +0.57,而校准集上键的解释度相关系数是 -0.20,等于没有关系甚至方向相反。这个结论对工程实践的意义很实在:要判断一对模型能不能迁移,别看回归拟合得多漂亮,去看注意力输出还剩多少相似度。论文也诚实地指出了这个指标的局限,它是事后诊断,必须先把映射器拟合出来才能测,所以还不能用来在拟合之前预筛模型对。找一个拟合前就能算的可迁移性信号,被列进了未来工作。
再往下追一层,论文提出了误差集中度的概念来解释为什么余弦会不同。做法是把映射器的键误差投影到目标查询矩阵的右奇异向量上,按对应奇异值的平方加权,再除以全部分量的平均误差。集中度大于一,说明误差恰好落在注意力会读取的方向上;小于一,说明误差落在注意力忽略的地方。同样大小的误差,藏对了位置就无害,藏错了位置就致命。
非线性能救回来,但救的是特定的病
既然线性在两对上失效,自然的追问是换成非线性能不能救。论文训练了一个多层感知机作为替代,两个 1024 单元的 ReLU 隐藏层,用和岭回归相同的均方误差损失,在推理时直接替换掉线性映射。除了映射器的函数形式,其他一切保持不变,这样对照才干净。评测覆盖了四对模型,从最成功的一直到最失败的都包含在内。值得注意的是,换成多层感知机就放弃了闭式求解的全部好处,重新回到需要梯度训练的路上。
结果分成两半,很值得玩味。在岭回归本来就成功的对上,多层感知机反而略微落后:Qwen3 14B 到 32B 从 97.6% 变成 97.3%,Ministral 3B 到 8B 从 93.3% 掉到 91.8%。在岭回归失败的两对上,它把 HellaSwag 保留率抬高了 24.3 到 36.8 个百分点,8B 到 14B 从 58.7% 直接拉到 95.5%,四对模型全部越过了 90%。3B 到 14B 那一对也从 68.0% 抬到了 92.3%,两个原本报废的组合都被救了回来。这个反差说明问题不在数据,也不在流程,而在映射函数的表达能力上。
换句话说,非线性不是普遍更强,它只在特定情况下有用。哪里的跨模型 KV 关系本来就是线性的,线性映射就够了,加复杂度反而引入了额外的拟合噪声。哪里的关系不够线性,才需要非线性来补。这个判断避免了一个很容易犯的错误,就是默认更复杂的映射器总能带来更好的结果。
失败对上究竟出了什么问题,误差集中度给了答案。在用于评测的 HellaSwag token 上,岭回归的键解释度是深度负值,3B 到 14B 是 -7.81,8B 到 14B 是 -3.22。负值意味着映射器的预测比直接用均值还差,也就是在校准集上拟合出来的线性关系根本没有外推到评测数据上。多层感知机把这个数字分别拉高了 7.62 和 3.08,虽然仍在零以下,但差距缩小了大半。
同时发生的是误差位置的重新分配。在这两对上,多层感知机把键的误差集中度平均降了约 2.5,注意力输出余弦平均提升约 0.45。误差总量并没有消失,它只是被挪到了注意力读不到的方向上去。这就是 24 到 37 个百分点收益的真正来源,不是拟合得更准,而是错得更不要紧。这个视角对做推理优化的人很有用:评估一个近似方法的时候,误差的分布位置可能比误差的总量更值得关心。同样的道理也适用于量化和稀疏化,它们本质上都是在往模型里注入可控的误差。
反过来的证据同样重要。Ministral 3B 到 8B 这一对,换成多层感知机之后集中度和余弦两项都改善了,HellaSwag 却还是掉了 1.5 个百分点。所以重新分配误差本身不是充分条件,它只在被放错位置的误差大到足以造成影响时才起作用。这条边界让整个机制解释站得住脚,而不是变成一个万能的事后归因。
速度这一面的真实数字
精度的账算完了,该看省下来的时间到底有多少。测量环境是单个八卡 H100 节点带 NVLink,bf16 精度,每个测量点跑 50 次预热和 30 次计时。对照组是目标模型跑一遍完整的预填充,用的是 flash_attention_2,不含语言模型头。实验组是映射器把源缓存翻译成目标格式,包含跨卡搬运缓存所需的传输时间。这个对照口径是公平的,因为它把迁移方案自己的额外开销也算进去了。下面这张表取 Qwen3 14B 和 32B 这一对,两个方向都列出来。
| 序列长度 | 映射器 14B→32B | 重新预填充 32B | 加速比 | 映射器 32B→14B | 重新预填充 14B | 加速比 |
|---|---|---|---|---|---|---|
| 64 | 14.0 ms | 61.7 ms | 4× | 11.6 ms | 39.2 ms | 3× |
| 8K | 67.8 ms | 1154.8 ms | 17× | 101.9 ms | 501.0 ms | 5× |
| 32K | 277.6 ms | 6975.3 ms | 25× | 427.1 ms | 2952.7 ms | 7× |
规律很清晰,序列越长收益越大。64 个 token 时只有 4 倍,因为这时候映射器的墙上时间被一个固定开销占满了,Python 调度和跨卡传输加起来 14 毫秒,跟实际计算量没什么关系。到 32K 时加速比冲到 25 倍,因为重新预填充的成本随长度和模型规模一起涨,而映射器只是一堆按层批处理的矩阵乘法,增长慢得多。这个趋势正好对上了长会话智能体的使用形态。
两个方向的收益不对称,值得单独说一下。小到大方向在 32K 上是 25 倍,大到小只有 7 倍。原因在分母上:接收方是 14B 的时候,它自己重新预填充本来就只要 2952 毫秒,比 32B 的 6975 毫秒便宜得多,所以省下来的绝对值和相对值都更小。反过来看,把缓存往大模型上搬,省掉的正是最贵的那一次计算。
七对模型的完整数据也给了:小到大方向从 2.7 倍到 25.1 倍,Llama 3.1 的 8B 到 70B 是 4.5 到 14.9 倍,把 11562 毫秒压到 777 毫秒。大到小方向 Qwen3 的 32B 到 14B 是 3.3 到 6.9 倍,Llama 70B 到 8B 是 2.8 到 7.6 倍。所有配置都在正收益区间,没有出现映射比重算还慢的情况。Ministral 3 的两对倍数最低,3B 到 8B 只有 2.7 到 4.0 倍,因为目标模型本身就小,重新预填充的基数不高。倍数最高的始终是目标模型最大的那些配置。
论文对这组数字的诚实程度值得肯定,它自己列了三条限制。两组测量都用合成输入,隔离出了纯计算成本;端到端的迁移还要把映射好的缓存发给目标进程,这部分没有测。映射器跑在 eager 模式下,没用 torch.compile 也没用 CUDA 图,意味着还有优化空间。Ministral 3 的重新预填充只算了语言模型解码器主体,不含视觉塔。
多轮切换会不会越漂越远
对话中途切换是论文列的三个应用场景之一,这个场景有一个特有的风险:如果每次切换都引入一点误差,那么来回切几十轮之后,误差会不会累积成灾。这个担心是合理的,因为映射之后的缓存会成为下一轮的历史,误差有机会自我放大。论文用 CoQA 做了测量,100 段对话,每段约 15 轮,覆盖五个领域,在 Qwen3 14B 和 32B 之间来回切。选这一对是因为它保留率最高,如果连它都漂,其他对就不用谈了。
衡量方式是漂移,定义为目标模型自己的 F1 分数与映射器在同一轮的 F1 分数之差。结果是两个方向的漂移都很小。小到大方向从第一轮到第十轮,差距只扩大了 1.7 个百分点,而且这个扩大主要来自 32B 的天花板在上升,映射器本身保持稳定。大到小方向的漂移呈线性增长,每轮 0.33 个百分点。
论文给的判断很克制:两个斜率都太小,不足以在十轮之内造成级联失败,但大到小方向的线性漂移在非常长的会话里仍然会累积。0.33 个百分点每轮听起来微不足道,跑到五十轮就是十六个百分点,这对一个长期运行的智能体会话来说不能忽略。工程上的对策是设一个漂移预算,超过阈值就强制做一次真实的预填充来重置状态。这样既拿到了大部分切换的加速,又给累积误差设了上限。
还有一个细节说明这个测量的可靠性。源层数这个超参数是在多选题基准上选定的,GSM8K、CoQA 多轮和预填充延迟三项从选择过程中排除,是构造上的留出集。论文还做了留一交叉验证,去掉一个选择基准再重新选超参,看被去掉的那个基准的分数变化多少。四对可用模型上最大变化 1.45 个百分点,全部六对上最大 2.49 个百分点。
另外三个完全没参与选择的基准也测了,PIQA、BoolQ 和 ARC-Easy。四对可用模型的平均保留率都在 96% 以上,两对失败的分别是 63.7% 和 59.3%,分档结构完整重现。这说明前面的分档不是超参过拟合出来的假象,而是真实的能力边界。这类留出验证在工程论文里经常被省掉,做了就值得指出来。保留率甚至比样本内还略高一点,因为 PIQA 和 ARC-Easy 本身比选择基准更简单。
这套方法进你的推理栈之前
把论文的全部结论收在一起,能得到几条可以直接用的判断。第一条是适用范围:只做族内迁移,源和目标必须共享注意力头数和每头维度,也就是论文说的键值匹配。跨家族的情况完全没测,论文把它列进了未来工作,因为不同家族可能共享足够的表示结构,也可能需要完全不同的机制。另外三个家族用的都是稠密全注意力,混合架构和线性注意力的情况同样没有覆盖。
第二条是筛选方法。键值匹配是必要条件但不是充分条件,六对里有两对匹配却失败了。真正的筛选指标是注意力输出余弦,它和保留率的相关系数是 +0.57,而拟合解释度是 -0.20。代价是这个指标必须先拟合映射器才能算,所以上线一对新模型的流程是:拟合一次,测余弦和一个代表性下游基准,通过了再进生产。
第三条是成本结构。拟合一次约 47 到 87 分钟单节点八卡,是一次性的离线开销;映射器本体 10.1 亿到 33.6 亿参数,占 4 到 12 GB 存储,是常驻成本。这笔存储要算进显存预算,尤其是当你有多对模型需要各自的映射器时,数量是按模型对而不是按模型算的。校准数据方面有个好消息:样本量超过 200 条就基本饱和,50 条也只差 1.6 个百分点,但校准语料的领域会影响结果,代码语料在 HellaSwag 上掉了 5.24 个百分点。
第四条是收益预期,别按 25 倍去算账。25 倍是 32K 上下文小到大方向的最好情况,短提示只有 3 到 4 倍,大到小方向普遍在 3 到 7 倍。真正能吃到高倍数的是长上下文加频繁上切的场景,也就是长会话智能体和成本质量级联。如果你的会话都很短,或者切换不频繁,这套方法的收益会被固定开销吃掉大半。
第五条关于精度取舍。四对可用的模型里,多选题类任务普遍保住九成以上,但数学推理衰减剧烈,最差的只有 18.2%。所以决策不该是"这对模型能不能迁移",而应该是"这对模型在我的任务上能不能迁移"。如果你的业务主要是检索问答和分类,风险可控;如果涉及多步推理、代码生成或者数学计算,必须用自己的任务重新测一遍。
最后一条是关于线性这件事本身的判断。这篇论文最有意思的地方不是 25 倍加速,而是它证明了同一家族不同尺寸的模型,对同一段文本算出的中间表示之间存在相当强的线性关系,强到用 500 条序列拟合的岭回归就能捕捉大半。这个事实的含义超出了缓存复用:它说明模型规模增长带来的表示变化,有很大一部分是可以用线性变换刻画的。至于这个结构从哪里来,是共享的预训练数据、相似的架构选择,还是别的什么,论文把它留成了开放问题。
更多推荐

所有评论(0)