目标:系统掌握大语言模型推理加速与资源优化的核心技术,理解量化、KV Cache 优化、投机解码、批处理策略等如何协同解决推理中的显存带宽、计算延迟、吞吐量三大瓶颈。

前置要求:了解 Transformer 架构的基本概念(自注意力、前馈网络)和自回归生成的基本流程。阅读过本系列前两篇会更有帮助,但不是必须的。

本系列博文从三个维度系统拆解了大语言模型的全生命周期。第一篇建立了模型架构与训练的理论地基——注意力机制、损失函数、优化器、正则化;第二篇聚焦工程实践——预训练数据工程、分布式训练、混合精度、显存优化。本篇将视角转向模型训练完成后:如何让一个数十亿参数的模型在用户提问时快速响应、在有限硬件上高效运行、在各种设备上灵活部署?

推理阶段的核心挑战可以归结为一个矛盾:模型越大越聪明,但推理越慢越费资源。本文将围绕显存带宽、计算延迟、吞吐量三大瓶颈,系统讲解推理优化的关键技术。


1. 推理的挑战与本质

1.1 自回归生成的串行特性

  大语言模型的推理过程是自回归的:每次只能生成一个 token,而每个 token 的生成都依赖前面所有已生成的 token。这意味着生成 100 个 token 就需要串行执行 100 次前向传播。

类比:抄写员。想象一个抄写员在抄书,他每次只能写一个字,而且必须先看清楚前面写了什么才能写下一个字。即使他写字很快,但 100 个字就要看 100 次前面的内容,这就是推理延迟的根本来源。

1.2 推理的两个阶段

  大语言模型的推理可以分为两个截然不同的阶段:

预填充(Prefill)阶段:一次性处理用户的整个 prompt(输入文本),计算所有 token 的 Key 和 Value 并缓存起来。

  • 特点:计算密集。需要处理大量 token 的矩阵乘法,GPU 的计算单元被充分利用。
  • 类比:老师一次性阅读学生的整篇作文。

解码(Decode)阶段:从 prompt 的最后一个 token 开始,逐个自回归地生成回复 token。

  • 特点:访存密集。每生成一个 token,都需要读取整个模型的权重完整的 KV Cache,但实际计算量很小(只计算一个 token)。
  • 类比:老师逐字批改——每改一个字都要翻阅整本参考书(模型权重)和学生的全部前文(KV Cache),但实际只是写一个批注。

Prefill 与 Decode 两阶段

1.3 瓶颈分析:访存带宽是关键

  解码阶段的计算强度(FLOPs / 字节数)极低。以一个 7B 参数模型为例:

  • 每生成一个 token,需要读取约 14GB 的模型权重(BF16 格式)。
  • 但实际浮点运算量只有约 14 GFLOPs。
  • A100 GPU 的计算能力是 312 TFLOPs,显存带宽是 2TB/s。
  • 读取 14GB 需要 7ms,计算 14GFLOPs 只需 0.045ms。
  • 瓶颈在显存读取,而非计算!

  这意味着,推理优化的核心思路是减少数据搬运量:减少需要读取的模型大小(量化)、减少需要读取的 KV Cache 大小(KV Cache 优化)、减少需要执行的推理步数(投机解码)。


2. 模型量化——把"高精度字典"变薄

2.1 量化的基本概念

  量化是将模型的权重和激活值从高精度浮点数(FP32/FP16)用更低位宽的整数(如 INT8、INT4)表示的技术。

类比:简笔画代替高清照片。一张 4K 照片占 10MB,但简笔画只占 10KB——虽然细节有所损失,但"主要内容"完全保留。量化就是用更少的比特来"画"模型的权重。

核心好处

  • 显存减少:INT4 量化后,7B 模型的权重从 14GB(BF16)降到约 3.5GB。

不同精度格式的模型大小对比

  • 访存加速:读取的数据量减少,直接缓解显存带宽瓶颈。
  • 计算加速:INT8/INT4 整数运算在专用硬件(如 Tensor Core)上比浮点运算更快。

