1. 基础

编码、解码与大语言模型的核心构件。

1 概览

大语言模型(LLM, Large Language Model)本质上是一类“下一个词元(token)预测器”:给定前文,预测最可能出现的下一个符号。本章不打算把你训练成“公式背诵机”,而是帮你建立一套够用、清晰、能上手排障的心智模型。

在推理(inference)阶段,典型流程是:文本先经过分词器(tokenizer)变成词元 ID(token ID);词元 ID 查表得到嵌入向量(embedding);向量经过多层 Transformer 模块(自注意力 + 前馈网络);最后输出 logits(词表上的未归一化分数)。解码器再根据这些 logits 选择下一个词元,循环生成文本。

面试和工程里真正有价值的,不是背全公式,而是能回答三件事: 1. 系统由哪些关键模块组成。 2. 每个模块有哪些核心权衡(质量 vs 成本、延迟 vs 吞吐)。 3. 常见故障通常发生在哪一层(分词、训练稳定性、解码策略等)。

1.1 术语约定

为保证全章一致,后文统一采用以下写法:

  • 词元(token)

  • 词元 ID(token ID)

  • 分词器(tokenizer)

  • 嵌入(embedding)

  • 注意力(attention)

  • 前馈网络(FFN, Feed-Forward Network)

读完本章后,你应该能够清楚解释:为什么子词分词成为主流;嵌入与位置编码怎么配合;自注意力与掩码如何工作,以及长上下文为何昂贵;残差、归一化与 FFN 的作用;绝对位置、RoPE、ALiBi 的差异;以及贪心、beam、top-k/top-p、温度等解码方法的适用场景与失效模式。

2 分词(Tokenization)

分词是把文本映射成模型可处理词元序列的过程,而且这个映射通常是确定性的。

它之所以重要,是因为分词会同时影响三件关键事情:

  • 质量:领域词汇能否被合理表达。

  • 成本:词元数量直接决定计算量。

  • 多语言表现:不同语言、噪声文本、特殊符号能否稳定处理。

更换 tokenizer 不是“小改动”,而是改变模型输入符号系统;这会影响训练假设、评估可比性,以及线上行为一致性。

2.1 分词粒度

常见分词粒度有词级、字符级和子词级。

词级(word-level):整词入表,直观,但有三个典型问题:OOV 严重、词表尾部过长、词形变化共享差(如 swimswimming)。

字符级(character-level):几乎没有 OOV,但序列会明显变长,导致注意力计算成本上升。

子词级(subword-level):在覆盖率和序列长度之间取得折中。它通常能比词级更稳健地覆盖新词,又比字符级更省计算,因此成为主流。

2.2 子词词表如何学习

多数子词算法输入是语料和目标词表大小 \(V\),输出是:

  • 一组子词词表。

  • 一套把文本切成这些子词的规则。

核心目标很明确:在保持覆盖的同时尽量减少词元数,从而降低注意力成本。

BPE(Byte-Pair Encoding)(包含 byte-level BPE)通过反复合并高频相邻单元来构建词表。 [code]

BBPE(byte-level BPE)在字节层面做合并,对多语言与脏数据通常更稳,也能处理任意字节序列。

WordPiece倾向于选择能提高语言模型似然的合并。直觉上,如果两个片段经常一起出现,就更值得合并成一个词元。

一种常见打分写法是对数增益(也可看作 PMI 风格):

\[ \log P(z) - (\log P(x) + \log P(y)) = \log \frac{P(z)}{P(x)P(y)} \]

其中 \(z=xy\)。若 \(P(z)\) 显著大于 \(P(x)P(y)\),说明“连在一起”比“分开出现”更合理,合并就更有收益。

Unigram LM路径相反:先从大候选集出发,再删掉对整体似然影响最小的词元。可把它理解为“在所有合法切分中找最可能的一种”: \[ p(t_{1:m}) = \prod_{i=1}^{m} p(t_i), \] \[ \hat{t}_{1:m} = \arg\max_{t_{1:m}:\,\mathrm{concat}(t_{1:m})=s} \prod_{i=1}^{m} p(t_i). \]

实践中常用动态规划(Viterbi 风格)求最优切分,并保留单字符回退,避免 OOV。

小例子:把 unhappiness 当成一个词看,词级分词容易 OOV;按字符切又太长。子词分词会更像 un + happi + ness,既看得懂,又不啰嗦。

一句话总结:BPE/WordPiece 是“从小到大合并”,Unigram 是“从大到小裁剪”。

2.3 面试题

2.3.1 为什么 LLM 更偏好子词分词?

它同时解决了覆盖和效率:比词级更少 OOV,比字符级序列更短。

2.3.2 词表大小如何影响质量与算力?

词表更大:嵌入/softmax 更重;词表更小:序列变长、注意力更贵。最佳点要结合语料分布和部署约束。

2.3.3 什么情况下要扩展 tokenizer?风险是什么?

当领域词(如医学术语、代码标识符)被过度切碎,或多语言覆盖明显不足时可考虑扩展。风险包括:嵌入失配、历史指标不可比、训练与线上预处理不一致。

2.3.4 分词常见坑有哪些?

