从 I love you 讲清楚大模型推理:Prefill、Decode、Q/K/V/O 与 KV Cache 到底在做什么

用一个极简例子「I love you」,把 Prefill、Decode、Q/K/V/O、KV Cache 的工程直觉讲清楚。

很多人在第一次接触大语言模型推理时,都会被这些概念绕晕:

这篇文章不追求"数学最完整",而追求"工程直觉最清楚"。只用一个非常简单的例子:

I love you

来把整个过程从头讲明白。


一、先说结论:推理其实分成两个阶段

当用户输入:

I love you

模型要做的不是"重新预测 love 和 you",而是:

给定 I love you,预测下一个 token

整个推理通常分成两个阶段:

1. Prefill

把用户已经给出的 prompt,也就是 I love you,一次性送进模型,完整跑一遍前向传播。

2. Decode

模型根据 Prefill 的结果,生成第一个新 token。如果模型生成了一个新词,比如 too,接下来要继续预测下一个词,这时进入逐 token 的 Decode 阶段。

一句话概括:


二、Q、K、V、O 分别是什么

先把几个最核心的符号说清楚。

Q:Query

当前这个位置"拿什么去查询上下文"。

K:Key

每个历史 token 的"索引"或者"可匹配表示"。

V:Value

每个历史 token 真正携带的信息内容。

O:Output

当前这个位置做完 attention 之后得到的新表示。

如果用一句非常直白的话说:

在 Transformer 的一层里,通常是:

输入 hidden states
  -> 线性变换得到 Q/K/V
  -> attention(Q, K, V)
  -> 得到 O
  -> O 再经过后续计算,作为下一层输入

注意一点:O 不是最终输出给用户的词,而是这一层 attention 的输出表示。真正预测词时,还要把最后一个位置的表示再投影到词表上。


三、为什么是多层网络,这一点决定了 Prefill 不能只算最后一个位置

这是很多人最容易误解的地方。

如果模型只有一层 attention,那么从纯数学上说,如果你的目标只是"预测下一个词",你似乎只需要最后一个位置的 Query 就够了。

但真实的大语言模型不是一层,而是很多层。这意味着:

所以,前面位置的输出不是"没用",而是会成为下一层的输入。也就是说:

第 L 层的输出,会用于构造第 L+1 层的 Q/K/V。

这件事特别重要,因为它解释了:


四、以 I love you 为例:Prefill 到底在算什么

现在正式进入例子。

用户输入 I love you,模型首先会把这三个 token 变成 embedding。这时内存中,最初有的是:

输入侧内存
token ids:I, love, you
embedding 向量
position 信息(位置编码或位置相关机制)

这时还没有 KV cache。现在开始 Prefill。


五、Prefill 的第 1 层:内存里会出现什么

进入第 1 层后,模型会基于这三个位置的输入 hidden states,一次性算出:

Q1, Q2, Q3
K1, K2, K3
V1, V2, V3

注意,这里是对整个 prompt 一次性并行计算的,因为用户给的 I love you 已经完整已知。

接下来会计算 attention,带 causal mask:

最终,第 1 层会输出:

O1:看完 I 之后,第 1 个位置的新表示
O2:看完 I love 之后,第 2 个位置的新表示
O3:看完 I love you 之后,第 3 个位置的新表示

此时,O1/O2/O3 会成为第 2 层的输入。


六、Prefill 的第 2 层以及后续层:为什么前面位置也必须算

进入第 2 层。第 2 层的输入不是原始 embedding 了,而是上一层的输出表示:

第 2 层输入位置 1:上一层的 O1
第 2 层输入位置 2:上一层的 O2
第 2 层输入位置 3:上一层的 O3

然后第 2 层再算自己的 Q/K/V,再做 attention,再得到 O1²、O2²、O3²。后面每一层都重复这个过程。

所以在 Prefill 期间,模型做的其实是:

整段 prompt
  -> 第1层完整前向
  -> 第2层完整前向
  -> 第3层完整前向
  -> ...
  -> 最后一层完整前向

