大语言模型的推理加速方案
2025-11-4
| 2025-11-4
字数 5028阅读时长 13 分钟
type
status
date
slug
summary
tags
category
icon
password

1. 大语言模型的推理加速方案

1.1. 为什么KV Cache不在training中使用只在inference中使用

回答:因为训练(Training)和推理(Inference)的计算方式完全不同。
解释:
  • 训练 (Training) 是“并行”的: 为了高效利用 GPU,我们一次性将整个完整序列(例如,一个完整的句子)输入模型,并使用 Causal Mask (注意力掩码) 来防止位置 i 的 token "偷看" 到位置 j > i 的 token。
    • notion image
  • 推理 (Inference) 是“串行”的: 我们一次只生成一个新 token,然后把它添加到输入序列中,再生成下一个。
KV Cache 正是为了优化这种“串行”的、逐字生成的推理过程而设计的。
KV Cache的存储方式
两个不同的句子是否需要重新计算所有的k和v的矩阵?
新的句子中相同的字符是否需要重新计算QKV?
回答:是的,必须重新计算。因为同一个词在句中的位置(Positional encoding)和上下文表示(Contextual representation)可能不同。
 
换一种说法:
p1: what are the two steps in LLM inference?
prefill and decode 预填充和解码
由于self-attention, 多个token输入的时候,可以做成keys Matrix x Queries Matrix进行并行计算,
  • Prefill (预填充) 阶段:负责创建填充初始的 KV Cache。
  • Decode (解码) 阶段:负责使用扩展这个 KV Cache。
notion image
notion image
怎么加速推理?
notion image
关键在于延迟和吞吐量。
Latency (延迟)Throughput (吞吐量) 是衡量系统性能的两个关键指标,它们描述的是不同的东西:
  • Latency关注的是响应时间。
  • Througtput关注的是单位时间内系统能处理的操作数量(流量)。
提问:
  • Why multi-head attention?
    • 多角度看问题。multi-head attention是设置多个注意力机制,详见子空间。
  • Theory: subspace 子空间
    • 每个注意力头都有自己的QKV矩阵
    • 由于初始化矩阵不同,所以训练后会学到不同的投影方式。(如一个学会动词和主语之间关系,一个学会同义词之间的关联性)
    • 多个注意力头在各自的小空间里独立学习,最后拼起来。怎么拼起来看下面。
  • Compute: parallel 并行计算多注意力头
    • gpu可以同时计算所有头,不是使用loop。而是批处理矩阵(batched matrix multiply, BMM)。比如原本要输入[BatchSize=2(两句句子), SeqLen(每句10个词), 512(模型维度)] [2,10,512]。
        1. Reshape(逻辑拆分)。将维度拆分成多个头[2,10,8,64]。
        1. Transpose(转置)。 都这么转 [2,8,10,64]。
        1. 矩阵乘法。 中的 [2,8,64,10]。相乘的时候pytorch会自动识别后两维是批次维度,并行处理完结果就是注意力分数矩阵 [2,8,10,10]。
  • How to use CUDA to optimized multi-head attention
    • 由于MHA只有在计算 的时候是在显存里,读结果的scale, mask,softmax都是在内存,所以有优化空间。
    • FlashAttention 是 MHA 优化技术。
      • 内核融合,将各步骤都融合到一个 CUDA 内核里完成。
      • 分块(Tiling):在计算 的时候,是一个大矩阵,可能放不进片上内存(SRAM,计算速度快)。将这个矩阵分成小块(Tiles),在SRAM上计算每个结果的Softmax,然后累加起来。

1.2. tensor parallelism和pipeline parallelism(并行)

这两个并行策略的作用:解决大模型过大一张显卡的显存装不下的问题。通过切分模型,分别在不同显卡中运算。
  • Pipeline parallelism(流水线并行):
    • 模型纵向切分
    • 不同GPU处理不同层(GPU1: 1-10Layers, GPU2: 11-20Layers, …)。显卡之间是串行的,一个处理完传输结果到下一个,最后出结果。
    • 优点:这样所有模型利用率拉满,在训练的时候用效果好。
    • 缺点:推理的时候延迟很大
  • Tensor parallelism(张量并行):
    • 模型横向切分
    • 所有GPU同时在一个Layer上工作,最后拼成结果。
    • 就是利用了 BMM 的原理,把不同的头分出来,然后装到不同显卡里计算。
    • 优点:延迟很低
    • 缺点:每一层处理的结果都必须共享并求和(All-Reduce),才能进行下一层计算,所以受通信速度的影响(需要NVLink的高速网络进行数据通信)
