跳到主要内容

LLM 是如何工作的?

一个典型的自回归 LLM 将文本作为 token 读取,通过一组围绕注意力机制(attention mechanism)构建的 Transformer 层进行处理,并一次生成一个 token 的输出。本页将逐步讲解这条流水线:文本如何变成 token、模型内部架构是什么样,以及推理如何经过预填充(prefill)和解码(decode)两个阶段。

什么是 token 和分词?

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

在模型处理文本之前,文本必须先经过分词(tokenization)。分词是将输入文本(如一个句子或段落)拆分为 token 的过程。

每个 LLM 都有一个词表(vocabulary):即模型可以表示的一组固定 token。词表中的每个 token 都映射到一个 token ID。在分词过程中,token 会先被转换为 token ID,然后在推理时传入模型。

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

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 都是仅解码器(decoder-only)的 Transformer 模型。从高层来看,其架构包含三大组件:

  1. 输入表示: Token ID 被转换为数值向量(嵌入,embedding)。模型还会加入位置信息,以便区分 token 的顺序。例如,旋转位置编码(RoPE)在注意力计算中编码位置信息。
  2. 一组 Transformer 层: 嵌入向量经过多层 Transformer 层。一个典型层将自注意力机制(在不同 token 之间混合信息)与前馈网络(独立地变换每个 token)结合起来。
  3. 输出头: 最终的隐藏状态被投影为 logits,即词表中每个 token 的一个原始分数。这些 logits 就是模型通过采样来选择下一个 token时所依据的内容。

许多推理优化都针对注意力计算,或针对注意力在生成过程中使用的 KV 缓存。

注意力机制

注意力(attention)机制让模型能够为不同的 token 分配不同的重要程度,从而捕获整个序列中的相互关系。

为此,每个注意力层学习三个权重矩阵:WQW_Q(查询权重)、WKW_K(键权重)和 WVW_V(值权重)。这些矩阵在所有 token 位置之间共享,并将每个 token 的隐藏表示转换为三个向量:

  • 查询向量(Query, Q): 表示该 token 想要从它可以关注的其它 token 那里收集的信息类型。
  • 键向量(Key, K): 表示该 token 所能提供的信息类型。如果某个键向量与另一个 token 的查询向量正向对齐,那么那个 token 就会「关注」到这个 token(关注程度由注意力分数决定,该分数基于查询向量与键向量的对齐程度计算)。
  • 值向量(Value, V): 表示该 token 实际提供给其它 token 的信息。如果另一个 token 关注到这个 token,它就会将值的一部分嵌入到自己的 token 表示中(嵌入比例由注意力分数决定)。

这三个向量让注意力机制能够确定每个 token 对其他每个 token 的关注程度,以及在关注时提取了什么。模型使用缩放点积(QKdk\frac{QK^\top}{\sqrt{d_k}})将当前 token 的查询向量与它被允许关注的所有 token 的键向量进行比较。得到的分数表示每个 token 与当前 token 的相关程度。值向量在这个评分步骤中不起作用。

随后,这些分数通过 softmax 函数进行归一化,变成总和为 1 的注意力权重。这些权重决定了每个值向量对当前 token 的贡献程度。每个值向量都乘以其注意力权重,然后将加权后的值求和,得到当前 token 的注意力输出,从而更新其表示。通过这种方式,每个 token 都会根据它对其他 token 的关注程度,融入来自其他 token 的信息。

在仅解码器的 LLM 中,因果掩码(causal mask)将每个 token 限制为只能关注当前及之前的位置。这就是为什么「bank」在「river bank(河岸)」和「bank account(银行账户)」中含义不同的原因:token 的表示会根据它所关注的 token 进行更新。

这里计算出的键和值,也正是模型存储在 KV 缓存中的内容,你将在下面的预填充和解码阶段看到。

更多信息,请参阅《Attention Is All You Need》论文。

注意力掩码

自注意力可以自然地同时查看序列中的每个 token,但这并不总是可取的。注意力掩码(attention mask)控制每个 token 被允许关注哪些 token。

对于标准的自回归生成,仅解码器的 LLM 会应用因果掩码(causal mask,也称为前瞻掩码 look-ahead mask)。掩码是架构的一部分,而不是可选的设置:自回归模型根据之前的 token 预测下一个 token,因此一个 token 绝不能看到位于它之后的 token。因果掩码通过在 softmax 之前将未来位置的注意力分数设置为负无穷来强制执行这一点,从而使这些位置的权重变为零,信息不会从后面的位置流向前面的位置。

每当有大量 token 被并行处理时(例如在训练和推理的预填充阶段),因果掩码就非常重要。没有它,较早的 token 就会关注到较晚的 token,从而产生与模型生成文本方式不一致的表示。在普通的单 token 解码过程中,模型一次生成一个 token,KV 缓存中只存在过去的 token,因此没有需要掩码的未来位置。

注意力掩码也用于隐藏填充 token(padding token),这些填充 token 是为了对齐批次(batch)中不同长度的序列而添加的填充性 token,本身不携带任何含义。

LLM 推理的两个阶段

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

