跳到主要内容

第15章:预训练

训练一个 70B 参数的大语言模型,需要多少资源?粗略估算:仅模型权重本身,以 BF16 格式存储就需要 140GB 显存,而一张顶级 A100 显卡只有 80GB。这还没算梯度、优化器状态……单卡训练从根本上行不通。

本章从这个硬件约束出发,逐步拆解大模型预训练背后的核心技术:如何把计算分散到成百上千张 GPU 上、如何用低精度计算省内存而不失准度、如何让训练过程稳定不崩溃,以及 Scaling Law 告诉我们模型到底该多大、数据该多多。


15.1 分布式训练

单卡的天花板

训练 LLM 需要存储以下几类数据:

类型70B 模型(BF16)70B 模型(Adam 优化器)
模型参数~140 GB~140 GB
梯度~140 GB~140 GB
优化器状态(Adam 的 m, v)-~560 GB(FP32 × 2)
合计~280 GB~980 GB

A100 80GB 根本装不下。唯一出路是分布式训练——把计算和内存拆分到多张 GPU 上。有三种正交的拆分维度。

数据并行(Data Parallelism,DDP)

最直觉的做法:每张 GPU 保存完整的模型副本,但每次处理不同的数据子集。

GPU 0: 模型副本 → batch_0 → 梯度_0
GPU 1: 模型副本 → batch_1 → 梯度_1
GPU 2: 模型副本 → batch_2 → 梯度_2
↓ AllReduce(梯度求和/平均)
统一更新参数

每个 step 结束时,所有 GPU 通过 AllReduce 操作同步梯度,然后各自用相同的梯度更新参数,保持权重一致。

优点:实现简单,通信量只有梯度(而非激活值)。缺点:每张 GPU 仍需完整模型——对 70B 模型无效。

张量并行(Tensor Parallelism,TP)

对于大矩阵乘法 Y=XWY = XW,可以把权重矩阵 WW 沿某个维度切分到多张 GPU 上并行计算。

以列切分为例(Megatron-LM 的做法):

W=[W1W2],Y=[XW1XW2]W = [W_1 \mid W_2], \quad Y = [XW_1 \mid XW_2]

GPU 0 计算 XW1XW_1,GPU 1 计算 XW2XW_2,最后 AllGather 合并结果。对于 Transformer 的 MLP 层(dmodel4dmodeldmodeld_{model} \to 4d_{model} \to d_{model}),第一个矩阵列切分,第二个矩阵行切分,可以实现通信最小化。

:::info 为什么 TP 需要高带宽互联 张量并行在每层前向/反向都需要 GPU 间通信。如果 GPU 之间只有 PCIe(~32 GB/s),通信会成为瓶颈。NVLink(~600 GB/s)是 TP 实用化的关键硬件前提。 :::

流水线并行(Pipeline Parallelism,PP)

把模型的不同层放在不同 GPU 上,数据像流水线一样流过。

GPU 0: 第 0-9 层 → micro_batch_1 → micro_batch_2 → ...
GPU 1: 第 10-19 层 → micro_batch_1 → micro_batch_2 → ...
GPU 2: 第 20-29 层 → micro_batch_1 → ...
GPU 3: 第 30-39 层

挑战是流水线气泡(bubble):GPU 0 处理完第一个 micro-batch 后需要等待反向传播信号,这段时间是空闲的。GPipe、PipeDream 等方案通过增大 micro-batch 数量来提高 GPU 利用率。

3D 并行:三种方式的组合

实际训练超大模型时,三种并行方式同时使用:

  • DP(数据并行):最外层,多路完全相同的"3D 并行组"处理不同数据
  • PP(流水线并行):中间层,不同 GPU 负责不同层
  • TP(张量并行):最内层,同一层内的矩阵切分

以训练 GPT-3(175B)为例,可以配置 64 路 DP × 8 路 PP × 8 路 TP,共使用 4096 张 GPU。

ZeRO 优化(DeepSpeed)

ZeRO(Zero Redundancy Optimizer)是 DeepSpeed 提出的另一条路:在数据并行的基础上,消除每张 GPU 上的冗余状态

ZeRO 分三个阶段递进:

阶段切分内容每 GPU 显存节省(以 7.5B 模型为例)
ZeRO-1优化器状态(m, v)~4×
ZeRO-2优化器状态 + 梯度~8×
ZeRO-3优化器状态 + 梯度 + 参数~64×(取决于 GPU 数量)

ZeRO-3 下,每张 GPU 只保存 1N\frac{1}{N} 的参数,需要时通过 AllGather 从其他 GPU 取回。代价是通信量增加,但对于显存严重不足的场景是救命稻草。

