对应 Maps to:Stanford CS324 Winter 2023 Advances in Foundation Models,两场客座报告:1/25 Aakanksha Chowdhery(Google):Pathways Language Model (PaLM) and Model Scaling;3/1 Susan Zhang(Meta;日历上没有公布题目)。以及 Syllabus “Training Methods and Infrastructure” 主题下的 Hugging Face 博客 The Technology Behind BLOOM Training。 依据说明:课程网站没有提供 slides 或录像。Chowdhery 的报告依据 PaLM 论文(Chowdhery et al. 2022)重建——第 4 讲 §10 已经从”规模”的角度讲过 PaLM,这一讲讲它的训练工程。Susan Zhang 的报告内容不可考:她当时在 Meta 领导 OPT-175B 的训练,并公开了训练日志,所以 §4 依据 OPT 论文(Zhang et al. 2022)第 2 节,以及 metaseq 代码库里公开的四篇训练进展记录(10%、27%、56% 和最终更新,2021 年 11 月到 2022 年 1 月);我没有读完整的 PDF 日志(OPT175B_Logbook),引用的都是这四篇记录和论文里的内容,我不能确定报告讲了其中哪些。§5 依据 BLOOM 博客、BLOOM 论文(BigScience 2022)和 BigScience 公开的训练说明。§2 依据 Micikevicius et al. 2018(Mixed Precision Training),§6 依据 Wortsman et al. 2023(Small-scale proxies for large-scale Transformer training instabilities),§7 引用了 Llama 3 论文第 3.3.4 节的可靠性数据。文献里的数字都是我在原文里读到的;§9”2023 年之后”是我补充的。 配套代码 Code:
code/lecture10/(PyTorch;5 个脚本 + 1 个模块。代码 01 约 25 秒,02 约 95 秒,03 约 50 秒,04 约 100 秒,05 不到 1 秒) 预计阅读时间:3.5 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标 Learning goals
学完这一讲,你应该能够:
- 说出 fp16 和 bf16 各缺什么(范围 vs 精度),以及混合精度训练的三个补丁:loss scaling、fp32 主参数、fp32 累加;解释为什么 BLOOM 选 bf16。
- 列出 PaLM 的架构选择(SwiGLU、并行层、多查询注意力、RoPE、去掉偏置)和各自的理由(质量、速度、推理、稳定),并解释为什么小模型上的”代价”正是大模型愿意付的价钱。
- 复述 PaLM 训练中的损失尖峰和它的处置(回滚约 100 步、跳过 200–500 个 batch),以及”尖峰来自数据和参数状态的组合”这个判断是怎样得出的。
- 从 OPT-175B 的训练记录里讲出一次真实训练的样子:几十次重启、学习率的反复调整、loss scale 掉到 0、换优化器的尝试、硬件故障和”每停一小时都在烧钱”。
- 用小模型 + 大学习率复现注意力 logit 失控,比较 QK 归一化、z-loss、降学习率、跳过数据、梯度裁剪这几种对策,并说明”处置要对准病因”。
- 解释检查点要保存什么、逐位可复现有什么用;用平均故障间隔和 Young–Daly 公式估算检查点间隔与有效训练时间(goodput)。
- 了解 2023 年之后的变化:完全开放的训练(OLMo)、更大规模下的可靠性工程、FP8 训练、稳定化技巧成为默认配置。
0.2 先修与本讲范围 Prerequisites & scope
| 默认你已经会 Assumed | 本讲只给直觉,后面几讲再展开 Previewed only |
|---|---|
| 第 3 讲 §3:Transformer 的结构;§9:RoPE、SwiGLU、GQA 等后来的标配 | 训练数据的构成与过滤(第 2 讲已讲,这里不重复) |
| 第 4 讲 §10:PaLM 的规模(540B、780B token、46.2% MFU);第 4 讲 §8.1:用小实验预测大实验 | 碳排放的完整计算(第 19 讲) |
| 第 8 讲:MFU / HFU、16 位格式让带宽受限的运算更快 | 模型发布的规范与许可(第 18、19 讲) |
| 第 9 讲:16 字节 / 参数、ZeRO / FSDP、3D 并行、BLOOM 的 4 × 12 × 8 |
如果你学过本目录下的 CS336,它的第 18 讲(端到端)从”自己动手训练一个模型”的角度讲了很多同样的问题。本讲的特点是读日志:三个真实的大模型训练,论文里写了什么、日志里发生了什么,再在第 1 讲的小模型上把关键的现象复现出来。
0.3 本讲地图 Map
§1 为什么要读训练日志:三个案例的总览
§2 16 位浮点数与混合精度:范围、精度、三个补丁、为什么 bf16 ← 代码 01
§3 PaLM:架构选择、训练配方、约 20 次损失尖峰 ← 代码 04、代码 03 演示 3
§4 OPT-175B:一本公开的训练日志(Susan Zhang 的报告背景)
§5 BLOOM:开放协作的训练
§6 训练不稳定:小模型代理、注意力 logit、QK 归一化与 z-loss、各种处置的比较 ← 代码 02、代码 03 演示 2
§7 故障、检查点与有效训练时间:逐位复现、MTBF、Young–Daly ← 代码 03 演示 1、代码 05
§8 评测、发布与成本
§9 2023 年之后
§10–14 小结、常见误区、练习、延伸阅读、下一讲预告
0.4 记号 Notation(只列本讲新增的,其余见第 1 讲 §0.4)
| 符号 | 含义 | 例子 |
|---|---|---|
| fp32 / fp16 / bf16 | 32 位浮点;16 位浮点(5 位指数、10 位尾数);bfloat16(8 位指数、7 位尾数) | |
| eps | 1 和比它大的下一个可表示数之间的距离 | bf16:0.0078 |
| loss scale:反向传播前把损失乘以 | 动态调整,常见 – | |
| 输出层 softmax 分母的对数 | z-loss = (PaLM) | |
| 注意力 logit | 失控时到几百上千 | |
| QK 归一化 | 先对每个头的 、 做 LayerNorm 再算点积 | |
| ,,, | 集群的平均故障间隔;检查点间隔;存一次检查点的时间;故障后恢复的时间 | Young–Daly: |
| goodput | 有效训练时间 ÷ 总时间 |
0.5 关于数字 · About the numbers
讲义里的实验数字都来自 code/lecture10/ 的实际输出(固定种子、PyTorch 单线程,同一台机器上重复运行逐字相同;代码 04 以 ⏱ 开头的计时行除外)。和前几讲一样,小模型演示的是机制,不是量级:第 1 讲的 base model 只有约 11 万参数,PaLM 有 5400 亿。代码 02、03 遵循 Wortsman et al. (2023) 的思路,用”小模型 + 很大的学习率”作为大模型训练不稳定的代理——它能复现现象、比较对策,但大模型里尖峰的具体原因未必相同。代码 01 演示 5 的训练用 32 位计算、在每次更新后把权重舍入成 16 位来模拟”16 位存储”(这台 CPU 上真正的 16 位矩阵乘法比 32 位慢好几倍)。有几处结果和我写代码前的预想不一样,讲义按实际结果来写(完整清单在代码 README 的”关于实验结果”里),最重要的是:z-loss 在这里远不如 QK 归一化(§6.3);“跳过数据”在这里像抽签(§6.4);以及我在写代码 03 时踩到的一个真实的检查点 bug(§7.2)。
1. 为什么要读训练日志 · From papers to logbooks
1.1 论文只写结果
一篇大模型论文通常这样写训练过程:“我们用 AdamW,峰值学习率 ,线性预热 2000 步,在 992 张 A100 上训练。“读起来像是按下了一个按钮。
真实的情况是:OPT-175B 的团队在两个月里手动重启了至少 35 次,估计还有 70 多次自动重启;中途好几次降学习率、调梯度裁剪的阈值、试着换成 SGD 又换回来、一次意外的软件升级改变了激活范数的走向;云服务商有一天误删了整个集群。他们记了 148 页笔记,并且把日志公开了——这在大模型训练里几乎是唯一的一份。
为什么这很重要? 第一,它告诉你训练大模型的真实成本不只是”FLOPs ÷ 利用率”(第 8 讲),还有故障、回滚、人力。第二,日志里的每一个决定(降学习率、换精度格式、加一个 LayerNorm)后来都沉淀成了今天的”标准配方”——理解它们是怎么来的,比记住配方本身更有用。第三,这些问题在小规模上很难看到:它们是规模本身带来的。
1.2 三个案例
| PaLM 540B | OPT-175B | BLOOM 176B | |
|---|---|---|---|
| 机构 | Meta | BigScience(1000 多名研究者的开放协作) | |
| 训练时间 | 论文 2022 年 4 月发表 | 2021 年 11 月 – 2022 年 1 月 | 2022 年 3 月 – 7 月 |
| 硬件 | 6144 块 TPU v4(两个 Pod) | 992 张 A100 80GB(云) | 384 张 A100 80GB(Jean Zay 超算) |
| 数据 | 780B token,一遍 | 约 180B token 的语料(学习率按 300B token 规划) | 366B token,46 种自然语言 + 13 种编程语言 |
| 精度 | bf16 | fp16 + 动态 loss scaling | bf16 |
| 并行 | Pod 内 12 路模型并行 × 256 路 FSDP,Pod 间数据并行 | FSDP + Megatron 张量并行 | 张量 4 × 流水线 12 × 数据 8(第 9 讲 §6.2) |
| 效率 | MFU 46.2% | 每卡最高 147 TFLOP/s | 每卡 156 TFLOP/s |
| 训练中的麻烦 | 约 20 次损失尖峰 | 几十次重启、反复发散 | 只有一次损失尖峰,每周 1–2 次 GPU 故障 |
| 开放程度 | 只有论文 | 125M–66B 开放,175B 申请使用;代码和日志公开 | 权重开放(RAIL 许可);训练过程全程公开 |
三个案例正好对应三种处境:资源最充足、最工程化的(PaLM);时间紧、在云上边训练边救火的(OPT);学术协作、一切公开的(BLOOM)。
2. 16 位浮点数与混合精度 · Mixed precision
2.1 两种 16 位格式,各缺一样
第 8 讲 §3.4 讲过,16 位让矩阵乘法快一倍以上(tensor core),也让受带宽限制的运算快一倍(每个数少搬一半字节)。但 16 位数能表示的东西很有限。代码 01 演示 1 列出了三种格式:
| 格式 | 符号 + 指数 + 尾数 | 最大值 | 最小正规数 | 最小次正规数 | eps |
|---|---|---|---|---|---|
| fp32 | 1 + 8 + 23 | 3.403e+38 | 1.175e-38 | 1.401e-45 | 1.192e-07 |
| fp16 | 1 + 5 + 10 | 6.55e+04 | 6.104e-05 | 5.96e-08 | 0.0009766 |
| bf16 | 1 + 8 + 7 | 3.39e+38 | 1.175e-38 | 9.184e-41 | 0.007812 |
- fp16 缺范围:最大只有 65504,大于它变成 inf;小于 变成 0。在 到 之间是”次正规数”,精度越来越差。
- bf16 缺精度:它用了和 fp32 一样多的指数位,范围几乎相同;代价是尾数只有 7 位,eps = 0.0078,只有两三位十进制有效数字。
(bf16 是 Google Brain 为 TPU 设计的格式,“b”就是 Brain;A100 起 NVIDIA 的 GPU 也支持它。)
2.2 三种”出事”的方式
溢出。 第 8 讲 §5.1 的例子:在 fp16 里 就已经溢出。激活、注意力分数、梯度只要有一个超过 65504,就变成 inf,接下来所有和它相关的数都变成 inf 或 nan。
下溢。 代码 01 演示 3 拿第 1 讲 base model 的真实梯度做实验:fp32 下梯度的中位数是 3.10e-04,12.6% 的梯度小于 fp16 的最小正规数,0.14% 小于 。用 fp16 做反向传播:
| loss scale | 被冲成 0 的梯度 | 变成 inf 的 | 与 fp32 梯度的相对误差 |
|---|---|---|---|
| 0.0073% | 0.00% | 0.0107 | |
| 0.0000% | 0.00% | 0.0057 | |
| 0.0000% | 0.00% | 0.0055 | |
| 0.0000% | 19.63% | —(有 inf) |
这个小模型的梯度不算太小,不缩放时被冲成 0 的只有 0.007%,但很多梯度落在次正规数区间,整体相对误差 1.07%。大模型、大 batch 的梯度更小(损失是对几百万个 token 求平均),下溢的问题严重得多。Micikevicius et al. (2018) 报告的一个例子:在 Multibox SSD 检测网络里,67% 的激活梯度在 fp32 训练时就是 0(太小了);不做 loss scaling,这个网络在 fp16 下会发散。
被吞掉。 代码 01 演示 2:把 10000 个 1e-4 加起来,fp32 得到 1.00005,fp16 停在 0.25,bf16 停在 0.03125——和变大以后,相邻两个可表示数的间距超过了 2 × 1e-4,再加 1e-4 就被舍入掉了。同样,1 + 0.001 在 bf16 里还是 1。对训练来说,这意味着:一个大小为 1 的权重加上 0.001 的更新,等于没加。
2.3 混合精度的三个补丁
Micikevicius et al. (2018) 的 Mixed Precision Training 给出了至今仍在用的配方:
-
fp32 主参数(master weights):前向、反向用 16 位的参数,但优化器在一份 fp32 的副本上更新,更新完再转回 16 位。代码 01 演示 5 量了一下”被吞掉”有多严重:base model 的权重直接存成 bf16 时,大小为 3e-4 的更新有 12.3% 被吞掉,3e-5(微调常用的学习率)时 90.4%;fp16 尾数多 3 位,在 3e-5 时也有 39.3%。训练后期学习率衰减到很小,问题更严重。从零训练 300 步:
做法 验证集 bit/字符 fp32(参照) 1.5067 权重存 bf16,没有主参数 1.6290 权重存 fp16,没有主参数 1.5179 bf16 + fp32 主参数 1.4978 没有主参数的 bf16 差了 0.12 bit/字符;加上主参数就和 fp32 一样。论文的例子:一个中文语音模型不用 fp32 主参数,相对准确率损失约 80%。代价是每个参数多存 4 字节——第 9 讲 §2.1 那 16 字节里就有这 4 字节。
-
loss scaling:反向传播前把损失乘以 。反向传播是线性的,所有梯度都乘以 ,小梯度被”抬”进 fp16 能表示的范围;更新前在 fp32 里除回去。 太大会溢出(上表的 ),所以实践中用动态 loss scaling:遇到 inf 就跳过这一步、把 减半;连续若干步没事就加倍。代码 01 演示 4 从一个故意太大的 开始,前 5 步连续溢出、每次减半,之后在 – 之间来回,40 步里跳过了 9 步。论文报告不同网络需要的 scale 从 8 到 32K 不等——没有一个固定值对所有模型都合适。
-
fp32 累加:矩阵乘法里的乘加、softmax 和 LayerNorm 里的求和、梯度的累加都在 fp32 里做,只在写回显存时转成 16 位。tensor core 的硬件就是这样设计的;BLOOM 也明确把梯度累加和 softmax 放在 fp32 里。
2.4 为什么 BLOOM 选 bf16
有了这三个补丁,fp16 训练在大多数模型上能达到 fp32 的效果。但在一千亿参数的规模上,fp16 的窄范围成了反复出现的问题:
- BLOOM:BigScience 在正式训练之前,用 V100 训练了一个 104B 的试验模型。用 fp16,“lm-loss 一直发散”,出现了不可逆的发散;他们把原因归结为 fp16 的动态范围太小、溢出。A100 支持 bf16,正式训练换成 bf16 混合精度(梯度累加和 softmax 仍在 fp32),博客的说法是 bf16 把”一场潜在的噩梦变成了相对顺利的过程”。整个 176B 的训练只出现了一次损失尖峰。
- OPT:用的是 fp16 + 动态 loss scaling。训练记录里反复出现”loss scale 掉到最小值”(§4.2)。
- PaLM:在 TPU 上用 bf16。
bf16 范围和 fp32 一样,基本不需要 loss scaling;但它的精度更差,所以 fp32 主参数更加必要(上面的实验:没有主参数时 bf16 比 fp16 差得多)。今天的大模型训练几乎都用 bf16 + fp32 主参数,fp16 的 loss scaling 主要出现在老代码和不支持 bf16 的硬件上。
3. PaLM:一次精心设计的训练 · PaLM
第 4 讲 §10 从”规模”的角度讲过 PaLM:8B、62B、540B 三个模型,同样的数据,一条干净的规模曲线,以及大约 25% 的 BIG-bench 任务上”不连续的改进”。Aakanksha Chowdhery 的报告题目是 PaLM and Model Scaling;这一节讲第 4 讲留下的部分:它是怎样被训练出来的。
3.1 架构选择
PaLM 是一个标准的 decoder-only Transformer,加上几处改动(论文第 2 节):
| 改动 | 论文给的理由 |
|---|---|
| SwiGLU 激活(MLP 用三个矩阵:) | 已有研究表明它比 ReLU、GeLU、Swish 的质量好得多 |
| 并行层: | 大规模下训练快约 15%(两个输入矩阵乘法可以合并);消融显示 8B 上质量略有下降,62B 上没有,因此推断 540B 上是中性的 |
| 多查询注意力(MQA):所有头共享一组 、 | 对质量和训练速度中性,但生成时大幅节省(KV cache) |
| RoPE 位置编码 | 在长序列上比绝对或相对位置嵌入更好 |
| 共享输入、输出嵌入 | — |
| 去掉所有偏置(线性层和 LayerNorm) | “提高了大模型的训练稳定性” |
| 256K 的 SentencePiece 词表,数字拆成单个数位 | 多语言、代码;数字拆开对算术有帮助 |
注意这些理由各不相同:有的为了质量(SwiGLU、RoPE),有的为了速度(并行层),有的为了推理(MQA),有的为了稳定(去掉偏置)。代码 04 演示 1 在第 1 讲 base model 的结构上(2 层,)一次只打开一个开关,训练 400 步、2 个种子:
| 配置 | 参数个数 | 验证集 bit/字符(2 个种子平均) | 与基线之差 |
|---|---|---|---|
| 基线(GELU、学出来的位置、带偏置) | 109,056 | 0.942 | — |
| + SwiGLU(隐藏维度 ) | 108,968 | 0.623 | −0.319 |
| + 并行层 | 108,800 | 0.956 | +0.014 |
| + 多查询注意力 | 96,576 | 0.892 | −0.050 |
| + RoPE(去掉位置嵌入) | 102,912 | 0.537 | −0.405 |
| − 去掉所有偏置 | 107,584 | 1.052 | +0.110 |
| PaLM 式:以上全部 | 88,768 | 0.582 | −0.360 |
两个种子之间的差别在 0.01–0.12。几个观察:
- RoPE 和 SwiGLU 的改进很大,是噪声的好几倍。RoPE 在这么短的训练里尤其占便宜:学出来的位置嵌入要从零学起,RoPE 一开始就把”相对位置”写进了注意力。训练更久,差距可能会缩小。
- MQA 的差别在噪声以内,参数少了 11%——和论文”对质量中性”的说法一致。
- 并行层几乎没有差别,去掉偏置变差了。这正是论文愿意付的价钱:并行层换来大规模下 15% 的速度(在 2 层 64 宽的模型上体现不出来),去掉偏置换来大模型的稳定性。在小模型上看到的”代价”,和在大模型上得到的”好处”,不在同一个尺度上——这是为什么架构选择要在目标规模附近验证。
- 全部打开的 PaLM 式配置比基线好,但不如只加 RoPE:几处改动的好处和代价叠在一起,不是简单相加。
MQA 的好处在推理时才体现。代码 04 演示 2 算了 PaLM 540B(118 层、48 个头、头维度 256)生成时每个 token 要缓存的 、:多头注意力 5.80 MB,多查询注意力 121 KB——48 倍。64 个 2048 token 的请求,多头注意力要 760.2 GB 的 KV cache,比读一遍 1.08 TB 的参数便宜不了多少;多查询注意力只要 15.84 GB。第 8 讲 §3.3 讲过,生成受带宽限制,每一步都要把 KV cache 读一遍——MQA 决定了能同时服务多少请求、每个 token 多快。
3.2 训练配方
PaLM 论文第 5 节的训练设置,几乎每一项都有讲究:
- 优化器:Adafactor,但不做因子分解——实际上等价于”按参数大小缩放”的 Adam。,( 是步数,前期 小、后期接近 1)。
- 学习率:前 10,000 步保持 ,之后按 衰减。
- batch 大小逐步加大:540B 模型前 50k 步 512 个序列(约 1M token),到 115k 步 1024(2M),到 255k 步 2048(4M)。前期用小 batch 样本效率更高,后期大 batch 硬件效率更高(第 4 讲 §8.2 的临界 batch 随训练增大)。
- 梯度裁剪:全局范数 1.0。权重衰减:动态地设为 。dropout:预训练时不用。
- z-loss:在损失上加 ,让 softmax 的分母保持在 1 附近(),论文说这能提高训练稳定性(§6.3 会在小模型上检验它)。
- 序列长度 2048,样本之间用 [eod] 拼接,不做填充。
- 逐位可复现:“从任何检查点出发都完全逐位可复现”——JAX + T5X 的确定性实现,加上确定性的数据管线。§7.1 会解释这有什么用。
基础设施在第 9 讲 §6.3 讲过:6144 块 TPU v4、两个 Pod,Pod 内 12 路模型并行 × 256 路全分片数据并行,Pod 间由 Pathways 协调做数据并行,不用流水线;MFU 46.2%(第 8 讲 §7.2)。
3.3 约 20 次损失尖峰
论文第 5.1 节坦率地写了训练中的不稳定:最大的模型在训练中出现了约 20 次损失尖峰,“尽管开着梯度裁剪”;而且尖峰出现的时间很不规律,有时在训练后期。较小的模型没有出现。
他们的处置很简单:从尖峰之前约 100 步的检查点重新开始,跳过约 200–500 个数据 batch。这样做之后,尖峰没有再出现。
为什么这样有效?他们做了一个对照实验:从尖峰之前更早的检查点出发,用同样的那些 batch 重新训练——这次没有出现尖峰。也就是说,尖峰不是某些”坏数据”单独造成的(同样的数据换一个参数状态就没事),而是特定的数据 batch 和特定的参数状态的组合。跳过数据,就是让模型绕过这个组合。注意这个实验之所以能做,靠的正是”逐位可复现”:能精确地重放某一段训练,才能做这样的对照。
这个处置的代价不大。代码 03 演示 3 做了一个粗略的账:约 20 次尖峰 × 每次回滚约 100 步 = 重算约 2,000 步,只占 255,000 步的 0.8%;跳过的数据最多约 10,000 个 batch,按最后阶段的 batch(4.2M token)算最多约 42B token,占 780B 的 5%(这是上限,前期的 batch 更小)。真正的代价是人:每次尖峰都要有人发现、判断、决定回滚到哪里、跳过多少。
4. OPT-175B:一本公开的训练日志 · OPT and Susan Zhang’s logbook
4.1 背景
2022 年 5 月,Meta 发布了 OPT(Open Pre-trained Transformers):一组从 125M 到 175B 参数的 decoder-only 模型,目标是开放地复现 GPT-3,让研究者能够研究这个规模的模型。Susan Zhang 是论文的第一作者,也是 OPT-175B 训练的负责人之一。
论文第 2 节的设置:
- 992 张 80GB A100;FSDP(第 9 讲 §3.4)+ Megatron 张量并行(第 9 讲 §4);每张卡最高 147 TFLOP/s。
- fp16 权重、fp32 的 Adam 状态(分片存在各台机器上)、动态 loss scaling。
- AdamW,,权重衰减 0.1;175B 的峰值学习率 ,前 2000 步线性预热,在 300B token 上线性衰减到峰值的 10%。
- 梯度裁剪 1.0,中途有时降到 0.3;dropout 0.1(嵌入层除外)。
- 语料约 180B token。
- 碳排放估计 75 吨 CO2eq,论文说约为 GPT-3(估计 500 吨)的 1/7。
4.2 日志里发生了什么
metaseq 代码库公开了四篇训练进展记录(分别写于训练进行到 10%、27%、56% 和结束时),以及完整的 PDF 日志。下面的时间线依据这四篇记录:
2021 年 11 月 5 日,“11.xx”系列。 第一个完整规模的实验 11.0 启动。设置:batch 2M token,学习率 ,权重衰减 0.05,Adam ,张量并行 8 路。很快出现损失爆炸——他们降低学习率、收紧梯度裁剪(2.5 → 1.5 → 1.0)。一次 ECC 内存故障迫使重启。从 11.4 开始,每次重启后几百步就出现梯度范数、损失爆炸和 nan,loss scale 下溢。他们把权重衰减提到 0.1、 降到 0.95、再降学习率,到 11.10 甚至把 GeLU 换成了 ReLU、换了一种更稳定的注意力计算。他们还发现学出来的位置嵌入因为用了正弦初始化而”实际上被冻住了”。11.10 在 4750 步到约 6700 步之间停滞不前。
11 月 11 日,“12.xx”系列。 他们放弃 11.xx,改用 Megatron / OpenAI GPT-3 的设置:换了权重初始化、去掉了几处自定义的改动、位置嵌入改回标准初始化,加了更多梯度和参数的监控。记录里写:之后的重启”全都和硬件有关”,激活、梯度、参数的范数”看起来健康多了”。
12 月 3 日(27%)。 上一次更新以来又重启了 40 多次,“绝大多数是硬件故障”:ECC 错误、NCCL / InfiniBand 的问题、坏节点。备用节点不够,通过云的接口换一台机器可能要几个小时,有时换回来的还是同一台坏机器;从对象存储下载 1.6TB 的检查点(992 个约 1.7GB 的文件)偶尔会卡住。训练稳定性上:梯度溢出把 loss scale 压到最小值,训练停住;从检查点重启有时能多走 100 多步,问题又出现。他们试过把 Adam 的设置改得更像 SGD(失败了:重新初始化时又读回了保存的 betas),试过普通的 SGD(可能学习率太低,停滞了),最后保留 Adam、降低学习率,梯度和激活范数下降,困惑度重新开始稳定下降。记录里一句话很能说明处境:“实验每停滞或中断一小时,都在花钱。”
12 月 16 日(56%)。 云服务商同意额外提供 18 台备用主机;12 月 6 日一次坏了 5 台,而当时的缓冲只有 4 台。run 12.43:梯度范数和激活范数开始飙升、漂移,学习率降低 10% 后稳定下来。run 12.45.2:几次大的梯度范数尖峰之后训练困惑度发散,学习率降到 GPT-3 175B 所用学习率的 2/3,继续训练。还有一个意外:Megatron 从 2.4 被无意中升级到 2.6,激活范数的走向明显改变,吞吐提高了约 2%——后来查到是一行关闭 JIT profiling 的代码没有执行,他们”还不知道为什么这会影响训练”。
2022 年 1 月 6 日,训练结束。 相当于在 1024 张 A100 上连续训练约 33 天(不算硬件问题和数值不稳定的损耗);12.xx 系列一共重启约 90 次;148 页笔记。节假日期间每天约有两台机器故障;12 月 21 日,云服务商的支持团队误删了整个集群,虽然很快恢复,但有 16 台机器没通过检查,当天结束时才勉强凑够机器继续训练。他们最后搭了自动监控和恢复,圣诞到新年之间自动处理了 8 次硬件故障。最终的验证损失接近 OpenAI 的 GPT-3 175B 但不完全相同,他们怀疑原因之一是语料只有 180B token、较早出现过拟合。记录里写道:“这不是一次跑分。”
论文第 2.5 节把这些总结为:两个月里至少 35 次手动重启、100 多台主机被换下,估计还有 70 多次自动重启;损失发散和动态 loss scaler 掉到 0、最后一层激活的 L2 范数飙升有相关性,所以他们选择重启点的标准是”loss scaler 仍然 ≥ 1.0、激活范数在下降”。
4.3 从日志里学到什么
- 训练是一个控制过程,不是一次运行。 学习率、梯度裁剪、优化器、软件版本都在训练中途被改过。论文里写的”峰值学习率 “只是起点。
- 盯着”体征”,不只是损失。 OPT 团队看的是梯度范数、激活范数、loss scale——它们在损失出问题之前就开始变坏。代码 02 演示 2 在小模型上看到了同样的模式(§6.2)。
- loss scale 掉到 0 是症状,不是病因。 梯度大到怎么缩都溢出,说明训练已经在发散;重置 loss scaler 只对一部分发散有用(论文原话)。
- 降学习率 + 回滚是最常用的处置,在 OPT 和后来的很多训练里都是如此。代码 03 在小模型上比较了这一招和 PaLM 的”跳过数据”(§6.4)。
- 基础设施占了一半的精力。 记录里大量篇幅是硬件故障、换机器、检查点下载卡住——这是 §7 的主题。
- 用现成的、被验证过的配方。 11.xx 系列的很多改动(自定义的初始化、Normformer、GeLU)是在没有充分小规模验证的情况下直接用在 175B 上的;12.xx 回到 Megatron / GPT-3 的设置之后才稳定下来。
5. BLOOM:开放协作的训练 · BLOOM
BLOOM 176B 由 BigScience 项目训练:一个由 Hugging Face 牵头、1000 多名研究者参与的开放协作,在法国国家超算 Jean Zay 上用公共资助的算力训练,训练说明、日志和损失曲线在训练期间就公开了。第 9 讲 §6.2 讲过它的 3D 并行;这里补充训练本身的几个决定:
- 架构:70 层、宽度 14336、112 个头,词表 250,680(46 种自然语言和 13 种编程语言)。位置编码用 ALiBi(在注意力分数上加一个随距离线性减小的偏置),能处理比训练时更长的输入。嵌入层之后加一个 LayerNorm——来自 bitsandbytes 的 StableEmbedding,他们发现这让训练稳定得多(论文也提到它对零样本泛化有一点损害;换成 bf16 之后它也许已经不必要,但没有验证)。
- 精度:bf16(§2.4,104B 的 fp16 试验失败之后)。
- 一些”小”问题:BigScience 的训练说明里写着,默认的初始化标准差 0.02 太大,他们”在最初几千步就过不去”,直到改小;在 48 个节点的规模上出现挂起,要加
CUDA_LAUNCH_BLOCKING=1;优化器的参数组要切小。这些问题都只在全规模时才出现。 - 故障:在约 400 张 GPU 的集群上平均每周 1–2 次 GPU 故障;每 3 小时存一次检查点(完整检查点 2.3TB),每次故障平均丢 1.5 小时;另外几次 5–10 小时的停机来自软件:PyTorch 的死锁、磁盘空间用完。
- 结果:整个训练只有一次损失尖峰,很快恢复;约 3.5 个月、1,082,990 个计算小时、366B 个 token。论文估计训练的能耗约 433 MWh、25 吨 CO2eq;算上设备制造和空闲功耗,全生命周期约 81 吨(第 19 讲)。
BLOOM 的训练比 OPT 顺利得多。原因之一是时间:BLOOM 晚了几个月,吸取了 Megatron-Turing 530B、OPT 等前人的经验(bf16、更保守的初始化、嵌入层的 LayerNorm);另一个原因是 bf16 本身。
6. 训练不稳定:从日志到机制 · Training instabilities
6.1 症状
三本日志里的”不稳定”有几种表现:
- 损失尖峰:损失突然跳高,然后(也许)回落(PaLM 的约 20 次,BLOOM 的 1 次)。
- 发散:损失跳高后回不来,变成 nan(OPT 的 11.xx 系列)。
- 停滞:损失长时间不下降(OPT 11.10 的 4750–6700 步)。
- 前兆:梯度范数、激活范数、loss scale 先出现异常。
困难在于:这些问题通常只在最大的模型上出现,复现一次要几百万美元。Wortsman et al. (2023) 的贡献是一个廉价的代理:用很大的学习率训练小模型,能复现同样的不稳定。他们研究了两种:注意力层里 logit 的增长(之前在 ViT-22B 上观察到),以及输出 logit 偏离 log 概率(PaLM 用 z-loss 处理的那一种);并发现大模型上用过的对策在小模型上同样有效。
6.2 在小模型上复现
代码 02 在一个 4 层、 的模型上(batch 16,400 步,不裁剪梯度)扫描学习率:
| 学习率 | 验证集 bit/字符 | 训练中最大的注意力 logit | 最后的 log Z | |
|---|---|---|---|---|
| 3e-3 | 什么都不加 | 1.033 | 13.9 | 7.38 |
| 3e-3 | QK 归一化 | 0.724 | 5.6 | 8.15 |
| 3e-2 | 什么都不加 | 0.557 | 57.3 | 11.55 |
| 3e-2 | QK 归一化 | 0.563 | 11.0 | 11.26 |
| 0.1 | 什么都不加 | 1.156 | 895.5 | 9.14 |
| 0.1 | QK 归一化 | 0.744 | 12.1 | 10.98 |
不加 QK 归一化时,注意力 logit 随学习率迅速变大:3e-3 时十几,0.1 时近千。logit 上千意味着 softmax 几乎变成了 one-hot:注意力只盯着一个位置,梯度几乎为 0,这一层”卡死”了。学习率 0.1 时模型明显变差。加上 QK 归一化(先对每个头的 、 做 LayerNorm 再算点积,把点积的大小限制住),logit 保持在十几以内,三个学习率下模型都正常——对学习率的敏感度小得多。
演示 2 打印了学习率 0.1、什么都不加的那次训练的”体征”(节选):
步 训练损失 梯度范数 注意力 logit log Z
0 3.791 3.16 0.1 3.8
50 2.278 0.50 107.2 5.7
100 2.008 0.28 325.9 5.2
120 2.011 0.27 895.5 5.1
130 2.228 1.88 819.0 5.3
140 2.528 1.41 660.0 4.1 ← 尖峰
200 1.933 0.40 495.4 5.2
300 1.423 0.36 361.3 6.1
399 0.746 0.49 156.9 9.1
前 100 步损失正常下降,但注意力 logit 一路涨到几百;第 100–260 步 logit 到了近千,损失停在 2.0 左右下不去,其间出现一次损失尖峰(第 130–140 步,2.0 → 2.5),梯度范数同时跳高;之后 logit 慢慢回落,损失才重新开始下降。同样学习率、加 QK 归一化的那次训练,第 200 步的训练损失已经是 1.717,最后 0.440。
这个模式和 OPT 的日志很像:先是某个体征慢慢变坏,然后损失才出问题。所以 OPT 团队盯着激活范数和梯度范数,而不只是损失。
6.3 QK 归一化 vs z-loss:多跑几个种子
PaLM 用 z-loss 来稳定训练;QK 归一化是 2023 年以后才流行的(Dehghani et al. 2023 用于 ViT-22B)。它们管的是不同的东西:z-loss 惩罚输出层 softmax 的 (演示 1 里 log Z 一路涨到 10 左右,说明输出 logit 在整体漂移),QK 归一化管的是注意力的 logit。
代码 02 演示 3 在学习率 0.1 下各跑 3 个种子:
| 做法 | 种子 0 | 种子 1 | 种子 2 | 平均 | 最差 |
|---|---|---|---|---|---|
| 什么都不加 | 1.156 | 2.391 | 1.737 | 1.761 | 2.391 |
| QK 归一化 | 0.744 | 1.189 | 1.063 | 0.999 | 1.189 |
| z-loss | 0.808 | 2.053 | 1.442 | 1.434 | 2.053 |
- 什么都不加时,三个种子差别很大——不稳定的训练像抽签。
- QK 归一化在每个种子上都是三者里最好的,对准了这里的病因;但它也没有完全消除种子之间的差别:学习率 0.1 对这个模型还是太大了。
- z-loss 在每个种子上都比什么都不加好一些(平均 1.761 → 1.434),但远不如 QK 归一化。只看种子 0(0.808 vs QK 的 0.744)会以为两者差不多——这正是要多跑几个种子的原因,也是第 5 讲以来反复出现的教训。
6.4 处置要对准病因
如果训练已经出了问题,该怎么办?代码 03 演示 2 从第 100 步的检查点(尖峰在第 130–140 步)出发,比较了几种处置:
| 处置 | 100–200 步最高损失 | 其间最大 logit | 第 200 步 | 第 300 步 | 验证集 bit/字符 |
|---|---|---|---|---|---|
| 什么都不做(参照) | 2.528 | 959.3 | 1.933 | 1.423 | 1.156 |
| 跳过第 100–129 步的数据(PaLM 式) | 2.095 | 1625.1 | 1.922 | 1.339 | 1.122 |
| 跳过第 110–139 步的数据(PaLM 式) | 2.221 | 1045.1 | 1.959 | 1.559 | 1.670 |
| 学习率减半(OPT 式) | 2.045 | 988.8 | 1.477 | 0.635 | 0.690 |
| 打开梯度裁剪(范数 1.0) | 2.228 | 959.3 | 1.844 | 1.449 | 1.306 |
- 学习率减半效果最好,但方式和我预想的不同:100–200 步之间注意力 logit 照样涨到近千,降学习率并没有马上把它压下来;改善来自模型更早走出了停滞(第 200 步损失 1.477 vs 1.933,第 300 步 0.635 vs 1.423)。它对准的是这个玩具里的病因:学习率太大。
- 跳过数据像抽签:跳过第 100–129 步,结果和参照几乎一样;跳过第 110–139 步,结果更差。学习率和模型状态没变,logit 照样失控。
- 梯度裁剪没有帮助:注意力 logit 的增长不是靠一两个巨大的梯度,而是很多步稳定的小更新累积起来的。(PaLM 一直开着梯度裁剪,仍然出现了约 20 次尖峰。)
这和 PaLM 的经验并不矛盾。PaLM 的尖峰是”某些 batch 和某种参数状态的偶然组合”——换一段数据就能绕过去;我们这里的病因是学习率本身,绕不过去。处置要对准病因,而要知道病因,就要能做对照实验——这就是 PaLM 花力气做到逐位可复现的原因(§7.1)。
6.5 实践中的稳定化工具箱
把三本日志和后来的研究放在一起,常见的稳定化手段有:
| 手段 | 对准的问题 | 出处 |
|---|---|---|
| 学习率预热、更保守的峰值学习率、发散时降学习率 | 学习率太大 | 几乎所有训练;OPT 的主要处置 |
| Adam 的 从 0.98–0.999 降到 0.95 | 二阶矩对突然变大的梯度反应太慢 | OPT(11.xx → 0.95)、BLOOM、LLaMA 都用 0.95 |
| bf16 代替 fp16 | 溢出、loss scale 崩溃 | BLOOM |
| 更小的初始化 | 早期激活过大 | BLOOM(0.02 太大)、OPT(12.xx 换初始化) |
| 去掉偏置 | 大模型的稳定性 | PaLM |
| 嵌入层后的 LayerNorm | 嵌入层的梯度 / 激活过大 | BLOOM |
| z-loss | 输出 logit 漂移 | PaLM |
| QK 归一化 | 注意力 logit 失控 | ViT-22B(2023)以后的很多模型 |
| 回滚 + 跳过数据 | 数据与状态的偶然组合 | PaLM |
| 梯度裁剪 | 偶发的大梯度 | 几乎所有训练(但不能解决慢慢累积的问题) |
7. 故障、检查点与有效训练时间 · Failures, checkpoints and goodput
7.1 检查点要存什么,以及逐位复现
一次训练会被打断几十上百次,每次都要从检查点恢复。代码 03 演示 1 在第 100 步保存检查点、恢复后训练到第 400 步:
| 做法 | 与参照的参数最大差 | 第 100–110 步最高损失 | 验证集 bit/字符 |
|---|---|---|---|
| 不中断地训练(参照) | 0.0e+00 | 2.070 | 1.156 |
| 恢复:参数 + 优化器状态 + 数据位置 | 0.0e+00 | 2.070 | 1.156 |
| 恢复:忘了优化器状态(新的 AdamW) | 3.1e+00 | 3.391 | 2.889 |
| 恢复:数据顺序变了 | 3.1e+00 | 2.095 | 0.994 |
保存了参数、优化器状态,并且第 步的数据只由(种子,)决定时,恢复后的训练和不中断地训练逐位相同(每一条记录的训练损失都完全相等)。忘了保存优化器状态:Adam 的 、 从 0 开始,第一步相当于按梯度的符号、以整个学习率更新每一个参数——损失立刻从 2.07 跳到 3.39,最终 2.889。数据顺序变了:训练走上另一条轨迹,这一次反而更好——在这个不稳定的设置里,换一条轨迹就像换一个种子。无论哪种,“恢复后的训练是不是原来那次训练”都无法再确认了。
所以一个完整的检查点包括:模型参数、优化器状态(第 9 讲:每个参数 12 字节,比模型本身大得多)、学习率调度的位置、数据管线的位置、随机数生成器的状态。BLOOM 的完整检查点 2.3TB,只存 bf16 参数是 329GB;OPT 的检查点 1.6TB、992 个文件。
逐位可复现还需要更多:每一个运算都要确定性(GPU 上的一些运算默认不是,例如某些原子加法的顺序),数据管线不能依赖文件系统的读取顺序,并行方式和卡数也要相同(第 9 讲 §3.1:换一种并行方式,舍入误差就会让训练轨迹分开)。PaLM 做到了这一点,换来的是 §3.3 那种对照实验的能力。
7.2 一个真实的坑
写代码 03 的第一版时,我自己踩到了一个检查点的 bug,值得记下来。我把检查点存在内存里(copy.deepcopy(opt.state_dict())),然后从同一个检查点恢复了好几次。结果第二次恢复之后的数字悄悄变了。原因是 PyTorch 的 optimizer.load_state_dict 在类型和设备都相同时直接引用传进来的 、 张量,不复制;恢复后的训练原地更新它们,就把内存里的检查点改坏了。修复方法是每次恢复前再 deepcopy 一份(写到磁盘再读回来的检查点没有这个问题)。
这个 bug 的教训和 OPT 日志里 Megatron 版本的意外升级是同一类:训练系统里有大量”看不见的状态”,任何一处没管好,结果就会悄悄改变——而且在一个不稳定的训练里,悄悄的改变可能被放大成完全不同的结果。
7.3 故障率随规模线性增长
代码 05 演示 1 从三份训练报告反推每张卡的平均故障间隔(MTBF):
| 训练 | 报告 | 集群 MTBF | 每张卡 MTBF |
|---|---|---|---|
| Llama 3 405B(2024) | 16,384 张 H100,54 天里 419 次意外中断 | 3.1 小时 | 5.8 年 |
| BLOOM 176B(2022) | 约 400 张 A100,平均每周 1–2 次 GPU 故障 | 112.0 小时 | 5.1 年 |
| OPT-175B(2021) | 1,024 张 A100,节假日期间约每天 2 台机器故障 | 12.0 小时 | 1.4 年 |
单张卡很可靠(Llama 3 和 BLOOM 的数字都是五六年坏一次),但故障率随卡数线性增加。如果每张卡 5 年坏一次:8 张卡平均 5,475 小时一次,1.6 万张卡每 2.7 小时一次,10 万张卡每 26 分钟一次。规模大到一定程度,“训练会被打断”不是意外,而是常态。
Llama 3 论文给了迄今最详细的可靠性数据:54 天里 466 次作业中断(47 次计划内,419 次意外),约 78% 的意外中断确认或怀疑是硬件问题,GPU 相关的占 58.7%(GPU 故障 148 次、显存(HBM3)故障 72 次……);尽管如此,有效训练时间仍然超过 90%。他们还观察到一些意想不到的现象:吞吐有 1–2% 的昼夜波动(白天温度高,影响 GPU 的动态调频);上万张 GPU 同时开始或停止计算,会让数据中心的功耗瞬间变化几十兆瓦,这对电网是一个挑战。
7.4 多久存一次检查点
检查点存得太少,每次故障丢掉的训练多;存得太勤,存检查点本身占掉太多时间。设集群的平均故障间隔 、检查点间隔 、存一次的时间 、故障后恢复的时间 ,有效训练时间的比例近似是
三项分别是:存检查点占的时间、每次故障平均丢掉半个间隔的训练、恢复时间。对 求最优,得到 Young (1974) / Daly (2006) 的经典结果 。
代码 05 演示 2 模拟了一次 54 天的训练(故障是泊松过程,模拟 5 次取平均):
- Llama 3 规模( 小时,假设 分钟、 分钟): 小时;即使用最优间隔,goodput 也只有 77% 左右——损失来自存检查点、作废的训练(每次故障约半个间隔)和恢复(381 次 × 15 分钟)。Llama 3 报告的”超过 90%“说明他们的 和 比这里假设的小得多。
- BLOOM 规模( 小时, 分钟, 小时): 小时,goodput 约 94%。BLOOM 实际每 3 小时存一次(goodput 92.7%,几乎一样),训练说明里写的”每次故障平均丢 1.5 小时”正好是半个间隔。
这解释了为什么大规模训练的工程重点越来越多地放在让检查点写得更快(异步写、分片写、先写到内存再慢慢落盘)和让恢复更快(自动检测故障、备用机器、快速重新调度)上: 随规模变小,只有同时压低 和 ,goodput 才能保持住。
7.5 总账
把第 8 讲的硬件利用率和这里的有效时间乘起来(代码 05 演示 3):MFU 46.2% × 有效时间 85% ≈ 39%。第 8 讲 §7.3 算过 PaLM 的实际芯片时间是”按实测吞吐不间断训练”的 1.50 倍,相当于有效时间约 67%(包括 batch 预热阶段吞吐低、停机、尖峰回滚),总效率约 31%。大模型训练里,买来的算力大约只有三分之一到五分之二变成了”模型必须做的运算”。
8. 评测、发布与成本 · Evaluation, release and cost
训练结束不是终点。三个案例在发布上的选择截然不同:
- PaLM:只发布了论文(以及后来的 API)。论文做了大量评测(BIG-bench、推理、代码、多语言、翻译),也专门分析了记忆、偏见和有害内容。
- OPT:125M–66B 的权重直接开放;175B 需要申请(研究用途);代码库(metaseq)和训练日志公开。论文专门有一节讨论”负责任的发布”,并承认模型在偏见、有害内容上的问题。
- BLOOM:权重开放,用 BigScience 的 RAIL 许可(允许使用,但限制某些有害用途)——一种”开放但有条件”的尝试(第 18 讲会讨论发布规范)。
成本。 三份报告都估算了碳排放,但口径不同:OPT 估计 75 吨 CO2eq(GPT-3 估计 500 吨,约 1/7);PaLM 报告 271.43 吨(同表中 GPT-3 是 552 吨)——同一个 GPT-3,两份报告用了不同的估计;BLOOM 训练能耗约 25 吨,全生命周期约 81 吨。这些数字强烈依赖数据中心的能源结构(PaLM 的数据中心 89% 是无碳能源)。第 19 讲会详细讨论怎样记这笔账。
OPT 的最后一条记录里有一句话值得回味:他们的损失曲线在”算力—损失”图上比 GPT-3 偏右,原因之一是”没有 OpenAI 的完整代码库”。训练配方本身就是一种稀缺资源——这也是为什么公开的日志、公开的代码、公开的配方对整个领域如此重要。
9. 2023 年之后 · Since 2023
这一讲的三个案例都在 2021–2022 年。此后的变化可以概括为:训练配方趋于稳定和公开,规模又大了一到两个数量级,可靠性成了核心问题。这里只列和本讲直接相关、比较确定的几条。
- 完全开放的训练。 AI2 的 OLMo(2024 年起)不仅开放权重,还开放训练数据、代码、中间检查点和训练日志——OPT 和 BLOOM 开的头被推到了底。Llama 2、Llama 3 的技术报告也比 2022 年的论文详细得多,Llama 3 专门用一节讲可靠性(§7.3)。
- 稳定化技巧成为默认配置。 bf16 + fp32 主参数、、去掉偏置、RMSNorm、SwiGLU、RoPE(第 3 讲 §9)几乎成了标准配方;QK 归一化、z-loss 等被不少后来的开放模型采用。Wortsman et al. (2023) 一类工作让”在小模型上研究大模型的不稳定”成为常规做法。
- 超参数迁移。 μP(Yang et al. 2022)等参数化方法让在小模型上调好的学习率等超参数可以直接迁移到大模型,减少了 OPT 11.xx 那种”在全规模上试错”的需要。
- 更低的精度。 FP8 训练在 H100 上开始被采用(例如 DeepSeek-V3 用 FP8 混合精度训练),对数值稳定性提出了比 fp16 更严格的要求——§2 的那些问题在更低的位数上又回来了,需要更细粒度的缩放。
- 学习率调度。 “常数学习率 + 最后冷却”(WSD)一类调度(第 4 讲 §11)让训练可以随时延长或从中途分出检查点,对”中途调整”更友好。
- 规模与可靠性。 训练集群从几百、几千张卡到上万、十万张卡(§7.3 的估算:10 万张卡每 26 分钟一次故障),检查点系统、故障检测、自动恢复成了大模型训练基础设施的核心组件。
哪些结论仍然成立? 混合精度的三个补丁;“盯着体征、回滚、降学习率”的基本处置;检查点要存完整状态;故障率随规模线性增长、goodput 要靠工程来保。被修正或补充的:fp16 + loss scaling 基本被 bf16 取代;很多当年靠人工处理的不稳定,现在在架构和配方层面就预防了(QK 归一化、更好的初始化和参数化);“训练日志只是内部资料”变成了”训练日志是论文的一部分”。
10. 小结 · Summary
- 读日志,不只是读论文:论文写配方,日志写决定。OPT-175B 两个月里几十次手动重启、反复降学习率、换优化器的尝试、集群被误删——真实的训练是一个控制过程。
- 16 位浮点:fp16 缺范围(最大 65504,小于 变 0),bf16 缺精度(eps 0.0078)。混合精度的三个补丁:loss scaling(动态调整)、fp32 主参数(bf16 存权重时 3e-4 的更新被吞掉 12%)、fp32 累加。BLOOM 的 104B fp16 试验发散,换成 bf16 之后整个训练只有一次尖峰。
- PaLM 的架构选择各有目的:SwiGLU、RoPE 为质量(小模型上也有大改进),并行层为速度,MQA 为推理(KV cache 小 48 倍),去掉偏置为稳定。小模型上的”代价”正是大模型愿意付的价钱。
- PaLM 的约 20 次损失尖峰:回滚约 100 步、跳过 200–500 个 batch;尖峰来自数据和参数状态的组合——靠逐位可复现做对照实验才得出这个结论。
- 小模型代理:大学习率下注意力 logit 失控(到近千)、训练停滞并出现尖峰;QK 归一化把 logit 限制在十几,三个种子上都最好;z-loss 管的是输出 logit,效果小得多。多跑种子才能看清。
- 处置要对准病因:在学习率过大导致的不稳定里,降学习率(OPT 式)最好,跳过数据(PaLM 式)像抽签,梯度裁剪无效。
- 检查点与逐位复现:完整状态 = 参数 + 优化器状态 + 调度位置 + 数据位置 + 随机数状态;忘了优化器状态会让损失立刻跳高。
- 故障与 goodput:每张卡五六年坏一次,一万六千张卡每三小时一次;最优检查点间隔 ;规模越大,越要靠快速的检查点和恢复保住有效时间。买来的算力大约只有三分之一到五分之二变成了有用的运算。
11. 常见误区 · Common pitfalls
- “论文里写的超参数就是训练时用的超参数。” OPT 的学习率、梯度裁剪、优化器都在训练中途被改过;论文写的往往只是起点。
- “bf16 比 fp16 更精确。” 正好相反:bf16 的尾数更少、精度更差;它的优势是范围。所以 bf16 不需要 loss scaling,但更需要 fp32 主参数。
- “loss scale 掉到 0 是训练发散的原因。” 它是症状:梯度大到怎么缩都溢出。重置 loss scaler 只对一部分发散有用。
- “梯度裁剪能防止所有不稳定。” 它只能挡住偶发的大梯度。注意力 logit 失控是很多步稳定的小更新累积起来的,PaLM 开着裁剪仍然有约 20 次尖峰。
- “跳过出问题的数据就能解决尖峰。” 只在尖峰来自”数据和状态的偶然组合”时有效(PaLM)。如果病因是学习率或架构,跳过数据只是换了一条轨迹(代码 03)。
- “一个对策在一个种子上有效,就是有效。” 不稳定的训练像抽签;代码 02 里只看种子 0,会以为 z-loss 和 QK 归一化差不多。
- “在小模型上变差的架构改动就不该用。” 并行层、去掉偏置在小模型上略差,但它们换来的是大规模下的速度和稳定(PaLM 的消融正是这样做的判断)。
- “检查点就是模型参数。” 还要存优化器状态、调度位置、数据位置、随机数状态,否则恢复后的训练不是原来那次训练。
- “故障是小概率事件。” 单张卡是,一万张卡不是:每隔几小时就有一次。goodput 是需要专门设计的指标。
12. 练习 · Exercises
练习 1(fp16 的范围) 一个注意力分数矩阵里最大的值是 15。(a) 在 fp16 里直接算 会怎样?(b) 安全 softmax(先减最大值)之后,指数部分最大是多少?(c) 如果一行有 2048 个元素、都等于最大值,分母是多少,fp16 放得下吗?
参考答案
(a) ,溢出成 inf,softmax 得到 nan。(b) 减去最大值后最大的指数是 。(c) 分母最大是 2048,远小于 65504,放得下。但如果在 fp16 里累加 2048 个数,会遇到演示 2 的”累加停滞”——所以 softmax 的求和要在 fp32 里做(第三个补丁)。
练习 2(loss scaling) (a) 一个梯度的真实值是 ,在 fp16 里不缩放会变成什么?乘以 呢?(b) 如果这一步最大的梯度是 2.0,loss scale 最大能取多少才不会溢出?
参考答案
(a) ,在 fp16 里变成 0。乘以 后是 ,可以表示(实测 0.000655)。(b) 要 ,,取 2 的幂就是 。这说明为什么需要动态 scaling:小梯度希望 大,大梯度希望 小,而它们的大小在训练中一直在变。
练习 3(被吞掉的更新) 一个 LayerNorm 的增益参数等于 1.0。(a) 存成 bf16 时,比多大还小的更新会被吞掉?fp16 呢?(b) 一个大小约 0.05 的权重呢?(c) 用这些数解释代码 01 演示 5 的表格。
参考答案
(a) 1.0 附近 bf16 的间距是 eps = 0.0078,小于一半(0.0039)的更新被舍入掉;fp16 的间距 0.00098,一半是 0.00049。(b) 0.05 附近的间距按 2 的幂缩小:bf16 约 0.00024,fp16 约 0.00003。(c) base model 的权重中位数是 0.048;学习率 3e-4 的更新对 0.05 附近的 bf16 权重刚好在边缘,对 1.0 附近的 LayerNorm 参数则完全被吞掉——所以 bf16 吞掉了 12.3%;3e-5 时大部分权重都吞掉了(90.4%)。fp16 的间距小 8 倍,所以吞得少得多。
练习 4(GQA 的 KV cache) PaLM 540B(118 层、头维度 256)如果用分组查询注意力,48 个查询头分成 8 组、每组共享一组 、,每个 token 的 KV cache 多大?64 个 2048 token 的请求呢?和多头(48 组)、多查询(1 组)比较。
参考答案
每个 token: 字节 ≈ 0.97 MB;64 × 2048 个 token 约 126.7 GB。多头注意力 5.80 MB / 760.2 GB,多查询注意力 0.12 MB / 15.8 GB。GQA 在两者之间:比多头小 6 倍,质量通常比 MQA 更接近多头。这是 Llama 2 70B 以后大模型普遍采用 GQA 的原因。
练习 5(Young–Daly) Llama 3 规模的集群 小时。(a) 存一次检查点 3 分钟( 小时)时,最优间隔和公式给出的 goodput 是多少( 分钟)?(b) 把 降到 36 秒(0.01 小时)、 降到 3 分钟呢?
参考答案
(a) 小时,公式 goodput ≈ 74.7%(模拟约 77%)。(b) 小时, 小时时公式 goodput ≈ 90.5%。把检查点写快 5 倍、恢复快 5 倍,有效时间从四分之三提高到九成——这正是 Llama 3 能报告”超过 90%“的那类工程。
练习 6(改一行代码,预测结果) 在代码 02 的 run 函数里,把 clip=None 改成 clip=1.0(所有训练都开梯度裁剪)。你预测学习率 0.1、什么都不加的那三个种子会变好吗?QK 归一化的那三个呢?
参考答案
根据代码 03 演示 2(从检查点开始打开裁剪,1.156 → 1.306,并没有变好),可以预测:梯度裁剪不能阻止注意力 logit 的累积增长,什么都不加的那三次训练大概仍然很差(具体数字以你运行的结果为准)。QK 归一化的训练本来就稳定,裁剪的影响应该不大。这个练习的目的是让你区分”偶发的大梯度”和”慢慢累积的失控”——梯度裁剪只对前者有效。
练习 7(改一行代码,预测结果) 在代码 03 演示 2 里加一种处置:既跳过第 100–129 步的数据,又把学习率减半。你预测它比单独减半学习率更好还是更差?为什么这类组合实验在大模型上几乎做不起?
参考答案
很可能和单独减半学习率差不多(病因是学习率,跳过数据只是换了一条轨迹,可能略好也可能略差)。在大模型上,每一种处置都要从检查点重新训练几百到几千步,每次都要几十万美元的算力和几个小时到几天的时间——所以 OPT、PaLM 的团队只能凭经验选一种,很少能做对照。这也是 Wortsman et al. 提倡在小模型上研究不稳定的原因。
练习 8(10 万张卡) 一个 10 万张卡的集群,每张卡 MTBF 5 年。(a) 集群平均多久出一次故障?(b) 如果存一次检查点要 1 分钟、恢复要 5 分钟,最优检查点间隔和 goodput 大约是多少?
参考答案
(a) 小时,约 26 分钟。(b) 小时,约 7 分钟;公式 goodput ≈ 55%——将近一半的时间浪费在检查点、作废的训练和恢复上。在这个规模,必须把恢复做到秒级、检查点做成异步的,或者让训练能在部分机器坏掉时继续进行(容错训练)。
练习 9(概念:为什么跳过数据对 PaLM 有效) 用 §3.3 的对照实验解释:PaLM 怎样判断”尖峰不是坏数据造成的”?如果对照实验的结果是”从更早的检查点出发、用同样的数据,尖峰又出现了”,应该得出什么结论、采取什么处置?
参考答案
PaLM 从尖峰之前更早的检查点出发,用同样的 batch 重新训练,没有出现尖峰——说明同样的数据配上不同的参数状态就没事,尖峰来自两者的组合,所以跳过这段数据(改变组合)就能绕开。如果用同样的数据又出现了尖峰,说明问题更可能在数据本身(例如一段格式错误、重复或极端的文本),应该检查并清理这段数据;如果换了数据也照样出现,说明问题在训练配方(学习率、架构),应该像 OPT 那样降学习率或改配方。这三种结论都依赖逐位可复现的重放能力。
练习 10(概念:bf16 与 fp32 主参数) 为什么”bf16 不需要 loss scaling”和”bf16 更需要 fp32 主参数”同时成立?
参考答案
loss scaling 解决的是范围问题(小梯度下溢);bf16 的指数位和 fp32 一样多,范围足够,所以不需要。fp32 主参数解决的是精度问题(小更新被吞掉);bf16 的尾数只有 7 位,比 fp16 的 10 位还少,小更新更容易被吞掉(代码 01 演示 5:3e-4 的更新 bf16 吞掉 12.3%,fp16 只吞 0.1%),所以更需要主参数。
练习 11(概念:OPT 日志里的诊断) OPT 论文说,损失发散和”loss scaler 掉到 0”、“最后一层激活的范数飙升”相关,于是他们选择”loss scaler 仍 ≥ 1.0、激活范数在下降”的检查点重启。为什么要往回退到这样的检查点,而不是就近的检查点?
参考答案
就近的检查点可能已经处在”坏状态”里:激活范数已经在上升,即使从那里重启、降学习率,模型也可能继续走向发散(OPT 的记录里就有”重启后多走 100 多步,问题又出现”)。往回退到体征还健康的地方,再配合降学习率,模型更有机会走上另一条稳定的轨迹。代价是重算更多的步数——这又是一个用算力换稳定的交易。代码 02 演示 2 里,注意力 logit 在损失尖峰之前几十步就开始失控,也是同一个道理。
练习 12(碳排放的口径) OPT 论文说 OPT-175B 的碳排放约为 GPT-3 的 1/7(75 vs 500 吨);PaLM 论文的表里,PaLM 540B 是 271.43 吨、GPT-3 是 552 吨。(a) 两份报告给 GPT-3 的估计为什么不同?(b) PaLM 的计算量是 GPT-3 的约 8 倍(第 4 讲 §3.3),碳排放却只有 GPT-3 估计值的一半左右,可能的原因是什么?
参考答案
(a) GPT-3 没有公开自己的碳排放,两份报告都是基于不同假设(能耗、数据中心的能源结构、PUE)的第三方估计,口径不同,数字自然不同。(b) 碳排放 = 能耗 × 电网的碳强度。PaLM 的数据中心 89% 用无碳能源、PUE 1.08,碳强度只有 0.079 tCO2e/MWh;TPU v4 的能效也更高。所以”算力 ≈ 碳排放”并不成立,选址和能源结构可以带来数倍的差别(第 19 讲)。
13. 延伸阅读 · Further reading
原课相关(1/25 PaLM,3/1 Susan Zhang,以及 Syllabus “Training Methods and Infrastructure”)
- Chowdhery et al. PaLM: Scaling Language Modeling with Pathways. 2022. arXiv:2204.02311——第 2 节(架构)、第 4 节(基础设施)、第 5 节(训练设置与不稳定)、附录 B(MFU 与碳排放)。
- Zhang et al. OPT: Open Pre-trained Transformer Language Models. 2022. arXiv:2205.01068——第 2.5 节”Training Processes”。
- OPT-175B 训练记录(metaseq 代码库 projects/OPT/chronicles):10%、27%、56% 和最终更新,以及完整的 PDF 日志。github.com/facebookresearch/metaseq/tree/main/projects/OPT/chronicles
- Stas Bekman et al. The Technology Behind BLOOM Training. Hugging Face 博客,2022. huggingface.co/blog/bloom-megatron-deepspeed
补充
- BigScience Workshop. BLOOM: A 176B-Parameter Open-Access Multilingual Language Model. 2022. arXiv:2211.05100
- Micikevicius et al. Mixed Precision Training. ICLR 2018. arXiv:1710.03740
- Wortsman et al. Small-scale proxies for large-scale Transformer training instabilities. 2023. arXiv:2309.14322
- Dehghani et al. Scaling Vision Transformers to 22 Billion Parameters. 2023. arXiv:2302.05442——QK 归一化。
- Llama Team. The Llama 3 Herd of Models. 2024. arXiv:2407.21783——第 3.3.4 节”Reliability and Operational Challenges”。
- Daly. A higher order estimate of the optimum checkpoint interval for restart dumps. Future Generation Computer Systems, 2006.
- 本目录 CS336 第 18 讲(端到端)。
14. 下一讲预告 · Next lecture
Part III(系统与训练)到这里结束。三讲合起来回答了”一个基础模型是怎样被训练出来的”:一块芯片怎样用满(第 8 讲),上千块芯片怎样分工(第 9 讲),几个月的训练怎样熬过故障和不稳定(第 10 讲)。
Part IV 换一个问题:它为什么能工作?我们能理解它学到了什么吗? 第 11 讲:预测即压缩——从香农到”压缩通向通用智能”(2/27 客座 Jack Rae)从信息论出发:一个语言模型的损失就是它压缩数据的码长;训练过程中损失曲线下的面积就是”把整个训练集传给别人”要花的比特数。为什么有人认为”更好的压缩”就是”更好的智能”?这个观点能解释什么、不能解释什么?代码会用第 1 讲的 n-gram 模型和 base model 做真正的算术编码,把迷你互联网压缩成比特流、再解压回来,和 gzip 比一比;再算一算”在线编码长度”。