第 13 章 · 训练与 teacher forcing

解释 LLM 实际被训练去做什么:在因果掩码下并行地预测每个位置的下一个 token,并对照真实标签计分。

我们已经看过一个训练好的模型如何运行。那些权重从哪来?模型当初被优化去做什么?

13 分钟
术语表 · 10 个术语
next-token prediction
训练任务:给定 token [t1..ti],预测 t_{i+1}。在每条训练序列的每个位置上重复。
gradient descent
训练背后的更新规则:梯度指向上坡——损失增长最快的方向——所以每个权重朝相反方向(下坡)微调:w ← w − lr·gradient。学习率(lr)是步长:太小爬得慢,太大会越过头并发散。AdamW 对全部约 800M 个权重同时逐参数执行这条规则。
batch
一步中一起处理的若干到许多条训练序列(真实预训练的 batch 通常是数百条序列、数百万 token,靠梯度累积拼出来——见第 14 章)。损失在它们之间取平均,于是一次权重更新反映许多样例而不是一个——梯度更稳,硬件利用率更高。
cross-entropy loss
单次预测的损失:−log p(target)。跨位置和 batch 元素求和或取平均,得到总损失。
teacher forcing
训练时,位置 i+1 的输入是位置 i 的真实 token(而不是模型预测出的 token)。这样每个位置都有合法的真实目标,模型从正确的上下文学习,而不是从自己(最初很糟糕的)猜测学习。
one training step
对一个 batch 的序列做前向传播 → 计算损失 → 反向传播 → 优化器更新权重。在一次预训练中重复几十万到几百万次(每一步都要吃掉数百万 token,所以 token 总数会累积到数以万亿计,尽管步数并不是)。
parallel positions
多亏因果掩码,一条序列的所有位置可以在一次前向传播中同时训练——模型永远看不到未来,预测因此保持有效。
pretraining vs fine-tuning
预训练:在数万亿 token 的通用文本上做下一个 token 预测。微调:同样的目标,更小的精选数据集,通常在预训练之后进行。
AdamW
LLM 训练几乎清一色使用的优化器。它为每个参数维护梯度及其平方的滑动平均(即“矩”),让每个权重获得自适应步长;权重衰减与梯度分开施加。
backpropagation (autograd)
通过把链式法则沿前向传播倒着应用,计算标量损失对每个权重的梯度的算法。下方的练习场在 WebGPU 上运行真实的自动微分——与生产训练器用的是同一个 value_and_grad 原语。

训练:LLM 究竟在优化什么

到目前为止,这门课讲的都是推理:一个训练好的模型接收提示词,一次吐出一个 token。但权重从哪来?那场优化到底求解了什么?答案出奇地简单,而且对每个现代 decoder LLM 都是同一个答案——GPT、Llama、Qwen、Gemma、Mistral,全都用同一个目标训练。

“训练”到底是什么意思

先看图景,再看公式。把模型想成一排可调的旋钮。墙上的恒温器只有一个旋钮:往上拧,房间变暖;往下拧,房间变凉。你靠微调来找到合适的设定——有点太冷,往上拨一点;拨过头了,再往回收——直到房间感觉刚好。训练就是同一个循环,只有一处不同:读房间的不是人,而是一道自动流程,它读取模型的预测错得有多离谱,然后把每个旋钮朝让预测不那么错的方向拨动一丝。没有谁手工挑选这些数值,它们是靠不断重复这次拨动而被找到的。

难点在于旋钮的数量。给一堆散点拟合一条直线,你调的是两个旋钮——一个斜率、一个截距。沿着这架梯子往上爬,数量便爆炸:在你浏览器里运行的 Qwen3.5 模型有 852,985,920 个旋钮,每一个都是 backpropagation 将要拨动的一个数。(要感受这架梯子能走多远:GPT-3 带着 1750 亿个旋钮——大约多 200 倍;每个旋钮的拨动方式完全相同,只是数量多得多,这正是 scaling的全部故事。)这些拨动正在压低的那个误差——整道流程读来决定每个旋钮往哪个方向拧的那一个数——正是下面的 −log p

一行写完的目标

就这么多。对每条序列的每个位置,预测下一个 token;最小化真实下一个 token 的负对数概率。在一条序列内部按位置取平均(如上式所示),再在 batch 内的各条序列上取一次平均(真实预训练的 batch 通常是数百条序列、数百万 token——见第 14 章),这就是优化器往下压的东西。没有奖励模型,(在预训练阶段)没有人在回路里——只有海量文本和一个错开一位的目标。

