重要提示:如需以 Markdown 形式查看本页,请在 URL 后追加 `.md`。 完整文档索引见 llms.txt
跳到主要内容
完整文档索引见 llms.txt。 在任意 URL 后追加 `.md` 即可查看该页面的 Markdown 版本。

LLM 是如何工作的?

一个典型的自回归 LLM 会将文本读作 token,通过围绕注意力机制构建的一叠 Transformer 层进行处理,并一次生成一个 token 的输出。本页将带你走完整条 流水线:文本如何变成 token、模型内部的架构是什么样,以及推理如何经过 prefill 和 decode 两个阶段。

什么是 token 和 tokenization?

token 是 LLM 用来处理文本的最小语言单位。根据 tokenizer 的不同,它可以是 一个单词、子词,甚至单个字符。 每个 LLM 都有自己的 tokenizer,对应不同的分词算法。

文本要被模型处理之前,必须先经过 tokenization(分词)。Tokenization 就是把 句子或段落等输入文本切分成 token 的过程。

每个 LLM 都有一个 vocabulary(词表):也就是模型能够表示的一组固定 token。 词表中的每个 token 都映射到一个 token ID。在 tokenization 过程中,token 会先被转换成 token ID,然后在推理时送入模型。

下面是对句子 The quick brown fox jumps over the lazy dog. 使用 GPT-5's tokenizer 进行分词的示例:

Tokens: "The", " quick", " brown", " fox", " jumps", " over", " the", " lazy", " dog", "."

Token IDs: [976, 4853, 19705, 68347, 65613, 1072, 290, 29082, 6446, 13]

对于输出,LLM 会以自回归方式生成新的 token。模型从一个初始 token 序列开始, 基于迄今为止见过的全部内容预测下一个 token。这个过程会重复,直到满足某个 停止条件。

LLM 内部有什么?

大多数现代 LLM 都是 仅解码器 Transformer 模型。 从高层看,这种架构有三个主要组成部分:

  1. 输入表示:Token ID 会被转换成数值向量(embedding,嵌入)。模型还会 融入位置信息,以区分 token 的顺序。例如,Rotary Positional Embeddings (RoPE)会在注意力计算中编码位置信息。
  2. 一叠 Transformer 层:嵌入向量会通过多层 Transformer。典型的一层会把 self-attention(自注意力)机制与 feed-forward network(前馈网络)结合起来: 前者在 token 之间混合信息,后者则独立地变换每个 token。
  3. 输出头:最终的隐藏状态会被投影成 logits,也就是对词表中每个 token 的 一个原始分数。模型随后会 从这些分数对应的分布中采样以选出下一个 token

许多推理优化要么针对注意力计算,要么针对生成时注意力所使用的 KV cache。

注意力机制

注意力机制让模型能够为不同 token 分配不同的重要性,从而捕捉整个序列中的关系。

为此,每一层注意力都会学习三个权重矩阵: WQW_Q(query weight)、WKW_K(key weight)和 WVW_V(value weight)。 这些矩阵在所有 token 位置之间共享,并把每个 token 的隐藏表示变换成三个向量:

  • Query (Q):表示该 token 想从它能够关注的其他 token 中收集哪一类信息。
  • Key (K):表示该 token 提供的是哪一类信息。如果某个 key 向量与另一个 token 的 query 向量正向对齐,那么另一个 token 就会“关注”当前 token (关注程度由 attention score 决定,而 attention score 取决于 query 和 key 向量的对齐程度)。
  • Value (V):表示该 token 实际提供给其他 token 的信息。如果另一个 token 关注了当前 token,它就会把这个 value 中的一部分融入自己的 token embedding 中(比例由 attention score 决定)。

这三个向量让注意力机制能够确定:每个 token 会对其他每个 token 关注多少,以及 在关注时具体获取什么信息。模型会把当前 token 的 query 向量与它被允许关注的 所有 token 的 key 向量进行 scaled dot product(缩放点积)比较: (QKdk\frac{QK^\top}{\sqrt{d_k}})。得到的分数表示每个 token 对当前 token 的相关性。 在这个打分步骤中,value 不参与计算。

随后,这些分数会通过 softmax 函数归一化,变成总和为 1 的 attention weights (注意力权重)。这些权重决定每个 value 向量对当前 token 的贡献有多大。每个 value 向量会乘以对应的 attention weight,再把加权后的 value 相加,得到当前 token 的 attention output,并用它更新该 token 的表示。也就是说,每个 token 都会按照自己分配出去的注意力多少,从其他 token 中吸收信息。

在仅解码器 LLM 中,causal mask(因果掩码)会限制每个 token 只能关注当前位置 及更早的位置。这就是为什么在 river bankbank account 里,bank 会表达不同含义:因为该 token 的表示会根据它所关注到的 token 而更新。

