LLM推理基础:Transformer解码器架构、自回归生成与KV Cache

核心原理:从Transformer解码器到Token预测

现代大语言模型(LLM)的推理过程,本质上是在一个Transformer解码器上执行逐Token的自回归生成。与编码器-解码器架构不同,当前主流LLM(如GPT系列、Llama、Qwen)仅使用解码器(Decoder-only),其核心组件包括:

  • 嵌入层(Embedding):将输入Token映射为高维向量,并叠加位置编码(RoPE或ALiBi)以保留序列顺序信息。
  • 多头自注意力(Multi-Head Self-Attention):每个Token的Query向量与所有Token的Key向量计算注意力分数,再对Value向量加权求和,捕捉序列内任意位置间的依赖关系。
  • 前馈网络(FFN):对每个Token独立进行两次线性变换与激活(如SwiGLU),增强非线性表达能力。
  • 层归一化与残差连接:稳定深层网络训练,加速收敛。

解码器通过堆叠上述模块(通常为32~80层),将输入序列逐步编码为上下文表示。在生成阶段,模型每次只预测下一个Token的概率分布,然后从分布中采样或取最大概率项,作为新Token追加到输入序列末尾,再进入下一轮迭代。

关键认知:LLM推理不是一次性输出整段文本,而是“生成一个Token → 拼接回输入 → 再生成下一个”的循环过程。这个循环的每一步都依赖完整的上下文历史,因此如何高效缓存历史信息成为吞吐优化的核心。
Transformer解码器自回归生成与KV Cache 输入序列 “我” “爱” “学” “习” 嵌入层 Token → 向量 + 位置编码 解码器层 × N 多头自注意力 前馈网络 输出层 Softmax 概率分布 → “习” (概率0.72) KV Cache:缓存历史Token的Key和Value矩阵 Token 1 K₁: [0.2, -0.5, ...] V₁: [0.8, 0.1, ...] Token 2 K₂: [0.5, 0.3, ...] V₂: [-0.2, 0.6, ...] Token 3 K₃: [-0.1, 0.9, ...] V₃: [0.4, -0.3, ...] Token 4 K₄: [0.7, -0.8, ...] V₄: [0.1, 0.5, ...] 新Token 5 K₅: 待计算 V₅: 待计算 新Token只需计算 自己的K、V, 历史K/V直接复用 ❌ 无KV Cache(每次重算) 第1步:计算“我”的K/V 第2步:计算“爱”的K/V(重算“我”) 第3步:计算“学”的K/V(重算“我”“爱”) 第4步:计算“习”的K/V(重算前3个) 总计算量:1+2+3+4 = 10次K/V计算 时间复杂度 O(n²) ✅ 有KV Cache(增量计算) 第1步:计算“我”的K/V → 存入缓存 第2步:计算“爱”的K/V → 追加缓存 第3步:计算“学”的K/V → 追加缓存 第4步:计算“习”的K/V → 追加缓存 总计算量:1+1+1+1 = 4次K/V计算 时间复杂度 O(n)

自回归生成:逐Token的迭代过程

自回归(Autoregressive)生成的核心约束是:每个新Token的生成都依赖于此前所有Token。以生成“我爱学习”为例,完整过程如下:

  1. 初始化:输入起始符<bos>,模型计算其隐藏状态。
  2. 第1次迭代:基于<bos>的表示,预测“我”为最高概率Token。
  3. 第2次迭代:将“我”拼接到输入末尾,模型基于<bos> 我预测“爱”。
  4. 第3次迭代:输入变为<bos> 我爱,预测“学”。
  5. 第4次迭代:输入变为<bos> 我爱学,预测“习”。
  6. 终止:遇到结束符<eos>或达到最大长度,停止生成。

每次迭代中,模型需要为所有历史Token重新计算注意力分数。如果不做任何缓存,第n次迭代的计算量将包含前n-1次的所有重复计算,导致总计算量呈平方级增长。这正是KV Cache存在的根本原因。

KV Cache:消除重复计算的关键机制

KV Cache的核心思想非常直观:在自注意力计算中,历史Token的Key和Value向量只取决于其自身,与后续新Token无关。因此,可以将已计算过的K、V矩阵缓存下来,新Token到来时只需:

  • 计算新Token自己的Q、K、V向量;
  • 用新Token的Q与缓存中所有K做点积得到注意力分数;
  • 用注意力分数对缓存中所有V加权求和,得到新Token的输出。

KV Cache的存储结构通常为两个张量:K_cache和V_cache,形状均为 [batch_size, num_heads, seq_len, head_dim]。每生成一个新Token,就将该Token在所有层、所有注意力头上的K、V向量追加到缓存末尾。

内存代价:KV Cache并非免费午餐。以Llama-2-7B为例,模型共32层、32个注意力头、每个头维度128,每个Token需要缓存 32×32×128×2×2字节 = 512KB(K和V各占一半)。当序列长度达到4096时,单条请求的KV Cache占用高达2GB。这解释了为什么KV Cache的内存管理成为吞吐优化的主战场。

案例:KV Cache如何加速长文本生成

假设使用Llama-2-7B生成一段2000字的文章,对比有无KV Cache的差异:

对比维度 无KV Cache 有KV Cache
单Token生成延迟 随序列长度线性增长 基本恒定(仅计算新Token)
总计算量 O(n²) 次K/V计算 O(n) 次K/V计算
2000 Token总耗时 约 2000²/2 = 200万次K/V计算 约 2000次K/V计算
显存占用 低(不缓存) 高(需缓存全部历史)
适用场景 几乎不适用 所有生产环境

实际测试中,在A100上使用KV Cache后,Llama-2-7B的生成速度可从约5 tokens/s提升至40 tokens/s以上,提升幅度达8倍。但代价是显存中需要预留大量空间存放KV Cache,这直接催生了PagedAttention等显存管理技术的出现。

误区与复盘:KV Cache的常见误解

误区1:KV Cache会改变模型输出结果。
正确理解:KV Cache只是缓存了中间计算结果,数学上与不缓存完全等价。它不会影响概率分布,只影响计算效率。
误区2:KV Cache只缓存最后一层。
正确理解:Transformer每一层都有独立的注意力模块,因此每一层都需要缓存。Llama-2-7B的32层每层都要维护自己的K/V缓存。
误区3:KV Cache越大越好。
正确理解:KV Cache占用显存后,会挤压模型权重和激活值的空间。当显存不足时,系统可能被迫降低batch size,反而降低整体吞吐。KV Cache的管理需要在缓存容量与批处理大小之间寻找平衡。

复盘一个典型的生产事故:某团队部署LLM服务时,将最大序列长度设为8192,结果发现并发请求稍多就触发显存溢出(OOM)。排查后发现,每个请求的KV Cache占用高达4GB(8192 Token × 512KB),8个并发请求就吃满了40GB显存。最终通过限制最大生成长度、使用PagedAttention按页分配缓存,才解决了问题。