逐位置的损失 -log p(target)交叉熵。当 p(target) 接近 1 时损失接近 0;当 p(target) 很小时损失飙升。为什么偏偏取对数?两个理由:它把“把整条序列里每个 token 的概率乘起来”变成“把逐 token 的代价加起来”(几千个小于 1 的数相乘会下溢成零;对数之和则表现良好);并且由于 -log p → ∞(当 p → 0),一次信心十足的错误预测会受到任意重的惩罚——模型恰恰在又错又笃定的时候被罚得最狠。优化器的工作就是把模型的概率质量推到真正的下一个 token 上。

那损失从哪里起步?初始化时权重是随机的,248,320 个词表 token 几乎人人均分概率——约 1/248,320。代进去:−ln(1/248,320) = ln(248,320) ≈ 12.4。这就是模型学到任何东西之前的损失,此后每一点下降都是学习。本页的实时训练练习场以同样的方式开局,只是词表是微型字符级的:默认语料只有 3 个不同字符,曲线从约 ln(3) ≈ 1.1 附近出发(两个 11 字符的语料从约 ln(11) ≈ 2.4 附近出发)。

Teacher forcing——让训练并行的技巧

对“预测下一个 token”的朴素理解可能是逐 token 跑模型:生成预测 1,喂回去,生成预测 2,依此类推——正是推理的工作方式。训练时这样做慢得要命,而且更糟:模型将从自己(最初很糟糕的)预测中学习。

解法是 teacher forcing(教师强制)。不把模型自己的预测喂回去当下一个输入,而是喂真实的 token。teacher forcing 做的就这一件事:在每个位置提供一个合法的真实目标,让模型从正确的上下文学习,而不是从自己最初糟糕的猜测学习。并行性来自另一个机制——因果掩码(第 4 章)。由于每个位置只能向后注意,所有位置的损失可以在单次前向传播中算完,谁也偷看不到自己的目标。 真实目标(teacher forcing)+ 单遍并行(因果掩码),合起来让全部 N 个位置同时训练。

Teacher forcing——在六个 token 上的一次训练步
第 1/4 步
第 1 步——把整条序列一次喂入(一次并行前向传播)。
输入序列
The
·cat
·sat
·on
·the
·mat
逐位置预测(softmax 之后)
cat
sat
on
the
mat
dog
cat
sat
on
the
mat
dog
cat
sat
on
the
mat
dog
cat
sat
on
the
mat
dog
cat
sat
on
the
mat
dog
cat
sat
on
the
mat
dog
逐位置交叉熵 = −log p(target)
无目标
loss = mean(−log p(target_i)),在 5 个受训位置上取平均
= — —

把损失条与上方的概率对照着读——桥梁只是 −ln−ln(0.78) = 0.25(容易的位置,短条);而 −ln(0.28) = 1.27(困难的位置)——概率越低,条被推得越高。

两个关键观察。其一:所有位置同时被训练——唯一让这件事保持成立的是因果掩码。其二:喂给位置 i+1 的输入是位置 i真实 token,而不是模型的预测。这就是「teacher forcing」:训练期间,模型从不需要从自己的错误中恢复。(推理时它当然必须如此——这正是长文本生成有时会跑偏的那个虽小但真实的原因。)

仅为示意——这里的逐位置概率是手工挑选来展示损失形状的,不是模型的实时输出。

曝光偏差(exposure bias)——推理为什么会跑偏
第 0/8 步
Teacher forcing(训练)下一个输入 = 真实 token
(起点)
自由生成(推理)下一个输入 = 自己的预测
(起点)
到目前为止,两条通道都贴着参考序列——模型还没有被迫从自己的某个选择中恢复。

仅为示意——这处分叉是预先写好的脚本,不是模型的实时输出。

Teacher forcing 意味着训练从不让模型看到自己的错误:每个位置的输入都是真实的前一个 token。但推理时模型必须消费自己的输出,于是一次偏离真实答案的选择就把上下文带上一条它从未被训练过的路径,小错误不断累积成漂移。teacher forcing 下的训练分布与自由生成的推理分布之间的这道鸿沟,就是exposure bias(曝光偏差)

推理和训练是同一个前向传播

一个值得停下来体会的推论:不存在单独的“训练时”架构。同一个前向传播——嵌入查表、24 个 Transformer 层(每层先混合 token:6 层靠全量注意力、18 层靠 GatedDeltaNet,再用 MLP 做变换)、顶端的 RMSNorm、LM head 矩阵乘法——既在训练时跑,也在推理时跑。训练只是一次对整条序列做这件事,并把输出对照真实答案打分;推理则一次一个 token 地做,并从输出中采样。