到最后一层时,我们会拿最后一个位置的表示去预测下一个 token。

这就是为什么:

因为它们是构造"最后那个位置的深层上下文表示"的必要中间过程。


七、Prefill 的最后一步:怎么预测下一个词

假设模型有很多层,全部跑完以后,最后一层给出三个位置的最终表示。和"下一个词预测"相关的是:

最后一个位置的最终 hidden state

为什么只有它有用?因为你当前的任务是:

给定 I love you,预测下一个 token

而最后一个位置代表的是:完整看完 I love you 后形成的表示。

接下来,模型会把这个最后位置的表示送入输出层:

最后位置 hidden state
  -> 词表投影(lm_head)
  -> logits
  -> softmax
  -> 得到下一个 token 的概率分布

假设这一步模型预测出了 too。到这里,Prefill 就完成了。


八、Prefill 完成后,内存里到底保留什么

这是最核心的工程问题之一。

会保留的:KV Cache

对于每一层,模型会把这次 prompt 对应的历史 K 和 V 保留下来。如果模型有 N 层,那么 Prefill 结束后,内存里会保留:

第 1 层的 K1, K2, K3 和 V1, V2, V3
第 2 层的 K1, K2, K3 和 V1, V2, V3
...
第 N 层的 K1, K2, K3 和 V1, V2, V3

这就是 KV cache。注意:KV cache 不是一份全局 K/V,而是每一层各有一份。

不会长期保留的:Q

Q 只是当前 step 用来做一次 attention 计算的查询。用完以后,通常就没有复用价值了。

一般也不会长期保留的:中间 O

每一层的 O 主要用于继续流向下一层。一旦整个前向传播结束,它通常也不是后续 decode 反复复用的对象。

一句话总结:

Prefill 结束后,长期有价值的是"历史 token 在每一层上的 K/V",因为它们会在之后的 Decode 中被反复使用。


九、为什么只缓存 KV,不缓存 Q

K/V 会被未来反复使用

假设已经有了 I love you,然后模型生成了 too,接下来还会生成更多 token。未来每一步新的 token,都要去"看"前面的历史:

所以历史 K/V 是典型的"会反复被访问的数据"。

Q 只在当前这一步用一次

第 4 个 token 的 Q4,只会用于这一次:Q4 与所有历史 K 做 attention。等这一步结束,下一步来的已经是 Q5 了。Q4 不会再被后面的 token 复用。

所以 Q 的生命周期非常短:生成出来 → 做一次 attention → 用完就可以扔掉。

因此,从工程上看:


十、进入 Decode:为什么这时只算新 token

Prefill 已经结束,模型预测出了第 4 个 token too。现在序列变成:

I love you too

接下来模型要预测第 5 个 token。这时已经不是"完整 prompt 的第一次处理"了,而是进入 Decode。

Decode 的核心特征是:每次只新增一个 token。

和 Prefill 最大的不同:


十一、Decode 时每一层到底怎么计算

假设现在要处理新 token too

第 1 层

只需要对这个新 token 计算 Q4¹、K4¹、V4¹,然后做 attention:

Q4¹ 去和 [K1¹, K2¹, K3¹, K4¹] 做匹配

其中 K1¹、K2¹、K3¹ 来自 Prefill 留下的 cache,K4¹ 是当前这个新 token 刚刚算出来的。做完 attention 后,得到第 1 层的新输出 O4¹。

第 2 层

把 O4¹ 送到第 2 层,算 Q4²、K4²、V4²,再用:

Q4² 去和 [K1², K2², K3², K4²] 做 attention

历史 K/V 都来自 cache,只需要新算当前 token 对应的那一份。后面所有层都如此。


十二、Decode 每一步结束后,内存会发生什么变化

这一步算完以后,会有两类数据:

要丢掉的临时数据:

要追加到 cache 的长期数据:

于是 KV cache 会从历史长度 3 变成历史长度 4。

