大语言模型推理原理详解

_avichawla 发布于 2026-06-28 阅读 20

本文深入解析了大语言模型推理的完整流程,从分词和嵌入开始,详细介绍了Transformer层、预填充(TTFT)与解码(ITL)两个阶段的计算与内存瓶颈差异。重点解释了KV缓存的作用、内存占用及优化策略(如INT8/INT4量化、滑动窗口注意力、PagedAttention),并介绍了DeepSeek V4中通过压缩注意力机制减少缓存大小的方法。最后讨论了连续批处理、推测解码等现代推理服务优化技术。文章指出长提示增加TTFT,长输出增加ITL,并给出了诊断瓶颈的实用建议。

图像

对 LLM 的每次 generate() 调用,都在同一块 GPU 上运行两个不同的计算阶段:预填充(处理提示词)是计算密集型,而解码(逐 token 生成)是内存密集型。大多数推理优化针对其中一个阶段进行,诊断哪个阶段是瓶颈,是加快部署速度的第一步。

在本文中,我将梳理完整流水线,从 token 化输入到流式输出,并查看每个阶段的时间消耗。

Token 化与嵌入

字节对编码(BPE)等 tokenizer 将原始文本转换为来自约 5 万个 token 词表的整数 ID。

prompt = "How does inference work?"
ids = tokenizer.encode(prompt)
## ids -> [2437, 1374, 32278, 670, 30]

每个 ID 映射到嵌入表中的一行,该表是一个形状为 [vocab_size, hidden_dim] 的学习矩阵。对于一个隐藏维度为 4096 的模型,每个 token 变成一个 4096 维的向量。

## embedding_table has shape [vocab_size, hidden_dim]
vectors = embedding_table[ids]   # shape: [num_tokens, 4096]

图像

位置信息在此阶段注入。大多数现代架构使用旋转位置嵌入(RoPE),它通过旋转嵌入向量而不是添加单独的位置向量来编码位置。

Transformer 层

嵌入后的序列经过一系列 transformer 层(通常 32 到 80+ 层,取决于模型大小)。每层依次执行两个操作:

  1. 自注意力 通过学习的权重矩阵为每个 token 计算三个投影(查询 Q、键 K、值 V)。

图像

每个 token 的查询与每个其他 token 的键进行评分,这些分数(经过缩放和 softmax 后)决定了每个 token 的值有多少被混合进来。

## scores: how much each token attends to every other token
Q, K, V  = x @ Wq, x @ Wk, x @ Wv

scores = (Q @ K.T) / sqrt(d_k)

weights = softmax(scaled) # one row per token, sums to 1

attn_output  = weights @ V
  1. 前馈网络(FFN) 通过一个两层 MLP 独立处理每个 token 的向量。注意力在位置间移动信息,FFN 则对其进行变换。

在最后一层之后,模型将最后 token 的隐藏状态投影回词表大小([hidden_dim, vocab_size]),应用 softmax,并从得到的分布中采样以产生第一个输出 token。

预填充:计算密集型阶段

处理输入提示词是第一阶段。所有 token 并行处理:同时计算每个 token 的 Q、K 和 V,注意力作为大型矩阵乘法运行。这是计算密集型工作。GPU 的算术吞吐量是瓶颈,利用率很高。衡量这个阶段的指标是 首 Token 时间(TTFT),即第一个输出 token 出现之前的延迟。

在预填充期间,模型还会填充 KV 缓存:每一层的 K 和 V 张量被存储在 GPU 内存中供后续重用。

## Prefill: process the whole prompt in one shot
hidden = embed(prompt_tokens) + positions
for layer in model.layers:
    Q, K, V = project(hidden)             # for ALL tokens at once
    hidden  = attention(Q, K, V) + hidden
    hidden  = feedforward(hidden) + hidden
    cache_kv(layer, K, V)                 # save for later
first_token = sample(project_to_vocab(hidden[-1]))

解码:内存密集型阶段

一旦第一个 token 生成,模型就切换到逐 token 生成。对于每个新 token,它只计算该单个 token 的 Q、K 和 V。所有之前 token 的 K 和 V 已经在缓存中。

## Decode: one token per iteration
token = first_token
steps = 0
while token != STOP and steps < MAX_STEPS:
    x = embed(token) + position(steps)
    for layer in model.layers:
        q, k, v = project(x)
        K_all, V_all = caches[layer].append(k, v) # cached history + new
        x = layer.forward(q, K_all, V_all, x)  # attention + FFN, residuals
    token = sample(project_to_vocab(x))
    steps += 1
    yield token

每一步的算力需求很小(一个查询向量与缓存的键矩阵相乘,而不是完整的矩阵乘法)。但 GPU 仍然需要为这个小计算加载每个权重矩阵和整个缓存的 K/V。瓶颈从计算转向内存带宽。

图像

这个阶段的指标是 Token 间延迟(ITL):连续输出 token 之间的时间。低 ITL 让模型感觉响应迅速。

KV 缓存

如果没有缓存,生成 1000 个 token 的响应需要在每一步重新计算整个增长序列上的注意力,导致二次复杂度。KV 缓存存储每一层的 K 和 V 张量一次,并逐步追加新条目。