分词器不一致。 训练和服务使用不同 tokenizer/chat template,容易引发格式、工具调用与安全规则问题。

领域词碎片化。 关键术语被切成过多词元,成本上升且语义表达变差。

指标不可比。 困惑度等词元级指标跨 tokenizer 不能直接比较。

数值脆弱性。 数字切分方式会影响计数、排序、算式行为。

3 嵌入(Embedding)

嵌入把离散词元映射到连续向量空间,让模型可以做线性代数计算。可以把它理解成“符号系统与神经网络计算之间的接口层”。

3.1 从 one-hot 到稠密向量

词元 ID 的数字本身没有语义,ID=9 并不比 ID=3“更大”或“更接近”。如果直接把 ID 当数值输入,模型会学到错误结构。

one-hot 表示避免了这个问题:词表大小为 \(V\) 时,词元 \(t\) 对应 \(x_t \in \mathbb{R}^V\),只有第 \(t\) 位是 1,其余为 0。它干净但高维,且任何两个词元都是正交关系。

稠密嵌入(dense embedding)把 \(V\) 维 one-hot 映射到 \(d\) 维向量(\(d \ll V\)),既降维又让模型学习“相似词元在向量空间更接近”的几何结构。

小例子:可以把 embedding 理解成“仓库坐标系”。功能相近的词元(比如 deployrelease)往往会被摆在相邻货架,而不是仓库两端。

3.2 嵌入查表(Embedding Lookup)

设词表大小 \(V\)、嵌入维度 \(d\),嵌入矩阵为 \[ E \in \mathbb{R}^{V \times d}. \]

给定词元 ID \(t\),查表得到 \(e_t = E[t]\)。在 PyTorch 中对应 nn.Embedding(vocab_size, embedding_dim)

等价地,若 \(x_t\) 是 one-hot,则 \[ e_t = x_t^\top E. \]

这个式子强调了一点:embedding lookup 本质是稀疏线性映射。

3.3 嵌入怎么学出来

经典路径是 word2vec 系列。

CBOW(Continuous Bag of Words):用上下文预测中心词。它训练快、语义效果好,但几乎不建模词序。

设上下文词为 \(w_1, w_2, \dots, w_C\),目标词为 \(w_t\)。每个上下文词可表示为 one-hot 向量 \(x_i \in \mathbb{R}^V\),嵌入矩阵 \(W \in \mathbb{R}^{V \times d}\) 把它映射为 \(v_i = W^T x_i\)。隐藏表示为平均值 \(h = \frac{1}{C}\sum_{i=1}^{C} v_i\),再通过第二个矩阵 \(W' \in \mathbb{R}^{d \times V}\) 得到分数 \(u = {W'}^T h\),于是: \[ p(w_t \mid \text{context}) = \frac{\exp(u_t)}{\sum_{j=1}^{V} \exp(u_j)}. \]

Skip-gram反过来:用中心词预测上下文。通常对稀有词更友好,但计算量更大。

FastText把词表示成字符 n-gram 向量之和,能更好处理形态变化和 OOV。例如 playing 可拆为 <pla, lay, ayi, yin, ing> 等片段: \[ v_{\text{playing}} = \sum_{g \in G} z_g. \] 其中 \(G\) 是字符 n-gram 集合,\(z_g\) 是对应 n-gram 的向量。训练时可在这个表示之上继续做 CBOW 或 skip-gram。

3.4 训练加速技巧

Hierarchical softmax用树结构近似全词表 softmax,复杂度由 \(|V|\) 降到 \(\log V\)

Negative sampling把多分类近似成“正样本 vs 若干负样本”的二分类训练。示例:

(target = "cat", context = "cute")
("cat", "banana")
("cat", "engine")
("cat", "chair")

其目标常写为: \[ L = \log \sigma(v_{+}^\top v_t) + \sum_{i=1}^{K} \log \sigma(-v_{\text{neg}_i}^\top v_t), \] 其中 \(v_+\) 为真实上下文,\(v_{\text{neg}_i}\) 为负样本向量。

3.5 权重绑定(Weight Tying)

在 decoder-only LLM 中,输出层常把隐藏态映射到词表 logits:\(W_{\text{out}} \in \mathbb{R}^{d \times V}\)。权重绑定指输出矩阵与输入 embedding 共享(常见形式 \(W_{\text{out}}=E^\top\))。

这样做通常有两个收益:参数更少,样本效率更好。

3.6 词向量 vs 句向量

词元嵌入(token embedding)表示单个词表项;句向量(sentence embedding)表示整段文本。后者通常来自词元隐状态池化(均值池化、[CLS] 等)或专门训练的表示模型。

3.7 面试题

3.7.1 为什么不能直接用词元 ID?

因为 ID 是任意标签。embedding 才能提供可学习、可度量的连续表示空间。

3.7.2 什么是 weight tying,为什么常用?

输入 embedding 和输出投影共享参数,减少模型参数量,常提升训练效率。

3.7.3 词向量和句向量的核心差异?

前者表示“词项”,后者表示“整段语义”。

4 注意力(Attention)

注意力机制让 Transformer 在每个位置动态选择“该看谁、看多少”。工程上它既是表达力来源,也是算力账单来源。