:::tip ZeRO vs 张量并行 ZeRO 在通信模式上属于数据并行的扩展(跨 DP 组切分),实现相对简单,无需修改模型代码。张量并行需要修改矩阵乘法的计算逻辑,但通信更规整,通常用于同一节点内的 GPU(依赖 NVLink)。实践中常见 ZeRO-3 + TP 的组合。 :::


15.2 混合精度训练(FP16 / BF16)

为什么要用低精度

解决了内存的分布问题,还要解决内存的总量问题。把数据从 FP32(4字节)改为 FP16/BF16(2字节),可以直接减半显存,还能让现代 GPU 的 Tensor Core 以更高吞吐量计算(A100 的 BF16 Tensor Core 算力是 FP32 的 8 倍)。

FP16 vs BF16:微妙但关键的区别

两种格式都使用 16 位,但位分配不同:

格式符号位指数位尾数位表示范围精度
FP321823±3.4×1038\pm 3.4 \times 10^{38}
FP161510±65504\pm 65504
BF16187±3.4×1038\pm 3.4 \times 10^{38}

BF16 与 FP32 的指数位数完全相同,表示范围一致,只是精度(尾数位)更低。这一点至关重要:LLM 训练中,梯度的数值范围跨度极大,FP16 的有限范围容易导致数值溢出(overflow)或下溢(underflow),而 BF16 保持了与 FP32 相同的动态范围,训练稳定性接近 FP32。

这就是为什么现代 LLM 训练几乎全面转向 BF16

混合精度的关键:主权重保留 FP32

完全用 BF16 训练会有问题:某些小梯度在 BF16 精度下无法被准确累加,长期累积会导致误差。混合精度训练的解法是:

前向传播:BF16(省内存,快)
反向传播:BF16(省内存,快)
梯度累积到 FP32 主权重:FP32(精确更新)
将更新后的 FP32 主权重复制回 BF16 工作副本

这样每个参数实际上存了两份:FP32 主权重(用于精确优化)+ BF16 工作副本(用于计算)。额外的显存开销约 50%(相比纯 BF16),但远低于纯 FP32。

梯度缩放(Gradient Scaling)

如果仍使用 FP16(而非 BF16),动态范围问题更严重。解法是梯度缩放

scaled_loss=loss×s\text{scaled\_loss} = \text{loss} \times s

其中 ss 是一个大的缩放因子(如 215=327682^{15} = 32768)。反向传播计算出的梯度自然也被缩放了 ss 倍,避免了 FP16 下溢。在参数更新前,再把梯度除以 ss 恢复原值。

动态缩放因子会根据是否出现 inf/nan 自动调整:没有溢出则翻倍,出现溢出则减半,跳过本步更新。


15.3 学习率调度与训练稳定性

分布式和低精度解决了硬件约束,但训练过程本身也可能"失控"——梯度爆炸、loss 突然飙升……这需要一套调度与保护机制。

Warmup:让模型平稳起步

训练初期,模型参数随机初始化,梯度方向混乱。如果直接用目标学习率(如 3×1043 \times 10^{-4})训练,参数会剧烈震荡。

解法是 Warmup:从极小的学习率(如 10710^{-7})线性增加到目标值,持续若干步(通常 1000–4000 步):

ηt=ηmaxtTwarmup,tTwarmup\eta_t = \eta_{\max} \cdot \frac{t}{T_{\text{warmup}}}, \quad t \leq T_{\text{warmup}}

Warmup 让优化器的动量估计(Adam 中的 mt,vtm_t, v_t)有时间积累,参数逐渐找到有意义的方向后再加速。

Cosine 退火:让学习率优雅落地

Warmup 之后,学习率按余弦函数从最大值降到最小值(通常为最大值的 10%):

ηt=ηmin+12(ηmaxηmin)(1+cosπtTtotal)\eta_t = \eta_{\min} + \frac{1}{2}(\eta_{\max} - \eta_{\min})\left(1 + \cos\frac{\pi \cdot t}{T_{\text{total}}}\right)

Cosine 调度的优点是平滑、无需手动设定何时降学习率。训练末期学习率极小,模型在已收敛的区域做精细调整。

梯度裁剪(Gradient Clipping)

即使使用了 Warmup 和 Cosine 调度,偶发的梯度爆炸仍可能让训练崩溃。梯度裁剪将全局梯度范数(L2 norm)限制在阈值 θ\theta(通常为 1.0)以内:

g^={gif g2θθg2gotherwise\hat{g} = \begin{cases} g & \text{if } \|g\|_2 \leq \theta \\ \frac{\theta}{\|g\|_2} g & \text{otherwise} \end{cases}

实现极其简单(PyTorch 的 clip_grad_norm_),但对训练稳定性有显著保护作用。监控每步的梯度范数也是发现训练问题的重要手段。

Loss Spike:检测与处理

大模型训练过程中,Loss 偶尔会突然飙升(loss spike),这是业界普遍观测到的现象,确切原因至今未完全搞清楚(可能与特定 batch 数据、数值不稳定性有关)。

:::warning Loss Spike 的处理策略

  1. 检测:监控每步 loss,设定阈值(如超过前 100 步均值的 4 倍)自动报警
  2. 回滚:保存频繁的检查点(checkpoint),spike 发生后回滚到 spike 前的检查点
  3. 跳过:有时只需跳过问题 batch 重新采样即可恢复
  4. 降学习率:从检查点以更低学习率重新训练该段

LLaMA、PaLM 等模型的技术报告均记录了 loss spike 的发生,这是大规模训练中的"正常意外"。 :::


15.4 Scaling Law

有了上述工程基础,终极问题来了:模型多大才够?数据多少才够?

Kaplan Scaling Law(2020)

OpenAI 的 Kaplan 等人发现,模型性能(测试 loss)与三个因素之间存在幂律(power-law)关系:

L(N)NαN,L(D)DαD,L(C)CαCL(N) \propto N^{-\alpha_N}, \quad L(D) \propto D^{-\alpha_D}, \quad L(C) \propto C^{-\alpha_C}

其中 NN 是模型参数量,DD 是训练数据量(token 数),CC 是总计算量(FLOPs)。三个系数大约都在 0.05–0.10 之间,意味着 loss 随规模缓慢但稳定地降低。

Kaplan 的结论偏向于优先扩大模型:给定固定算力,把大部分 FLOP 用于更大的模型,而不是用更多数据训练更小的模型。这一结论推动了 GPT-3(175B)等大模型的诞生。

Chinchilla Scaling Law(2022)

DeepMind 的 Hoffmann 等人重新用更严格的实验方法拟合,得出了不同结论:

给定算力预算 CC,最优的模型大小 NN^* 和训练数据量 DD^* 应满足

NC0.5,DC0.5N^* \propto C^{0.5}, \quad D^* \propto C^{0.5}

换句话说,模型大小和数据量应该等比例增加。以 10× 算力为例:模型大 ~3×,数据也多 ~3×,而不是模型大 10× 数据不变。

他们用同等算力训练了 Chinchilla(70B),数据量是 GPT-3 的 4 倍以上,结果在几乎所有基准上超过了 Gopher(280B)。

:::info Chinchilla 定律的核心公式

DeepMind 拟合的最优配比约为:

D20×ND^* \approx 20 \times N

即每个参数对应约 20 个 token 的训练数据。例如一个 7B 模型,最优数据量约为 140B token。

:::

实践含义:之前的大模型都"欠训练"了

按 Chinchilla 的标准回看:

  • GPT-3(175B)仅训练了 300B token,理想应训练 3.5T token
  • Gopher(280B)也严重欠训练

这一发现改变了整个行业的训练策略。

当前趋势:训练更长而非更大

Chinchilla 之后,业界开始重新平衡"模型大小 vs 数据量":

模型参数量训练数据Chinchilla 最优数据
GPT-3175B300B token~3.5T token
Chinchilla70B1.4T token~1.4T token(匹配)
LLaMA-165B1.4T token~1.3T token(接近)
LLaMA-270B2T token~1.4T token(超过)
LLaMA-38B15T token~160B token(远超)

LLaMA-3 的 8B 模型训练了 15T token,远超 Chinchilla 最优——为什么?因为推理成本比训练成本更重要:一个小而训练充分的模型,推理时更快更便宜,实际部署价值更高。"过度训练"的小模型反而成了主流。

:::tip Scaling Law 的局限 Scaling Law 描述的是 loss 的趋势,但 loss 下降并非与所有能力线性相关——某些能力(如逻辑推理、代码生成)会在某个规模阈值后涌现(emergence),即量变引发质变。这也是为什么单靠 Scaling Law 无法完全预测模型能力边界。 :::


本章小结

技术解决的问题关键要点
数据并行(DDP)加速训练,模型仍需完整AllReduce 同步梯度
张量并行(TP)单层矩阵太大放不下一卡需要高带宽 NVLink
流水线并行(PP)模型层数太多放不下一卡需处理流水线气泡
ZeRO-3优化器状态显存占用巨大3D 并行的显存补充
BF16 混合精度显存不足,计算慢主权重 FP32,计算 BF16
Warmup + Cosine训练初期不稳定,末期震荡标配调度策略
梯度裁剪梯度爆炸clip_grad_norm_ ≤ 1.0
Chinchilla Law如何分配算力预算模型大小与数据量等比扩展

预训练完成后,模型具备了语言理解和生成的基础能力,但它还不知道如何"听从指令"——它只会续写文本,不会回答问题。下一章将介绍如何通过指令微调(Instruction Tuning)和 RLHF 把一个预训练基座模型变成一个真正有用的对话助手。