下面的视频展示了有 / 无 KV 缓存时 LLM 推理速度的对比:

0:00 / 0:47

对于长序列生成,加速比约为 5 倍或更多。

代价是缓存随序列长度线性增长,且每层都存在。对于一个 13B 参数的模型,每个 token 大约消耗 1 MB 缓存。一个 4K token 的上下文仅缓存就占用 4 GB 的 VRAM。这就是长上下文成本高的原因。缓存直接与 batch size 竞争 GPU 内存,即每个请求的缓存越大,每块 GPU 能同时处理的请求就越少。

标准的缓解措施包括:将缓存量化到 INT8 或 INT4,滑动窗口注意力(丢弃固定窗口外的 token),分组查询注意力(GQA,跨注意力头共享 K/V 以减少缓存张量数量),以及 PagedAttention(vLLM 背后的内存管理技巧,像操作系统管理虚拟内存一样分页管理缓存,消除碎片)。

围绕缓存重新设计注意力

量化和分页将 KV 缓存视为需要管理的固定成本。DeepSeek 的 V4 系列(2025 年 4 月发布)采用不同的方法:重新设计注意力,使缓存从一开始就在结构上更小。

图像

V4 使用两种压缩注意力机制的混合。

  • 压缩稀疏注意力(CSA) 使用 softmax 门控池化将 KV 条目压缩 4 倍,然后在压缩后的 token 上应用稀疏注意力。
  • 重度压缩注意力(HCA) 更激进。它将 128 个 token 的 KV 条目合并为单个压缩条目,并对这些表示应用密集注意力。

在 1M token 上下文中,V4-Pro 需要 DeepSeek-V3.2 单 token 推理 FLOPs 的 27%,KV 缓存的 10%。绝对值上,在 bf16 下 1M 上下文每序列占用 9.62 GiB 的 KV 缓存,而 V3.2 风格的架构估计需要 83.9 GiB。再加上 fp4/fp8 量化,缓存再缩小 2 倍。

KV 缓存已成为该领域优化模型架构的主要约束。

量化

训练使用 FP32 或 BF16 以保证梯度稳定性。推理不需要那样的精度。减少位宽带来的内存节省是线性的:

  • 7B 参数 FP32:28 GB
  • 7B 参数 FP16/BF16:14 GB
  • 7B 参数 INT8:7 GB
  • 7B 参数 INT4:3.5 GB

图像

INT4 使得 7B 模型可以在 4-6 GB VRAM 的笔记本 GPU 上运行。GPTQ 和 AWQ 等方法使用逐通道缩放因子来最小化有损压缩带来的质量下降。做得好时,INT4 在标准基准测试上比全精度模型仅差 1-2 个百分点。

从 FP16 到 INT8 通常能减少一半的推理延迟,且质量损失可忽略不计,这使得量化成为大多数部署中杠杆效应最高的单项优化。

服务基础设施

现代推理服务器在预填充-解码循环基础上叠加了若干优化:

图像

  • 连续批处理 在同一 GPU 步骤中交织多个请求的 token,即使在内存密集的解码阶段也能保持高利用率。
  • 推测解码 使用一个小的草稿模型提议多个 token,然后大模型在单次前向传播中验证它们。当草稿模型的接受率高时,这有效地将多个顺序的解码步骤转换为一次并行验证。

图像

  • PagedAttention(vLLM)以固定大小的块管理 KV 缓存内存,消除碎片,使每块 GPU 能处理更多并发请求。

像 vLLM、TensorRT-LLM 和 Text Generation Inference(TGI)这样的框架结合了这些技术。一块 GPU 可以为几十个并发用户提供服务,因为解码阶段大部分算力空闲,而连续批处理用其他请求填充这些空闲算力。

完整推理路径

图像

  1. Token 化:文本通过 BPE 转换为整数 ID。
  2. 嵌入:ID 变为向量。RoPE 编码位置。
  3. 预填充:所有输入 token 通过每一层并行处理。计算密集型。填充 KV 缓存。发出第一个 token。
  4. 解码循环:每步一个 token:为新 token 投影 Q,在缓存的 K/V 上做注意力,运行 FFN,采样。将新 K/V 追加到缓存。内存密集型。
  5. 反 token 化:将 token ID 映射回文本并流式输出。

一些实际影响

  • 长提示词在 TTFT(预填充)上成本高,长输出在 ITL(解码)上成本高,且它们对不同的硬件资源造成压力。
  • 上下文长度不是免费的,因为它会膨胀 KV 缓存并直接降低批处理容量。
  • 即使在满载服务器上,解码期间的 GPU 利用率也可能降至 30%,因为瓶颈是内存带宽而不是算力。
  • 解决方案不是增加算力,而是更快的内存、更小的缓存或更好的批处理。

当有人告诉你他们的模型很慢时,第一步诊断是:它是启动慢(预填充瓶颈,需优化 TTFT),还是输出流慢(解码瓶颈,需优化 ITL)。

  • 原文链接: x.com/_avichawla/status/...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~

相关文章

0 条评论