2.2 量化的数学基础

  量化的核心公式:

$$ q = \text{round}\left(\frac{x}{s}\right) + z $$

其中:

  • $x$ 是原始浮点数值
  • $s$ 是缩放因子(scale),控制浮点数到整数的映射范围
  • $z$ 是零点(zero point),对齐浮点数中的 0 到整数中的某个值
  • $q$ 是量化后的整数值

反量化(将整数恢复为浮点近似值):

$$ \hat{x} = s \cdot (q - z) $$

对称量化:假设 $z = 0$,浮点数的 0 对应整数的 0,公式简化为 $q = \text{round}(x/s)$。

非对称量化:保留 $z$,适用于分布不对称的情况(如 ReLU 激活值只有正值)。

2.3 PTQ vs QAT

方法全称思路适用场景
PTQ训练后量化用少量校准数据统计权重/激活分布,直接量化LLM 的主流选择
QAT量化感知训练在训练时模拟量化误差,让模型学会适应需要从头训练,成本高

  对于大语言模型,PTQ 是主流——因为从头训练一个大模型的成本太高,而 PTQ 只需要一小批校准数据(几百到几千条样本)就能完成量化。

2.4 权重量化与激活量化的区别

  这两个概念经常被混淆,但它们解决的是不同的问题:

  • 权重量化:只将模型权重从 FP16 量化为 INT4/INT8,存储时节省显存,但计算时仍需反量化为 FP16 执行矩阵乘法。节省显存,但不一定加速计算
  • 激活量化:将激活值(矩阵乘法的输入)也量化为整数。这样矩阵乘法可以完全在整数计算单元上执行。同时节省显存和加速计算

  激活量化的难点在于:激活值的分布比权重更不规则,容易出现异常值(outlier)——少数极大或极小的值,导致量化精度崩溃。

2.5 主流量化方法

GPTQ——权重量化的经典

  GPTQ 基于近似海森矩阵信息,逐层对权重进行量化。核心思想是:在量化某个权重时,通过调整同层其他未量化权重来补偿量化误差,使整体输出误差最小化。

  • 输入:FP16 模型 + 少量校准数据(128~256 条)
  • 输出:INT4 量化模型
  • 特点:速度快(几分钟即可完成一个 7B 模型的量化),效果好,是权重量化的标准方法

AWQ(激活感知权重量化)

  AWQ 的核心洞察:不是所有权重同等重要——那些对应激活值较大的权重通道更关键,量化时应该给予更多保护。

  方法:观察校准数据的激活分布,找到"关键通道",对这些通道的权重使用更高精度(或做缩放后再量化),其余通道正常量化。

  • 优势:在极低比特(如 INT3/INT4)下比 GPTQ 更好地保留精度。

SmoothQuant——实现 W8A8

  SmoothQuant 解决的是激活量化的难题。核心技巧是"平滑转移":

$$ Y = (X \cdot \text{diag}(s)^{-1}) \cdot (\text{diag}(s) \cdot W) = \hat{X} \cdot \hat{W} $$

  通过引入平滑因子 $s$,将激活中的异常值"转移到"权重上。因为权重的分布更规则、更容易量化,所以转移后再做 W8A8 量化效果很好。

类比:均匀搅拌。激活值中有些"特别突出"的异常值,就像果汁里有几块没搅开的果肉。SmoothQuant 的做法是把果肉搅匀(平滑转移),让整杯果汁的浓度更均匀,这样用统一的容器(量化精度)就能准确量取。

LLM.int8()(bitsandbytes)

  另一种思路:对异常值特殊处理

  • 找出激活中维度最大的 0.1% 的特征通道(异常值通道)。
  • 这些通道保持 FP16 精度。
  • 其余 99.9% 的通道正常量化为 INT8。
  • 通过混合精度矩阵乘法实现加速。