这里计算出的 key 和 value 也是模型会存入 KV cache 的内容,后面介绍 prefill 和 decode 阶段时会再次看到它们。

更多信息可参考论文 Attention Is All You Need

注意力掩码

self-attention 天然可以一次同时查看序列中的所有 token,但这并不总是我们想要的。 attention mask(注意力掩码)用于控制每个 token 被允许关注哪些 token。

在标准的自回归生成中,仅解码器 LLM 会应用 causal mask(也叫 look-ahead mask)。这个掩码是架构本身的一部分,而不是一个可选设置:自回归模型要基于前面的 token 预测下一个 token,因此某个 token 绝不能看到它后面的 token。Causal mask 会在 softmax 之前,把未来位置的 attention score 设为负无穷,使对应权重变为 零,从而阻断从后面位置流向前面位置的信息。

当许多 token 被并行处理时,例如训练阶段和推理的 prefill 阶段,causal mask 尤其重要。没有它,前面的 token 就会关注后面的 token,得到与模型实际生成文本 方式不一致的表示。而在普通的单 token decode 中,模型一次只生成一个 token, KV cache 里也只存在过去的 token,因此已经没有未来位置需要掩码了。

attention mask 还会用来隐藏 padding token。Padding token 是为了让一个 batch 中不同长度的序列对齐而添加的填充 token,本身不携带语义。

LLM 推理的两个阶段

对于像 GPT-4 这样的仅解码器 Transformer 模型,整个推理过程可以分解为两个阶段: prefill 和 decode

Prefill 阶段

当用户发送一个查询时,LLM 的 tokenizer 会把 prompt 转换为一个 token 序列。 Tokenization 完成后,prefill 阶段开始:

  1. 这些 token(或 token ID)会被嵌入成 LLM 能理解的数值向量。
  2. 这些向量会经过多层 transformer,每层都包含一个 self-attention 机制。此时, 每个 token 都会计算出 query (Q)、key (K) 和 value (V) 向量。 这些向量决定 token 如何彼此关注(受 causal mask 约束),从而捕捉上下文语义。
  3. 当模型处理 prompt 时,它会构建一个 KV cache,用于存储每一层中每个 token 的 key 和 value 向量。它相当于一种内部记忆,以便在解码时更快查找。

在 prefill 阶段,整个 prompt(也就是完整的输入 token 序列)在 LLM 开始实际 计算之前就已经全部可用。这意味着 LLM 可以借助高度并行化的矩阵运算同时处理所有 token,尤其是在注意力计算中。

因此,prefill 阶段通常是 compute-bound(算力受限)的,并且经常会打满 GPU 利用率。实际利用率会受到序列长度、batch size 以及硬件规格等因素影响。

监控 prefill 的一个关键指标是 Time to First Token(TTFT,首 token 时间), 它衡量从提交 prompt 到生成第一个 token 的延迟。更多细节将在 推理优化 章节中介绍。

展示 tokenization、prefill、decode 和 detokenization 阶段,并标出首 token 时间(TTFT)的 LLM 推理流程图展示 tokenization、prefill、decode 和 detokenization 阶段,并标出首 token 时间(TTFT)的 LLM 推理流程图

Decode 阶段

prefill 之后,LLM 进入 decode 阶段,在这个阶段中它会按顺序一次生成一个新 token。

对于每个新 token,模型都会基于 prompt 以及此前已经生成的全部 token 所形成的 概率分布 进行采样。 这一过程是自回归的:token T₀ 到 Tₙ₋₁ 用来生成 token Tₙ,接着 T₀ 到 Tₙ 又用来生成 Tₙ₊₁,如此继续。

模型在解码时只能从自己的词表中预测 token。更大的词表可以直接表示更多文本模式, 但它也会让最终的预测层更大,因为模型必须为每一个可能的下一个 token 打分。

使用下面的 stepper 可以慢速查看这个自回归循环。每点击一次,都会基于当前已经构成 的完整序列预测一个新 token。

逐 Token 解码循环
逐步查看解码循环。每一步都会从一个分布中选出一个 token,并把它追加到 序列末尾。
步骤 1 / 8
T0Modular
T1
T2一个
T3统一的
T4推理
T5?
已有 · 5已生成 · 0下一个
平台64%
18%
引擎11%
服务7%
复用了 5 组已缓存的 K/V 对;本步会 新计算 1 组。

每个新生成的 token 都会被追加到不断增长的序列末尾。这个自回归循环会持续,直到:

  • 达到最大 token 数限制,
  • 生成了 stop word,
  • 或出现某个特殊的序列结束 token(例如 <end>)。

最后,生成出的 token 序列会被解码回人类可读的文本。