然后模型再拿最后一层第 4 个位置的表示去预测第 5 个 token。假设第 5 个 token 是 much,接下来进入下一轮 decode,重复同样的过程:只算新 token 的 Q5/K5/V5,历史 KV 全复用,Q5 用完就丢,K5/V5 追加进 cache。


十三、把整个推理流程用时间线梳理一遍

T0:用户输入到达

内存里有 token ids(I, love, you)、embedding、位置信息。此时还没有 KV cache。

T1:Prefill 开始

模型按层处理整个 prompt:每层都计算完整的 Q/K/V,得到完整的 O,上一层 O 作为下一层输入。

T2:Prefill 结束

内存里有每层关于历史三个 token 的 K/V cache。Q 和各层中间 O 通常不长期保留。真正保留的是所有层的历史 K/V。假设预测结果是 too

T3:第一个 Decode step

模型处理新 token too:每层只为这个新 token 计算 Q4/K4/V4,用 Q4 读取历史 K cache,得到新输出,最后预测第 5 个 token。

T4:第一个 Decode step 结束

KV cache 长度从 3 变成 4,本 step 的 Q4 可以丢弃。

T5 及之后:

对每个新 token 重复同样过程,KV cache 持续增长。


十四、为什么 Prefill 和 Decode 的内存特征差别很大

Prefill 的特点:

Decode 的特点:

工程上可以粗略理解为:

这也是为什么很多推理系统会分别优化这两个阶段。


十五、为什么 KV cache 会越来越占内存

因为每生成一个新 token,KV cache 都会在每一层追加一份新的 K/V,cache 大小随已生成序列长度线性增长

如果模型层数很多、注意力头很多、hidden size 很大、上下文很长,每一层都要存一长串历史 K/V,累计起来就会很可观。

你可以把它想成:

模型在每一层都维护了一份"历史记忆库",历史越长,记忆库越大。


十六、这套机制的本质直觉

如果把整个注意力过程类比成"查数据库":

概念类比
Query当前一步发起的查询请求
Key历史记录的索引
Value历史记录的内容
KV cache已经建好的、可复用的历史数据库
Decode每来一个新 token,就发起一次新的查询

大模型推理本质上很像:用当前 token 的 Query,去查询历史 token 在每一层上积累起来的 KV 记忆。

这也是为什么历史 K/V 要缓存,当前 Q 没必要缓存。


十七、推理过程中的内存曲线:从 Prefill 峰值到 Decode 线性增长

在实际的大模型推理过程中,内存占用并不是简单"逐步增长"的,而是呈现出一个非常典型的三阶段变化曲线:

0
→ 100(Prefill 峰值,包含大量临时 tensor)
→ 70(释放临时 tensor,仅保留 KV cache)
→ 71
→ 72
→ 73
→ ...

1. Prefill 阶段:瞬时内存峰值

当模型接收到完整的 prompt 并进入 Prefill 时,会进行一次完整的前向传播。这一阶段的特点是:

这些中间数据体积很大,但生命周期很短,仅在当前 forward 过程中存在。因此,内存占用会快速攀升到一个峰值:

0 → 100

这个"100"代表的是:KV cache + 所有临时计算 tensor 的叠加

2. Prefill 结束:回落到稳定基线

当 Prefill 完成并成功预测出第一个 token 后,这一整轮前向传播结束。此时:

因此内存会出现一个明显回落:

100 → 70

这个"70"可以理解为:当前 prompt 对应的 KV cache 所占用的内存。此时内存的"长期占用部分"基本已经确定。

3. Decode 阶段:线性缓慢增长

进入 Decode 阶段后,模型开始逐 token 生成输出。每生成一个新 token,会发生两件事:

因此,内存变化变成:

70 → 71 → 72 → 73 → ...

随着生成 token 数量的增加,KV cache 按序列长度线性增长。

小结

整个推理过程的内存行为可以总结为:

理解这一点,对于分析推理性能、显存瓶颈以及 KV cache 优化(例如 paged KV cache)至关重要。