2.6 量化的精度损失与恢复

  量化不可避免地会引入精度损失,但程度因任务而异:

  • 知识密集型任务(如事实问答):对量化更敏感,因为数值微小的变化可能导致输出不同的知识。
  • 生成质量(如流畅度、连贯性):通常影响较小,INT4 量化后仍能生成流畅文本。
  • 数学/编程推理:对精度较敏感,低比特量化可能导致推理错误。

恢复手段:在量化后的模型上做少量微调(如 QLoRA),可以部分恢复精度损失。


3. KV Cache 优化——给注意力记忆"瘦身"

3.1 KV Cache 原理回顾

  在第一篇中我们介绍了因果注意力和 KV Cache 的基本概念。在解码阶段,每生成一个新 token,注意力需要访问所有历史 token 的 Key 和 Value。为了避免重复计算,这些 K、V 向量被缓存下来,即 KV Cache

3.2 KV Cache 的显存压力

  KV Cache 的显存公式:

$$ \text{KV Cache 大小} = 2 \times L \times n_{\text{heads}} \times d_{\text{head}} \times \text{seq\_len} \times \text{batch\_size} \times \text{bytes\_per\_param} $$

  以 LLaMA-2 70B 为例(80 层,64 头,维度 128,BF16):

$$ 2 \times 80 \times 64 \times 128 \times 4096 \times 2 \text{ bytes} \approx 10.7 \text{ GB per sequence} $$

  如果 batch size 为 32,KV Cache 就需要 342 GB——远超模型权重本身!

KV Cache 显存增长曲线

KV Cache 已经成为长序列推理中最大的显存瓶颈。

3.3 多查询注意力(MQA)与分组查询注意力(GQA)

MQA(Multi-Query Attention)

  在标准的多头注意力中,每个头都有自己独立的 Q、K、V 投影。MQA 的做法是:所有头共享同一组 K 和 V,只有 Q 保持独立。

$$ \text{MQA}: \quad Q_1, Q_2, \ldots, Q_h \quad \text{共享同一个} \quad K, V $$

  效果:KV Cache 的大小从 $h \times d_{\text{head}}$ 缩减为 $1 \times d_{\text{head}}$,压缩了 $h$ 倍。

类比:共享笔记。在标准多头注意力中,每个"专家"(头)都记自己的笔记(K、V);MQA 中所有专家共享一份笔记,虽然可能有信息损失,但存储成本大幅降低。

GQA(Grouped-Query Attention)

  MQA 的极端共享可能导致表达力下降。GQA 是折中方案:将 $h$ 个头分成 $g$ 组,每组共享一组 K、V。

$$ \text{GQA}: \quad \underbrace{Q_1, \ldots, Q_{h/g}}_{\text{共享 } K_1, V_1}, \quad \underbrace{Q_{h/g+1}, \ldots, Q_{2h/g}}_{\text{共享 } K_2, V_2}, \quad \ldots $$

  当 $g = 1$ 时退化为 MQA;当 $g = h$ 时退化为标准 MHA。LLaMA-2 70B 和 LLaMA-3 使用 $g = 8$ 的 GQA,在效率和性能间取得了良好平衡。

MHA vs GQA 对比

3.4 KV Cache 的量化与驱逐

KV Cache 量化:KV Cache 本身也可以量化存储。例如,将 K、V 从 BF16 量化为 INT8 甚至 INT4,显存进一步压缩 2~4 倍。由于 K、V 的分布通常比激活值更稳定,量化精度损失较小。

滑动窗口注意力(Sliding Window Attention):只保留最近 $W$ 个 token 的 KV Cache,丢弃更早的。这在 Mistral 等模型中被使用。

驱逐/丢弃策略

  • H2O(Heavy-Hitter Oracle):根据注意力分数,保留"高注意力"的 token 的 KV,丢弃"低注意力"的。
  • StreamingLLM:发现注意力分布中第一个 token(“注意力汇聚点”)和最近的窗口最重要,保留这两部分即可。

