前面几个章节讲的是训练时注意力怎么算。这一章节切换到推理(inference)——模型训练完之后,和你对话时的计算过程。
推理时有一个跟训练不同的关键特征:模型一次只生成一个 token。
推理的逐步过程
回忆第 03 章节的自回归生成:模型看到已有的全部文本,预测下一个 token,把它接到末尾,然后再预测下一个。
假设已有的文本是「今天天气」(4 个 token),模型要预测第 5 个 token。
笨办法:把 4 个 token 全部送进模型,经过 28 层 Transformer,得到第 5 个 token 的预测——假设是「不」。然后把 5 个 token「今天天气不」全部送进模型,得到第 6 个 token 的预测。然后 6 个 token 全部送进去……
每一步都把整个序列从头算一遍。如果已经生成了 500 个 token,第 501 步就要处理 501 个 token 的完整序列——绝大部分计算(前 500 个 token 的处理)和上一步完全一样,全是重复劳动。
聪明办法:第一次计算 4 个 token 时,把中间结果存下来。第二次只需要计算新增的第 5 个 token,利用存下来的结果补全注意力计算。第三次只算第 6 个,以此类推。
这个「存下来的中间结果」就是 KV cache。
KV cache 存的是什么
在注意力计算中,新 token 需要和前面所有 token 交互。具体来说:
新 token 的 Q 向量 × 所有历史 token 的 K 向量 → 注意力分数
注意力权重 × 所有历史 token 的 V 向量 → 输出
新 token 的 Q 是新算的,但前面所有 token 的 K 和 V 在上一步已经算过了——只要把它们存下来,就不需要重新计算。
KV cache 存储的就是:每一层、每一个注意力头、每一个已处理 token 的 K 向量和 V 向量。
每生成一个新 token,就往 cache 中追加一行新的 K 和 V。cache 随着生成的进行越来越长。
占多少内存
用 Sophia 的参数来算。
每一层有 16 个注意力头,每个头的维度是 96。每个 token 在每一层需要存储一个 K 向量(96 维)和一个 V 向量(96 维)。
一个 token 在一层的 KV cache 大小 = 16 头 × (96 + 96) 维 = 3,072 个数字。
28 层的总 KV cache = 3,072 × 28 = 85,984 个数字。
如果用 BF16 精度(每个数字 2 字节),一个 token 的 KV cache ≈ 172 KB。
如果上下文长度是 4,096 个 token:
4,096 × 172 KB ≈ 688 MB
将近 700 MB 的显存——仅仅用来存储 KV cache。这还只是一个对话的 cache。如果同时服务多个用户,每个用户都有自己的 KV cache,显存消耗会翻倍增长。
对 Sophia 来说,在一张 RTX 5090(32 GB 显存)上:
模型参数本身 ≈ 2 GB(BF16)
一个 4K 上下文的 KV cache ≈ 0.7 GB
留给操作系统和其他开销 ≈ 若干 GB
还算充裕。但如果上下文扩展到 32K 甚至 128K,KV cache 就会占到好几 GB 甚至十几 GB——成为显存的主要消耗者,远超模型参数本身。
为什么 KV cache 是瓶颈
KV cache 的问题不仅仅是「占内存」,更严重的是:它随序列长度线性增长。
序列长度翻倍,KV cache 翻倍。对话越长,cache 越大。到某个长度之后,显存装不下了,推理就无法继续。
这是所有标准 Transformer 模型共同面对的瓶颈。大模型(几百亿参数)在处理长上下文时,KV cache 的显存消耗常常远超模型参数本身。
整个领域花了大量精力来缓解这个问题。大致有三个方向:
方向一:压缩 K 和 V。 不需要每个头都存完整的 K 和 V。GQA(Grouped-Query Attention)让多个 Q 头共享一组 K、V,减少需要存储的数量。MLA(Multi-head Latent Attention)把 K 和 V 压缩成一个低维的 latent 向量再存储——这是 Sophia 每四层中第四层使用的方法,第 22 章节会讲。
方向二:限制注意力范围。 不让每个 token 看到整个历史序列,只看最近的若干个 token(滑动窗口注意力)。这把 cache 大小从「随序列线性增长」变成「固定上限」,但代价是模型无法关注远处的信息。
方向三:用递推状态替代 cache。 把历史信息压缩成一个固定大小的状态向量,不管序列多长,状态大小不变。线性注意力就是这个方向——下一章节会讲。这是 Sophia 前三层(KDA)使用的方法。
KV cache 的存在解释了什么
理解了 KV cache,你就能理解很多日常使用语言模型时观察到的现象:
- 长对话为什么越来越慢:不是因为模型变「累」了,而是因为每生成一个 token 都要跟越来越长的 KV cache 做点积,计算量线性增长。
- 为什么有上下文长度限制:不是模型「记不住」,而是显存装不下那么长的 KV cache。
- 为什么有些模型支持更长的上下文:通常是因为它们用了 GQA、MLA 或线性注意力来压缩 cache。
下一章节来看 Sophia 处理 KV cache 的核心策略之一:线性注意力——用一个固定大小的递推状态完全替代 KV cache。