KV Cache 从哪来:自回归生成的记忆账本
Transformer 自回归生成时,每生成一个 token 都要对「全部历史 token」做注意力。如果每步都重算所有历史 token 的 Key 与 Value,生成第 10000 个 token 就要重复计算前 9999 个——完全不可行。KV Cache 的解法:把每个 token 算过的 Key/Value 缓存下来,后续步骤只计算新 token 的 Q/K/V,历史部分直接查缓存。
这个缓存是长上下文的隐形账单:KV Cache 大小 ∝ 序列长度 × 层数 × 头数 × 头维度 × 精度字节数。算一笔典型账:一个 32 层、64 头的模型,批量 1、序列 128K、FP16 精度,KV Cache 可以达到几十 GB——超过模型权重本身,成为显存的第一大头。更狠的是它随批量线性放大:服务 32 路并发,KV Cache 再乘 32。长上下文模型的推理成本,账单上写的是 KV Cache,不是参数量。
KV Cache ≈ 2 (K和V) × 层数 × 序列长度 × KV头数 × 头维度 × 字节
64 头模型 128K 序列:几十 GB / 单请求 —— 超过权重本身
并发 ×32:显存瞬间爆掉
三代瘦身:MQA、GQA 与 MLA
既然 KV Cache 正比于「KV 头数」,瘦身的第一刀就砍向它。MQA(Multi-Query Attention):所有查询头共享一组 KV——KV 头数从 64 降到 1,缓存缩小 64 倍;代价是表达力受损(所有头看同一个「记忆视角」),质量有可感知的下降。GQA(Grouped-Query Attention)是折中的胜利:KV 头分组共享(如 8 组),缓存缩小 8 倍、质量损失几乎不可测——当前主流开源模型几乎清一色 GQA,这个 2023 年的论文如今已是默认配置。
MLA(Multi-head Latent Attention)是 DeepSeek-V2/V3 带来的第三代:把 KV 压缩到低秩潜在向量再按需解压——缓存存的不是完整 KV 而是压缩表示,显存开销与 GQA 相当甚至更低,同时保留多头注意力的表达力。MLA 的工程巧思在于压缩矩阵的设计让「解压」可以被吸收进上一步计算,推理时无需显式解压。三代的演进逻辑一致:KV Cache 的显存经济学,是当代注意力架构创新的第一驱动力——比「更强的表达力」更迫切。
稀疏与线性:换一种注意力形态
GQA/MLA 压缩的是「每个 token 存多少」,另一条路线压缩「每个 token 看多少」。稀疏注意力的观察:注意力矩阵里大量位置的分接近零——本地窗口 + 少量全局锚点的稀疏模式可以逼近全注意力(Longformer 时代已验证),近年的大模型级别实践(如 DeepSeek 的 NSA、原生的稀疏注意力设计)把「只算重要位置」做进训练与推理,超长序列的计算与缓存同步下降。难点在「重要性」的判定:固定模式(窗口+锚点)简单可靠但可能漏掉远距离依赖;动态选择(按分数挑 token)更聪明但要小心「漏看关键信息」的长尾风险。
线性注意力走得更远:把 softmax 注意力的内积结构改写,让复杂度从 O(n²) 降到 O(n)——Mamba 这类状态空间模型(本库已有专篇)是同思想的另一实现:用一个固定大小的「状态」总结全部历史,缓存从「线性于序列长度」变成「常数大小」。代价是精确回忆能力下降——固定状态记不住全部细节,这对需要精确引用长文档首位细节的任务是硬伤。当前的工程共识:线性/状态空间路线适合流式与超长场景,需要精确长程检索的任务仍是(稀疏化或压缩化的)标准注意力的领地。
上下文长度宣传的真实约束
「支持 1M 上下文」的参数页需要四道折扣来读。第一道:有效上下文——窗口开到 1M 不代表模型用得好:位置编码外推(RoPE 的缩放策略如 NTK/YaRN)让模型「能处理」超长输入,但中部的召回质量随长度衰减(本库后续会讲 Lost in the Middle 的位置偏差),末尾与开头强、中间弱是普遍形态。第二道:性能约束——1M 上下文的首 token 延迟与显存占用,实际部署中常被迫限流或降并发。第三道:数据配比——模型在预训练里见过多少超长样本,决定长上下文能力是「真会」还是「外推勉强撑住」;长上下文的后训练(长文档指令数据)质量是各家拉开差距的地方。第四道:缓存复用——前缀相同的多次请求可以共享 KV Cache(前缀缓存),实际服务成本高度依赖请求模式——这是工程账,不在参数页上。
部署视角的缓存账:前缀缓存与并发经济学
把 KV Cache 的知识落到部署账本上,两个概念决定服务成本。前缀缓存(prefix caching / KV cache reuse):多轮对话与共享 system prompt 的场景里,请求序列的前缀高度重复——把公共前缀的 KV 缓存保留复用,第二个请求起就不必重算前缀(首 token 延迟从「重算全部」降到「只算增量」)。智能体工作流(每次都带同一个长 system prompt + 工具说明)是前缀缓存的最大受益者:prompt 越长、复用率越高,缓存命中的请求成本可以降一个数量级。厂商 API 已把它产品化(缓存的 token 按折扣计价)——「写 prompt 时把稳定内容放前面、动态内容放后面」从最佳实践变成了省钱技巧。并发的显存经济学:KV Cache 随并发线性放大(正文的账),推理引擎的「批处理大小」因此被显存而非算力约束——PagedAttention 类技术(把缓存像操作系统内存分页一样精细管理,vLLM 引擎的核心)把碎片浪费从百分之几十压到个位数,同一张卡的并发容量翻倍。这两个概念合起来的服务视角结论:长上下文模型的真实服务成本 = 权重显存 + KV 显存(被并发与缓存策略放大)+ 计算——参数页看不到的这三项,才是选型时该算的账。观察一个服务的价格表(缓存命中价、长上下文加价、批量折扣),基本能反推它的缓存与调度策略——部署经济学写在价格里。
无限上下文的幻影:流式与记忆的混合路线
「无限上下文」是长上下文竞赛的营销终点,技术上的现实路径是混合架构。KV Cache 的物理极限:缓存线性膨胀意味着「无限上下文」的显存需求是无限的——纯缓存路线必有上限。压缩与遗忘的混合:把远端历史压缩成「记忆摘要」(进 KV 的压缩 token 或外部记忆库),近期历史保留完整缓存——「完整窗口 + 记忆层」的混合让有效上下文理论上无限(记忆层兜底),代价是远端细节的损失(压缩是有损的)。流式推理的工程形态:无限流输入(直播字幕、传感器流)要求「边收边处理边遗忘」——滑动窗口 + 周期性巩固(把窗口内容总结成记忆注入)是工作形态,与测试时训练篇的「经验沉淀」思想合流。这条混合路线与记忆研究(本库智能体记忆篇)在概念上会师:「无限上下文」的正确实现不是更大的缓存,是「缓存 + 记忆系统」的分层——上下文工程(Agent 工程篇的主题)在这里从技巧变成架构。给选型者的判断:宣传里的「1M/10M 上下文」要看实现方式——纯外推的窗口(长但贵且位置偏差重)与混合记忆架构(无限但远端有损)是两种产品,混淆它们会导致错误的期待。
一次推理的显存流水账
把 KV Cache 的知识变成一张可自查的流水账模板。以「7B 模型、GQA 8 组 KV 头、32 层、128K 上下文、FP16、单请求」为例:权重侧约 14GB;KV 侧按公式估算(2 × 32 层 × 128K × 8 头 × 128 维 × 2 字节 ≈ 16GB)——KV 反超权重,这就是长上下文的显存真相。叠加并发的放大:8 路并发 = 128GB 的 KV 需求,单卡不可能,要么张量并行分摊、要么 KV 量化(FP8/INT4 把 KV 再压一半到四分之一,精度损失可控)、要么限制并发或上下文长度。自查的三个动作:读模型卡拿到「层数、KV 头数、头维度」(GQA 后的 KV 头数,不是注意力头数);用公式算目标长度与并发的 KV 需求;对照部署卡的显存算「还能塞几个并发」。这套算术五分钟就能做完,却能在选型阶段避免「跑不起来」或「并发惨淡」的返工——显存经济学是长上下文部署的第一课,也是最便宜的一课。
选型的三个实操动作
收尾给三个立即可用的实操动作。动作一:读对模型卡的两个字段——「KV 头数」(GQA 之后远小于注意力头数,别抄错)与「上下文长度对应的推荐显存」,多数严谨的模型卡会给出不同长度的显存参考表,直接查表比自己估快。动作二:用真实负载压测并发——公式给出理论值,实际吞吐还受调度与碎片影响,用目标长度 + 目标并发跑一次压测(vLLM 的 benchmark 脚本现成),拿到「每卡每小时的请求容量」再谈成本。动作三:为不同场景配置不同上下文档位——对话类业务限 8K–16K(KV 省、快)、文档类业务按需放开到 128K,一个服务两套档位比一刀切省一半以上的显存与账单。KV Cache 的学问最终都落在部署决策上:会算这笔账的团队,同样的卡多服务一倍的请求——这就是「内存经济学」的实践含义。
小结
长上下文的成本结构由 KV Cache 主导:它随序列长度与并发线性膨胀,轻易压过模型权重。架构创新的三代主线——MQA 的激进共享、GQA 的分组折中(已成默认)、MLA 的低秩压缩(DeepSeek 路线)——与两条并行路线(稀疏化「只看重要的」、线性化「常数状态」)共同构成当前注意力架构的全景。「上下文长度」的参数页数字要过四道折扣:有效上下文、性能约束、训练数据配比与缓存复用模式。注意力架构的下一个创新点,大概率仍由这份内存账单驱动。下一篇我们转向另一条「打开黑箱」的路线:机械可解释性——把模型的内部表征拆成人类可读的特征。
读者留言
COMMENTS 暂无还没有留言,来说第一句?