GPT:因果语言建模


文档摘要

GPT:因果语言建模 本节摘要:BERT 看两侧,GPT 只看过去。那张三角掩码,是现代 AI 里后果最严重的一行代码。一个语言模型只回答一个问题:给定前 个 token,第 个 token 的概率分布是什么?在这个信号——下一 token 预测——上训练,你就得到一个能逐 token 生成任意文本的模型。要在一整条序列上端到端并行训练,每个位置的预测就只能依赖它之前的位置,否则模型会偷看答案作弊。因果掩码(Causal Mask)就是干这个的:它是一张上三角全 的矩阵,在 softmax 前加到注意力分数上,softmax 后那些位置变成 0,每个位置只能关注自己和更早的位置。于是你一次前向就得到 N 个并行的下一 token 预测。

GPT:因果语言建模

本节摘要: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 以来核心架构几乎没变。

学习目标

阅读完本节,你应当能够:

  1. 实现因果掩码(一行 torch.tril),并解释它在 softmax 前加 -inf 后如何把上三角清零。
  2. 用「前缀平均 → 学习权重 → 内容相关权重」三阶段推导说明三角形不是补丁,而是因果运算的循环边界。
  3. 说清训练并行、推理串行的不对称:训练一次前向得 N 个损失,推理却必须逐 token 生成(自回归税)。
  4. 写出 shift-by-one 的交叉熵损失(输入 [t1,t2,t3]、目标 [t2,t3,t4]),并知道所有 Transformer LM 都训这个损失。
  5. 区分 greedy / temperature / top-k / top-p / min-p / 投机解码 六种采样策略,知道 2026 年的默认组合。

一、问题与直觉

要让模型在整条序列上并行训练,每个位置的预测只能依赖更早的位置,否则它就偷看答案。因果掩码做这件事:一张 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 的延迟瓶颈。

损失——shift-by-one

给定 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 配方为什么 work

  1. Decoder-only。 无编码器开销,每层一次注意力 + FFN。
  2. 缩放。 124M → 1.5B → 175B → 万亿级,Chinchilla 缩放定律(第 13 节)告诉你怎么花算力。
  3. 上下文学习。 约 6B~13B 涌现,模型无需微调就能跟 few-shot 示例走。
  4. RLHF。 在人类偏好上后训练,把原始预训练文本变成聊天助手。
  5. Pre-norm + RoPE + SwiGLU。 大规模稳定训练。

核心架构自 GPT-2 以来几乎没变,真正有趣的事都发生在数据、规模、后训练里。

二、从零实现

完整代码见原课程 phases/07-transformers-deep-dive/07-gpt-causal-language-modeling/code/main.py

Step 1:因果掩码(一行)

def causal_mask(n): return [[0.0 if j <= i else float("-inf") for j in range(n)] for i in range(n)]

在 softmax 前加到注意力分数上。整个机制就这一行。

Step 2:2 层类 GPT 模型

堆两个 decoder 块(掩码自注意力 + FFN,无交叉注意力),加 token 嵌入、位置编码、反嵌入(与 token 嵌入矩阵绑定——GPT-2 以来的标准技巧,省一半参数)。

Step 3:端到端下一 token 预测

在 20 token 的玩具词表上,每位置产出 logits,对 shift-by-one 目标算交叉熵。本节无梯度,只做前向合理性检查。

Step 4:采样

实现 greedy / temperature / top-k / top-p / min-p,在固定 prompt 上跑,对比输出。一个采样函数 10 行。

设计要点:绑定的输入/输出嵌入(W_embW_unemb 共享或转置)既省参数,又让「读词」和「写词」共用同一套语义空间——这是 GPT-2 的关键工程决策之一。

三、框架对比:HuggingFace 的因果 LM

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,为新生成任务选采样参数,并标出何时必须用确定性解码。

五、练习

  1. (Easy)code/main.py,验证因果注意力矩阵在 softmax 后是下三角。抽查:第 3 行应只在第 0~3 列有权重。
  2. (Medium) 实现束搜索(width=4),在 10 个短 prompt 上对比束搜索 vs greedy 的困惑度。束搜索总赢吗?(提示:翻译通常赢,开放式聊天通常不赢。)
  3. (Hard) 实现投机解码:用 2 层小模型起草、6 层模型验证。在 100 条长度 64 的补全上测墙钟加速,确认输出与验证模型的 greedy 完全一致。

本节要点回顾

  1. 因果掩码是一行代码:上三角 -inf 在 softmax 前加上,每位置只看自己和过去,exp(-inf)=0
  2. 三角形不是补丁,是继承:它是「前缀平均」的循环边界,经三阶段(均匀权重→静态学习→内容相关)精化后保留下来。
  3. 训练并行、推理串行:训练一次前向得 N 个损失(沿序列并行),推理逐 token 生成(自回归税)。
  4. shift-by-one 损失:输入 [t1,t2,t3]、目标 [t2,t3,t4],每位置算交叉熵求和——所有 Transformer LM 都训这个。
  5. 绑定嵌入:输入嵌入和反嵌入共享矩阵,GPT-2 以来的标准,省一半参数。
  6. 六种采样:greedy/temperature/top-k/top-p/min-p/投机解码;2026 默认 min-p + temp 0.7,生产栈必上投机解码。
  7. GPT 配方五件套:decoder-only + scale + 上下文学习 + RLHF + pre-norm/RoPE/SwiGLU;核心架构自 GPT-2 几乎没变。
  8. GPT vs BERT 一句话:损失决定能不能生成——P(x_t|x_{<t}) vs P(x_masked|x_unmasked)

下一节,我们把双向编码器和自回归解码器拼到一起,看 T5 和 BART 这类编码器-解码器模型如何用「文本到文本」统一所有 NLP 任务。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U