特性
Tensor Parallelism (TP, 张量并行)
Pipeline Parallelism (PP, 流水线并行)
核心思想
层内并行 (Intra-layer Parallelism)
层间并行 (Inter-layer Parallelism)
切分方式
横向切分:把单层的权重矩阵(如 W_Q, W_K, W_V)“切碎”
纵向切分:把多层(Layers)“切断”成不同的“阶段” (Stage)
通信开销
极高且频繁。每计算完一层,GPU 间都需通信 (All-Reduce)
低且不频繁。只在两个“阶段”的边界处通信 (Point-to-Point)
带宽依赖
极高。必须使用 NVLink 这样的高速总线,基本限于单机
较低。可以使用 InfiniBand/Ethernet,可以跨服务器(节点)
主要优点
低延迟。所有 GPU 同时计算,响应快。
高吞吐 (训练时)。可跨节点扩展,装下超大模型。
主要缺点
扩展性差。受限于单机 GPU 数量 (如 8 卡)
延迟极高。串行计算,总延迟是所有阶段之和。
关键问题
通信是瓶颈。
“流水线气泡” (Bubble):大量 GPU 处于空闲等待状态。
设备
最好是同一个host,多张显卡
可以是不同host
  • Sequence parallelism(序列并行):(最新方案,可输入超长tokens)
    • 前面介绍的两种策略TP和PP,在每块GPU中都需要存储和处理完整序列长度(Full Sequence Length)的激活值(在训练反向传播时,模型必须在显存里保留所有中间计算结果(Attention layer, FFN layer的输出等),这就是激活值)。而激活值的显存占用量与序列长度 (输入的tokens长度)和批次大小 (一个GPU同时并行处理的独立样本数量)成正比。所以,当 很长时,激活值会比模型权重还大,成为新的显存瓶颈。导致就算TP和PP把权重分得再碎,激活值都有可能让显存爆。
    • 解决方法:既然 很长,那把 也切了。
      • 问题是,self-attention对于上下文有依赖,切开会破坏这种依赖。
      • self-attention有依赖,但是FFM/MLP(前馈网络)的序列没有依赖,对每个token都是独立计算的。
      • SP和TP需要一起用。
        1. FFN/MLP layer
          1. 输入:[S, B, H]
          2. 切分:在输入FFN前,先把输入切成8份。
            1. notion image
          3. 每个GPU独立计算。
        1. Attention layer
          1. 将所有GPU的FFN结果拼回完整序列,并共享到所有GPU(All-Gather)。
          2. 使用TP策略。
        1. 通信(Reduce-Scatter):
          1. 所有GPU执行一次 Reduce-Scatter(求和-分散)。就是所有TP结果相加,然后做切分(第1步的切分成8份)。输入下一个FFN。

1.2.1. FFN

FFN 通常是一个非常简单的两层全连接神经网络 (MLP)
notion image
notion image

1.3. Quantization

FP32 → FP4 大幅减少模型参数量来做到加速。
FP32: -128~+127 (256)
FP4: -8~+7 (16)
操作步骤:
notion image
notion image
关键点:在推理时,模型会用 INT8 整数进行极快的矩阵乘法(INT8 计算比 FP32 快得多),只在最后需要输出时,才用 S 和 Z 把结果“反量化”回 FP32。
例子:
notion image
zero point是将量化值和原始值对齐的操作。
notion image

1.3.1. 量化的主要策略

1.3.1.1. Post-Trainging Quantization (PTQ - 训练后量化)

  1. 训练高精度模型后,冻结参数
  1. 小批量样本推理,记录每一层权重和激活值矩阵的最大和最小值。
  1. 根据量化值和记录的值计算S和Z。
  1. 所有权重从FP32转换为INT8,甚至INT4。

1.3.1.2. Quantization-aware training (QAT - 量化感知训练)

由于量化对精度的损失很大,所有需要QAT。
策略:在训练时就模拟量化。
  1. 前向传播 Forward pass
    1. 拿到FP32权重,量化为INT8,倒回FP32。
  1. 反向传播 Backward pass
    1. 计算梯度。(注意梯度回绕过round函数,因为它不可导)
    2. 使用梯度更新FP32权重
这样模型就会在训练中自己优化量化造成的误差。
注意细节:
S 和 Z 是针对谁设计的?
  1. Per-Tensor(逐张量)
      • 就是整个权重只给一组S和Z。
      • 缺点:这样如果权重中有异常值,导致原始值范围巨大 → S 巨大,导致其他值被压缩成0,精度崩塌。
  1. Per-Channel / Per-Group(逐通道 / 逐组)
      • LLM里的标准做法。
      • 对权重的每一行(或每一列)都独立计算一组S和Z。
      • 比如:nxn的矩阵,会获得n组S和Z。
      • 优点: 极大地隔离了异常值,精度保持得非常好。
      • 缺点:如下面的矩阵,最后一行转换后值相等了,就会不可取分。
        • notion image
          在LLM上量化会有巨大的损失(这里RTN是指Round-to-Nearest(四舍五入))
          notion image
针对 LLM 的特殊挑战:激活值异常点 (Outliers)
LLM 的量化有一个大难题:
  • 权重 (Weights) 很“乖”,是漂亮的正态分布(钟形曲线),很容易量化。
  • 激活值 (Activations)(即 MHA 和 FFN 的中间输出)很“野”。它们 99.9% 的值都接近 0,但有几个极其巨大的“异常值”。