3.5 PagedAttention——vLLM 的核心创新

  传统 KV Cache 管理的一个严重问题是内存碎片化:每个请求需要连续的显存空间存储 KV Cache,但请求的长度不同,导致显存中充满碎片——就像停车场中大小不一的空位,虽然总面积够用但停不进大车。

  PagedAttention 借鉴了操作系统的虚拟内存分页机制:

  • 将 KV Cache 分成固定大小的页(Page),每页存储固定数量 token 的 K、V。
  • 不同请求的 KV Cache 页可以不连续存储在物理显存中。
  • 通过页表(Page Table) 维护逻辑到物理的映射。

类比:虚拟内存。就像操作系统中,每个程序以为自己有连续的内存空间,但实际物理内存是分散的——PagedAttention 让每个请求以为自己的 KV Cache 是连续的,但实际显存是分页管理的。

效果

  • 显存利用率:从约 60%~70% 提升到 95% 以上(几乎消除了碎片)。
  • 吞吐量:可以在同样的显存中服务更多的并发请求,吞吐量提升 2~4 倍。

4. 投机解码——用小模型给大模型"打草稿"

4.1 动机

  自回归生成的串行特性是推理延迟的根本瓶颈。如果能一次验证多个 token,就能大幅减少前向传播的次数。

投机解码流程

类比:老师批改作文。逐字批改很慢(标准自回归)。如果学生先自己写一份草稿(草稿模型),老师只需通读一遍确认哪些字写对了(并行验证),不对的字再改——这样老师的工作量大幅减少。

4.2 原理

  投机解码(Speculative Decoding)的核心流程:

  1. 草稿阶段:使用一个轻量级的"草稿模型"(Draft Model),快速自回归生成 $K$ 个候选 token:$\tilde{x}_1, \tilde{x}_2, \ldots, \tilde{x}_K$。
  2. 验证阶段:将这 $K$ 个候选 token 一次性输入大模型(Target Model),大模型并行计算每个位置的条件概率。
  3. 接受/拒绝:从第一个 token 开始,按顺序检查:
    • 如果大模型对该 token 的概率不低于草稿模型的预测概率,接受
    • 如果不满足,用拒绝采样决定是否接受,或者从修正后的分布中重新采样。
  4. 结果:每轮至少前进 1 步(即使所有草稿都被拒绝),最多前进 $K + 1$ 步。

关键性质:通过精心设计的接受/拒绝条件,投机解码保证生成的分布与直接用大模型生成完全一致,是无损加速

4.3 接受条件的数学描述

  对于草稿 token $\tilde{x}$ 在位置 $t$,大模型概率为 $p(x_t = \tilde{x})$,草稿模型概率为 $q(x_t = \tilde{x})$:

$$ \text{接受概率} = \min\left(1, \frac{p(\tilde{x})}{q(\tilde{x})}\right) $$
  • 当 $p \geq q$ 时(大模型比草稿模型更"确定"这个 token),概率为 1,一定接受
  • 当 $p < q$ 时(草稿模型过于自信),以 $p/q$ 的概率接受。

  如果被拒绝,从修正分布 $\max(0, p(x) - q(x))$ 归一化后重新采样。

4.4 草稿模型的选择

方案描述优缺点
独立小模型使用同架构的小模型(如 7B 给 70B 打草稿)简单,但需要额外加载模型
提前退出(Self-Speculative)大模型的中间层输出直接作为草稿无需额外模型,但草稿质量可能较低
Medusa在大模型上添加多个预测头,同时预测未来多个位置无需草稿模型,训练额外的预测头
Lookahead Decoding利用 Jacobi 迭代的思想,并行猜测多个位置无需草稿模型,但接受率较低

4.5 令牌树与分支验证

  更激进的策略是令牌树(Token Tree):草稿模型不是生成一条线性的候选序列,而是生成一棵——在每个位置保留多个候选 token,形成多条分支。

  大模型对整棵树做一次并行验证,沿着概率最高的路径选择接受的 token。这提高了"命中率"(至少有一条分支被接受的概率更高),代价是需要更大的计算量。

