预训练 — 把「差多远」算成一个数,再把 1.24 亿个旋钮调小它
这一章讲两件事: 怎么把「模型写得好不好」变成一个可以优化的数; 以及那个数怎么驱动 1.24 亿个参数一起动——训练的完整循环。 然后书里真的点了一次火:把模型在一篇短篇小说上训 10 轮, 我们逐行读训练日志,亲眼看「学会」和「背下来」长什么样。 这是全书的中点:前面五章造出的机器,从这一章开始会干活。
1. 这一章讲什么
第 05 章结尾,组装完毕的 GPT 说了胡话("Featureiman Byeswickattribute argue")—— 因为参数全是随机数。这一章回答:从「随机」到「会续写」,中间到底发生了什么。 答案是两步:先造一把尺子(损失),再写一个循环(让尺子读数变小)。
2. 顶层全景
一把尺子(本章 §3.1):
模型的输出概率 → 挑出「正确答案那个词元」的概率 → 取对数 → 平均 → 取负
= 交叉熵损失,一个数,越小越好
一个循环(本章 §3.3):
┌─ 取一批样本 ─→ 算损失 ─→ 反向传播(算每个参数该往哪挪)─→ 挪一小步 ─┐
└──────────────── 换下一批,重复几万亿次 ←───────────────────────────┘
每隔一段:在没训过的验证集上量一次,看是真学会还是在背题
点火实录(本章 §3.4):
训练损失 9.78 → 0.39 ✓ 学会了
验证损失 9.93 → 6.45 ✗ 卡住不动——它在背原文,不是在学语言
图说:尺子、循环、点火,三件套组成全部现代 LLM 训练的骨架;
真模型的区别只在规模(数据、参数、GPU 数量)。
3. 核心原理
3.1 主走查:六步,把「差多远」算成一个数
「好不好」要变成数,机器才能优化它。这把尺子叫交叉熵(cross entropy)损失。 书里用两条真实输入走完六步1:
输入:"every effort moves" 与 "I really like"
目标(右移一位):"effort moves you" 与 "really like chocolate"
第 ①②步:过模型,取概率。 未训练的模型对两个输入各吐出三行 50,257 维的 logits, 过 softmax 变成概率。此刻它对正确答案给的分数几乎等于瞎猜: 1/50,257 ≈ 0.00002——每个目标词元只拿到约十万分之二的概率2。 真实读数:第一个样本三个目标词的概率是 [7.45×10⁻⁵, 3.11×10⁻⁵, 1.16×10⁻⁵]3。
第 ③步:只取目标位置。 每个位置只关心「正确答案那个词元」的概率,其余 5 万个不看。 训练的全部目标就是把这些位置的概率顶上去4。
第 ④步:取对数。 对概率取自然对数:[-9.50, -10.38, -11.37, -11.48, -9.78, -12.26]。 概率越接近 1,对数越接近 0;概率越小,对数越是大的负数5。 为什么取对数?书里坦白说「这在数学优化上更好处理,超出本书范围」,并指了附录 B 的讲义6。
第 ⑤⑥步:平均、取负。 六个对数概率平均得 -10.7940;乘以 -1,得 10.7940。
这个「负的平均对数概率」就是交叉熵损失——值越小,模型给正确答案的分越高7。
PyTorch 的 cross_entropy 一步算完这六步,结果逐位相同8。
这把尺子还有一个更好读的换算:困惑度(perplexity)= e 的「损失」次方。 10.7940 代进去得 48,725——读法是:模型此刻的茫然程度,等效于「每一步都在 48,725 个候选词里抓阄」。 参 照物:词表一共才 50,257 个词,也就是说它几乎是纯瞎猜9。 训练有效的标志就是困惑度往下掉:从 48,725 往 10、往 5 掉。
3.2 一份数据切成两份:训练集与验证集
尺子有了,还要防止「作弊式的高分」:模型把见过的题背下来,尺子在见过的题上读数再漂亮也没用。 所以数据切成两份:训练集用来调参数,验证集训练时绝不参与调参,只用来量10。
书里把《The Verdict》的 5,145 个词元按 90%/10% 切开,批大小 2、窗口 256, 得到 9 个训练批、1 个验证批。未训练模型的初始读数:训练损失 10.99、验证损失 10.98—— 两份都没见过,自然一样差11。
3.3 训练循环:八步,一万次
有了尺子和数据,训练本身是一个固定动作的循环。书里给的 train_model_simple 就是这 八步12:
① 轮(epoch):把整个训练集完整过一遍,叫一轮;一共训若干轮
② 批:一轮里按批取数据
③ 清零梯度:把上一批算出的「调整信号」归零(不清会累积,方向就乱了)
④ 算损失:这一批改完模型输出,用 §3.1 的尺子量
⑤ 反向传播(backpropagation):从损失出发,沿计算图倒着走,
算出每个参数对损失该负多少责——这就是每个参数的「该往哪挪、挪多少」
⑥ 优化器挪参数:按信号把每个参数挪一小步(步长 = 学习率)
⑦ 每隔几步,用验证集量一次(关掉 dropout、关掉梯度记录)
⑧ 每轮末尾,让模型续写一句话,肉眼检查
第 ⑤步是这个行业的基石,值得多说一句。反向传播就是把微积分的链式法则 (复合函数求导:外壳的导数乘内核的导数)在神经网络的计算图上从输出往输入推一遍, 一次算出全部参数各自的调整方向——1.24 亿个参数,一次反向传播全部拿到自己的那份信号13。 这就是为什么「调 1.24 亿个旋钮」在工程上可行:不是一个个试,是一次算清。
第 ⑥步由优化器(照着反向传播算出的信号、决定每个参数具体怎么挪的那个部件)执行,书里用的叫 AdamW。
它在每个参数上自带一份「近期梯度走势」的记忆(最近的梯度一直在往哪边推,它记着), 让该快快的参数快、该慢慢的慢;W 是 Adam 的改良版——把「惩罚过大的权重」 (weight decay)单独拎出来做,防过拟合的效果更好,是训 LLM 的默认选择14。 「学习率」是每步挪多远的步长:太大来回震荡,太小学不动——它是人定的超参数,要试。
3.4 点火实录:学会与背下来,同时发生
现在真的点火。 配置:124M 模型、《The Verdict》5,145 词元、AdamW(lr=0.0004)、 10 轮,MacBook Air 上约 5 分钟15。训练日志的真实读数:
第 1 轮:训练 9.781 / 验证 9.933 生成:"Every effort moves you,,,,,,,,,,,,,"
第 2 轮:训练 6.661 / 验证 7.048 生成:"…you, and, and, and, and, and…"
……
第 9 轮:训练 0.541 / 验证 6.393 生成:语法通顺的英文段落
第 10 轮:训练 0.391 / 验证 6.452 生成:"…quite insensible to the irony. She wanted…"
读这条曲线有三层,一层比一层重要16:
- 第一层:它学会了。 训练损失从 9.78 降到 0.39,从「只会接逗号」到写出语法正确的段落。
- 第二层:它在背。 验证损失降到 6.45 就卡住不动,和训练损失拉开鸿沟; 生成的句子里 "quite insensible to the irony" 这段,拿去原文一搜,逐字在小说里。 训练损失远小于验证损失、且第二轮后两线分叉——这就是过拟合的完 整长相17。
- 第三层:这是故意的。 5,145 个词元喂给一个 1.24 亿参数的模型训 10 轮,参数量是数据量的两万倍, 背下来是必然。真实预训练的做法正好相反:海量数据、只过一轮,根本不给它背的机会18。
这个「失败」是全书最值钱的一课: 你亲手看到了「训练损失降」和「模型变聪明」之间的全部距离。 评估一章(原书第 5 章前半)存在的理由,就是把这层距离变成读数。
顺带把成本账补齐:这本书的教学预训练花 5 分钟笔记本电脑;真实世界里, Llama 2(70 亿参数、2 万亿词元)的账单是 18.4 万个 A100 GPU 小时、约合 69 万美元19。 同一套代码,同一套流程,差别全在规模——这正是这本书的教学法成立的原因。
4. 作者的判断与证据
有证据的: 六步损失的每个中间数、初始损失 10.99/10.98、10 轮训练日志、 验证集卡住而训练集到底的分叉、生成文本能在原文搜到——全部是书里印出的真实运行记录3111617。
作者的判断:
- 「批大小、轮数、学习率没有通解,要试」——书里在多处明说这些是超参数,要靠观察损失曲线来调20。
- 用一篇短篇小说教学是刻意的:5 分钟跑完 vs 几周,教学目的足够21。
- AdamW 优于 Adam 的理由(单独做 weight decay)是文献结论,作者直接采用14。
判断(我们的,不是书里的): 很多材料把「过拟合」讲成一条定义,这本书把它讲成一段录像: 你能看到逗号复读阶段、"and"复读阶段、语法通顺阶段、背原文阶段——四个阶段在同一次训练里依次出现。 读完这一章,「训练损失降、验证损失平」这八个字在你脑子里应该永远带着这段录像。 如果错,会错在: 如果读者据此以为「过拟合 = 训练失败」——不对。在小模型+小数据上它是教学演示; 在大模型+大数 据上,一定程度的记忆甚至与泛化能力纠缠在一起,今天的研究还没把它完全分开。
5. 边界与局限
- 这个训练循环是「能讲清楚」的最小版:没有学习率预热(训练开头几步把步长从小慢慢爬到峰值,不一脚油门踩到底)、没有余弦衰减、没有梯度裁剪。 这三件真实训练里的标配在附录 D,我们放在第 10 章讲。
- 困惑度只能跟「同一词表」的模型比;换分词器,数就不可比——书里没强调,值得记住。
- 「反向传播一次算清全部参数」的工程细节(计算图怎么存、梯度怎么累)在附录 A, 同样归到第 10 章。
- 验证书只用 5 个批估一次(eval_iter=5),是省时间的近似;小数据上够了,大项目要放全量。
6. 可带走的
- 交叉熵损失六步:概率 → 取目标位置 → 对数 → 平均 → 取负;它把「写得好不好」变成一个可优化的数。
- 困惑度 = e^损失,读作「等效于在多少个词里抓阄」;未训练的 GPT-2 词表模型约等于 48,725(≈词表大小)。
- 训练集调参数,验证集只量不调;两线分叉 = 过拟合的现场。
- 训练循环八步是固定动作;第 ③ 步「清零梯度」忘了,方向会乱(梯度累积:上一批的调整信号残留下来、叠进这一批)。
- 反向传播 = 链式法则在计算图上从右往左推,一次算清所有参 数的调整方向——这是「能训 1.24 亿个旋钮」的原因。
- AdamW = 带记忆的步长 + 单独做的权重惩罚;学习率没有通解,要试。
- 过拟合不是抽象概念:训练 0.39 vs 验证 6.45,生成句在原文里逐字搜得到,这就是它。
- 真实预训练用海量数据只过一轮;教学演示用一篇小说过十轮——同一个循环,两个极端。
- 成本量感:教学版 5 分钟笔记本;Llama 2 7B 约 69 万美元。规模差六个数量级,代码同一套。
7. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 未训练模型约等于瞎猜 | 5 Pretraining on unlabeled data | text/13-ch05-5-pretraining-on-unlabeled-data.txt:351(搜「0.00002」) |
| 六步算损失 | 同上 | text/13-ch05-5-pretraining-on-unlabeled-data.txt:400(搜「7.4541e-05」) · text/13-ch05-5-pretraining-on-unlabeled-data.txt:452(搜「-9.5042」) · text/13-ch05-5-pretraining-on-unlabeled-data.txt:529(搜「10.7940」) |
| 困惑度 | 同上 | text/13-ch05-5-pretraining-on-unlabeled-data.txt:531(搜「Perplexity」) |
| 训练/验证切分与初始损失 | 同上 | text/13-ch05-5-pretraining-on-unlabeled-data.txt:686(搜「train_ratio」) · text/13-ch05-5-pretraining-on-unlabeled-data.txt:812(搜「10.98758347829183」) |
| 八步训练循环 | 同上 | text/13-ch05-5-pretraining-on-unlabeled-data.txt:849(搜「pass over a training set」) · text/13-ch05-5-pretraining-on-unlabeled-data.txt:897(搜「train_model_simple」) |
| AdamW | 同上 | text/13-ch05-5-pretraining-on-unlabeled-data.txt:984(搜「AdamW」) |
| 10 轮点火实录与过拟合 | 同上 | text/13-ch05-5-pretraining-on-unlabeled-data.txt:1014(搜「9.781」) · text/13-ch05-5-pretraining-on-unlabeled-data.txt:1024(搜「quite insensible to the irony」) |
| 真实预训练只训一轮 | 同上 | text/13-ch05-5-pretraining-on-unlabeled-data.txt:1081(搜「only one epoch」) |
| Llama 2 成本账 | 同上 | text/13-ch05-5-pretraining-on-unlabeled-data.txt:595(搜「184,320 GPU hours」) |