预填充(Prefill)

当用户发送查询时,LLM 的分词器将提示词转换为 token 序列。预填充阶段在分词之后开始:

  1. 这些 token(或 token ID)被嵌入为 LLM 可以理解的数值向量。
  2. 向量经过多个 Transformer 层,每个层都包含自注意力机制。在这里,为每个 token 计算查询(Q)、键(K)和值(V)向量。这些向量决定了 token 之间如何相互关注(受因果掩码约束),从而捕获上下文含义。
  3. 在模型处理提示词的过程中,它会构建一个 KV 缓存,用于存储每一层、每个 token 的键和值向量。它充当内部记忆,以便在解码过程中更快地查找。

在预填充阶段,整个提示词(即完整的输入 token 序列)在 LLM 开始任何实际计算之前就已经可用。这意味着 LLM 可以通过高度并行化的矩阵运算同时处理所有 token,尤其是在注意力计算中。

因此,预填充阶段是计算密集(compute-bound)的,通常会使 GPU 利用率达到饱和。实际利用率取决于序列长度、批次大小和硬件规格等因素。

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

LLM 推理预填充流水线,展示分词、预填充、解码和去分词阶段在首 token 时间(TTFT)内的分布

解码(Decode)

预填充之后,LLM 进入解码阶段,在这个阶段它逐个地顺序生成新 token。

对于每个新 token,模型根据提示词和之前生成的所有 token,从概率分布中采样。这个过程是自回归的,即用 token T₀ 到 Tₙ₋₁ 生成 token Tₙ,再用 T₀ 到 Tₙ 生成 Tₙ₊₁,依此类推。

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

使用下方的步进演示,以慢动作查看自回归循环。每次点击都会基于到目前为止构建的完整序列预测一个新 token。

提示: 若无法显示,请直接打开 独立工具页面

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

  • 达到最大 token 数量上限,
  • 生成了停止词,
  • 或出现特殊的序列结束 token(例如 <end>)。

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

与预填充相比,解码更受内存带宽限制(memory-bound),因为它需要频繁地从内存中读取模型权重和不断增长的 KV 缓存。KV 缓存将这些键和值矩阵存储在内存中,这样在后续 token 生成时,LLM 只需为新 token 计算键和值,而无需从头重新计算所有内容。

这种 KV 缓存机制通过避免冗余计算显著加快了推理速度。然而,它的代价是内存消耗增加,因为缓存会随着生成序列的长度增长。即使模型权重已经能放进 GPU,KV 缓存内存仍可能成为服务瓶颈。一些推理系统通过压缩或量化 KV 缓存来减轻这种压力,另一些则通过KV 缓存卸载将不活跃的缓存块移动到更廉价的内存中。

解码阶段需要监控的一个关键指标是 token 间延迟(ITL,Inter-Token Latency),即序列中连续两个 token 生成之间的时间。

提示: 若无法显示,请直接打开 独立工具页面

将预填充和解码共置

传统的 LLM 服务系统通常在同一硬件上运行预填充和解码两个阶段。然而,这种设置会带来一些挑战。

一个主要问题是预填充和解码阶段之间的相互干扰,因为它们无法完全并行运行。在生产环境中,多个请求可能同时到达,每个请求都有自己的预填充和解码阶段,这些阶段在不同请求之间相互重叠。然而,同一时间只能运行一个阶段。当 GPU 忙于计算密集的预填充任务时,解码任务必须等待,从而增加 token 延迟;反之亦然。这使得为两个阶段调度资源变得困难。

开源社区正在积极探索不同的策略来分离预填充和解码。更多信息,请参阅预填充-解码分离

什么是上下文窗口,它在 LLM 推理中如何运作?

上下文窗口(context window)是 LLM 在单次推理中能处理的 token 数量。它包含每一轮都必须重新发送以维持连贯性的完整对话历史。

提示: 若无法显示,请直接打开 独立工具页面

从技术上讲,LLM 并没有真正的记忆。为了保持上下文,每个新请求都必须重新发送之前的所有消息,以便模型能再次「看到」完整对话(这发生在底层,用户看不到)。换句话说,连贯性是通过每次在输入提示词中重建上下文来维持的。

对话上下文如何随轮次增长,增加处理的 token 数量

这种持续累积的文本历史被称为上下文窗口,它有一个最大长度(例如 8K、32K 或 128K 个 token)。

如前所述,LLM 使用之前 token 的 KV 缓存来避免在解码阶段完整地重新处理所有内容,这有助于降低延迟。当在多个请求之间复用 KV 缓存时,更准确的叫法是前缀缓存技术。

扩散 LLM(dLLM)

LLM 的自回归模式有一个天然瓶颈:速度慢且计算成本高。

扩散 LLM(Diffusion LLM,dLLM)颠覆了这种逻辑。它们通过一种受 Stable Diffusion 等图像生成模型启发的去噪(denoising)过程,并行地输出整个响应。

基本思路如下:

  1. 模型从一片噪声开始,它表示可能输出的粗略草图。
  2. 通过多个去噪步骤,它逐渐将噪声精炼为连贯的文本。
  3. 最终答案一次性呈现,就像图像逐渐对焦变得清晰一样。

