9.5 KiB
title, draft, tags
| title | draft | tags | ||||
|---|---|---|---|---|---|---|
| 01-GPT | false |
|
1. 架构深度解构:大道至简的 Decoder-only
为什么是 Decoder-only 架构?
早期的自然语言处理迷恋 Encoder-Decoder 架构(如 T5、BART),因为 Encoder 可以提供强大的双向上下文表示。然而,GPT 系列自始至终坚守纯 Decoder 架构。这是因为:
-
内在统一性与无隐式对齐瓶颈:Encoder-Decoder 架构在 Cross-Attention 处存在一个信息瓶颈(Encoder 的输出需被 Decoder 持续查询)。Decoder-only 架构将所有输入视为同一个序列,自然打破了 Prompt 和生成内容之间的隔离。
-
极致的上下文压缩:自回归(Autoregressive)的本质是”预测下一个词”。当模型能够完美预测下一个词时,它必然已经深刻理解了前面的所有上下文。类比来说:Encoder 像是一个速记员,把文章提炼成摘要再交出去;而 Decoder-only 像是一个深谙人性的说书人,顺着情节的发展,自然流淌出接下来的故事。
GPT vs BERT:核心设计哲学差异
GPT 和 BERT 是 Transformer 架构的两种不同应用范式,其本质差异在于掩码机制决定的信息流动方向:
| 维度 | GPT (Decoder-only) | BERT (Encoder-only) |
|---|---|---|
| 注意力模式 | 因果(Causal):下三角 Mask | 双向全局:无 Mask |
| 训练目标 | 自回归下一 Token 预测(NLL) | MLM(掩码预测)+ NSP(下一句预测) |
| 特征性质 | 单向特征,适合生成式任务 | 双向特征,适合理解式任务(分类、NER 等) |
| 代表性工作 | GPT-2/3/4, LLaMA, PaLM | BERT, RoBERTa, ALBERT |
数学上的本质差异:GPT 将语言建模为联合概率的链式分解 $P(x_1, ..., x_T) = \prod_{t=1}^T P(x_t | x_{<t})$,而 BERT 利用双向上下文同时感知 $[$\text{mask}] 位置的全局信息,损失函数为:
\mathcal{L}_{BERT} = -\sum_{i \in \mathcal{M}} \log P(x_i | \hat{X})
其中 \hat{X} 表示将 x_i 替换为 $[$\text{mask}] 后的完整上下文序列。
深入 Masked Self-Attention
自回归的核心在于因果性(Causality):在预测第 t 个词时,模型绝不能“偷看”第 t+1 个词。我们在数学上通过 Mask 矩阵来实现这一点。
在矩阵运算层面,注意力机制可以表示为:
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}} + M\right)V
其中,M 是一个严格的上三角矩阵(包含对角线之上),其元素定义为:
M_{ij} = \begin{cases} 0, & i \ge j \\ -\infty, & i < j \end{cases}
当 QK^T 的结果加上矩阵 M 后,上三角区域的值变为 $-\infty$。在经过 \text{softmax} 操作时,$e^{-\infty} \to 0$,从而彻底切断了当前词对未来词的注意力权重。
维度拆解与 Multi-Head 机制
假设模型总隐层维度为 $d_{model}$,注意力头数为 $h$。为了保证计算量与单头一致,每个头的维度被严格划定为:
d_k = \frac{d_{model}}{h}
增加头数的作用:单头注意力只能将权重集中在一种上下文关联上。多头机制允许模型在不同的表示子空间(Representation Subspaces)中并行学习。
- 类比:这就像是一个评审团,头 1 关注语法结构,头 2 关注情感色彩,头 3 追踪代词指代。即使总的知识容量($d_{model}$)不变,切分成多个视角($h$)也能极大地提升模型的表达丰度。
2. 数学推导与训练稳定性
缩放因子 1/\sqrt{d_k} 的数学必要性(方差漂移)
为什么在 QK^T 之后必须除以 $\sqrt{d_k}$?这关乎模型在初始化阶段的生死存亡。
假设 Query 和 Key 的向量分量 q_i 和 k_i 均为独立同分布的随机变量,均值为 $0$,方差为 $1$。
它们的点积结果为 $z = \sum_{i=1}^{d_k} q_i k_i$。
根据概率论,点积的均值依然是 $0$,但方差会线性累加:
\text{Var}(z) = \sum_{i=1}^{d_k} \text{Var}(q_i k_i) = d_k
当 d_k 很大时(例如 128),点积结果的方差也会变得极大(例如 128)。输入到 softmax 函数的 logits 绝对值会非常大,导致 softmax 的输出极度逼近 0 或 $1$(one-hot 状态)。此时,梯度会趋近于 $0$,导致梯度消失。
引入 1/\sqrt{d_k} 作为缩放因子后,方差被重新拉回至规范化水平:
\text{Var}\left(\frac{z}{\sqrt{d_k}}\right) = \frac{1}{d_k} \text{Var}(z) = 1
Layer Normalization:Pre-LN 与 Post-LN 之争
早期 Transformer 使用 Post-LN:$x_{l+1} = \text{LN}(x_l + F(x_l))$。但在深层网络中,这会导致靠近输入层的梯度极小,必须依赖极其保守的 Warmup 策略。
现代 GPT 架构全面转向 Pre-LN:
x_{l+1} = x_l + F(\text{LN}(x_l))
Pre-LN 建立了一条从第一层直达最后一层的恒等映射超级通道(Identity Pathway)。无论网络多深,残差分支的梯度都能无损地反向传播。
Layer Normalization 的核心公式如下:
\text{LN}(x) = \gamma \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} + \beta
其中 \mu 和 \sigma^2 是向量 x 内部特征维度的均值和方差,\gamma 和 \beta 是可学习的仿射变换参数。LN 将激活值拉回到合理的动态范围内,有效防止了激活值爆炸引起的数值不稳定。
3. 极致的训练细节:从理论到工程
预训练目标:自回归似然函数
GPT 的唯一任务就是:在给定前置词的情况下,最大化当前词的概率。其损失函数是负对数似然(Negative Log-Likelihood):
\mathcal{L} = -\sum_{i=1}^N \log P(x_i | x_{<i}; \theta)
这看似简单,但根据香农信息论,最小化交叉熵实际上是在最小化模型分布与真实世界语言分布之间的 KL 散度。只要数据量足够大,模型就被迫在参数 \theta 中压缩整个世界的运行规律。
优化器与学习率调度
-
AdamW:引入解耦的权重衰减(Decoupled Weight Decay),避免了传统 L2 正则化在自适应学习率下的失效,确保了模型权重的平滑。
-
Warmup + Cosine Decay:在训练初期,模型参数是随机的,如果直接给予大学习率,破坏性极大。因此需要几千步的 Warmup 让学习率从
0爬升至峰值。随后使用余弦退火平滑下降,这有助于模型在损失平原中缓慢沉降到最优的局部极小值。
现代位置编码:RoPE 与 ALiBi
原始论文使用绝对位置编码(正弦/余弦),但这打破了文本的平移不变性。现代 GPT 广泛使用:
-
RoPE (Rotary Position Embedding):不直接相加,而是将位置信息转化为 Query 和 Key 向量的旋转角度。通过复数乘法,使得
q_m和k_n的内积仅与它们的相对距离(m-n)相关,完美结合了绝对位置和相对距离。 -
ALiBi:直接在 Attention 的
QK^T矩阵上加上一个与相对距离成正比的静态偏置惩罚矩阵。这种方法在长度外推(Length Extrapolation)上表现极佳,是支持超长上下文(如 100K tokens)的关键。
大规模训练的幽灵:Loss Spikes 与混合精度
-
BF16 vs FP16:FP16 常常因为指数位不够(仅 5 位)导致上溢出。现代集群通常采用 BF16(Brain Float 16),它牺牲了尾数的精度,但保留了与 FP32 相同的 8 位指数,拥有巨大的动态范围,极大地减少了训练初期的 NaN 爆炸。
-
Gradient Clipping(梯度裁剪):当梯度范数超过设定阈值(如 1.0)时,按比例缩放梯度向量。这是对抗 Loss Spikes(突然出现的损失飙升)的最后一道防线。
4. 推理与生成:性能与分布的艺术
KV Cache:避免冗余的魔法
在推理(生成)阶段,自回归是一个词一个词蹦出来的。假设生成第 t 个词,如果把完整的序列重新输入 Transformer,复杂度是 $O(t^2)$,极其昂贵。
数学逻辑:在 Attention 中,历史的 Key 和 Value 在未来的步骤中是完全不会改变的。因此,我们只需要计算当前步骤 t 的 $q_t, k_t, v_t$。
我们将 k_t 和 v_t 拼接到内存缓存中:
K_{1:t} = [K_{1:t-1}; k_t], \quad V_{1:t} = [V_{1:t-1}; v_t]
然后只需计算单步的注意力:
\text{Attention}_t = \text{softmax}\left(\frac{q_t K_{1:t}^T}{\sqrt{d_k}}\right) V_{1:t}
类比:这就像听课做笔记。你不需要每次听到新内容时,都把教材从头到尾重读一遍,你只需要查阅你做好的“历史笔记(Cache)”加上当前的一句新话即可。
采样策略:塑造生成的灵魂
最后一层输出 logits $z_i$,通过 softmax 转化为概率分布:
p_i = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)}
-
Temperature (
T):-
$T \to 0$:分布趋近于 one-hot,模型变得贪婪,输出枯燥但确定的事实(熵极低)。
-
$T > 1$:分布被压平,低概率词被提拔,模型开始“胡言乱语”或“充满创造力”(熵极高)。
-
-
Top-K:强行截断分布,只保留概率最高的 K 个词进行归一化采样。防止长尾中极低概率的“乱码词”被选中。
-
Top-P (Nucleus Sampling):将概率从高到低累加,直到累加和超过 $p$(例如 0.9)。相比 Top-K,Top-P 更加动态:在预测确定时(少数词概率极高),它只保留几个词;在预测不确定时分布平缓,它会保留更多词。