与 prefill 相比,decode 更偏 memory-bound(内存带宽受限),因为它需要频繁从 内存中读取模型权重和不断增长的 KV cache。KV caching 会把这些 key 和 value 矩阵存储在内存里,这样在后续 token 生成过程中,LLM 只需要为新 token 计算 key 和 value,而不必从头重新计算全部内容。

这种 KV caching 机制通过避免重复计算显著加速了推理。但代价是内存消耗增加, 因为 cache 会随着生成序列长度增长。即使模型权重已经能放进 GPU,KV cache 内存也可能成为服务瓶颈。一些推理系统会通过压缩或量化 KV cache 来减轻这个压力, 另一些系统则会通过 KV cache offloading 把不活跃的 cache block 移到更便宜的内存中。

监控 decode 的一个关键指标是 Inter-Token Latency(ITL,token 间延迟),也就是 序列中相邻两个 token 生成之间的时间间隔。

延迟时间线可视化器
点击播放,观察每一步解码如何在经过 detokenization 后产出一个 token。 选择一个指标,查看它覆盖了哪些阶段。
6 个输出 token
首 token 时间207 ms
提示词提交 → 第一个 token 显示出来
分词 + 预填充 + 解码 + 反分词 = 3 + 180 + 22 + 2
TTFT
0 ms · 0/6 个 token输出会显示在这里…
分词 预填充 解码 反分词
假设分词 = 3 ms,预填充 = 180 ms,解码 = 22 ms, 反分词 = 2 ms,且不同解码步之间的 ITL 保持不变。真实数值会随 模型、硬件和序列长度而变化。

将 prefill 与 decode 放在同一处运行

传统的 LLM serving 系统通常会在同一套硬件上同时运行 prefill 和 decode 两个阶段。 但这种部署方式会带来若干挑战。

一个主要问题是 prefill 和 decode 之间会互相干扰,因为它们无法真正完全并行。 在生产环境中,多个请求可能同时到达,每个请求都有自己的 prefill 和 decode 阶段,这些阶段会与其他请求的阶段交错重叠。但任意时刻通常只能运行一种阶段。 当 GPU 正在忙于计算密集的 prefill 任务时,decode 任务就必须等待,token 延迟会上升;反之亦然。这会让两个阶段的资源调度变得困难。

开源社区正在积极探索不同策略,以把 prefill 和 decode 拆分开来。更多信息可见 prefill-decode disaggregation

什么是 context window,它在 LLM 推理中如何工作?

context window(上下文窗口)是 LLM 在一次推理过程中能够处理的 token 数量。 它包含为了维持连贯性而需要在每一轮重新发送的整个对话历史。

上下文窗口模拟器
每一轮里,完整对话都会被重新发送给模型。观察窗口如何逐步被填满。
模型上下文大小
发送给模型的输入
每一轮都会重新发送此前的全部消息。
点击 + 添加一轮 开始对话。
Token 使用量
0 / 32,000
0%上下文已使用
先选择一个模型窗口大小,再添加轮次观察输入如何增长。

严格来说,LLM 并没有真正的“记忆”。为了保持上下文,每一次新请求都必须重新发送 之前的所有消息,这样模型才能再次“看到”完整对话(这个过程发生在底层,用户通常 看不到)。换句话说,连续性是通过每次在输入 prompt 中重新构建上下文来维持的。

展示对话上下文如何随着轮次增长,从而增加被处理 token 数量的示意图展示对话上下文如何随着轮次增长,从而增加被处理 token 数量的示意图

这段不断累积的文本历史就叫作 context window,它有一个最大长度 (例如 8K、32K 或 128K token)。

如上所述,LLM 会在 decode 阶段利用前面 token 的 KV cache,避免把所有内容 完全重新处理一遍,从而有助于降低延迟。如果是在多个请求之间复用 KV cache, 更准确的叫法是 prefix caching

Diffusion LLM(dLLM)

自回归式 LLM 有一个天然瓶颈:生成速度慢,而且计算开销高。

Diffusion LLM(dLLM)反转了这一逻辑。它们借鉴 Stable Diffusion 这类图像生成 模型中的去噪过程,并行地产生整段响应。

基本思路如下:

  1. 模型从一团噪声开始,把它看作可能输出的粗略草图。
  2. 经过多次去噪步骤,它会逐步把这些噪声细化成连贯文本。
  3. 最终答案会一次性显现出来,就像一张图像逐渐清晰对焦。

这种并行过程消除了逐 token 生成的瓶颈。它还允许 dLLM 在生成过程中迭代并自我 修正,因此在编辑、数学推理和代码补全等任务上尤其有优势。