这种并行过程消除了逐 token 生成的瓶颈。它还允许 dLLM 在生成过程中进行迭代和自我纠正,这使得它们在编辑、数学推理和代码补全等任务上尤为强大。

dLLM 的早期示例包括:

  • Inception AI 推出的 Mercury:据报道,其推理速度和效率比传统 LLM 高出 10 倍。
  • Google DeepMind 推出的 Gemini Diffusion:将扩散应用于文本生成的早期探索。它以实验性演示的形式提供,以帮助开发和完善未来的模型。

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

常见问题(FAQs)

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

不是。LLM 可以使用不同的 Transformer 架构构建,包括仅编码器(encoder-only)、编码器-解码器(encoder-decoder)和仅解码器(decoder-only)模型。

然而,今天人们谈论 LLM 时,通常指的是仅解码器的 Transformer 模型。它们主导着现代生成式 AI 应用,如聊天机器人和编码助手。当前讨论的大多数推理技术,包括 KV 缓存、连续批处理、投机解码和预填充-解码分离,都是围绕这种架构设计的。

架构示例预填充和解码阶段备注
仅编码器(Encoder-only)BERT、RoBERTa没有。输入在单次前向传播中处理,不进行自回归生成。主要用于分类、嵌入和搜索
编码器-解码器(Encoder-decoder)T5、FLAN-T5、BART部分。编码器一次性处理输入,而解码器以自回归方式生成 token。在前沿 LLM 中较少见
仅解码器(Decoder-only)GPT、Llama、Qwen、DeepSeek、Claude有。推理包含预填充阶段,随后是逐 token 的解码阶段。现代 LLM 的主导架构
备注

除非另有说明,本手册中的「LLM」均指仅解码器的 Transformer 模型。

什么是 KV 缓存?

KV 缓存是 LLM 用来存储已经计算过的键(K)和值(V)向量的内存,这样它就不必再次计算它们。

注意力机制的工作原理是让每个 token 关注它之前的所有 token。如果没有缓存,生成第 1,000 个 token 就需要重新计算前 999 个 token 的键和值,然后在生成第 1,001 个 token 时重复同样的工作。因为这些向量只取决于 token 本身,而不取决于它之后的内容,所以一旦计算出来就永远不会改变。缓存它们使得每个解码步骤只需要为单个新 token 计算 K 和 V,其余部分直接从内存中读取。这就是 KV 缓存机制帮助加速推理的方式。

随着推理过程中序列长度的增长,KV 缓存成为内存使用的主导因素。例如,对于 FP16 精度的 Llama 3 8B,一个 8K token 的序列大约会占用 1 GB 的 KV 缓存(了解更多关于计算方法的信息)。模型权重大约占用 16 GB。在 80 GB 的 GPU 上,这最多给 KV 缓存和其他运行时数据留下约 64 GB 的空间,理论上限约为 60 个这样的序列。这就是为什么通常限制服务器能处理多少用户的是 KV 缓存,而不是模型权重。

KV 缓存是本手册中若干优化技术的核心:

  • PagedAttention 将缓存存储在固定大小的块中,以避免碎片化和过度预留
  • 前缀缓存 跨请求复用共享提示词前缀的缓存
  • KV 缓存卸载 将不活跃的块移动到 CPU 内存或磁盘,并包含一个用于上述示例的计算器
  • 预填充-解码分离 将两个阶段拆分到不同的工作节点上,并在它们之间传输缓存

采样时是如何选择 token 的?

在每一个解码步骤中,模型并不会直接输出一个词。相反,它会生成一个覆盖所有可能 token 的概率分布。

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

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

  • 较低的温度: 分布变得更加尖峰(少数 token 占主导)
  • 较高的温度: 分布变得更加平坦(概率分散到更多 token 上)

在应用温度并将 logits 转换为概率之后,由采样策略决定选择哪个 token。常见的策略包括贪心解码(greedy decoding)、top-k 和 top-p。

了解更多关于LLM 推理参数的信息。

LLM 推理过程中一步一步发生了什么?

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

  1. 输入文本被转换为 token
  2. token 在预填充阶段被处理,以构建上下文和 KV 缓存
  3. 模型进入解码阶段。在每个步骤中:
    • 模型生成一个概率分布
    • 采样策略选择下一个 token
  4. 该过程不断重复,直到满足停止条件
  5. token 被转换回可读的文本

尽管输出感觉是即时的,但在解码过程中,这个过程是一个 token 一个 token 地运行的。

为什么 LLM 推理很慢?

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

  • 顺序解码: token 一个接一个地生成,限制了并行度
  • 内存瓶颈: 在解码过程中,模型反复从 GPU 内存中的 KV 缓存读取数据

其他因素也会影响延迟:

  • 模型大小(参数越多,计算量越大)
  • 输入长度(更长的提示词会增加预填充时间)
  • 硬件(GPU 类型和内存带宽)

这就是为什么推理优化是生产系统中的一大重点。

其他资源