4.1 自注意力(Self-attention)

对序列中每个位置,模型会构造:

  • Query:我要找什么信息。

  • Key:我能提供什么匹配线索。

  • Value:如果我被关注,我贡献什么内容。

小例子:句子“服务器重启后,恢复了”。这里“它”更可能关注“服务器”而不是“重启”,这就是注意力在做的“指代配对”。

设输入隐藏态为 \(X \in \mathbb{R}^{L\times d}\),单头注意力中: \[ Q = XW_Q,\quad K = XW_K,\quad V = XW_V, \] 其中 \(W_Q, W_K \in \mathbb{R}^{d\times d_k}\)\(W_V \in \mathbb{R}^{d\times d_v}\)

然后执行三步:

  1. 打分: \[ S = \frac{QK^\top}{\sqrt{d_k}}. \] 除以 \(\sqrt{d_k}\) 是为了防止维度增大导致 softmax 饱和。

  2. 掩码 + 归一化: \[ A = \operatorname{softmax}(S). \]

  3. 加权聚合: \[ Y = AV. \]

合并写法: \[ \mathrm{Attention}(Q, K, V) = \operatorname{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V. \]

伪代码(scaled dot-product attention)

# X: [batch, seq, d_model]
# Wq, Wk: [d_model, d_k], Wv: [d_model, d_v]

Q = X @ Wq  # [batch, seq, d_k]
K = X @ Wk  # [batch, seq, d_k]
V = X @ Wv  # [batch, seq, d_v]

# scores: [batch, seq, seq]
scores = Q @ K.transpose(-2, -1)
scores = scores / sqrt(d_k)

# Causal mask (decoder-only)
# set j > i to -inf so softmax gives 0 probability.
# causal_mask: [1, seq, seq] or [batch, seq, seq]
scores = scores + causal_mask

weights = softmax(scores, dim=-1)  # row-wise over keys
Y = weights @ V  # [batch, seq, d_v]

多头注意力(MHA)是把上述过程并行做多次(每头维度更小),再拼接回模型维度。这样不同头能关注不同关系模式(局部语法、长程依赖、位置模式等)。

4.2 因果掩码(Causal masking)

decoder-only 训练与推理都必须遵守“不能看未来词元”。因果掩码通过把 \(j>i\) 的分数设为 \(-\infty\)(或极大负数),让 softmax 后该概率为 0,从而保证自回归分解 \(p(x_t \mid x_{<t})\)

4.3 交叉注意力(Cross-attention)

交叉注意力中,Query 来自一个序列,Key/Value 来自另一个序列。典型场景是 encoder-decoder:decoder 在生成时读取 encoder 输出;多模态模型也常用该机制(文本关注图像/音频特征)。

4.4 多头注意力(MHA)

多头的价值在于“分工”:每个头学到不同模式,组合后表达力更强。

4.5 为什么长上下文成本高

标准注意力每头都要构造 \(L\times L\) 分数矩阵,因此计算和(朴素实现下)内存都是 \(O(L^2)\)。这也是长上下文 prefill 成本高、TTFT 变慢的根源。

4.6 注意力加速思路

稀疏注意力:限制可见模式(滑窗、块稀疏),减少计算条目。见 [code]

线性注意力:用核映射近似 softmax,将计算重排到 \(O(L)\)

KV cache:推理时缓存历史 K/V,避免每步重算历史词元。

FlashAttention:通过分块与稳定归约提升显存效率,不显式物化完整 \(L\times L\)

MQA/GQA:共享部分或全部 K/V,主要优化解码期内存和带宽。

4.7 面试题

4.7.1 什么是因果掩码,为什么必须有?

它阻止未来信息泄露,保证训练目标与生成过程一致。

4.7.2 为什么标准注意力是 \(O(L^2)\)

每个位置都要与所有位置交互,形成 \(L\times L\) 矩阵。

4.7.3 如何把标准注意力改成“线性注意力”?

用核特征映射近似 softmax: \[ \exp(q^\top k) \approx \phi(q)^\top \phi(k), \] \[ \mathrm{Attn}(Q,K,V) \approx \frac{\phi(Q)\bigl(\phi(K)^\top V\bigr)}{\phi(Q)\bigl(\phi(K)^\top \mathbf{1}\bigr)}. \]

这样可流式累计而不构造全矩阵,代价是归纳偏置改变、精度可能受影响。

4.7.4 KV cache 的作用是什么?

缓存历史 K/V,生成新词元时只算新增一步,显著降低解码计算量。

4.7.5 MQA/GQA 改了什么,为什么重要?

它们通过共享 K/V 缩小 KV cache,直接缓解解码内存与带宽瓶颈。

5 前馈网络、激活函数、残差连接与归一化

5.1 FFN

FFN(也叫 MLP)是 Transformer 中“每个词元自己做计算”的子层。注意力负责跨词元传递信息,FFN 负责在单词元内构造更复杂特征。

小例子:注意力像“把信息从各个服务拉过来”,FFN 像“在本地做业务逻辑处理”。只拉数据不处理,你拿到的还是半成品。

没有非线性时,多层线性变换可折叠成一层,表达力受限。FFN 提供非线性建模能力,也是 Transformer 的主要计算预算之一。

标准两层形式: \[ \mathrm{FFN}(x) = W_2\,\sigma(W_1 x + b_1) + b_2. \]

实践中常先扩展维度(如 \(d_\text{ff}\approx 4d_\text{model}\)),再用门控激活(如 SwiGLU)提高质量/算力比。

5.2 激活函数

激活函数决定 FFN 的非线性形态,也影响训练稳定性和效率。

5.2.1 激活类型

5.2.1.1 经典:ReLU

ReLU 简单高效,但负半轴梯度为 0,可能出现“死亡 ReLU”。

\[ \mathrm{FFN}(x) = f(xW_1 + b_1)W_2 + b_2, \] \[ \mathrm{FFN}_{\text{ReLU}}(x) = \operatorname{ReLU}(xW_1 + b_1)W_2 + b_2. \]

5.2.1.2 平滑:GELU / Swish

GELU、Swish 在大模型里往往优化更平滑,是常见默认。

5.2.1.3 门控:GLU 家族(SwiGLU)

门控 MLP 的通式: \[ \mathrm{FFN}_{\text{gated}}(x) = \bigl((xW_{\text{up}}) \odot g(xW_{\text{gate}})\bigr) W_{\text{down}} + b, \] 其中 \(g(\cdot)\) 是门控函数。GLU 常取 sigmoid,SwiGLU 常取 Swish。

激活函数 公式 说明
Sigmoid \[\sigma(x) = \frac{1}{1 + e^{-x}}\] \(|x|\) 时易饱和,可能导致梯度消失;含指数运算
Tanh \[\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}\] 零中心,但仍会饱和;含指数运算
ReLU \[\text{ReLU}(x) = \max(0, x)\] 简单高效;\(x<0\) 区域梯度为 0(“死亡 ReLU”)
Leaky ReLU \[\text{LeakyReLU}(x) = \begin{cases}x, & x>0 \\ \alpha x, & x<0 \end{cases}\] 通过负半轴小斜率缓解死亡 ReLU
ELU \[\text{ELU}(x) = \begin{cases}x, & x>0 \\ \alpha(e^x - 1), & x<0 \end{cases}\] 平滑;负值输出有助于均值回正;指数计算成本较高
Swish \[\text{Swish}(x) = x \cdot \sigma(x)\] 平滑、非单调;实证表现常较好
GELU \[\text{GELU}(x) = x \cdot \Phi(x)\] Transformer 常用默认;可理解为概率门控
SwiGLU \[\text{SwiGLU}(x) = \text{Swish}(x_1) \odot x_2\] 现代 LLM 中常见且有效的门控 MLP 激活
Softmax \[\text{softmax}(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}}\] 将 logits 映射为类别概率分布

