读懂大模型的"记忆":KV Cache 到底是什么?

大模型每生成一个 token,都要从头开始计算吗? 为什么加了一块小小的缓存,生成速度就能快几十倍? 这篇文章用最通俗的方式,把 KV Cache 一次讲透。
本文收录于微信公众号:愤怒的代码 AngryCode


0x00、大模型是如何推理的

在聊 KV Cache 之前,得先搞清楚一件容易被想当然的事:

大模型不是一次性把整句话想好了再说出来的,而是一个字接一个字"蹦"出来的。简单的说大语言模型是做文字接龙的游戏。

这里的"字",专业术语叫 Token(词元)。它可能是一个完整的词、半个词,甚至一个标点符号。为方便理解,你可以先把它当成"一个字"。

举个例子,你输入:

深圳今天

模型看着这四个字,预测出下一个最可能的字是"天":

深圳今天天

然后它把"天"接到后面,再看"深圳今天天"这五个字,预测下一个字"气":

深圳今天天气

如此循环,直到模型觉得该停了为止。这个过程叫自回归生成(Autoregressive Generation)。

关键点来了:模型每预测一个新字,都必须"看"一眼前面所有的字,才能决定下一个字写什么。

这句话,是理解 KV Cache 一切的起点。


0x01、注意力机制:Query、Key、Value 三兄弟

那"看一眼前面的字"具体是怎么做的?

大模型内部有个核心组件叫注意力层(Attention Layer) ,是整个 Transformer 架构的心脏。它的职责是:判断前面的字里,哪些对预测下一个字更重要。

比如预测"深圳今天天"后面的字时,"今天""天"显然比"深圳"更重要。注意力的作用,就是自动算出这种"重要程度"。

在注意力层里,每个字都会被转化成三样东西:

  • Query(查询,Q):这个字"想找什么"。
  • Key(键,K):这个字"身上有什么标签"。
  • Value(值,V):这个字"实际携带的信息"。

打个比方,你去图书馆想找讲 Transformer 的书:

  • Query 是你的需求:"我想找讲 Transformer 的书"。
  • Key 是每本书侧边贴的标签:"深度学习""机器学习""川菜菜谱"......
  • Value 是书本身的正文内容。

过程是:拿你的 Query 去和所有书的 Key 比对相似度,算出每本书有多"相关",然后按相关度,把最相关那几本书的 Value(正文)拿过来用。

翻译成数学公式,就是那行著名的:

scss 复制代码
Attention(Q, K, V) = softmax(Q · Kᵀ / √d) · V
  • Q · Kᵀ:拿 Query 和所有 Key 做点积,算出"相关度分数"。
  • softmax:把分数归一化成"注意力权重",加起来等于 1。
  • · V:用权重对所有 Value 加权求和,得到最终的输出。

注意一个细节 :每个字的 K 和 V,只由这个字自己决定,和后面来了什么字无关。这一点极其重要,它正是 KV Cache 能成立的根本原因(后面会讲)。


0x02、问题:大量重复计算

现在回到生成过程,看看哪里浪费了。

没有缓存 时,模型每预测一个新字,都要把整句话所有字重新过一遍网络------它们的 Q、K、V 要重算,前馈层(FFN)要重跑,注意力分数矩阵也要重新算。而这一轮算出来的东西,除了最后一个位置,其余全部丢掉。

所以浪费的算力远不止"Q、K、V 重算"这一项。真正的大头其实是那些旧 token 的前馈层和投影计算,只是它们算完就被扔了,谁也没用上。

以上面的例子走一遍:

步骤 输入 需要计算 Q/K/V 的字
第 1 步 深圳今天 深、圳、今、天(4 个)
第 2 步 深圳今天天 深、圳、今、天、天(5 个)
第 3 步 深圳今天天气 深、圳、今、天、天、气(6 个)

发现问题了吗?

"深"的 K 和 V,在第 1 步算过一遍,第 2 步又算一遍,第 3 步再算一遍。每个已经见过的字,都在被反复计算。

