LLM推理基础:Transformer解码流程、自回归生成与KV Cache
在深入vLLM的动态批处理与延迟优化之前,我们必须先建立对LLM推理底层机制的精确认知。大语言模型(LLM)的推理过程并非一次性的前向计算,而是一个逐Token(Token)生成的、高度串行的自回归过程。理解Transformer的解码流程、自回归生成范式以及KV Cache(键值缓存)的引入动机,是后续理解连续批处理(Continuous Batching)、PagedAttention等高级优化技术的基石。本章将拆解这三个核心概念,并揭示它们与推理延迟之间的直接关联。
2.1 Transformer解码流程:从输入到输出的逐层变换
现代LLM(如GPT系列、LLaMA、Qwen等)均基于Transformer架构的解码器(Decoder)部分。与编码器(Encoder)一次性处理整个输入序列不同,解码器在推理时是逐步生成输出的。其核心流程可以概括为:输入嵌入 → 位置编码 → 多层解码块(Masked Self-Attention + Feed-Forward Network) → 输出层(LM Head) → 概率分布。
在每一层解码块中,最关键的是掩码自注意力(Masked Self-Attention)机制。该机制确保模型在预测第 \( i \) 个Token时,只能看到位置 \( 1 \) 到 \( i-1 \) 的Token,而无法“偷看”未来的Token。这种因果约束(Causal Constraint)是自回归生成的基础。具体流程如下:
- 输入嵌入(Embedding):将当前输入的Token ID映射为高维向量(如4096维)。
- 位置编码(Positional Encoding):由于Attention机制本身不具备顺序感知能力,必须注入位置信息(如RoPE旋转位置编码)。
- 解码块堆叠:向量依次通过 \( N \) 个(如32层或80层)解码块。每个块内包含:
- Masked Multi-Head Self-Attention:计算Token间的关联权重。
- 残差连接与LayerNorm:稳定训练并加速收敛。
- Feed-Forward Network(FFN):逐位置的非线性变换(通常为MLP)。
- 输出层(LM Head):将最后一层的隐藏状态映射为词表大小的向量(如32000维)。
- Softmax归一化:将向量转换为概率分布。
值得注意的是,在推理阶段,Batch Size通常为1(或动态批处理中的多个请求),且序列长度是动态增长的。这与训练阶段(固定序列长度、并行处理所有位置)有本质区别。
2.2 自回归生成:逐Token的串行逻辑
自回归(Autoregressive)生成是LLM推理的基本范式。其数学表达为:\( P(y_1, y_2, ..., y_T) = \prod_{t=1}^{T} P(y_t | y_1, ..., y_{t-1}) \)。即,当前Token的概率依赖于之前所有已生成的Token。
在实际推理中,这个过程是串行的:
- 初始化(Prefill阶段):输入用户的Prompt(如“请介绍一下北京”),模型并行计算所有Prompt Token的隐藏状态,并生成第一个输出Token。此阶段计算量大,但并行度高。
- 迭代生成(Decode阶段):将上一步生成的Token拼接到输入末尾,再次进行前向计算,生成下一个Token。此阶段每次只处理1个Token,计算量小,但必须串行执行。
- 终止条件:当生成结束符(EOS)或达到最大长度限制时停止。
这种串行机制带来了两个关键问题:
- 延迟累积:生成 \( T \) 个Token,就需要 \( T \) 次串行前向计算。对于长文本生成(如1000个Token),延迟会显著增加。
- 计算浪费:在Decode阶段,虽然只输入1个新Token,但Attention机制需要计算它与之前所有Token的关联。如果不做缓存,每次都要重新计算之前所有Token的Key和Value向量,导致巨大的算力浪费。
2.3 KV Cache:消除重复计算的核心机制
为了解决上述“重复计算”问题,KV Cache应运而生。其核心思想是:在生成第 \( t \) 个Token时,将前 \( t-1 \) 个Token的Key和Value向量缓存下来,供后续计算使用。
在Attention计算中,对于每个Token,我们都会计算三个向量:Query(Q)、Key(K)、Value(V)。在生成第 \( t \) 个Token时,我们只需要计算当前Token的Q、K、V,然后将其K、V与之前缓存的K、V拼接,进行Attention计算。这样,之前Token的K、V无需重新计算。
| 对比维度 | 无KV Cache | 有KV Cache |
|---|---|---|
| 计算量(Decode阶段) | 每次需计算所有Token的K、V,复杂度 \( O(n^2) \) | 仅计算新Token的K、V,复杂度 \( O(n) \) |
| 显存占用 | 低(无需额外缓存) | 高(需缓存所有Token的K、V) |
| Decode速度 | 极慢(重复计算严重) | 快(减少冗余计算) |
| 典型应用 | 早期实验代码 | 所有生产级推理引擎(vLLM、TensorRT-LLM等) |
KV Cache的引入虽然大幅提升了Decode速度,但也带来了新的挑战:显存占用。对于一个 \( L \) 层、\( H \) 个注意力头、头维度为 \( D \) 的模型,每个Token需要缓存 \( 2 \times L \times H \times D \) 个浮点数(K和V各一份)。以LLaMA-7B为例,每个Token约占用0.5MB显存(FP16精度)。当并发请求数增加、序列变长时,KV Cache的显存开销会迅速膨胀,成为限制吞吐量的主要瓶颈。
2.4 实战场景:从KV Cache视角理解推理引擎设计
假设我们有一个生产环境,需要部署一个7B模型,最大序列长度为2048,GPU显存为40GB(如A100)。我们来估算KV Cache的显存需求:
- 模型参数:7B参数,FP16精度,占用约14GB显存。
- KV Cache大小:对于7B模型,通常有32层,32个注意力头,头维度128。每个Token的KV Cache大小 = \( 2 \times 32 \times 32 \times 128 \times 2 \) 字节 = 512KB。
- 单请求最大KV Cache:2048 Token × 512KB = 1GB。
- 可用显存:40GB - 14GB(模型) - 4GB(激活值/临时缓冲) = 22GB。
- 理论最大并发:22GB / 1GB ≈ 22个请求。
然而,在实际运行中,并非所有请求都会达到2048长度。如果使用静态分配,每个请求都预留1GB,那么当请求平均长度只有512时,显存利用率仅为25%。vLLM的PagedAttention通过分页机制,允许不同请求的KV Cache按需增长,从而将并发数从22提升至接近80(假设平均长度512)。这就是KV Cache管理对吞吐量的直接影响。
从这个案例可以看出,理解KV Cache不仅是理解模型原理,更是理解推理引擎设计哲学(如vLLM)的钥匙。后续章节中,我们将深入探讨vLLM如何通过PagedAttention和Continuous Batching来最大化KV Cache的利用效率。
2.5 常见误区与总结复盘
在学习和实践过程中,工程师常对以下概念产生混淆:
- 误区一:认为KV Cache是模型的一部分。实际上,KV Cache是推理时的临时缓存数据,不参与模型权重更新,也不影响模型精度。
- 误区二:忽略Prefill阶段的KV Cache构建。Prefill阶段虽然计算密集,但同样需要生成KV Cache供后续Decode使用。优化Prefill阶段(如使用FlashAttention)同样重要。
- 误区三:认为KV Cache越大越好。虽然KV Cache能加速计算,但过大的KV Cache会占用显存,限制批处理大小,反而降低整体吞吐量。需要在延迟和吞吐之间做权衡。
- 误区四:混淆“序列长度”与“批处理大小”。KV Cache的显存占用与序列长度和批处理大小均成正比。动态批处理(Continuous Batching)正是通过动态调整批处理大小来平衡KV Cache的显存压力。
总结复盘:本章节是理解vLLM优化的基石。我们明确了Transformer解码的逐步流程,理解了自回归生成的串行约束,并深入剖析了KV Cache作为“空间换时间”策略的核心价值。它解决了重复计算问题,却引入了显存管理难题。vLLM的所有核心优化——PagedAttention、Continuous Batching、Prefix Caching——本质上都是在围绕“如何更高效地管理KV Cache”这一命题展开。掌握这些基础,你才能在后期的实战调优中,准确判断瓶颈是来自计算、显存还是调度策略。