4.6 实际加速效果

  投机解码的加速比取决于:

  • 草稿模型与大模型的一致性:一致性越高(接受率越高),加速越大。
  • 草稿模型的速度:草稿模型越快,每次"打草稿"的时间越短。
  • 实际经验:2~3 倍的加速是常见的,对于某些任务(如代码生成)可达 4 倍以上。

5. 批处理策略与吞吐优化

5.1 静态批处理(Static Batching)

  最简单的方案:等待一批请求凑齐后一起推理。但问题是——短请求完成后必须等待最长的请求结束才能处理下一批,GPU 在等待期间处于空闲状态。

静态批处理 vs 连续批处理

类比:班车。班车必须等所有乘客到齐才能发车——即使有人只坐一站就下车,也得等最后一站的乘客到达。

5.2 连续批处理(Continuous Batching)

  连续批处理(也叫 In-flight Batching)是推理服务的关键优化:当一个请求完成生成后,立即插入新的请求到当前批次中,无需等待整批结束。

flowchart LR
    subgraph 静态批处理
        R1["请求1: 3 tokens"] --> W1["等待最长请求"]
        R2["请求2: 10 tokens"] --> W1
        R3["请求3: 5 tokens"] --> W1
        W1 --> N1["开始下一批"]
    end
    subgraph 连续批处理
        R4["请求1: 3 tokens"] --> D1["完成后立即插入新请求"]
        R5["请求2: 10 tokens"]
        R6["请求3: 5 tokens"]
        D1 --> R7["请求4: 即刻开始"]
    end

效果:GPU 利用率从 50%~70% 提升到 90%+,吞吐量显著提升。vLLM、TensorRT-LLM 等框架都支持连续批处理。

5.3 Prefill 与 Decode 的混合调度

  Prefill 和 Decode 的计算特征截然不同:

特性PrefillDecode
计算模式计算密集访存密集
并行度高(处理整个 prompt)低(只处理 1 个 token)
延迟要求低(用户在等待首次输出)中(已开始流式输出)

  如果不加区分地混合调度,Prefill 的突发大量计算可能阻塞正在 Decode 的请求,导致延迟抖动。

  解决方案包括优先级调度(优先处理 Prefill 以降低首 token 延迟)和分块 Prefill(将长 prompt 的 Prefill 分成多个小块穿插执行)。

5.4 分离式架构

  更进一步的优化是将 Prefill 和 Decode 分离部署到不同的 GPU 上:

  • Prefill 节点:配置高计算能力的 GPU,专门处理 prompt 的预填充。
  • Decode 节点:配置高显存带宽的 GPU,专门处理自回归解码。

  这样每个节点可以针对各自的瓶颈做专门优化,整体效率更高。


6. 推理框架与部署架构

6.1 主流推理框架对比

框架核心特性适用场景语言
vLLMPagedAttention、连续批处理高吞吐在线服务Python
TensorRT-LLMC++ 编译优化、极致性能对延迟要求极高的生产环境C++/Python
llama.cpp纯 CPU 推理、GGUF 量化格式本地/边缘设备部署C/C++
HuggingFace TGI开箱即用、与 HF 生态集成快速原型和中等规模服务Rust/Python
SGLangRadixAttention、结构化生成优化复杂推理管线Python

6.2 推理服务化

  生产环境中的推理服务需要关注:

  • RESTful API:提供标准的 HTTP 接口,兼容 OpenAI API 格式。
  • 流式输出(Streaming):通过 Server-Sent Events(SSE)逐 token 返回生成结果,降低用户感知延迟。
  • 负载均衡:将请求分发到多个推理实例,避免单点过载。
  • 自动扩缩容:根据请求量动态调整 GPU 实例数量,平衡成本和性能。

6.3 边缘部署

  将大语言模型部署到端侧设备(手机、浏览器、嵌入式设备)是一个快速发展的方向:

llama.cpp:Georgi Gerganov 开发的纯 C/C++ 推理引擎,支持 GGUF 量化格式,可以在 CPU 上高效运行。通过 Metal(macOS)、CUDA 等后端利用 GPU 加速。

