GPT:因果语言建模 本节摘要:BERT 看两侧,GPT 只看过去。那张三角掩码,是现代 AI 里后果最严重的一行代码。一个语言模型只回答一个问题:给定前 个 token,第 个 token 的概率分布是什么?在这个信号——下一 token 预测——上训练,你就得到一个能逐 token 生成任意文本的模型。要在一整条序列上端到端并行训练,每个位置的预测就只能依赖它之前的位置,否则模型会偷看答案作弊。因果掩码(Causal Mask)就是干这个的:它是一张上三角全 的矩阵,在 softmax 前加到注意力分数上,softmax 后那些位置变成 0,每个位置只能关注自己和更早的位置。于是你一次前向就得到 N 个并行的下一 token 预测。
本节摘要:BERT 看两侧,GPT 只看过去。那张三角掩码,是现代 AI 里后果最严重的一行代码。一个语言模型只回答一个问题:给定前
t-1个 token,第t个 token 的概率分布是什么?在这个信号——下一 token 预测——上训练,你就得到一个能逐 token 生成任意文本的模型。要在一整条序列上端到端并行训练,每个位置的预测就只能依赖它之前的位置,否则模型会偷看答案作弊。因果掩码(Causal Mask)就是干这个的:它是一张上三角全-inf的矩阵,在 softmax 前加到注意力分数上,softmax 后那些位置变成 0,每个位置只能关注自己和更早的位置。于是你一次前向就得到 N 个并行的下一 token 预测。本节会带你反推这张三角形的来历——它不是「贴在注意力上的补丁」,而是「前缀平均」这个最朴素因果运算的循环边界写成矩阵后的遗迹。读完本节,你能说清训练并行、推理串行的代价(自回归税),以及为什么 GPT 配方(decoder-only + scale + 上下文学习 + RLHF + pre-norm/RoPE/SwiGLU)自 GPT-2 以来核心架构几乎没变。
阅读完本节,你应当能够:
torch.tril),并解释它在 softmax 前加 -inf 后如何把上三角清零。[t1,t2,t3]、目标 [t2,t3,t4]),并知道所有 Transformer LM 都训这个损失。要让模型在整条序列上并行训练,每个位置的预测只能依赖更早的位置,否则它就偷看答案。因果掩码做这件事:一张 N×N 上三角全 -inf 的矩阵,在 softmax 前加到分数上。
M[i, j] = 0 若 j <= i M[i, j] = -inf 若 j > i
exp(-inf) = 0,掩码位置的权重为零,每一行是只覆盖过去位置的概率分布。实现成本:torch.tril() 一次调用。计算耗时:纳秒级。对领域的影响:翻天覆地。
掩码通常被讲成「贴在注意力上的补丁」。把推导反过来跑,它就不神秘了:注意力是「前缀平均」的第三次精化,三角形就是那个平均的循环边界写成矩阵。
阶段 1——前缀平均。 最朴素的因果摘要:位置 i 变成位置 0…i 的均值。写成循环是 out[i] = X[:i+1].mean(0),但同样计算是一次矩阵乘法——取一个全 1 的下三角矩阵,每行除以自己的元素数,再乘序列:
A = np.tril(np.ones((n, n))) A = A / A.sum(axis=1, keepdims=True) out = A @ X
A 的第 i 行是 [1/(i+1), …, 1/(i+1), 0, …, 0]。对角线以上的零就是因果性——未来不是「被掩掉了」,而是「从一开始就不在求和里」。
阶段 2——学习权重。 均匀平均把每个过去 token 当作同等相关。把全 1 换成学习的打分矩阵 S。这时行不再天然归一,改用 softmax 而非除以计数。但 softmax 永不输出精确零,因果性被破坏——除非未来分数以 -inf 进去,因为 exp(-inf) = 0:
S = S + np.triu(np.full((n, n), -np.inf), k=1) # 上三角填 -inf A = softmax(S, axis=1) out = A @ X
同一个三角形,同一个行随机矩阵,同一次矩阵乘。-inf 掩码不是新机制,它是阶段 1 的零元素,翻译到 softmax 的输入域里。
阶段 3——内容相关权重。 阶段 2 的 S 训完后固定:位置 7 永远以同一个权重看位置 3,不管 token 是什么。让分数依赖 token 本身:S = Q @ Kᵀ / √dk。其他全不变——掩码、softmax、矩阵乘,一模一样。
三阶段,一个不变量:下三角行随机矩阵 × 序列。均匀平均、静态学习权重、内容相关权重。掩码从未被「加」到注意力上,它是从前缀平均里继承下来的。
💡 关键洞察:
-inf掩码不改变注意力「谁匹配谁」的排序(见第 02 节),它只是把上三角强制清零,确保因果性。把它想成「循环边界」而非「补丁」,整个自回归框架就顺了。
训练:整条 (N, d_model) 序列一次前向,算 N 个交叉熵损失(每位置一个),求和,反向。沿序列方向并行。这就是 GPT 训练能 scale 的原因——一次 GPU pass 处理批里 100 万 token。
推理:逐 token 生成。喂 [t1,t2,t3] 得 t4;喂 [t1,t2,t3,t4] 得 t5;喂 [t1,t2,t3,t4,t5] 得 t6。KV 缓存(第 12 节)保存 t1…tn 的隐状态,避免每步重算。但推理的串行深度 = 输出长度——这是自回归税,也是为什么解码是每个 LLM 的延迟瓶颈。
给定 tokens [t1, t2, t3, t4]:
[t1, t2, t3][t2, t3, t4]对每个位置 i 算 -log P(target_i | inputs[:i+1]),求和——这就是整条序列的交叉熵。你听过的每个 Transformer LM 都训这个损失:预训练、微调、SFT,同一个损失,不同的数据。
训练完后,采样选择比人们以为的更重要:
| 方法 | 做什么 | 何时用 |
|---|---|---|
| Greedy | 每步取 argmax | 确定性任务、代码补全 |
| Temperature | logits 除以 T 再采样 | 创意任务,T 越大多样性越高 |
| Top-k | 只在前 k 个 token 里采样 | 砍掉低概率长尾 |
| Top-p(核采样) | 在累计概率 ≥ p 的最小集合里采样 | 2020+ 默认,自适应分布形状 |
| Min-p | 保留 p > min_p · max_p 的 token |
2024+,比 top-p 更能拒长尾 |
| 投机解码 | 小模型提议 N 个 token,大模型验证 | 同质量下延迟降 2~3 倍 |
2026 年,开源模型上 min-p + temperature 0.7 是合理默认;投机解码是任何生产推理栈的标配(第 16 节详述)。
核心架构自 GPT-2 以来几乎没变,真正有趣的事都发生在数据、规模、后训练里。
完整代码见原课程 phases/07-transformers-deep-dive/07-gpt-causal-language-modeling/code/main.py。
def causal_mask(n): return [[0.0 if j <= i else float("-inf") for j in range(n)] for i in range(n)]
在 softmax 前加到注意力分数上。整个机制就这一行。
堆两个 decoder 块(掩码自注意力 + FFN,无交叉注意力),加 token 嵌入、位置编码、反嵌入(与 token 嵌入矩阵绑定——GPT-2 以来的标准技巧,省一半参数)。
在 20 token 的玩具词表上,每位置产出 logits,对 shift-by-one 目标算交叉熵。本节无梯度,只做前向合理性检查。
实现 greedy / temperature / top-k / top-p / min-p,在固定 prompt 上跑,对比输出。一个采样函数 10 行。
设计要点:绑定的输入/输出嵌入(
W_emb与W_unemb共享或转置)既省参数,又让「读词」和「写词」共用同一套语义空间——这是 GPT-2 的关键工程决策之一。
2026 年的 PyTorch 惯用法:
from transformers import AutoModelForCausalLM, AutoTokenizer model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.2-3B-Instruct") tok = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-3B-Instruct") prompt = "Attention is all you need because" inputs = tok(prompt, return_tensors="pt") out = model.generate(**inputs, max_new_tokens=64, temperature=0.7, top_p=0.9, do_sample=True) print(tok.decode(out[0]))
底层,generate() 跑前向、取末位置 logits、采下一 token、追加、重复。每个生产 LLM 推理栈(vLLM、TensorRT-LLM、llama.cpp、Ollama、MLX)实现同一个循环,但加了重优化——批 prefill、连续批处理、KV 缓存分页、投机解码。
GPT vs BERT,各一句话:GPT 预测 P(x_t | x_{<t}),BERT 预测 P(x_masked | x_unmasked)。损失决定了模型能不能生成。
原课程产出 outputs/skill-sampling-tuner.md:一个采样调参 Skill,为新生成任务选采样参数,并标出何时必须用确定性解码。
code/main.py,验证因果注意力矩阵在 softmax 后是下三角。抽查:第 3 行应只在第 0~3 列有权重。-inf 在 softmax 前加上,每位置只看自己和过去,exp(-inf)=0。[t1,t2,t3]、目标 [t2,t3,t4],每位置算交叉熵求和——所有 Transformer LM 都训这个。P(x_t|x_{<t}) vs P(x_masked|x_unmasked)。下一节,我们把双向编码器和自回归解码器拼到一起,看 T5 和 BART 这类编码器-解码器模型如何用「文本到文本」统一所有 NLP 任务。