这种对称性是本课程的推理套路能推广到训练的部分原因。KV 缓存(第 12 章)是仅推理的优化,但被缓存的运算与训练时运行的运算一模一样,只是训练时没有缓存。

预训练 vs 微调 vs 指令/RLHF

上面的目标描述的是预训练:数万亿 token 的通用网页文本、书籍和代码,模型学习在任意上下文中预测下一个 token。Qwen3.5 约 0.8B 参数的绝大部分知识来自这里——LLM 研发的几乎全部 GPU 时也耗在这里。

微调用同一个损失、同一个前向传播——只是换成一个更小的、精心挑选的数据集(例如指令跟随对话)。模型继续预测下一个 token;变的是数据集。

RLHF / DPO / 直接偏好类方法偏离了简单的交叉熵故事——它们用人类偏好或奖励模型给模型输出打分,损失不再是纯粹的下一个 token 交叉熵。这些超出本章范围;重要的是,每一个这类后训练阶段的基础行为,仍然建立在这里打下的下一个 token 交叉熵地基之上。

优化器实际在做什么

损失是一个标量。反向传播——把链式法则沿前向传播倒着算——计算它对模型每个权重的梯度(Qwen3.5-0.8B 约有 800M 个)。下面是不需要微积分的完整算法——损失沿着堆叠一块一块往下回流:

反向传播——损失沿堆叠往下回流
损失一个数前向 ↑(已完成)梯度 ↓⋮ 还有 22 层LM head第 24 层第 1 层嵌入

前向传播已完成(朝的浅色箭头):整个模型把这个 batch 浓缩成了一个数——损失。现在这个数沿同样的块往回流。

这种一问一答的接力,就是链式法则的实际运作——每个块只需要知道自己的输出让损失动了多少,从不需要一次看到整个模型。反向传播只负责计算梯度;把梯度变成对权重的实际微调,是优化器(AdamW)的另一个独立步骤。

这一次反向传播是对架构盲视的。链式法则并不在意 Qwen3.5 的 24 层里有 6 层跑 softmax attention,另外 18 层是线性递归的 GatedDeltaNet——两类层里的每个权重都从同一次下坡行走中拿到自己的梯度,这正是为什么一个 AdamW 循环就能把这两种层并排训练,而从不需要知道哪个是哪个。

每个块都拿到自己的梯度后,优化器(几乎总是 AdamW,第 14 章拆解)朝降低损失的方向迈一小步。简化到单个权重,这条规则一目了然:梯度指向上坡,所以朝反方向迈步——而这一步的大小(学习率)存在一个甜点区。

单个权重上的梯度下降
朝下坡迈步: w ← w − lr·L'(w)
-5-2.502.55权重 w012.5损失 L(w)极小值
01.0(一步)2.0(振荡)
权重 w
4.00
损失 L(w)
8.00
梯度 dL/dw
4.00
已走步数
0
合适 → 快速收敛

金色切线就是梯度 L'(w)——它的符号指出哪边是上坡,所以这一步(粉色箭头)让 w 朝相反方向、也就是下坡移动。太小 lr 几乎不动、缓慢爬行;合适的几步就落到谷底;把 lr 推过 2.0,每一步都会比上一步越得更远——权重弹出碗外,损失爆炸。

仅为示意——一幅只有 1 个参数的卡通。真实模型约有 800M 个权重,同一条规则(每个权重沿自己的梯度往下坡迈一步)在所有权重上同时运行——这正是 AdamW 逐参数自动化的事情。

把这个逐权重的步骤对全部约 800M 个权重同时重复,跑上几百万到几万亿个 token。下一章讲让这个循环在这么深的堆叠上保持数值稳定的工程技巧——预热、余弦衰减、梯度裁剪、dropout。

工程要点
  • 整个训练目标就一行:在每条序列的每个位置上最小化 -log p(next_token)。
  • 因果掩码让训练得以并行——每个位置在一次前向传播里算出自己的损失而不偷看未来;teacher forcing 在每个位置提供真实目标,使这些并行预测都从正确的上下文中学习。
  • 不存在单独的“训练模式”架构——推理时跑的那个前向传播,训练时跑的也是它,只是改成对照真实答案计分。
动手练习

在动画里,位置 5(最后一个输入 " mat")显示 “no target”。为什么最后一个位置从不被训练?如果硬要训练它,会发生什么?

随堂测验
1. 像 Qwen3.5 这样的 decoder-only LLM 的训练目标是什么?
2. “teacher forcing” 是什么意思?
3. 对长度为 N 的训练序列,标准的输入/目标设置是什么?

动手试试

互动演示加载中……