MLC LLM:将模型编译为各种硬件的原生代码(CUDA、Metal、WebGPU),支持在浏览器中运行。

端侧的特殊挑战

  • 内存极有限(手机通常 6~12GB 共享内存)。
  • 功耗敏感,不能长时间满载。
  • 通常使用 4-bit 甚至更激进的量化。

7. 端侧推理与硬件加速

7.1 硬件适配

NPU(神经网络处理单元):手机 SoC 中的专用 AI 加速器(如 Apple Neural Engine、高通 Hexagon),针对低比特整数运算优化。

Apple Silicon 的统一内存优势:M 系列芯片的 CPU 和 GPU 共享同一块物理内存,模型不需要在 CPU 内存和 GPU 显存之间拷贝——这对大模型推理是一个显著优势,因为显存带宽不再是瓶颈。

7.2 内存卸载(Offloading)

  当模型太大装不进 GPU 显存时,可以将部分层卸载到 CPU 内存甚至 SSD:

  • CPU Offloading:将不常用的层(如 FFN)放到 CPU 内存,需要时再加载到 GPU。
  • SSD Offloading:利用 NVMe SSD 的高带宽(~7GB/s),将部分参数存储在 SSD 上。

类比:书架与书桌。GPU 显存是书桌(容量小但取用快),CPU 内存是书架(容量大但要走过去拿),SSD 是仓库(容量最大但取用最慢)。聪明的做法是把最常用的书放在桌上,不常用的放回书架。

代价:卸载引入了额外的数据传输延迟,推理速度会明显下降。但在"跑不起来"和"跑得慢"之间,“跑得慢"是更好的选择。

7.3 模型剪枝与蒸馏

模型剪枝(Pruning):移除模型中不重要的参数或结构。

  • 非结构化剪枝:将单个权重置零,形成稀疏矩阵。需要专门的稀疏计算硬件才能加速。
  • 结构化剪枝:移除整个注意力头、FFN 神经元或层。可以直接减小模型大小,通用性更强。

知识蒸馏(Knowledge Distillation):用大模型(教师模型)的输出来训练小模型(学生模型),让小模型学习大模型的"知识”。

  • 常见做法:用 GPT-4 的输出训练 7B 模型,让 7B 模型接近 GPT-4 的能力。
  • 蒸馏后的小模型天然适合推理——更小、更快、更容易部署。

8. 全文总结——推理优化的技术全景

flowchart TD
    B1["显存带宽瓶颈"] --> Q["模型量化(GPTQ/AWQ/SmoothQuant)"]
    B1 --> KV["KV Cache 优化(GQA/PagedAttention)"]
    B1 --> SD["投机解码(减少推理步数)"]
    B2["计算延迟瓶颈"] --> SD
    B2 --> MP["批处理策略(连续批处理)"]
    B2 --> SP["Prefill/Decode 分离"]
    B3["吞吐量瓶颈"] --> MP
    B3 --> KV
    B3 --> Q
    Q --> D["高效推理服务"]
    KV --> D
    SD --> D
    MP --> D
    D --> E1["云端部署(vLLM/TRT-LLM)"]
    D --> E2["边缘部署(llama.cpp/MLC LLM)"]

量化(INT4/INT8)减少模型权重的存储和搬运量,直接缓解显存带宽瓶颈。

KV Cache 优化(GQA、PagedAttention、驱逐策略)减少推理过程中最大的显存消耗者,提升并发能力。

投机解码通过小模型打草稿 + 大模型并行验证,减少自回归推理的串行步数,降低延迟。

批处理策略(连续批处理、Prefill/Decode 分离)提升 GPU 利用率和吞吐量。

推理框架(vLLM、TensorRT-LLM、llama.cpp)将上述技术集成到开箱即用的工具中。

  这些技术并非相互独立——在实际部署中,它们通常组合使用:一个典型的生产级推理服务可能同时使用 AWQ INT4 量化 + GQA + PagedAttention + 连续批处理 + 投机解码,将推理延迟降低 5-10 倍,吞吐量提升 3-5 倍。


系列导航