5.3 残差连接(Residual)

深层网络常见三类问题:梯度消失/爆炸、优化困难、表示尺度漂移。残差连接通过显式恒等路径改善这些问题: \[ x_{l+1} = x_l + f(x_l). \]

这意味着每层只需学习“增量”,训练更稳定,也更容易加深网络。

5.4 归一化(Normalization)

随着层数加深,激活尺度会漂移,导致训练脆弱,尤其在混合精度下更明显。归一化让每层输入尺度更可控。

Transformer 中最常见的是 LayerNorm 和 RMSNorm:

方法 公式 说明
LayerNorm \[x_{\text{norm}} = \frac{x - \mathbb{E}[x]}{\sqrt{\operatorname{Var}(x)+\epsilon}} \cdot \gamma + \beta\] 经典选择;早期 Transformer 中非常常见
RMSNorm \[\mathrm{RMS}(x)=\sqrt{\frac{1}{H}\sum_{i=1}^{H} x_i^2 + \epsilon},\quad x_{\text{norm}} = \frac{x}{\mathrm{RMS}(x)} \cdot \gamma\] 比 LayerNorm 更简洁;现代 LLM(如 LLaMA 系)广泛使用

5.4.1 常见 Transformer 变体

关键差异通常是“归一化在前还是在后”:

  • Post-norm\[x_{l+1} = \mathrm{Norm}(x_l + f(x_l)).\]

  • Pre-norm\[x_{l+1} = x_l + f(\mathrm{Norm}(x_l)).\]

现代大多数 decoder-only LLM 更偏好 pre-norm,因为深层下更稳定。

方法 公式 说明
标准残差(Standard Residual) \[x_{l+1} = x_l + f(x_l)\] 经典 ResNet 风格跳连
后归一化 Transformer(Post‑Norm) \[x_{l+1} = \text{Norm}(x_l + f(x_l))\] 早期 Transformer(含 BERT)常见;超深时稳定性较弱
前归一化 Transformer(Pre‑Norm) \[x_{l+1} = x_l + f(\text{Norm}(x_l))\] 现代 LLM(GPT、LLaMA)常见;通常更稳定
DeepNorm \[x_{l+1} = x_l + \alpha \cdot f(\text{Norm}(x_l))\]
Encoder: \[\alpha = \frac{1}{\sqrt{2N}}\]
Decoder: \[\alpha = \frac{1}{\sqrt{4N}}\]
通过残差缩放提升超深模型稳定性;\(N\) 为层数
ReZero \[x_{l+1} = x_l + \alpha \cdot f(x_l)\] (\(\alpha\) learnable, init = 0) 从近零残差起步,可稳定训练早期阶段