句子越写越长,浪费越严重:如果已经生成了 1000 个字,那么预测第 1001 个字时,就要把前面 1000 个字的 K、V 全部重算一遍,只为算出 1 个新字。

这就是自回归生成最致命的性能瓶颈------计算量随序列长度呈平方级增长。

↑ 同样的「深圳今天天气」,左边每一步都在重算整句,右边只算新 token


0x03、KV Cache 的核心思想:算一次,存起来,反复用

解决思路也很简单:

每个字的 K 和 V,只算一次,存进一块内存里,以后每次直接用,不再重算。

这块内存,就是 KV Cache。

再看一遍刚才的例子,这次有缓存:

第 1 步:输入"深圳今天"

  • 计算 4 个字的 K、V,存进缓存。
  • 预测出"天"。
  • 缓存里现在有:深、圳、今、天 的 K 和 V。

第 2 步:这次只需要输入新字"天"

  • 前面的 K、V 直接从缓存里读,不重算。
  • 只为新字"天"算一次 K、V、Q,算完也存进缓存。
  • 预测出"气"。
  • 缓存里现在有:深、圳、今、天、天 的 K 和 V。

第 3 步:只需输入新字"气"

  • 前面 5 个字的 K、V 全部从缓存读。
  • 只为"气"算一次 K、V、Q,存进缓存。
  • 预测出下一个字......

看出来了吧:每一步只算 1 个新字,剩下的全部复用缓存。


0x04、为什么只缓存 K 和 V,不缓存 Q?

这是几乎每个人都会问的问题。答案一句话:

Q 是一次性的,K 和 V 是要反复用的。

展开说:

  • Query 只在"当前这个字被预测"的那一刻用一次。当前字拿自己的 Q 去和前面所有字的 K 比对,比对完,这个 Q 的使命就结束了,再也不会有人用到它。
  • K 和 V 则要在未来每一个字生成时都被用到。因为每一个新字都要"看"前面所有字,而"看"的动作,本质上就是用新字的 Q 去查所有旧字的 K,再取旧字的 V。

所以缓存里只存 K 和 V,不存 Q------这也是它叫 KV Cache 而不是 QKV Cache 的原因。


0x05、到底快了多少?算一笔账

假设从 1 个字开始,生成到 100 个字:

没有 KV Cache,每个字都要对整句算一遍 K、V,总计算量是:

ini 复制代码
1 + 2 + 3 + ... + 100 = 5,050 次

有 KV Cache,第 1 步算 1 次,之后每步只算新字 1 次:

ini 复制代码
1 + 1 + 1 + ... + 1 = 100 次

约 50 倍的差距。而且序列越长,差距越大------这是典型的"把 O(n²) 降成 O(n)"。

需要说明的是,上面这个计数是个简化模型:它只数了 K、V 的投影次数,没有把 FFN、注意力分数这些开销算进来。但即便如此,实际的加速比通常还要更可观。

这就是为什么 KV Cache 是 LLM 推理加速里最基础、也最重要的一个技巧。


0x06、天下没有免费的午餐:速度换内存

KV Cache 用速度换来了一个代价:内存。

缓存里要存下"到目前为止每一个字的 K 和 V",所以:

序列越长,缓存越大。

缓存大小可以用一个公式精确算出来:

复制代码
KV Cache 大小 = 2 × 层数 × KV头数 × 头维度 × 序列长度 × 每元素字节数
  • 2:因为 K 和 V 各一份。
  • 每元素字节数:FP16 精度下是 2 字节。

代入几个真实模型(FP16、单个 token):

模型 层数 KV 头数 头维度 每个 token 的缓存
Llama 2 7B 32 32 128 ~0.5 MB
Llama 2 13B 40 40 128 ~0.8 MB
Llama 2 70B(GQA) 80 8 128 ~0.3 MB

看着每 token 才 0.5 MB 不吓人?算上序列长度就惊人了:

7B 模型,2048 个 token 的上下文,单个请求就要 ~1 GB 的 KV 缓存。

如果同时服务 32 个用户(batch = 32),光 KV 缓存就要 32 GB 显存。这还没算模型本身的权重。