这些异常值会毁了 Per-Tensor 的量化。
解决方案 (如 LLM.int8() / SmoothQuant):
  • 这是一种“混合精度”量化。
  • 它在计算时,动态地检测出这些“异常值”所在的维度。
  • 99.9% 的“正常”计算用 INT8 完成。
  • 0.1% 的“异常”计算,切换回 FP16 来处理。
  • 这样既保证了速度,又保证了精度。
而更高级的 GPTQ / AWQ / QLoRA (NF4) 等 4-bit 量化,则是在 PTQ 的基础上,增加了“智能调整”步骤:它们会在量化,就“重构”权重矩阵,把量化误差的伤害降到最低。
策略:保留一部分全精度,如图loss降回原本的值。
notion image
问题:哪些行保留?哪些行量化?
  1. 看哪一行数值大就保留哪一行。→ 实验发现有26%要保留。
  1. 看X(取列的平均)乘这一行获得activation数值大(说明活跃)。(AWQ)
问题:这样权重里有的是FP32有的是INT8怎么办?
如果这一行activation大,那这一行 S 值乘2来凸显出这一行重要。(AWQ)
在数学约定中激活值是 ,但在工程约定中是 (为了方便批处理)
notion image
notion image
notion image
notion image

1.3.1.3. Activation-aware Weight Quantization (激活感知权重Z化 AWQ)

AWQ 是一种极快高精度训练后量化 (PTQ) 方法。它的核心洞察是:“我们不需要保护所有的权重,我们只需要保护那些与‘异常激活值’相乘的 1% 的‘显著权重’(Salient Weights) 就够了。”
原理:
这个 s 是一个缩放因子 (Scale)。这个等式看起来毫无意义,但 AWQ 的魔力在于: 1. 这个 s 是逐通道 (Per-Channel) 的。 2. 这个 s 是激活感知的。
AWQ的工作流程:
推理前执行一次:
  1. 搜索 (Search / Observe)
    1. 运用少量数据通过模型。
    2. 观察激活值X,识别出包含异常值的通道。
    3. 标记这些通道对应的权重为显著权重
  1. 计算并缩放 (Scale & Apply)
    1. AWQ 为每一个权重通道(Channel) 计算一个最佳缩放因子
    2. 目标:找到一个 ,使得预缩放后的权重 在被四舍五入 (RTN) 时,误差最小
  1. 量化与保存 (Quantize & Save)
    1. 将预缩放后的 矩阵,用最简单、最快的 RTN 量化为 INT4 整数
    2. 将这个 (INT4 矩阵) 和 s (FP16 向量) 保存下来。
推理时:
  1. 加载 INT4 权重 和 FP16 缩放
  1. 执行计算
  1. 这个 操作可以被 CUDA 内核高度优化(先反量化 ,再与 相乘),速度极快。
 

1.4. Flash Attention

vllm一般用的都是flash attention。
简单来说,Flash Attention 的核心贡献是:让注意力计算变得又快又省显存,从而使处理超长序列(如 128K 甚至更长的上下文)成为可能。
它的发明人 Tri Dao 的核心洞察是:标准注意力的瓶颈不在于计算(FLOPs),而在于显存的读写(Memory I/O)。
标准注意力的操作过程:
notion image
出现的问题是:GPU 的计算核心(SRAM,片上内存)速度极快,但容量很小;而 GPU 的显存(HBM)容量很大,但速度相对很慢。更糟糕的是,它必须在 HBM 中完整地创建(物化)那个 的 S 和 P 矩阵。当序列长度 N 很大时(比如 64K), 是一个天文数字,这会耗尽所有显存
解决方法:
FlashAttention 采用两种技术,将所有计算步骤“融合”到一个 CUDA 内核(Kernel)中:
  1. 内核融合(Kernel Fusion)
    1. FlashAttention把所有计算步骤都放到GPU里不需要传进传出。
  1. 分块/瓦片技术(Tiling)
    1. notion image
注意基础概念:
CUDA概念:显卡的高带宽显存(HBM,速度慢容量大);高速片上内存(on-chip memory);这个内存中有静态RAM(SRAM,速度快容量小)。
FlashAttention 简单来说就是用了两层for循环:第一层循环Q;第二层循环K和V。
伪代码如下:

2. Reinforcement learning frameworks

TODO
 

资料

  • 谷歌ML电子书:https://jax-ml.github.io/scaling-book/index:
  • 英伟达博客:https://developer.nvidia.com/blog/mastering-llm-techniques-inference-optimization/
  • Lilian网红博客:https://lilianweng.github.io/posts/2023-01-10-inference-optimization/ (long article with quantization, etc)
  • 硬核ML博客:https://ralphmao.github.io/ML-software-system/
论文
  • DeepSeek V3 Technical Report
  • Mooncake: KV cache
  • DistServe: disaggregated serving
  • LLM
  • 其他问题整理Fuji Lake Hotel
    Loading...