LLM推理基础:Transformer架构、自回归生成与KV Cache概念
要理解vLLM为何能实现高吞吐、低延迟的推理,首先必须回到大语言模型(LLM)推理的底层机制。绝大多数现代LLM(如GPT、Llama、Qwen)都基于Transformer解码器架构,其推理过程由两个核心机制主导:自回归生成与KV Cache。本节将拆解这三块基石,并揭示它们如何共同决定了推理系统的性能瓶颈。
2.1 Transformer解码器:推理的基本单元
LLM的推理并非一次性生成整段文本,而是逐token(词元)进行的。每个token的生成都经过一次完整的前向传播,其核心计算发生在Transformer解码器层中。一个典型的解码器层包含以下关键子模块:
- 自注意力(Self-Attention):让每个位置都能“看到”序列中所有其他位置,计算它们之间的关联权重。这是模型理解上下文的核心机制。
- 前馈网络(FFN):对每个位置的表示进行独立的非线性变换,通常包含两个线性层和一个激活函数(如SiLU或GELU)。
- 层归一化(LayerNorm)与残差连接:稳定训练和推理过程中的数值流动,加速收敛。
在推理时,输入序列(即当前已生成的所有token)会依次通过所有解码器层。每一层都输出一个同样长度的向量序列,最终经过一个输出投影层(lm_head),将最后一个位置的隐藏状态映射为词表大小的概率分布,从中采样或贪心地选出下一个token。
核心计算视角:推理时的计算量主要由两部分构成:注意力计算(QK^T 矩阵乘法)和 FFN计算(两个大矩阵乘法)。其中,注意力计算的时间复杂度与序列长度的平方成正比(O(n²)),而FFN计算与序列长度成正比(O(n))。当序列变长时,注意力计算将成为主要瓶颈。
2.2 自回归生成:逐token的循环过程
LLM的文本生成是一个典型的自回归(Autoregressive)过程:模型将已生成的token序列作为输入,预测下一个token,然后将新token拼接回输入序列,继续预测下一个,如此循环往复,直到遇到终止符(如 <eos>)或达到最大长度。
以生成“人工智能”为例,其过程如下:
- 初始化:输入起始符
<s>,模型预测第一个token“人”。 - 第1步:输入
<s>人,模型预测“工”。 - 第2步:输入
<s>人工,模型预测“智”。 - 第3步:输入
<s>人工智能,模型预测“<eos>”,生成结束。
这个循环中,每一步都依赖前一步的输出,因此无法并行生成。这是LLM推理延迟的根本来源。然而,仔细观察会发现一个关键现象:在第2步时,输入序列为 <s>人工,其中前两个token <s>人 与第1步完全相同,但模型却要重新计算它们的注意力表示——这显然是巨大的浪费。
关键洞察:自回归生成中,每一步的输入只比上一步多一个token。对于已计算过的历史token,其K(Key)和V(Value)向量在每一步都是完全相同的(因为它们的上下文没有变化)。因此,我们可以将这些向量缓存起来,避免重复计算。
2.3 KV Cache:消除重复计算的利器
KV Cache(键值缓存)正是为了解决上述重复计算问题而设计的。在推理过程中,每生成一个token,我们将其对应的K向量和V向量保存到显存中。在后续步骤中,只需计算新token的Q、K、V,然后用新token的Q与所有历史K做注意力计算,用所有历史V加权求和即可。
下图对比了有无KV Cache时的计算差异:
| 步骤 | 无KV Cache(重复计算) | 有KV Cache(增量计算) |
|---|---|---|
| 第1步 | 计算 token1 的 K,V | 计算 token1 的 K,V,存入缓存 |
| 第2步 | 重新计算 token1 的 K,V + 计算 token2 的 K,V | 仅计算 token2 的 K,V,从缓存读取 token1 的 K,V |
| 第3步 | 重新计算 token1,2 的 K,V + 计算 token3 的 K,V | 仅计算 token3 的 K,V,从缓存读取 token1,2 的 K,V |
| 总计算量 | O(n²) 次K/V计算 | O(n) 次K/V计算 |
KV Cache带来的性能提升是显著的:对于生成长度为n的序列,它将注意力计算的时间复杂度从O(n²)降为O(n)(仅计算新token的注意力),同时避免了历史token的重复前向传播。在vLLM中,KV Cache的管理是核心优化点之一——它占用了推理时的大部分显存,且其分配策略直接影响系统的吞吐量。
重要权衡:KV Cache以显存换计算。对于7B模型,每个token的KV Cache大小约为 2 × num_layers × num_heads × head_dim × 2 bytes(以FP16计)。例如,Llama-2-7B(32层,32头,head_dim=128)每个token的KV Cache约为 2 × 32 × 32 × 128 × 2 = 512KB。生成1024个token时,仅KV Cache就需要约512MB显存。因此,KV Cache的显存管理是推理系统设计的核心挑战。
2.4 案例:一次完整的推理过程
假设我们使用一个简化模型(2层Transformer,hidden_size=4)生成“深度学习”这四个字。其推理过程如下:
输入: <s>
第1步: 计算 <s> 的 K,V → 缓存
预测下一个token: "深" (概率最高)
输入: <s> 深
第2步: 仅计算 "深" 的 K,V → 追加到缓存
用 "深" 的Q与缓存中所有K计算注意力
预测下一个token: "度"
输入: <s> 深度
第3步: 仅计算 "度" 的 K,V → 追加到缓存
预测下一个token: "学"
输入: <s> 深度学习
第4步: 仅计算 "学" 的 K,V → 追加到缓存
预测下一个token: "<eos>"
生成结束: "深度学习"
每一步中,模型只对最新token执行完整的QKV计算和FFN计算,而历史token的K/V直接从缓存中读取。这大大减少了每一步的计算量,使得生成速度主要受限于显存带宽(读取KV Cache)而非计算能力。
2.5 常见误区与复盘
- 误区1:认为KV Cache是模型参数的一部分。实际上,KV Cache是推理过程中动态生成的临时数据,不参与模型训练,也不属于权重。它只存在于推理时的显存中。
- 误区2:忽略KV Cache的显存占用。许多初学者只关注模型权重的大小(如7B模型约14GB FP16),却忽略了KV Cache可能占据与权重相当甚至更多的显存。在长序列生成或高并发场景下,KV Cache往往是显存压力的主要来源。
- 误区3:认为自回归生成可以完全并行。由于每一步依赖前一步的输出,自回归本质上是串行的。但可以通过投机采样(Speculative Decoding)等技术,用小模型草拟多个候选token,再用大模型并行验证,从而在保持质量的同时加速。
- 误区4:混淆训练和推理的注意力计算。训练时,所有token的Q/K/V可以一次性并行计算(因为输入是完整的);推理时,由于自回归特性,必须逐步计算。KV Cache正是利用了推理时“历史token的K/V不变”这一特性。
本节要点回顾:
- Transformer解码器由自注意力和FFN组成,推理时逐token前向传播。
- 自回归生成是串行过程,每一步依赖前一步输出,无法并行。
- KV Cache缓存历史token的K/V向量,将注意力计算从O(n²)降为O(n)。
- KV Cache以显存换计算,其显存管理是推理系统(如vLLM)的核心优化方向。