十八、进阶:真实推理系统中的 KV Cache 管理与显存计算

在前面的分析中,我们把 KV cache 理解为"随着 token 增长逐步累加的一段内存"。这个理解在逻辑上是正确的,但在真实工程系统中,还有两个非常关键的细节。

1. KV Cache 并不是"每步动态申请"的

在理想化模型里,我们会这样想:

每生成一个 token:
  新申请一段内存 → 存 K/V

但在真实系统中(例如 vLLM、TensorRT-LLM),通常不会这样做。原因很简单:

实际做法:预分配(Pre-allocation)

真实系统通常会在推理一开始,就预先分配一整块 KV cache 内存池:

先申请一大块连续显存
然后在里面"按需填充"

这就是为什么你可能会看到:显存一开始就突然占了一大块(例如直接到 80%),然后随着生成进行,显存几乎不再增长,只是"被逐渐填满"。

Paged KV Cache(核心优化)

以 vLLM 为例,它引入了类似操作系统的"分页"机制:

可以把它理解为:KV cache ≈ GPU 上的"虚拟内存系统"

2. KV Cache 显存到底怎么算(面试高频)

这是面试中非常经典的一题:

"一个 7B 模型,context 长度 1000,大概需要多少 KV cache 显存?"

核心公式

KV cache 大小 ≈ 层数 × 序列长度 × hidden_size × 2 × 数据类型字节数

举个典型例子(7B 模型)

假设:hidden_size = 4096,层数 = 32,seq_len = 1000,dtype = fp16(2 bytes)

4096 × 2 × 2 = 16384 bytes ≈ 16 KB / token / layer

16 KB × 1000 tokens = 16 MB / layer

16 MB × 32 层 ≈ 512 MB

结论:一个 7B 模型,1000 token,大约需要 500MB KV cache。

重要补充(面试加分)

  1. batch 会线性放大:batch = 8 时,500 MB × 8 = 4 GB
  2. context 长度是线性影响:seq_len 从 1k → 8k,500 MB → 4 GB
  3. KV cache 通常比模型权重更"吃显存":在长上下文 / 高并发场景下,KV cache 才是主要瓶颈,而不是模型权重本身

3. 为什么这是推理优化的核心

现在你可以理解为什么:

在真实推理系统中,KV cache 通常通过预分配或分页机制进行管理,而不是逐步动态申请;其显存占用与层数、序列长度、hidden size 和 batch 成线性关系,在长上下文或高并发场景下往往成为主要瓶颈。


十九、最终总结

1. Prefill 是什么

把用户已经给出的 prompt 一次性送进模型,完整跑过所有层,建立起历史 token 的深层表示,并生成每一层的 KV cache。

2. Decode 是什么

在模型已经有历史 KV cache 的基础上,每次只对一个新 token 计算新的 Q/K/V,然后用新的 Q 去读取历史 KV,逐步生成后续 token。

3. O 是什么

某一层 attention 的输出表示,也就是该层中某个位置在融合上下文后的新 hidden state。

4. 为什么 Prefill 不能只算最后一个位置

因为真实模型是多层的,前面位置的输出会作为下一层的输入,用于构造下一层的 Q/K/V,所以整个序列都必须参与前向传播。

5. 为什么只缓存 KV,不缓存 Q

因为历史 K/V 会被未来每一步反复使用,而 Query 只在当前 step 使用一次,用完就失去复用价值。

6. KV cache 缓存的是哪一层

不是某一层,而是每一层都有自己的 KV cache


一个可以直接记住的脑图:

用户输入 prompt
   ↓
Prefill:整段并行跑过所有层
   ↓
得到最后位置表示,预测第一个新 token
   ↓
同时保留所有层的历史 KV cache
   ↓
进入 Decode
   ↓
每一步只算新 token 的 Q/K/V
   ↓
Q 读取所有历史 KV
   ↓
预测下一个 token
   ↓
Q 丢弃,K/V 追加到 cache
   ↓
重复
← 返回主页