这就是为什么"并发服务很多用户"时,KV Cache 的内存会成为真正的瓶颈 (可以理解今天内存那么昂贵了吧T_T)。


0x07、进阶:当缓存本身成为瓶颈怎么办

理解了上面这些,你就能看懂业界一系列优化是在干什么了。它们本质上都在回答同一个问题:怎么让这块缓存更小、更省、更好管?

1. GQA / MQA:从源头减少缓存

  • MQA(多查询注意力,Multi-Query Attention):让所有注意力头共享同一份 K 和 V,缓存直接砍到原来的 1/头数。
  • GQA(分组查询注意力,Grouped-Query Attention):折中方案,几个头共享一份 K、V。上表里 Llama 2 70B 的 KV 头数只有 8,就是用 GQA 把缓存压了下来。

(顺便提一下老一代的多头注意力 MHA(Multi-Head Attention,多头注意力):每个头都有自己独立的 K 和 V,缓存最大。MQA 和 GQA 本质上都是往 MHA 这个方向做"减法"。)

2. KV Cache 量化

缓存不用存 FP16,改成 8-bit 甚至 4-bit,内存占用再砍一半以上,而精度损失通常很小。(可以翻阅前面量化的数学原理的文章)

3. PagedAttention(分页注意力)

这是 vLLM 提出的经典方案:把 KV 缓存像操作系统管理内存一样,切成固定大小的"页"来管理,按需分配、灵活回收,彻底解决"预留一块连续大内存"造成的碎片和浪费,大幅提升并发能力。

4. 滑动窗口 / 注意力汇聚(Attention Sink)

对于无限长的对话,只保留最近一段窗口,加上开头几个"注意力锚点",让缓存固定在一个大小,不再无限膨胀。

这些名字你现在听来可能陌生,但只要记住一句话就够了:它们全部是冲着 KV Cache 的内存开销去的。


0x08、总结

把这篇文章浓缩成几句话:

  1. 大模型逐字生成,每预测一个字都要参考前面所有字。
  2. 回头看前面那些字,靠的是 Q、K、V:Q 查、K 匹配、V 取值。
  3. 如果不缓存,每个旧字的 K、V 会被反复重算,计算量呈平方级增长。
  4. KV Cache 的核心:每个字的 K、V 只算一次,存起来反复用,把计算量从 O(n²) 降到 O(n)。
  5. 只缓存 K、V 不缓存 Q,因为 Q 是一次性的,K、V 要反复用。
  6. 代价是内存------序列越长、并发越高,缓存越吃显存。
  7. 后续的 GQA/MQA、量化、PagedAttention 等优化,都是在跟这块缓存的内存开销较劲。

KV Cache 是大模型推理优化的第一块基石。理解了它,你就拿到了进入 LLM 推理、vLLM、量化压缩这些话题的入场券。

相关推荐
Joecien1 小时前
qwen-mt-uni 多模态翻译实战:从接入到四类输入的完整步骤
人工智能·自然语言处理·机器翻译
怪力左手1 小时前
cuda-toolkit离线安装
linux·运维·人工智能
鬼手点金1 小时前
带工具调用的多轮交互:Agent 的核心循环
服务器·人工智能·windows·深度学习·microsoft·机器学习·交互
Dawson Zhu1 小时前
【CUDA编程入门②】CUDA性能优化基础:内存访问、Warp Shuffle与Occupancy调优
人工智能·语言模型·架构·aigc·agi
DFT计算杂谈1 小时前
基于大语言模型的用户画像提取与修正操作
人工智能·语言模型·自然语言处理
明志数科1 小时前
具身智能训练数据的质量口径问题:从规模扩张到评测基准
大数据·人工智能
寰宇视讯1 小时前
焙烤颜值经济持续升温,膳印科技以食品喷花打印技术树立行业标杆
大数据·人工智能·科技
Cosolar1 小时前
上下文工程的第一公里:LangChain 讲透 Agent 检索的三大坑与六套解法
人工智能·llm·agent