第 9 讲导览 · 大规模并行训练的秘诀
这一讲要解决的真实问题
前八讲都在回答同一个方向的问题:怎么设计一个能学好的模型。第 9 讲换了一个正交的问题:当模型与数据大到一张卡装不下时,怎么把它训起来。这不是「多买几张卡」那么简单——把 7B 参数的模型按混合精度 Adam 训练,光与优化器相关的静态状态就要 字节 (算式见 9.1),而一张主流训练卡的显存是 80 GB。模型还没开始算,就已经装不下了。
真实的大规模训练由两条线索驱动。
第一条是切:把数据切给多张卡(数据并行)、把单个权重矩阵切给多张卡(张量并行)、把层切成段分给多张卡(流水线并行), 三者正交,可组合成工业界的 3D 并行;再往前一步,激活也能沿序列维切(序列并行 / 上下文并行), 专家层还能沿专家维切(专家并行),于是有了「5D 并行」。
第二条是省:用低精度换显存与吞吐(BF16 / FP8)、用算力换显存(激活重计算)、 用通信换显存(ZeRO / FSDP 把冗余的模型状态分片),以及用「只训一小部分参数」换显存(LoRA / QLoRA)。
这两条线索的每一次取舍都要落在三个量上:显存够不够、算力用满没有、通信扛不扛得住。 本讲的全部公式与清单,都是为了让这三个量在动手之前就能被算出来——因为在大规模训练里,「先跑一次看看」的代价是几小时到几天。
母本 MIT 6.S191 在讲 Transformer 时提到过「大模型需要分布式训练」,但没有展开;本教程把这一讲补成一条可操作的工程线:
先算账(9.1)→ 再切分(9.2 / 9.3 / 9.4)→ 同时省(9.5)→ 处理工程摩擦(9.6)→ 落到自己的硬件上(9.7)。
凡是超出这本书能核实范围的地方(具体的框架内部实现、某次训练的确切吞吐),都会明确标注「需核实」, 而不是给一个好看的数字。
七节一览
| 节 | 核心问题 | 关键结论 | 典型陷阱 |
|---|---|---|---|
| 9.1 问题设定:超过单卡容量 | 显存到底被什么占满了?「放不下」有几种? | 混合精度 Adam 下模型状态 字节(参数 2 + 梯度 2 + fp32 主权重 4 + 4 + 4);激活与批大小、序列长度成正比,且不受分片影响 | 只算参数量、忘了优化器状态与激活;把「放不下」笼统当成一个问题,不分模型状态 / 激活 / 单层 |
| 9.2 数据并行:从 DP 到 FSDP | 每卡一份完整副本为什么浪费?分片能省多少、代价是什么? | 梯度 All-Reduce 通信量 (字节,bf16)且与批大小无关;ZeRO-1/2/3 逐级分片后每卡模型状态 (8 卡) | 以为加大批大小会加大通信量(恰好相反);以为分片越彻底越好(ZeRO-3 通信更多、可能更慢) |
| 9.3 张量并行:把一层切开 | 单层放不下时怎么切?为什么要列切与行切成对? | 列切免通信、行切需一次 All-Reduce;MLP「列切 → 逐元素激活 → 行切」每块前向仅一次 All-Reduce;注意力按头切;通信密集 → 必须留在节点内 | 以为张量并行的通信量会随并行度下降(不会,只随消息变小);跨节点硬上张量并行 |
| 9.4 流水线并行:把网络切成段 | 按层切分后,为什么卡在空转?怎么压掉? | 朴素调度气泡占比 (推导见 9.4.2);三种手段:增大 micro-batch 数 、交错调度、zero-bubble 拆分反向 | 以为流水线能缓解单卡显存(它降低单卡 batch,激活压力更小但每段要排队);以为 1F1B 消除气泡 |
| 9.5 显存优化:混合精度、重计算与分片 | 不切模型能省多少?每种手段的代价是什么? | BF16 靠宽指数范围免损失缩放;FP8 需缩放且需新硬件;激活重计算用约 1/3 的额外前向换亚线性激活显存;8-bit 优化器省 37.5% 静态显存;QLoRA 把 7B 微调压到「一张 24 GB 卡」量级 | 以为混合精度「白拿不掉点」;把 FP8 缩放与 FP16 的损失缩放搞混;把激活重计算当成能省参数显存 |
| 9.6 通信、容错与工程实践 | 通信量怎么估?万卡规模下坏了怎么办? | 环状 All-Reduce 每卡 ;重叠与分桶是吞吐关键;故障是常态(checkpoint 策略与恢复时间要一起设计);确定性与吞吐常常互斥,要先定口径 | 只算通信量不算通信时间(漏了带宽与延迟);把「能重启」当成「不用做 checkpoint 策略」 |
| 9.7 动手:为 Lab 3 选一套并行策略 | 手上只有 1–8 张卡时,究竟该怎么配? | 五步决策:显存估算 → 是否分片 → 是否 TP/PP → 批大小与梯度累积 → 精度与重计算;接着算吞吐与验收指标;受限显存下 LoRA / QLoRA 往往比任何并行都省 | 照搬大厂配置;只调一个旋钮(批大小 / 梯度累积 / 重计算要成套改);不看扩展效率只看总吞吐 |
本讲地图
第 9 讲 · 大规模并行训练的秘诀
9.1 算账(问题设定) ── 显存五账单 16N ── 三堵墙:显存 / 算力 / 通信
│
│ 算完账才知道缺什么
▼
┌─────────────────┬─────────────────┬──────────────────┐
│ 9.2 切数据 │ 9.3 切单层 │ 9.4 切层 │
│ DP → ZeRO → FSDP│ TP + 序列并行 │ PP + 1F1B │
│ 通信:梯度 │ 通信:激活(密) │ 通信:边界(稀) │
└────────┬────────┴────────┬────────┴─────────┬────────┘
│ │ │
└──────────┬──────┴──────────────────┘
▼
9.5 省:混合精度 / FP8 / 激活重计算 / 8-bit 优化器 / 量化
│
▼
9.6 工程:通信重叠 · checkpoint 与容错 · 确定性 · 5D 配平
│
▼
9.7 落地:给定 1–8 张卡与模型规模 → 一套可运行的配置 + 验收指标
│
▼
Lab 3(微调一个大语言模型)
依赖关系:9.2 / 9.3 / 9.4 相互独立,但都建立在 9.1 的账本上;
9.5 与 9.2–9.4 是「省」与「切」两条并行线索;
9.6 讲两者叠加后的摩擦;9.7 把它们合成一张决策表。
与母本讲次的对应
| 本讲内容 | 母本(MIT 6.S191)的处理 | 本教程的补齐 |
|---|---|---|
| 数据并行与梯度同步 | 提到「多 GPU 训练时同步梯度」 | 环状 All-Reduce 的通信量推导、梯度累积与等效批、线性缩放的适用边界(9.2) |
| 显存构成 | 未展开 | 五个账单的逐项来源与 推导、可复算的 7B 例题(9.1) |
| 分布式训练框架 | 未展开 | ZeRO-1/2/3 与 FSDP2、张量并行、流水线并行与气泡公式(9.2–9.4) |
| 混合精度 | 一句话提示 | FP16 / BF16 位布局差异、损失缩放的动态策略、FP8 与缩放(9.5) |
| 微调与 PEFT | 在 Lab 语境提到微调 | LoRA / QLoRA 的显存账重算与「什么时候不必并行」(9.5、9.7) |
先修目标与前置知识对照
| 本讲要用的东西 | 来自哪里 | 在本讲里的角色 |
|---|---|---|
| 链式法则与计算图 | 1.5 反向传播 | 反向传播要保存哪些中间激活——激活显存的来源 |
| Adam 的一阶 / 二阶矩 | 1.6 优化器 | 显存账本里最贵的两项(每参数 8 字节) |
| 批大小与学习率的联动 | 1.6 优化器 | 梯度累积后要不要放大学习率、要不要预热 |
| 注意力与前馈的矩阵形状 | 2.4、2.5 | 张量并行按哪个维切、通信量按哪个张量算 |
| 归一化层与残差 | 1.2、2.5 | 张量并行时 LayerNorm 要复制还是切分 |
| 显存估算与排查清单 | 工具箱 | 本讲账本的简化版,可先读它建立手感 |
怎么用这一讲
A. 只做过一次单卡训练(建议顺序读):9.1 全读 → 9.2 全读 → 9.3 / 9.4 读「定义 → 直觉 → 例题」,proof 框第一遍可跳 → 9.5 全读 → 9.6 读第一、三、四小节 → 9.7 全读并动手做一遍账。预计 4–6 小时。
B. 立刻要跑一次多卡训练(倒着读):先读 9.7 的五步决策表 → 回来读 9.1 的账本与 9.5 的对照表 → 遇到具体瓶颈(OOM / GPU 利用率低 / 扩展效率差)时再查 9.2–9.4 与 9.6。预计 2 小时拿到可运行配置。
C. 面向面试或答辩(按判断读):只读每节的「关键结论」表、errata 框与 9.6.5 的 5D 配平表,再把 9.7 的排错清单背下来。本讲在海报、简历与面试中出现频率最高的问题是「ZeRO 三个阶段分别分片了什么、通信量怎么变」。
可以跳的框:::: note[skip] 与嵌套的 ::: proof(第一遍)。不能跳的框:每节的 intuition(建立手感)与 errata(踩过的坑)。
打卡路径:读完一节 → 做该节的「即时自测」(折叠答案先自己写)→ 用 工具箱 第四节的公式把你手头的模型算一遍 → 全部读完后来到 9.7 做一次完整配置。
本讲与 Lab 3 的关系
Lab 3 的任务是「加载一个预训练大语言模型,用 LoRA 在受限显存下微调」。它与本讲的关系是互补而非重叠:
- Lab 3 走「省」路线:冻结主干、只训低秩适配器 (定义 9.5.1),于是需要优化器照料的参数从 降到 ,显存账本从 变成「低精度主干 + 适配器」。这条路不需要多卡,也不需要通信。
- 本讲走「切」路线:当真要改动全部参数(全参微调、预训练、继续预训练)时, 的账本无法靠 PEFT 绕过,只能靠 9.2–9.4 的并行与 9.5 的省显存手段。
- 两讲的接点在 9.7:那一节把「1–8 张卡」的所有情形列成一张表——什么规模该全参 + ZeRO、什么规模该 QLoRA、什么时候两者叠加,并给出排错清单。Lab 3 里的 OOM 排查(先降 batch、再上梯度累积、开混合精度、减 或 max_length)正是那张表的简化版。
一句话:Lab 3 教你「不动大模型就能适配它」,本讲教你「非要动它时怎么动得起」。两者合起来才是完整的工程答案。
本讲不覆盖什么(避免期望错位)
- 不讲集群运维的体力活:机房、网络布线、驱动与 CUDA 版本矩阵、容器与调度器(Slurm / Kubernetes)只在本讲需要的地方点到,不展开。
- 不给具体厂商硬件的选型建议:所有带宽与算力数字都写成「量级 + 算式」,你可以用自己机器的实测值替换。
- 不复述某个框架的完整文档:本讲给接口片段与配置字段,用来锚定概念;真实项目请以对应版本的官方文档为准。
- 不承诺任何数字能直接套在你的任务上:账本是下界估算,通信与利用率依赖拓扑与实现,必须实测对账(9.7 的验收步骤)。