dLLM 的早期示例包括:

  • Inception AI 的 Mercury: 据称其推理速度和效率最高可达传统 LLM 的 10 倍。
  • Google DeepMind 的 Gemini Diffusion: 这是将 diffusion 应用于文本生成的一项早期探索,目前以实验性 demo 的形式提供, 用于帮助开发和打磨未来模型。

就目前而言,自回归 LLM 仍然是主流架构。不过,dLLM 代表了驱动下一代推理系统的 最有前景方向之一。如果你今天正在使用自回归 LLM,值得持续关注 dLLM。

常见问题

所有 LLM 都是仅解码器 Transformer 模型吗?

不是。LLM 可以基于不同的 Transformer 架构构建,包括 encoder-only、 encoder-decoder 和 decoder-only 模型。

不过,今天人们提到 LLM 时,通常指的是 decoder-only Transformer 模型。 它们主导着现代生成式 AI 应用,例如聊天机器人和代码助手。当前讨论的大多数推理 技术,包括 KV caching、continuous batching、speculative decoding 以及 prefill-decode disaggregation,都是围绕这种架构设计的。

架构示例是否有 Prefill 和 Decode 阶段说明
仅编码器(Encoder-only)BERT, RoBERTa否。输入会在一次前向传播中处理完成,不进行自回归生成。主要用于分类、嵌入和搜索
编码器-解码器(Encoder-decoder)T5, FLAN-T5, BART部分有。编码器处理一次输入,解码器再以自回归方式生成 token。在前沿 LLM 中较少见
仅解码器(Decoder-only)GPT, Llama, Qwen, DeepSeek, Claude有。推理由 prefill 阶段开始,随后进入逐 token 的 decode 阶段。现代 LLM 的主导架构

什么是 KV cache?

KV cache 是 LLM 用来存储已经计算过的 key(K)和 value(V)向量的内存,这样 它就不必重复计算这些向量。

注意力机制要求每个 token 关注它前面的所有 token。如果没有 cache,生成第 1,000 个 token 时,就意味着要重新计算前面 999 个 token 的 key 和 value;生成第 1,001 个 token 时,又要把这项工作再做一遍。由于这些向量只依赖 token 本身, 而不依赖其后面的内容,所以一旦计算出来就不会改变。把它们缓存起来之后,每个 decode 步骤只需要为一个新的 token 计算 K 和 V,其余内容直接从内存中读回。 这就是 KV cache 机制如何帮助加速推理的。

随着推理过程中序列长度不断增长,KV cache 会成为内存使用中的主导因素。以 FP16 格式的 Llama 3 8B 为例,单个 8K-token 序列大约会占用 1 GB 的 KV cache (更多见 计算方法)。 而模型权重大约占 16 GB。在一张 80 GB 的 GPU 上,这意味着最多大约只剩 64 GB 可用于 KV cache 和其他运行时数据,对应的理论上限约为 60 条这样的序列。这也是 为什么通常限制服务器可服务用户数量的不是模型权重,而是 KV cache。

本手册中的多种优化技术都围绕 KV cache 展开:

token 是如何通过采样被选中的?

在每一个 decode 步骤,模型不会直接输出一个单词。相反,它会给出一个覆盖所有可能 token 的概率分布。

sampling(采样)就是从这个分布中选出下一个 token 的过程。

在采样之前,首先会对 logits(softmax 之前的原始分数)施加 temperature(温度)。 它会对 logits 进行重缩放,从而在计算概率之前改变分布形状。

  • 较低 temperature:分布会更尖锐(少数 token 占主导)
  • 较高 temperature:分布会更平坦(概率分散到更多 token 上)

应用 temperature 并将 logits 转成概率之后,具体由哪一个采样策略决定选哪个 token。常见策略包括 greedy decoding、top-k 和 top-p。

更多内容可参考 LLM inference parameters

LLM 推理逐步会发生什么?

从高层看,LLM 推理遵循一个简单循环:

  1. 输入文本被转换为 token
  2. 在 prefill 阶段处理这些 token,以构建上下文和 KV cache
  3. 模型进入 decode 阶段。每一步中:
    • 模型产生一个概率分布
    • 采样策略选出下一个 token
  4. 重复上述过程,直到满足停止条件
  5. token 被转换回可读文本

尽管输出看起来几乎是即时出现的,但在 decode 过程中,这一过程实际上是一次一个 token 地运行。

为什么 LLM 推理会很慢?

LLM 推理变慢主要有两个原因:

  • 顺序解码:token 必须一个接一个生成,这限制了并行性
  • 内存瓶颈:在 decode 过程中,模型会反复从 GPU 内存中的 KV cache 读取数据

其他因素也会影响延迟:

  • 模型大小(参数越多,计算越多)
  • 输入长度(prompt 越长,prefill 时间越长)
  • 硬件(GPU 类型和内存带宽)

这也是为什么 inference optimization 会成为生产系统中的一个核心关注点。