5.5 面试题

5.5.1 已有 attention,为什么还需要 FFN?

注意力擅长“信息路由”,FFN 负责“非线性特征构造”,两者职责互补。

5.5.2 为什么现代 LLM 喜欢 SwiGLU 这类门控激活?

门控相当于动态特征开关,通常在相近预算下带来更高表达效率。

5.5.3 表达力与计算的权衡是什么?

更强激活通常带来更多参数与 matmul,成本上升;换来更好训练与精度。

5.5.4 残差连接为什么有效?

它提供稳定梯度通路,让每层学习增量而不是重写全部表示。

5.5.5 pre-norm 和 post-norm 的核心区别?

pre-norm 先归一化再进入子层,通常在深层模型中更稳定;post-norm 在较浅模型中也常见。

6 位置编码(Positional Encoding)

Note

位置编码告诉模型“先后顺序”。没有位置信号,词袋顺序变化在注意力里难以区分。

6.1 为什么需要位置编码

自注意力本身是“集合运算”倾向的:如果不给位置信号,模型很难可靠地区分词序变化。语言里词序会改变语义,所以必须显式注入位置信息。

它还直接影响长上下文能力:

  • 是否能外推到训练之外的长度。

  • 是否天然偏好局部依赖或长程依赖。

6.2 位置编码是什么

位置编码可以放在不同层级:

  • 嵌入层:给每个位置加位置向量(绝对位置)。

  • 注意力分数层:按距离加偏置(相对位置)。

  • 几何层:旋转 Q/K(RoPE),把位置信息编码到点积几何关系里。

小例子:没有位置编码时,“先上线再回滚”和“先回滚再上线”在模型眼里容易像同一堆词元。线上事故会告诉你:这两句话绝对不是一回事。

6.3 实现方式

6.4 核心家族

绝对位置编码通常直接加到 token embedding 上:

# token_ids: [batch, seq]
x = tok_embed(token_ids)                  # [batch, seq, d_model]
pos = pos_embed(arange(seq_len))          # [seq, d_model] (learned or sinusoidal)
x = x + pos[None, :, :]                   # broadcast over batch

经典正弦位置编码: \[ PE(t,2i)=\sin\left(\frac{t}{10000^{2i/d_{\text{model}}}}\right),\quad PE(t,2i+1)=\cos\left(\frac{t}{10000^{2i/d_{\text{model}}}}\right) \]

相对位置编码在注意力分数上加距离偏置:

# scores = Q @ K.transpose(-2, -1) / sqrt(d_k)   # [batch, heads, seq, seq]
scores = scores + relative_bias                  # [1 or batch, heads, seq, seq]
weights = softmax(scores, dim=-1)
out = weights @ V

RoPE对 Q/K 做位置相关旋转:

# Q, K: [batch, heads, seq, head_dim]
Q = apply_rope(Q, seq_positions)
K = apply_rope(K, seq_positions)
scores = Q @ K.transpose(-2, -1) / sqrt(head_dim)

6.5 常见方法与直觉

方法 公式 编码内容 直觉解释
正弦(绝对位置) \[PE(t)_i = \sin(t \cdot \omega_i),\quad \omega_i = 10000^{-i/d}\] 绝对位置索引 每个位置都有独特“波形签名”;多频率组合让模型感知顺序。
可学习绝对位置 \[PE(t) = E_t\] 绝对位置索引 模型直接学习一张“位置词表”。
相对位置(Shaw 等) \[\text{score}_{ij} = Q_i K_j^\top + a_{i-j}\] 相对距离 \((i-j)\) 模型学习“相隔多远”而不是“绝对在哪”,更符合语言局部依赖。
词元内距离(一般形式) \[PE(i,j) = f(\lvert i-j \rvert)\] 任意距离函数 可以定义任意“距离→偏置”映射;ALiBi 与 T5 都可视为特例。
ALiBi(线性注意力偏置) \[\text{score}_{ij} += w_h \cdot (i - j)\] 线性距离惩罚 对远距离词元施加软惩罚,鼓励关注近邻,也利于长长度泛化。
RoPE(旋转位置编码) \[Q_t^{\text{rot}}=R_tQ_t,\quad K_t^{\text{rot}}=R_tK_t\]
\[R_t=\begin{bmatrix}\cos(\theta t)&-\sin(\theta t)\\\sin(\theta t)&\cos(\theta t)\end{bmatrix}\]
通过几何旋转编码相对位移 位置让 Q/K 在旋转空间“转角”,相对距离体现在角度差里。
T5 / DeBERTa(相对偏置) \[\text{score}_{ij} = Q_i K_j^\top + b_{\lvert i-j \rvert}\] 分桶距离 把距离分桶处理;工程稳定、实现成熟。

6.5.1 长度外推(Length Extrapolation)

长度外推指:推理时上下文长度远超训练长度(例如训练 20k,推理 128k)。很多位置方案在这种情况下会退化,核心是“模型是否见过相近索引/相近距离模式”。

方法 数学核心 为什么有帮助 优点 缺点
RPE(相对位置编码) \[\text{score}_{ij} = Q_i K_j^\top + a(i-j)\] 学的是距离,不是绝对索引 外推通常较好;符合语言局部性 需要学习偏置/嵌入;常要分桶
RoPE(旋转位置编码) \[Q_t^{\text{rot}}=R_tQ_t,\; K_t^{\text{rot}}=R_tK_t\] 相对位移由角度差体现 平滑、主流、效果稳健 超长上下文常需额外缩放
ALiBi(线性偏置) \[\text{score}_{ij} += w_h (i-j)\] 通过距离惩罚引入近因偏置 实现简单;长度泛化好 对部分长程模式建模较弱
位置插值(PI) \[t' = \frac{L}{L_{\text{new}}}\cdot t\] 把位置压回训练熟悉范围 极易实现;有时很有效 插值可能扭曲结构
Base-N 位置编码 \[t = \sum_k d_k B^k,\quad PE_k(t) = d_k\] 多尺度数字表示可持续扩展 理论上长度上限高 不连续,常需平滑/调参
NTK-aware RoPE 缩放 \[\theta' = \theta / s\] 全局放慢 RoPE 频率 简单且常有效 可能过度或不足修正
分段 NTK 缩放 \[\theta'_i = \theta_i / s\cdot\text{part}(i)\] 高频缩放大于低频 比全局缩放失真小 参数更多、调参复杂
Dynamic NTK \[\theta'_i = \theta_i \cdot \frac{m}{\beta^{d/2-1}}\] 按维度自适应缩放 调好后长程更稳 实现细节复杂
YaRN \[\sqrt{1/t}=0.1\ln(s)+1\](配合 RoPE 频率缩放) 在缩放与稳定间做平衡 长上下文扩展鲁棒 实现略复杂

6.6 直觉速查表

方法 核心直觉
RPE 学“距离”
RoPE 用旋转编码位移
NTK 全局放慢旋转频率
分段 NTK 按频段放慢旋转
Dynamic NTK 按维度自适应缩放
YaRN 更平衡、更稳定的 RoPE 缩放
ALiBi 线性近因偏置(软掩码)
PI 把索引压回训练范围
Base‑N 把位置看成多位数字
词元内距离 任意距离映射

6.7 面试题

6.7.1 为什么 Transformer 必须有位置编码?

因为注意力本身不自带顺序感,必须额外注入“谁在前谁在后”。

6.7.2 绝对、相对、RoPE 一句话怎么区分?

绝对:给每个位置一个向量。相对:按距离改分数。RoPE:通过旋转把位置写进 Q/K 几何关系。

6.7.3 什么是长度外推,为什么重要?

就是推理长度超出训练长度。长上下文场景里,它决定模型是否“还能正常工作”。

6.7.4 生产里哪些会预计算,哪些在线算?

Sinusoidal 常预计算;相对偏置可按长度缓存;RoPE多在线作用于 Q/K。

7 解码(Decoding)

Note

解码决定“从概率到文本”的最后一步。很多看似模型能力问题,实际是解码参数问题。

解码是把“概率”变成“可读文本”的最后一公里。在每个时间步,模型会给出下一个词元的分布 \(p(x_t \mid x_{<t})\),而解码策略决定“到底选哪个词元”。

形式化地说,解码就是把 next-token 分布反复转成实际选择,直到命中终止条件(EOS、stop 序列或 max tokens)。

生产中常见管线是: 1. logits 变换(温度、重复惩罚)。 2. 候选截断(top-k / top-p)。 3. 选择策略(argmax 或采样)。 4. 终止条件(EOS、stop 序列、max tokens)。

从工程角度,解码往往比你想象得更“有决定性”:很多看似“模型能力不够”的问题,实际是参数与策略没配好,比如重复、过度保守、格式跑偏、随机性失控。

7.1 贪心搜索(Greedy Search) [code]

\[ x_t = \arg\max_i p(i \mid x_{<t}) \] 最快、最稳定,但容易模板化和重复。

7.2 束搜索(Beam Search) [code]

\[ \mathrm{score}(x_{1:t}) = \sum_{\tau=1}^{t} \log p(x_\tau \mid x_{<\tau}),\quad \mathrm{Beam}_t = \operatorname{TopB}\bigl(\mathrm{score}(x_{1:t})\bigr) \] 适合追求高似然输出,但开放式对话中常显得保守、缺乏变化。

7.3 Top‑k 采样(Top‑k Sampling) [code]

\[ \widetilde{p}(i) \propto p(i)\,\mathbf{1}[i \in \operatorname{TopK}(p)],\quad x_t \sim \widetilde{p} \] 固定候选大小,简单有效,但在分布平坦时不够稳。

7.4 Top‑p 采样(Nucleus Sampling) [code]

\[ \sum_{i\in S} p(i) \ge p \] \[ x_t \sim \widetilde{p}(\cdot \mid \cdot \in S) \] 候选集合随分布形状自适应,通常比固定 top-k 更稳。

7.5 温度采样(Temperature Sampling)

\[ p_i' = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)} \] \(T<1\) 更确定,\(T>1\) 更多样。

7.6 Top-\(k\)/Top-\(p\) + temperature(常见组合)

温度控制“随机程度”,top-k/top-p 控制“候选边界”。实际部署中常再配重复惩罚和 stop 序列,保证格式稳定(如 JSON、tool call)。

7.7 Best‑of‑N(自评分)

\[ \{y^{(1)}, y^{(2)}, \ldots, y^{(N)}\} \] 采样 N 条,再用模型或奖励函数选最优。质量常更高,但计算几乎线性增加。

7.8 多数投票 / 自一致性(Self‑Consistency)

\[ y^{(1)}, y^{(2)}, \ldots, y^{(N)} \] 对多次采样结果做投票/聚合,常用于推理任务,能降低单次采样噪声。

7.8.1 总结

方法 是否确定性 多样性 说明
贪心 速度快;但可能重复
束搜索 部分 优化似然;聊天场景常偏“模板化”
Top-\(k\) 截断简单;分布平坦时较敏感
Top-\(p\) 自适应截断;创作类场景常用
温度 可调 通过重缩放 logits 控制随机性
Top-\(k\)/Top-\(p\) + 温度 生产中最常见参数组合
Best-of-\(N\) 生成阶段否 若评分器靠谱,质量通常更好
自一致性 生成阶段否 很高 推理题常用;成本更高

解码是最容易被低估、但最有杠杆的层。你常看到“模型回答变差”,根因不是权重退化,而是解码参数不匹配。

小例子:客服机器人如果温度设得太高,回答会像“深夜加班后语速过快”一样飘;温度太低,又会像“只会读模板”的机器人。

8 架构(Architecture)

架构设计回答的是:同样是 Transformer,为什么有的适合分类,有的适合对话,有的适合翻译,还有的更适合超大规模推理。

常见失败模式:

  • 训练成本爆炸(深度/宽度/注意力选择不合理)。

  • 推理受限(KV cache 过大,带宽吃紧)。

  • 任务不匹配(目标函数和掩码模式错配)。

更具体一点,架构其实在回答四个系统问题:
1. 训练目标是什么(MLM、next-token、seq2seq)。
2. 注意力掩码怎么设计(双向、因果、混合)。
3. 基础模块怎么堆(self-attention + FFN,堆几层、多宽)。
4. 是否引入额外机制(cross-attention、MoE、长上下文技巧)。

高层上,架构要定四件事: 1. 训练目标(MLM、next-token、seq2seq)。 2. 掩码模式(双向、因果、混合)。 3. 块结构(注意力 + FFN 堆叠)。 4. 额外机制(cross-attention、MoE、长上下文策略)。

8.1 仅编码器(Encoder-only, 双向)

\[ \mathrm{Attention}(x_i) = f(x_{<i}, x_i, x_{>i}) \] 双向建模,擅长表示学习任务(分类、检索向量等),典型预训练目标是 MLM。代表:BERT、RoBERTa、DeBERTa。

8.2 仅解码器(Decoder-only, 自回归)

\[ p(x_t \mid x_{<t}) \] 因果建模,天然适合生成。当前主流聊天模型几乎都在这一范式。代表:GPT、LLaMA、Mistral、OPT、BLOOM。

8.3 编码器-解码器(Encoder–decoder, Seq2Seq)

Encoder: \[ h = \text{Enc}(x_{1:n}), \quad \text{bi‑directional} \] Decoder: \[ p(y_t \mid y_{<t}, h), \quad \text{auto‑regressive} \] 天然适合翻译、摘要这类“输入序列 -> 输出序列”任务。代表:T5、BART。

8.4 PrefixLM(部分因果掩码)

\[ M_{ij} = \begin{cases} 0, & j \leq P \quad (\text{prefix, bi-directional})\\ 0, & j < i \quad (\text{suffix, causal})\\ -\infty, & \text{otherwise} \end{cases} \] 前缀双向、后缀因果,是一种折中式设计。

8.5 专家混合(MoE, Mixture of Experts)

\[ \mathrm{FFN}(x) = \sum_{k=1}^{N} g_k(x)\, E_k(x) \] 词元只路由到少量专家,达到“参数大、每步计算相对可控”的效果。

小例子:把 MoE 想成“医院分诊台”。每个病人(词元)不需要看所有科室(专家),先分诊到 1~2 个最相关科室,效率和质量都更容易兼顾。

SoftMoE 形式: \[ \mathrm{SoftMoE}(x)=W\Big(\sum_k g_k(x) E_k(x)\Big) \]

路由质量决定实际效果:负载均衡差会拖慢吞吐,容量溢出会伤害质量。

8.6 总结

架构 注意力样式 训练目标 优势 代表模型
Encoder-only 双向 MLM 表示提取强 BERT
Decoder-only 因果 NLL 生成与提示学习强 GPT, LLaMA
Encoder–decoder 双向 + 因果 Seq2Seq 翻译、摘要效果好 T5, BART
PrefixLM 混合掩码 Prefix-style LM 推理与对话折中 GLM
MoE 稀疏路由专家 Sparse 高效扩展参数规模 DeepSeek, Mixtral

8.7 面试题

8.7.1 为什么聊天模型大多是 decoder-only?

因为聊天本质是连续生成,decoder-only 的目标函数和推理方式天然一致。

8.7.2 什么时候选 encoder-only,什么时候选 encoder-decoder?

要表示学习选 encoder-only;要高质量 seq2seq 生成选 encoder-decoder。

8.7.3 一句话解释 MoE,为什么路由关键?

MoE 是“多专家 + 稀疏路由”;路由决定负载、容量和最终质量。

8.7.4 推理内存最常由什么主导?

decoder-only 里通常是 KV cache。

8.8 面试题

8.8.1 解码在工程流程里到底做了什么?

logits 变换 -> 候选过滤 -> 词元选择 -> 终止控制,循环执行。

8.8.2 为什么 beam search 在开放生成里常显得“同质化”?

它偏向高似然路径,容易收敛到“安全模板”。

8.8.3 Top-\(k\) 和 top-\(p\) 区别?哪个更稳?

top-k 固定候选数量,top-p 固定累计概率。top-p 通常跨 prompt 更稳。

8.8.4 温度极低/极高会怎样?

极低:近似贪心,容易无聊重复;极高:随机过头,易失真和跑偏。

8.8.5 重复惩罚/no-repeat n-gram 会带来什么副作用?

抑制循环有效,但可能误伤“合法重复结构”(列表、代码模板、JSON 键等)。

8.8.6 何时用 best-of-\(N\) / self-consistency?代价是什么?

追求可靠性时用,代价是近似 \(N\) 倍生成成本加额外评分成本。

8.8.7 什么是 stop sequence,为什么重要?

它是“命中即停”的结束标记,能保证结构化输出边界干净可解析。

9 超越 Transformer(Beyond Transformers)

Transformer 仍是今天的主流答案,但它有一个明确的扩展瓶颈:标准注意力在长上下文下成本很高。

生产里最常见的两个压力点是:

  • Prefill 计算:随序列长度 \(L\) 近似按 \(O(L^2)\) 增长,常主导首 token 延迟(TTFT)。

  • Decode 内存/带宽:自回归生成依赖 KV cache,随着上下文和层数增长,内存与带宽压力迅速上升。

“超越 Transformer”并不只指“换模型架构”,也包括推理期策略变化。核心是改变“序列信息如何被处理”。有些方法改网络本体(如状态空间模型),有些保持 Transformer 主干但改系统编排(如递归、检索、分块)。

一个实用心智图是:
- Transformer(decoder-only):标准栈,质量强、生态成熟,但注意力与 KV 成本高。
- SSM / 状态空间(如 Mamba):用状态更新替代 softmax 注意力,长序列效率更好,但归纳偏置不同。
- Retention / 混合(如 RetNet):尝试在递归特性与并行训练之间折中。
- 循环混合(如 RWKV):训练像 Transformer、推理更像 RNN,弱化对 KV cache 的依赖。
- 扩散语言模型:通过迭代细化生成,常有更强可控性,但步骤更多。
- 递归式 LM(推理期):底模不变,通过检索/分块/汇总/循环等系统手段处理超长任务。

模型家族 生成方式 主要收益 主要代价 常见场景
Transformer(decoder-only) 自回归(逐词元) 质量强、生态成熟 注意力/KV cache 成本高 聊天 LLM 默认方案
SSM / 状态空间(如 Mamba) 自回归 序列处理近线性 归纳偏置不同;生态较小 长序列效率研究与部分生产
Retention / 混合(如 RetNet) 自回归 兼顾递归与并行训练 工程标准化不足 研究与特定部署
循环混合(如 RWKV) 自回归 无 KV cache,推理更像 RNN 主流生态相对弱 边缘端与效率导向场景
扩散语言模型 迭代细化 可控性更强、并行潜力更高 去噪步骤多,基础设施不同 新兴研究与少量垂直场景
递归式 LM(推理期) 自回归 + 系统编排 处理超上下文窗口任务 编排复杂,不只是模型问题 Agent 与长文档工作流

实践中通常不需要背公式,但必须说清两点: 1. 它解决了哪个瓶颈。 2. 它引入了哪些新的成本或风险。

10 总结

本章建立了现代 LLM 的工作全景:分词把文本变成离散符号,嵌入把符号变成可计算向量,位置编码补齐顺序信息,Transformer 块通过注意力在词元间路由信息、通过 FFN 在词元内做非线性计算,最后由解码把 logits 变成可读文本。

工程上最关键的不是单点技巧,而是全链路权衡,尤其是“规模化”带来的连锁影响:

  • 分词与词表影响覆盖、长度与成本。

  • 注意力在长上下文下成本陡增,prefill 常近似 \(O(L^2)\),KV cache 常主导推理内存/带宽。

  • 残差与归一化决定深层训练稳定性。

  • 位置编码决定顺序建模与长度外推表现。

  • 解码策略决定输出风格、可靠性与延迟成本。

  • 架构(encoder-only / decoder-only / encoder-decoder / MoE)要与任务目标和硬件约束共同设计。

一句话收尾:LLM 不只是“更大的网络”,而是一套从符号到生成、从算法到系统的协同工程。