LLM 面试备考
首页/Agent 基础设施

训练系统(Training Systems)

所属:01-fundamentals / 06-agent-infra
面试权重:★★★
来源:《深入理解 AI Infra:量化分析与系统设计》第 10 章(李博杰,github.com/bojieli/ai-infra-book);章内所有数字都带任务条件(Qwen3-8B 全参、100B token、8,192 序列长),非本仓库测量
相关知识点:batching-parallelism.md(那篇讲并行怎么切,这篇讲切完怎么跑起来、坏了怎么恢复)、07-distributed-inference.md(同一套分析方法用在推理侧)、02-post-training/(算法侧 —— RL 的 loss 与优势怎么算,本篇讲它的系统侧)、../00-toolchain.md(训练与后训练层的点名表:Verl / TRL / Megatron-LM / DeepSpeed / FSDP / PEFT·LoRA / Unsloth / LLaMA-Factory / Axolotl)

一句话#

训练系统的设计问题可以压成一句话:「每步预算 ÷ 每步要做的工作」。显存决定能不能开始跑,计算决定能不能按期跑完,而 checkpoint 与故障恢复决定已经做过的工作有多少要重做。理解它的最小抓手是三个数:每参数 16 bytes 的常驻训练状态、流水线的气泡、最优保存间隔 τ∗=2c/λ\tau^*=\sqrt{2c/\lambda}。

为什么重要(面试角度)#

  • 它是「算法题」之外的另一半,而且面试官默认你会。 问 LoRA / DPO / GRPO 时他在考算法;一旦问到「8B 全参要几张卡、多少天、坏了怎么办」,考的是系统。仓库此前的并行策略只讲到「怎么切」,没讲切完的每步耗时、显存峰值和恢复代价 —— 这篇补的正是那一层。
  • 它有一批能当场算出来的数字:16 bytes/参数、ZeRO 四阶段的容量公式、流水线气泡 (m+p−1)(tf+tb)(m+p-1)(t_f+t_b)、最优保存间隔。答案是可以推导的,不是背的。
  • 它的反直觉特别多,正好用来分层:分片数翻倍显存峰值并不减半;通信处理时间更短训练步不一定更短;卡数翻倍且 batch 跟着翻倍,完成时间并不减半。能说清这三条为什么,就说明你真的理解了这个系统。
  • 它接得上仓库已有的两块:02-post-training/ 讲 RL 的算法,08-agentic-rl.md 讲多轮 RL 的缝;这篇补的是 RL 的系统侧 —— 三阶段流水、训推一致性、异步盈亏平衡点。面试问「你的 RL 训练为什么慢」,答算法是答偏了。

⚠️ 边界声明:本篇是《AI Infra》第 10 章的知识梳理,不是一手经验。你没有训练过 8B 全参模型、没搭过 48 卡集群、没做过 checkpoint 恢复。面试时按「这套账我会这么算」讲,不要说成「我们训练时就是这么配的」。你在 RL 侧的真实经验是 HKJC 的单轮 RLVR(Verl 两阶段),那是小规模、单轮的,与本篇的千卡级训练不是一回事 —— 这条边界要自己先说。

核心内容#

1. 常驻训练状态:每参数 16 bytes#

这是全篇最该记住的一个数。混合精度 Adam 下,一个参数要占五份状态:

状态 每参数字节 Qwen3-8B(8.19B 参数) 用在哪
BF16 模型权重 2 16.4 GB 前向与反向矩阵运算
BF16 梯度 2 16.4 GB 反向产生,算新参数时用
FP32 主权重 4 32.8 GB 保存新参数,再转 BF16 副本
Adam 一阶矩 4 32.8 GB 跨迭代保留梯度方向历史
Adam 二阶矩 4 32.8 GB 跨迭代保留梯度平方历史
合计 16 131.1 GB
Mpersistent=16NM_{\mathrm{persistent}}=16N

一句话记住量级:常驻训练状态约等于八份 BF16 权重。 8B 模型推理只要 16.4 GB,训练却要 131 GB —— 这是「单卡能推理 ≠ 单卡能训练」的根源。

两个容易答错的细节:

  • 「混合精度」不规定梯度格式。 梯度改用 FP32 就是每参数 18 bytes,这是数据格式的选择,不是混合精度定义的一部分。
  • 峰值不等于各分量之和,而是同一时刻的最大重叠:
    Mpeak,i=max⁡t[Mpersistent,i(t)+Mactivation,i(t)+Mtemporary,i(t)]M_{\mathrm{peak},i}=\max_t\left[M_{\mathrm{persistent},i}(t)+M_{\mathrm{activation},i}(t)+M_{\mathrm{temporary},i}(t)\right]
    8 GiB 激活与 4 GiB 收集缓冲同时出现就要 12 GiB;先释放激活再收集参数,同一片空间可以复用。所以容量优化有两条路:减小张量本身,或减少同时驻留的数量 —— 后面的分片、重计算、卸载分别从这两条路下手。

精度与数值风险(这张表能答出「为什么需要 FP32 主权重」):

格式 矩阵运算权重(含 scale) Qwen3-8B 读取量 缩放管理 主要风险
FP16 2 bytes 16.4 GB 全局损失缩放(因子 8–32K) 梯度下溢、缩放溢出
BF16 2 bytes 16.4 GB 不需要 尾数短,靠 FP32 累加器补偿
FP8(E4M3)操作数副本 约 1.0002 bytes 约 8.2 GB 分块缩放 + 定期高精度累加 块内舍入、scale 过期
  • FP16 为什么必须配 FP32 主权重:它只有 5 位指数,最小正规数 2−14≈6.1×10−52^{-14}\approx6.1\times10^{-5},小于 2−24≈6×10−82^{-24}\approx6\times10^{-8} 直接归零。混合精度训练论文实测:语音识别模型的权重梯度约 5% 的值指数小于 −24,会变成零。
  • BF16 为什么不用损失缩放:8 位指数与 FP32 相同,表示范围一致,代价是尾数只有 7 位。
  • FP8 的关键限定:它改变的是送入矩阵单元的那份权重副本(8.2 GB vs 16.4 GB),主权重、梯度、优化器状态仍以高精度保存 —— 上面 16 bytes/参数 的常驻状态不会降到 1 byte。DeepSeek-V3 的做法是激活按 1×1281\times128 块、权重按 128×128128\times128 块求 scale,矩阵单元每累加 128 个元素就转 FP32,与 BF16 对照训练的相对损失误差低于 0.25%。

训练偏离的表现形式是损失尖峰(loss spike,起因可能是缩放失效、数据异常或硬件错误)。处理方式是回滚到上一份 checkpoint 重训 —— 于是「训练稳定性」这个问题最终变成了保存周期问题,代价由下面的 checkpoint 模型给出。这条链路(数值 → 尖峰 → 回滚 → 保存周期)是面试里很好用的一段因果。

2. 三个旋钮:分片、重计算、卸载#

2.1 状态分片(ZeRO / FSDP)#

设 DP 组有 dd 张卡,每卡常驻训练状态:

M0=16N,M1=4N+12Nd,M2=2N+14Nd,M3=16NdM_0=16N,\quad M_1=4N+\frac{12N}{d},\quad M_2=2N+\frac{14N}{d},\quad M_3=\frac{16N}{d}
配置 每卡常驻状态(d=8d=8) 仍完整复制的部分
普通 DP 122.1 GiB 权重、梯度、优化器状态
ZeRO-1 42.0 GiB 权重、梯度
ZeRO-2 28.6 GiB 权重
ZeRO-3 15.3 GiB 全部已分片

执行路径:每个模块先 AllGather 取回完整参数 → 前向/反向 → ReduceScatter 汇总梯度并分片 → 各自更新自己负责的参数。

⚠️ 最大的坑:分片数翻倍,显存峰值并不减半。 分片只缩小常驻的那部分;执行时完整权重缓冲区与原有分片同时存在,而完整缓冲区的大小由模块本身决定,与卡数无关。FSDP2 的 CPU 小实验直接量到了:进程从 2 增到 4,常驻状态 18 → 9 MiB(减半),但执行峰值只从 39 → 30 MiB(减 23%),因为执行中还有约 21 MiB 的其他张量与缓冲区。所以「加卡就能装下」这句话要靠峰值算,不能靠常驻状态算。

2.2 激活重计算#

原理:反向要算 dW=XTdYdW=X^{\mathsf T}dY,需要前向输入 XX。保留 XX 就要让它从前向一直驻留到反向用完;释放 XX 则要在反向需要前重算。

一个具体算例:Qwen3-8B 的 MLP 中,门控结果 aa 与上投影 uu 相乘得 h=a⊙uh=a\odot u,下投影反向需要 hh。若已为非线性算子反向保留了 a,ua,u,就可以在下投影反向前重新相乘得到 hh,不必一直保留它:

  • hh 的形状 [128,12288][128,12288]、FP32 → 6 MiB,重建约需 157 万次乘法;
  • 另两个归一化加权输出各 [128,4096][128,4096]、各 2 MiB 也可重建;
  • 每层省 6+2+2=106+2+2=10 MiB,九层流水阶段省 90 MiB(约 94 MB);该阶段从 135 MiB 降到 45 MiB。

关键在生命周期:重建只需一块 6 MiB 工作区,逐层复用;而等待反向的 micro-batch 越多,长期保存的数据越多(四个 micro-batch 同时等待时省 4×90=3604\times90=360 MiB)。所以重计算与流水调度是同一件事的两面 —— 反向开始得越早,需要保存的激活越少。

2.3 卸载:把显存问题换成传输问题#

一份状态大小 VV、链路带宽 BB、从可开始传输到算子需要它有 WW 秒:

Twait=max⁡(0, VB−W)T_{\mathrm{wait}}=\max\left(0,\ \frac{V}{B}-W\right)

一个很好用的判据算例:梯度该在 CPU 还是 GPU 上转换格式? 门控投影梯度 [12288,4096][12288,4096],BF16 为 G=96G=96 MiB,FP32 为 192 MiB。两条路径:

Tc=GB+3GCc(先传 BF16,CPU 转换),Tg=3GCg+2GB(GPU 转换,传 FP32)T_c=\frac{G}{B}+\frac{3G}{C_c}\quad(\text{先传 BF16,CPU 转换}),\qquad T_g=\frac{3G}{C_g}+\frac{2G}{B}\quad(\text{GPU 转换,传 FP32})

RTX 4090 经 PCIe 4.0 x16(32 GB/s):CPU 路径约 3.15+0.98=4.133.15+0.98=\mathbf{4.13} ms,GPU 路径约 0.30+6.29=6.590.30+6.29=\mathbf{6.59} ms —— GPU 转换更快,却更慢整体,因为要多传 96 MiB。求两者相等:

B∗=13(1/Cc−1/Cg)≈147 GB/sB^*=\frac{1}{3(1/C_c-1/C_g)}\approx147\ \mathrm{GB/s}

147 GB/s 高于 PCIe 5.0 x16 的每方向 64 GB/s,所以经 PCIe 连接的卡都该「先传 BF16、在 CPU 上转」。 换成 GH200 的 NVLink-C2C(450 GB/s),多传 96 MiB 只多约 0.22 ms,GPU 转换才划算。这就是「转换速度 vs 传输量 vs 缓冲区」的三方权衡(GPU 转换时输入输出共存,峰值 288 MiB,还多要 192 MiB 缓冲)。

2.4 三种办法放在一起怎么选#

方法 减少什么 增加什么 收益取决于
状态分片 重复保存的权重/梯度/优化器状态 参数收集、梯度归约并分片 收集能否赶在模块执行前完成
激活重计算 前向到反向之间保存的中间值 重建计算 重建是否延迟关键路径上的反向
状态卸载 加速器上暂时不用的状态 主机↔加速器传输 数据能否在使用前传回

一个可解的取舍:RTX 4090 每卡可用 22 GiB,Qwen3-8B ZeRO-3 八卡每卡 15.3 GiB,若激活与缓冲区同时要 10 GiB → 总计 25.3 GiB,超 3.3 GiB。两条路:用重计算省下这 3.3 GiB,或者加到十六卡把常驻降到 7.6 GiB(总计 17.6 GiB)。前者增加计算,后者增加卡数并改变通信组 —— 先按容量筛出可行方案,再比每步耗时。

3. 每步时间:流水线调度与通信重叠#

3.1 气泡#

pp 个平衡阶段、每阶段前向 tft_f、反向 tbt_b、mm 个 micro-batch,填满排空(fill–drain)需要

T=(m+p−1)(tf+tb),u=mm+p−1T=(m+p-1)(t_f+t_b),\qquad u=\frac{m}{m+p-1}

p=4,m=8p=4,m=8 时 u=8/11≈72.7%u=8/11\approx72.7\%。mm 项是真实工作,p−1p-1 项是填充与排空的浪费。

3.2 五种调度的同一算例对照(这张表是本篇最值钱的一页)#

条件统一:Qwen3-8B 的 36 层分四个九层阶段、8 个 micro-batch、每阶段前向 10 ms、反向 20 ms、边界传输 1 ms、参数更新 1 ms。

调度 完成时间 每卡空闲 阶段 0 激活峰值 用什么换气泡 主要代价
填满排空 337 ms 96 ms 1,840 MiB —(不压气泡) 激活驻留最高,容量紧时跑不了
1F1B 347 ms 106 ms 921 MiB 无(气泡相同) 比填满排空慢约 3%
交错式 1F1B(v=2v=2) 298 ms 57 ms 1,329 MiB 每卡层块拆成 vv 段 前向消息 24 → 56 次
零气泡 ZB-H1 283 ms 42 ms 921(阶段 3 达 1,226) dWdW 延后填进空隙 靠后阶段的激活要留到 dWdW 执行
DualPipe 306 ms 65 ms 1,228(阶段 1、2 达 1,612) micro-batch 从两端相向流动 每卡参数两份

这张表最该记住的不是哪个最快,而是「没有一种在两轴上同时占优」:完成时间最短的是零气泡(283 ms),最大单阶段峰值最低的却是 1F1B(921 MiB)。

反直觉一:1F1B 在时间上更慢,只在显存上赢。 从 346 ms 变 347 ms(约 +3%),但激活与收发缓冲峰值从 2.57 GB 降到 0.97 GB(−62%)。所以「1F1B 优于填满排空」只在显存这根轴上成立。若只给中间结果留 1 GB,只有提前反向的调度能跑完这一步;容量充裕时填满排空反而更早完成。显存驻留往往不是快慢问题,而是能不能跑的问题。

压气泡的三种思路(同一算例):

  • 交错式把每卡的层块拆成 vv 段,气泡比例从 (p−1)/m(p-1)/m 降到 1v⋅p−1m\frac{1}{v}\cdot\frac{p-1}{m}:347 → 298 ms,每卡空闲 106 → 57 ms。代价是每个 micro-batch 多穿 4 条边界,全步前向消息 24 → 56 次。
  • 零气泡把反向拆成输入梯度 dXdX(在关键路径上,要尽早算)和权重梯度 dWdW(只进本层累积,可延后填空隙):1F1B 气泡 (p−1)(TF+TB+TW)(p-1)(T_F+T_B+T_W),ZB-H1 降到 (p−1)(TF+TB−TW)(p-1)(T_F+T_B-T_W),ZB-H2 再降到 (p−1)(TF+TB−2TW)(p-1)(T_F+T_B-2T_W)。本例 TF=TB=TWT_F=T_B=T_W 时 ZB-H2 的气泡下界是 0。代价落在靠后阶段(阶段 3 的峰值 308 → 约 1,226 MiB)。
  • DualPipe 让 micro-batch 从流水线两端相向注入,气泡 (PP/2−1)(F&B+B−3W)(PP/2-1)(F\&B+B-3W);代价是每卡保存两份参数。

mm 越大,压气泡越不值。 16 个 micro-batch 时,1F1B 的气泡占比已从 37.5% 降到 18.8%,而交错式增加的通信次数、零气泡增加的激活驻留、DualPipe 增加的参数量都不随 mm 缩小。

3.3 通信重叠:桶大小与提前发送#

反向逐层产生梯度,实现上把多个张量合成通信桶再发。桶越大越可能等最后生成的梯度;桶越小启动次数越多。

反直觉二:通信处理时间更短,训练步不一定更短。 一个具体算例:72 MiB 的 FP32 专家梯度环形 AllReduce,整桶约 4.55 ms,放不进一个 2 ms 的空闲时段 → 这段通信落到计算之后,延长训练步。切成三个 24 MiB 子桶后,每桶约 1.53 ms,总处理时间反而增加到 4.59 ms,但每桶都能在计算结束前传完,所以训练步更短了。决定训练步的是通信何时完成,不是通信花了多久。 (换 400 Gbit/s 网卡整桶仍需 2.28 ms,放不进 2 ms 空隙,切桶依然必要 —— 说明这不是「带宽不够」的问题。)

能藏住的通信有多少? ZeRO-3 每个 micro-batch 三次集合通信:前向前一遍 AllGather、反向前再一遍、反向后 ReduceScatter。每卡收发 d−1d⋅2N\frac{d-1}{d}\cdot2N,48 卡时约 16.0 GB,一步 V=3m⋅d−1d⋅2N≈385V=3m\cdot\frac{d-1}{d}\cdot2N\approx385 GB。逐层看,收集一层约 0.39 GB 只要约 12 ms,远短于这一层的计算,所以大部分能落在计算里。真正藏不住的只剩第一步的 AllGather 和最后一步的 ReduceScatter —— 它们都作用于约 1.24 GB 的词嵌入(前向第一层、反向最后一层),合计约 0.08 s。

这条给优化划了上限:暴露的通信只有 0.08 s,即使完全消除也只省 0.08 s。若还差 0.5 s 才能达标,就必须同时缩短别的关键路径工作。

扩展效率的实测参照(MegaScale):175B 模型保持全局 batch 6144,卡数 3072 → 12288,每步 23.7 s → 6.3 s,总加速 3.7×,扩展效率约 93%(理想 4× 应是 5.9 s)。差的那 0.4 s 来自跨组同步与负载不均 —— 卡越多,等待最慢一组的时间占比越大,这是扩展本身带来的工作。

4. MoE 与长上下文带来的调度变化#

① 容量因子(capacity factor)。 路由不均时,每个专家的输入矩阵行数上限是

每专家容量=⌊c×token 数×kE⌋\text{每专家容量}=\left\lfloor c\times\frac{\text{token 数}\times k}{E}\right\rfloor

超出的 dispatch 被丢弃:这些 token 该层不处理,表示经残差直接传给下一层;容量没用满的槽位补零,仍然占计算和通信。Qwen3-235B-A22B(E=128E=128、k=8k=8、8,192 token、一半专家 1.5 倍热):

容量因子 cc 每专家容量 丢弃的 dispatch 补零行占执行量
1.0 512 25% 25%
1.25 640 12.5% 30%
1.5 768 0 33%
2.0 1,024 0 50%

这是一个没有免费选项的旋钮:cc 小则丢 token(改变了模型看到的信息),cc 大则一半的计算花在补零上。路由越均衡,需要的 cc 越接近 1 —— 这就是辅助损失和无辅助损失动态偏置(DeepSeek-V3)在做的事。

② 相同 token 总数,序列长度分布会改变注意力计算量。 因果注意力下长度 ss 的序列有 s(s+1)/2s(s+1)/2 个配对:两条 4096 合计约 1,680 万对;同样 8192 个 token 改成 7168 + 1024,配对数增至约 2,620 万,增加约 56%。长三角形的增加面积超过短三角形的减少面积 —— 这部分是真实注意力关系,删填充 token 消不掉。所以打包(减少无效填充)、按长度分组(让各卡承担的面积接近)、上下文并行(把长序列分到多卡)三者解决的是不同的问题。

5. checkpoint 与故障恢复:训练系统的「长期账」#

5.1 什么才算可恢复状态#

只有权重是不够的 —— 权重能推理,但不足以确定下一次更新。checkpoint 要保存与同一训练进度对应的全部状态:权重、一阶矩、二阶矩、步数、学习率状态、随机状态、下一批数据的组成,以及打包器里剩余的 token。

一个具体的坑:打包器保留了 2,000 个 token,下一份数据给 6,192 个,两者拼成下一条 8,192 的序列。丢掉这 2,000 个 token,就只能从后续数据里多取补齐 —— 改变了这条序列的前缀、注意力关系和标签。 与「batch 位置」相比,这一项最容易被漏掉。 同理还有数据预取与训练进度的错位:加载器已经把任务派到第 108 个 batch,训练只完成第 100 个 —— 恢复时若直接从第 109 个开始,就跳过了八个 batch。

每参数要存的是权重 2 + 主权重 4 + 两份 Adam 状态 8 = 14 bytes,Qwen3-8B 的 checkpoint 约 115 GB。

恢复时可能要改并行布局:TP4 → TP8 的重分片按全局行坐标做([12288,4096][12288,4096] 的权重四分每片 3072 行、八分每片 1536 行;目标 rank rr 读旧分片 ⌊r/2⌋\lfloor r/2\rfloor,偶取前半、奇取后半)。该矩阵在 checkpoint 里的全部状态是 96×(1+2+2+2)=67296\times(1+2+2+2)=672 MiB。权重的内容与顺序不变,变的只是各卡负责的行范围。

5.2 同步 vs 异步保存:提交(commit)才算可用#

  • 同步保存:先生成内容固定的快照,写完再继续训练 —— 前台停顿 = 写完整份的时间。
  • 异步保存:快照复制到独立缓冲后训练就恢复,后台再写。但训练恢复执行时快照可能还没写完 —— 只有写入并提交完成后,这份 checkpoint 才能用于恢复。

反直觉三:异步保存的调用返回 ≠ 已有进度可恢复。 一个算例:每份 112 GB,存储写入 7 GB/s(一份 16 s)。每 20 s 捕获一份、各花 0.5 s 复制,则两份分别在 36.5 s、56.5 s 提交。第 50 s 故障时只能恢复到第 20 s,重做 30 s。 换 20 GB/s 的存储,两份在 26.1、46.1 s 提交,同一故障可恢复到第 40 s,只重做 10 s。 前台暂停都是 1 秒,重做量却是 30 s vs 10 s —— 差别全在后台写入的完成时刻。 另外要注意:数据文件之外还有元数据提交(实测保存 API 约 7 ms 返回,恢复元数据约 48 ms 才提交;提交前进程终止,恢复程序会选上一份完整的 checkpoint)。

5.3 最优保存间隔:τ∗=2c/λ\tau^*=\sqrt{2c/\lambda}#

设每次保存耗时 cc、保存之间完成 τ\tau 秒有用训练、作业故障率 λ\lambda、恢复耗时 rr,单位有用训练时间的附加成本:

L(τ)=cτ+λτ2+λr ⟹ τ∗=2cλL(\tau)=\frac{c}{\tau}+\lambda\frac{\tau}{2}+\lambda r\ \Longrightarrow\ \tau^*=\sqrt{\frac{2c}{\lambda}}

(第一项是保存频率的代价,第二项是故障平均落在间隔中点、丢半个间隔,第三项是恢复次数 × 恢复时长。两项一减一增,所以有最优点。)

实测输入:Meta 统计的研究集群,1024 卡作业平均 7.9 小时中断一次,中断率与卡数成正比(≈每卡 337 天一次故障);任一卡故障即中断整个作业。c≈16.4c\approx16.4 s(115 GB @ 7 GB/s)、r=120r=120 s → τ∗≈965\tau^*\approx965 s,约 16 分钟。

有用训练间隔 保存成本 重做成本 恢复成本 合计
300 s 5.5% 0.5% 0.4% 6.4%
900 s 1.8% 1.6% 0.4% 3.8%
1800 s 0.9% 3.2% 0.4% 4.5%

最低点比较平缓,可以选附近方便的间隔。 两个可导出的推论:卡数翻倍 → λ\lambda 翻倍 → τ∗\tau^* 缩短为 1/21/\sqrt2;保存速度翻倍 → cc 减半 → 同样缩短 1/21/\sqrt2。

损失尖峰要走同一个模型。 尖峰不损坏硬件,但处理方式相同(丢弃参数、回到快照),于是

L(τ)=cτ+(λhw+λspike)(τ2+r),τ∗=2cλhw+λspikeL(\tau)=\frac{c}{\tau}+(\lambda_{\mathrm{hw}}+\lambda_{\mathrm{spike}})\left(\frac{\tau}{2}+r\right),\qquad \tau^*=\sqrt{\frac{2c}{\lambda_{\mathrm{hw}}+\lambda_{\mathrm{spike}}}}

翻转条件由两个发生率的相对大小给出:尖峰越频繁最优周期越短;λspike≫λhw\lambda_{\mathrm{spike}}\gg\lambda_{\mathrm{hw}} 时保存周期几乎只由数值稳定性决定,而不是硬件可靠性。

5.4 掉队者:步时间由最大值决定,不是均值#

同步数据并行每步要等最慢的卡,步时间是 NN 个样本的最大值。NN 个独立正态样本最大值的期望约为 μ\mu 加若干倍 σ\sigma:

卡数 E[max⁡]−μE[\max]-\mu(倍 σ\sigma) 每步(σ=2%\sigma=2\%) 每步(σ=5%\sigma=5\%)
8 1.42 54.3 s 56.5 s
48 2.23 55.1 s 58.6 s
1024 3.25 56.2 s 61.3 s

无波动时每步约 52.8 s。 卡越多,最大值的期望越大:48 卡在 σ=5%\sigma=5\% 时每步多约 5.8 s,吃掉本方案 14.4 s 余量的四成。慢卡可以从通信等待里检测出来:一张卡在梯度桶 AllReduce 上等的时间 = 步结束时刻 − 自己算完的时刻。

三种响应,各有代价(这段最容易被追问):

响应 做法 代价
等待 本步按慢卡结束 一张 μ+3σ\mu+3\sigma 的卡:σ=2%\sigma=2\% 时 8 卡多等约 1.72 s,48 卡多等约 0.81 s,1024 卡时其余卡的最大值本身已超过 μ+3σ\mu+3\sigma,多等为零
重分配 把慢卡的活摊给其余 N−1N-1 张 均值变 μN/(N−1)\mu N/(N-1):48 卡约 56.2 s、8 卡约 61.6 s,都高于同一 σ\sigma 下等待的期望(55.1 / 54.3 s) —— 摊派让每卡多做 1/(N−1)1/(N-1),卡越少越不划算
驱逐 移出作业、从上一个 checkpoint 重启 τ/2+r\tau/2+r:τ=600\tau=600 s、r=120r=120 s 时约 420 s ≈ 八步。一次 μ+3σ\mu+3\sigma 抖动只损失几秒 —— 只有慢卡持续多步时驱逐才值得

真实世界的规模感(这几个数很有说服力):Meta 集群上 1024 卡作业平均无故障 7.9 小时,8 卡作业 47.7 天;MegaScale 报告约 0.5% 的机器明显变慢。

中断到底值多少钱(MiMo-V2.6 的公开看板):Pro 与 Flash 的 RL 后训练分别约 260 万和 90 万美元(每小时约 2.06 万和 1.03 万美元)。Pro 共重启 14 次,中断区间累计约 30.1 h,占总运行时间 23.6%,折合约 61.9 万美元;其中一次 infra 错误未被及时检出,直到完成第 17 步后才从第 15 步重训,第 16、17 步的约 4 h 随之作废。重启原因主要是三类:硬件(显存双比特错误)、服务(容器崩溃、评分服务网络不通)、内存不足(推理时 KV 耗尽、训练时专家负载不均导致显存溢出、打包时主机内存不足)。

一段能讲出判断力的话:把中断总耗时砍一半,同样工作少用约 15 h,按费率省约 31 万美元。 这可以来自两条完全不同的路 —— ① 消除反复触发的故障(MiMo 团队就调整过训练并行方式、降低激活显存以容纳专家负载峰值);② 更早发现、更快恢复,用更近的 checkpoint 减少重做(缩短 rr 或丢掉的间隔)。这两条比「买更快的卡」更常是被忽略的 ROI 来源。

6. RL 训练的系统侧(与 02-post-training/ 互补)#

前三节假定数据已给定。RL 让模型参与产生下一批数据 —— 系统从「处理固定数据的流水」变成反馈循环,新问题集中在三处:各阶段处理速度、切换时的状态重叠、以及样本究竟由哪一版策略产生。

6.1 三阶段流水:扩哪个?#

生成 → 验证 → 学习是首尾相接的三道工序,设三者能力为 rg,rv,rlr_g,r_v,r_l、验证后符合策略版本的比例为 qq:

reffective=min⁡(rl, qmin⁡(rg,rv))r_{\mathrm{effective}}=\min\left(r_l,\ q\min(r_g,r_v)\right)

算例:生成 12 条/s、验证 6 条/s、学习 8 条/s、验证后保留 75% → 实际进学习的是 6×0.75=4.56\times0.75=\mathbf{4.5} 条/s。把生成翻倍到 24 条/s,进学习仍是 4.5 条/s(验证是瓶颈);把验证翻倍到 12 条/s,进学习到 9 条/s,此时学习端的 8 条/s 成为瓶颈。 所以 RL 训练慢的第一诊断不是「卡不够」,而是先找出当前最慢的那道工序。

6.2 训推一致性:三个策略版本#

πθμ=πθπold⋅πoldμ\frac{\pi_\theta}{\mu}=\frac{\pi_\theta}{\pi_{\mathrm{old}}}\cdot\frac{\pi_{\mathrm{old}}}{\mu}
  • μ\mu:实际行为分布(采样时真正用的策略,含版本滞后、采样配置、跨引擎差异);
  • πold\pi_{\mathrm{old}}:本轮优化起点;
  • πθ\pi_\theta:当前被优化的策略。

右边第一项是本轮参数优化带来的概率变化,第二项是采样分布与起点策略的差异。这两项必须分开,否则训练端会把「计算差异」当成「策略变化」。

一个能把数字背下来的算例:若行为策略与当前策略对该 token 的 logprob 都是 −3-3,比值是 1;若误用重新算出的 logprob(−3.25-3.25)代替行为策略原先记录的值,比值变成 exp⁡(0.25)≈1.28\exp(0.25)\approx1.28 —— 参数没变,样本权重却放大了约 28%。 在 PPO 一类更新里,概率比既决定优势对梯度的权重,也参与裁剪判断 —— 所以误用会同时造成权重错误和错误触发裁剪;而用序列概率比时各位置 logprob 之差会相加,轨迹越长积累的偏差越大。

为什么第二项天然不等于 1:生成端逐 token decode 并读 KV,训练端整段轨迹并行输入、重算概率再反向 —— 不同的注意力实现、归约次序、batch 形状、权重与 KV 精度都会让 logprob 不同(第 5 章实测过:同一行输入按不同段数归约,平方和相差 166 个 ULP,而段数取决于当时的 batch 大小)。采样配置也要存:温度、top-k / top-p 会改变 Softmax 分布,所以应保存变换后的概率及采样配置;训练端要用相同的分词、对话模板、位置编号、掩码、EOS/截断位置重建输入。

MoE 把数值差异放大成路径差异。 专家选择是离散的:top-2 下,专家分数 0.500 / 0.301 / 0.300 选专家 1、2;第三个分数因计算次序变成 0.302,就改选 1、3 —— 极小的分数变化换了另一个专家的权重矩阵,影响沿后续层继续传播。解法是路由重放(Routing Replay):记录生成时选中的逻辑专家 ID,训练时按这些 ID 选专家,离散路径重放、数值用当前权重算。记录成本很小(Qwen3-30B-A3B 48 层 top-8、8192 token、uint16 → 8192×48×8×2=6 MiB8192\times48\times8\times2=\mathbf{6\ MiB}),但记录要跟着 token 走(序列打包改变位置、上下文切分改变卡、重计算要再读同一层)。NVIDIA 的 R3 验证报告四组开关各训 100 步:开启后对数概率误差中位数均下降,每步总耗时中位数略长,训练奖励没有一致提高 —— 减少的是不一致,不是直接买来奖励。

6.3 异步训练:盈亏平衡点是丢弃率#

同步循环的周期是「生成 + 学习 + 权重同步」。异步让生成下一批与学习上一批重叠:一个具体例子,生成 40 s、学习 16 s、同步 4 s,同步周期 60 s;异步稳态周期 max⁡(40,16)+4=44 s\max(40,16)+4=\mathbf{44\ s}。

但生成得更早意味着样本可能来自较旧的权重。 设每批 BB 条等效轨迹、异步后保留比例 qq,则有效吞吐从 B/60B/60 变成 qB/44qB/44,异步更快要求

q>4460≈73.3%q>\frac{44}{60}\approx\mathbf{73.3\%}

q=0.8q=0.8 时有效样本吞吐提高约 9%;q=0.7q=0.7 时虽然每批更早结束,有效吞吐反而减少约 5%。 —— 异步不是默认更优,它有一个明确的盈亏平衡点。

长轨迹还要考虑恢复:生成期间的持续 tt 的轨迹能完整生成的概率是 e−λte^{-\lambda t},轨迹越长越容易被中断丢掉,于是进学习的数据比例下降、并产生长度偏移(长轨迹被系统性丢弃)。做法是逐 token 记录生成进度和策略版本,恢复后继续生成同一条轨迹。注意 KV 是「给定权重对前缀的执行结果」:相同权重可以续用已保存的 KV,换权重就必须重新 prefill —— Qwen3-8B 的 8K BF16 KV 是 1.125 GiB,所以恢复方案要在取回时间与重建时间之间比。

7. 把账合起来:一阶完成时间模型#

Tfinish=T0+⌈Db⌉ts(1+L)T_{\mathrm{finish}}=T_0+\left\lceil\frac{D}{b}\right\rceil t_s(1+L)

(tst_s 是每步训练时间,已含单卡计算、通信等待与输入等待;LL 是保存与恢复的附加比例;T0T_0 是计划性停顿。)章首任务(Qwen3-8B 全参、100B token、8,192 序列长、30 天期限、预留 5 天):

设计项 32 卡方案 48 卡方案
每卡预计显存占用 / 可用 13.8 / 22 GiB 12.5 / 22 GiB
每步单卡计算 78.3 s 52.2 s
每步 ZeRO-3 链路时间 17.9 s 12.0 s
每步通信等待 0.08 s 0.08 s
每步输入等待 0.5 s 0.5 s
每步训练时间 78.9 s 52.8 s
保存+重做+恢复附加 1.02% 1.08%
最终完成时间(含 5 天预留) 34.3 天 24.6 天

结论:选 48 卡。 两者容量都够;32 卡超过 30 天期限被排除;48 卡约 24.6 天,余约 5.4 天。增加卡数的主要价值是减少每卡承担的工作,额外故障成本只抵消很小一部分收益(附加比例从 1.02% 到 1.08%,只差 0.06 个百分点)。

余量怎么用(这是设计题真正的答案)。 48 卡每步上限 ts≤25×8640031790×1.0108≈67.2t_s\le\frac{25\times86400}{31790\times1.0108}\approx67.2 s,本方案 52.8 s,余量约 14.4 s/步。固定单卡计算 52.2 s、输入等待 0.5 s,则总通信等待最多可到约 14.5 s,而实际只有 0.08 s —— 即使预取完全失效、12.0 s 链路时间全部暴露,每步约 64.8 s,仍在上限内。

换硬件用 Amdahl 判:

T′T=1−f+fr\frac{T'}{T}=1-f+\frac{f}{r}

(ff 是该资源占原每步耗时的比例,rr 是能力倍数。)本例暴露通信只占每步 0.14% —— PCIe 带宽减半,每步从 52.8 s 增到 52.9 s,几乎不变。反过来,单卡计算效率从 40% 降到 30%,单卡计算变 69.6 s、每步约 70.2 s,直接超过 67.2 s 的目标。

所以对这套方案,决定能否按期完成的是单卡计算效率,不是带宽。 带宽只有在预取失效、通信整段暴露时才变成 ff 较大的那一项。「更强的单项指标是否值得买,取决于它能减少多少完成时间或卡数」 —— 这就是 Amdahl 在采购决策上的用法。

最后一道外推:稠密模型粗略算量 F=6NDF=6ND,固定 D=20D=20T token、40% MFU、期限 TT,则

p≥⌈6NDPηT⌉p\ge\left\lceil\frac{6ND}{P\eta T}\right\rceil

对 1T 参数模型、90 天期限、16,384 张卡:A100 / H100 / B200 在 40% MFU 下分别约需 679 / 214 / 94 天 —— 三者都超期(B200 也超约 4 天,需增至约 17,147 张)。只有 MFU 达到 50%,B200 才降到约 75 天,余下 15 天可分配给停顿与恢复。MFU 差 10 个百分点,就决定了 16,384 张 B200 能否在 90 天内训完 1T。

注意固定数据量时的尺度:参数量增至五倍 → 计算量增至五倍;若同时取 D=20ND=20N,则 F=120N2F=120N^2,参数量增至五倍,计算量增至 25 倍。规模问题的答案由模型和数据共同决定。

常见面试问法#

Q:训练一个 8B 模型要多少显存?为什么单卡能推理不能训练? A:常驻训练状态每参数 16 bytes —— BF16 权重 2 + BF16 梯度 2 + FP32 主权重 4 + Adam 一阶矩 4 + 二阶矩 4。8.19B 参数 → 16N≈16N\approx 131.1 GB,约等于八份 BF16 权重。推理只要 16.4 GB,所以能推理的卡远不够训练。要补两点:① 「混合精度」不规定梯度格式 —— 梯度改 FP32 就是 18 bytes/参数;② 峰值不等于各项之和,而是同一时刻的最大重叠 Mpeak=max⁡t[Mpersistent+Mactivation+Mtemporary]M_{\mathrm{peak}}=\max_t[M_{\mathrm{persistent}}+M_{\mathrm{activation}}+M_{\mathrm{temporary}}],8 GiB 激活与 4 GiB 收集缓冲同时出现要 12 GiB,先释放激活再收集参数就能复用同一片空间。

Q:ZeRO 的三个阶段各分片什么?加卡能把显存降下来吗? A:M0=16NM_0=16N、M1=4N+12N/dM_1=4N+12N/d、M2=2N+14N/dM_2=2N+14N/d、M3=16N/dM_3=16N/d;d=8d=8 时每卡 122.1 / 42.0 / 28.6 / 15.3 GiB。ZeRO-1 分 FP32 主权重与两份 Adam 状态(12 bytes/参数),ZeRO-2 再分梯度(只剩 BF16 权重完整复制),ZeRO-3 连权重也分。执行是 AllGather 取参数 → 前向/反向 → ReduceScatter 分梯度 → 各更各的。 但要提醒一个坑:分片数翻倍,显存峰值并不减半。 分片只缩小常驻部分;执行时完整权重缓冲区与原有分片同时存在,而缓冲区大小由模块本身决定、与卡数无关。FSDP2 的小实验量到:进程 2→4,常驻 18→9 MiB(减半),峰值只从 39→30 MiB(减 23%),因为执行中另有约 21 MiB 其他张量。所以「加卡能不能装下」必须按峰值算。

Q:显存不够,你会先加卡、加重计算,还是卸载? A:先按容量筛可行方案,再比每步耗时 —— 三种办法减少的东西不同(分片减少重复保存的状态、重计算减少前向到反向之间保存的中间值、卸载把暂时不用的状态挪到主机内存),增加的工作也不同(收集/归约、重建计算、传输)。具体算例:RTX 4090 每卡可用 22 GiB,ZeRO-3 八卡每卡 15.3 GiB,若激活与缓冲区同要 10 GiB 就超 3.3 GiB —— 可以用重计算省这 3.3 GiB,或加到十六卡把常驻降到 7.6 GiB,前者增计算、后者增卡数并改通信组。 重计算的收益取决于生命周期:Qwen3-8B 的 MLP 里重建 h=a⊙uh=a\odot u 每层省 10 MiB(九层阶段 135 → 45 MiB),因为重建只需一块 6 MiB 工作区逐层复用,而保留它要每个等待反向的 micro-batch 各留一份(四个并行等待就省 360 MiB)——所以重计算和流水调度是同一件事的两面。 卸载的判据是 Twait=max⁡(0,V/B−W)T_{\mathrm{wait}}=\max(0,V/B-W),一个漂亮的例子:梯度该在 CPU 还是 GPU 转 FP32?临界带宽 B∗=13(1/Cc−1/Cg)≈B^*=\frac{1}{3(1/C_c-1/C_g)}\approx 147 GB/s,高于 PCIe 5.0 x16 的 64 GB/s,所以经 PCIe 的卡都该先传 BF16、在 CPU 上转;只有 GH200 那种 NVLink-C2C(450 GB/s)才轮到 GPU 转换赢。

Q:流水线并行的气泡怎么算?1F1B 是不是一定比填满排空好? A:填满排空气泡是 (p−1)(tf+tb)(p-1)(t_f+t_b),利用率 u=m/(m+p−1)u=m/(m+p-1)(p=4,m=8p=4,m=8 时约 72.7%)。但「1F1B 更好」只在一根轴上成立 —— 同一算例中填满排空 337 ms、1F1B 347 ms(慢约 3%),因为交错引入依赖等待;而 1F1B 的激活与收发缓冲峰值从 2.57 GB 降到 0.97 GB(−62%)。 所以先确定约束在时间还是容量:只给 1 GB 中间结果时只有提前反向的调度能跑;容量充裕时 337 ms 的填满排空反而更早完成。压气泡的三种手段各有代价:交错式 1F1B 298 ms(前向消息 24→56 次)、零气泡 ZB-H1 283 ms(把 dWdW 延后填空隙,靠后阶段激活峰值升到约 1,226 MiB)、DualPipe 306 ms(每卡参数两份)。没有任何一种在两轴上同时占优:时间最短的是零气泡,最大单阶段峰值最低的是 1F1B。而且 mm 越大压气泡越不值(16 个 micro-batch 时 1F1B 气泡占比已从 37.5% 降到 18.8%)。

Q:通信是不是训练的主要瓶颈? A:要看暴露了多少,而不是链路时间有多长。 通信大多能被计算盖住:ZeRO-3 逐层预取参数、反向一结束就归约梯度,一层 0.39 GB 只要约 12 ms,远短于该层计算。本例一步链路时间 12.0 s,但真正藏不住的只剩第一步 AllGather 和最后一步 ReduceScatter,合计约 0.08 s(都作用于 1.24 GB 的词嵌入)。 这给优化划了上限:暴露只有 0.08 s,消除它最多省 0.08 s,若还差 0.5 s 就得动别处。还有个反直觉的判据:通信处理时间更短,训练步不一定更短 —— 72 MiB 整桶 AllReduce 约 4.55 ms 放不进 2 ms 空隙,切成三个子桶总时间反而升到 4.59 ms,但每桶都能在计算结束前传完,所以训练步更短。决定训练步的是通信何时完成,不是它花了多久。

Q:checkpoint 该多久存一次? A:τ∗=2c/λ\tau^*=\sqrt{2c/\lambda}(cc 是每次保存耗时、λ\lambda 是故障率)。实测输入:Meta 集群 1024 卡作业平均 7.9 小时中断一次,中断率与卡数成正比(≈每卡 337 天一次);115 GB checkpoint 按 7 GB/s 保存 c≈16.4c\approx16.4 s;恢复 r=120r=120 s → τ∗≈965\tau^*\approx965 s,约 16 分钟。三档对照:300 s 合计 6.4%、900 s 合计 3.8%、1800 s 合计 4.5% —— 最低点很平缓,可以选附近好用的间隔。 两个可导出的推论:卡数翻倍(λ\lambda 翻倍)→ 最优间隔缩短为 1/21/\sqrt2;保存速度翻倍(cc 减半)→ 同样缩短 1/21/\sqrt2。 还要补三点:① 损失尖峰要走同一个模型,λ\lambda 换成 λhw+λspike\lambda_{\mathrm{hw}}+\lambda_{\mathrm{spike}} —— 尖峰越频繁,保存周期越短,λspike≫λhw\lambda_{\mathrm{spike}}\gg\lambda_{\mathrm{hw}} 时保存周期几乎只由数值稳定性决定;② 恢复点由「提交」决定,不是由保存调用返回决定 —— 同一 1 秒前台暂停可以对应重做 30 s 或 10 s,差别全在后台写入何时完成;③ checkpoint 不只有权重 —— 还要步数、学习率状态、随机状态、下一批数据组成,以及打包器里剩余的 token(丢了它就用后续数据补齐,改变了序列前缀、注意力关系和标签)。

Q:训练时某张卡一直慢,你怎么处理? A:先意识到步时间由最大值决定,不是均值。 NN 张卡时步时间的计算部分是 NN 个样本的最大值:N=8/48/1024N=8/48/1024 时 E[max⁡]−μE[\max]-\mu 约为 1.42/2.23/3.251.42/2.23/3.25 倍 σ\sigma。σ=5%\sigma=5\%、48 卡时每步多约 5.8 s,吃掉本方案 14.4 s 余量的四成。慢卡可以从通信等待里检出来:一张卡在梯度桶 AllReduce 上等的时间 = 步结束时刻 − 自己算完的时刻。 三种响应各有代价,而且结论反直觉:① 等待 —— 一张 μ+3σ\mu+3\sigma 的卡,σ=2%\sigma=2\% 时 8 卡多等约 1.72 s、48 卡约 0.81 s,1024 卡时其余卡的最大值本身已超过它,多等为零;② 重分配 —— 把活摊给其余 N−1N-1 张,48 卡约 56.2 s、8 卡约 61.6 s,都高于等待(55.1 / 54.3 s),因为每卡多做 1/(N−1)1/(N-1) 的增量超过了一张慢卡的等待,卡越少越不划算;③ 驱逐 —— 从上一个 checkpoint 重启,代价约 τ/2+r\tau/2+r(600 s 间隔 + 120 s 恢复 ≈ 420 s ≈ 八步),一次 μ+3σ\mu+3\sigma 的抖动只损失几秒,只有慢卡持续多步才值得驱逐。

Q:RL 训练慢,是不是该加生成资源? A:先找出最慢的那道工序。 生成 → 验证 → 学习是首尾相接的三道工序:reff=min⁡(rl, qmin⁡(rg,rv))r_{\mathrm{eff}}=\min(r_l,\ q\min(r_g,r_v))。生成 12 条/s、验证 6 条/s、学习 8 条/s、验证后保留 75% → 进学习只有 4.5 条/s;生成翻倍到 24 条/s 仍是 4.5 条/s,验证翻倍才把瓶颈推到学习端。 RL 还有两个纯系统侧的问题:① 训推一致性 —— 三个策略版本 μ/πold/πθ\mu/\pi_{\mathrm{old}}/\pi_\theta,概率比要分解成 πθπold⋅πoldμ\frac{\pi_\theta}{\pi_{\mathrm{old}}}\cdot\frac{\pi_{\mathrm{old}}}{\mu};误用重算的 logprob 代替行为策略记录的值,比值会从 1 变成约 1.28,样本权重放大 28%,在 PPO 里还会错误触发裁剪。② 异步有盈亏平衡点 —— 生成 40 s、学习 16 s、同步 4 s 时同步周期 60 s、异步 44 s,但 q>44/60≈73.3%q>44/60\approx73.3\% 异步才划算(q=0.7q=0.7 时有效吞吐反而降约 5%)。

易错点#

易错说法 问题 应该说
「分片数翻倍,显存峰值就减半」 只看了常驻状态 分片只缩小常驻部分;执行时完整权重缓冲区与分片并存。FSDP2 小实验:常驻 18→9 MiB(减半),峰值只 39→30 MiB(−23%)
「8B 训练要 131 GB,那 16 张 80G 卡就够」 只算了常驻状态 还要算 Mpeak=max⁡t[常驻+激活+临时]M_{\mathrm{peak}}=\max_t[\text{常驻}+\text{激活}+\text{临时}],以及通信组与拓扑带来的约束
「混合精度训练一定用 FP16 梯度」 把「混合精度」当成规定 「混合精度」不规定梯度格式;本例梯度是 BF16,改 FP32 就是 18 bytes/参数
「FP8 训练能把状态降到 1 byte/参数」 混淆了「操作数副本」与「常驻状态」 FP8 只改送入矩阵单元的权重副本(16.4 → 约 8.2 GB);主权重、梯度、优化器状态仍高精度保存
「1F1B 比填满排空好」 只看了一根轴 1F1B 时间上慢约 3%(347 vs 337 ms),只在显存轴上占优(2.57 → 0.97 GB)。先确定约束在时间还是容量
「零气泡调度全面最优」 忽略了激活驻留 零气泡完成时间最短(283 ms),但靠后阶段的激活峰值升到约 1,226 MiB;最大单阶段峰值最低的是 1F1B
「通信更快 → 训练步更短」 把「处理时间」当成「完成时刻」 三个小桶总时间 4.59 ms > 整桶 4.55 ms,但训练步更短 —— 决定训练步的是通信何时完成
「链路时间是通信瓶颈」 忽略了重叠 一步链路 12.0 s,真正暴露的只有 0.08 s(首尾两次词嵌入通信)。优化上限就是这 0.08 s
「异步保存的 API 返回了,进度就保住了」 漏了「提交」 只有写入并提交完成后 checkpoint 才可用。同一 1 秒前台暂停,可对应重做 30 s 或 10 s
「checkpoint 存权重就够了」 漏了优化器状态与数据位置 还要一阶/二阶矩、步数、学习率状态、随机状态、下一批数据组成,以及打包器里剩余的 token(丢了会改变序列前缀与标签)
「保存间隔越短越安全」 忽略了保存成本也有上限 τ∗=2c/λ\tau^*=\sqrt{2c/\lambda}:300 s 总附加 6.4%、900 s 3.8%、1800 s 4.5% —— 两边都贵,中间最省
「卡数翻倍、batch 也跟着翻倍,完成时间就减半」 撞上临界 batch size 超过 BnoiseB_{\mathrm{noise}} 后步数不再明显下降:batch 从 315 万放大到约一亿(1536 卡),时间只从 19.4 天降到 12.1 天,上限 1.64×。只有保持 batch 不变的强扩展不受此限
「慢卡就把它踢掉重来」 没算驱逐成本 驱逐代价 τ/2+r\tau/2+r(600 s + 120 s ≈ 八步),一次 μ+3σ\mu+3\sigma 抖动只损失几秒;而重分配在卡少时反而比等待更贵(8 卡 61.6 s vs 等待 54.3 s)
「RL 训练慢就加生成资源」 搞错了工序 reff=min⁡(rl,qmin⁡(rg,rv))r_{\mathrm{eff}}=\min(r_l,q\min(r_g,r_v)) —— 先找最慢的那道工序;生成翻倍常常毫无收益
「异步 RL 一定比同步快」 漏了过期样本 有明确盈亏平衡点:同步 60 s / 异步 44 s 时需 q>73.3%q>73.3\%,q=0.7q=0.7 反而慢约 5%
「MoE 容量因子取大一点就不会丢 token」 忽略了补零成本 c=1.0c=1.0 丢 25% 的 dispatch;c=2.0c=2.0 不丢但补零占执行量 50%。这是一个没有免费选项的旋钮
把本章的 48 卡方案说成「我们线上就是这么训的」 触碰仓库硬规矩 这是书里的设计算例;你的真实 RL 经验是 HKJC 的单轮 RLVR、小规模,两者不是一回事,要自己先说这条边界

自测#

1. 有人问你「训一个 8B 模型,单卡 80G 够不够」。请给出完整的容量分析,并说明为什么「常驻状态能放下」不等于「能训练」。

答案

① 先算常驻训练状态:混合精度 Adam 下每参数 16 bytes —— BF16 权重 2 + BF16 梯度 2 + FP32 主权重 4 + Adam 一阶矩 4 + 二阶矩 4。Qwen3-8B(8.19B 参数):

Mpersistent=16N≈131.1 GB≈122.1 GiBM_{\mathrm{persistent}}=16N\approx131.1\ \mathrm{GB}\approx122.1\ \mathrm{GiB}

一张 80 GB 卡(约 74.5 GiB)连常驻状态都放不下,所以「单卡 80G」直接不够。

② 但真正的答案是「要按峰值算,不是按常驻算」:

Mpeak,i=max⁡t[Mpersistent,i(t)+Mactivation,i(t)+Mtemporary,i(t)]M_{\mathrm{peak},i}=\max_t\left[M_{\mathrm{persistent},i}(t)+M_{\mathrm{activation},i}(t)+M_{\mathrm{temporary},i}(t)\right]

同一时刻驻留的还有激活与临时缓冲区(参数收集缓冲、重计算工作区等)。关键在「同一时刻」:8 GiB 激活与 4 GiB 收集缓冲同时出现就要 12 GiB;先释放激活再收集参数,同一片空间可以复用。所以容量优化有两类手段 —— 减小张量(精度、分片)或减少同时驻留的数量(调度、重计算、卸载)。

③ 还要看分片以后是不是真的够了:d=8d=8 时 ZeRO-3 每卡 16N/d≈15.316N/d\approx15.3 GiB,看似宽裕;但执行时要收集完整权重,完整权重缓冲区与原有分片同时存在,而缓冲区大小由模块决定、与卡数无关。FSDP2 的小实验:进程 2→4,常驻 18→9 MiB(减半),峰值只从 39→30 MiB(减 23%)。所以「加卡就能装下」这句话必须按峰值验证。

④ 最后一层:显存够只是能不能开始;还有通信组与拓扑的约束(卡间有没有 NVLink/P2P 会决定集合通信走哪条路),以及算不算是按期跑完 —— 那要另算每步耗时。

2. 你的训练任务显存不够,缺约 3.3 GiB。同事说「加到两倍卡就行」。请给出两种方案并说明各自的代价。

答案

用第 10.2.4 节的算例:RTX 4090 每卡可用 22 GiB,Qwen3-8B ZeRO-3 在八卡上每卡常驻约 15.3 GiB,留给其他张量与缓冲区约 6.7 GiB;若激活与临时缓冲区同时需要 10 GiB,总计约 25.3 GiB,超出约 3.3 GiB。

方案 A:八卡不变,用重计算省下这 3.3 GiB。

  • 做法:把「前向产生、反向才用」的中间值改成反向需要前重建。例如 MLP 里重建 h=a⊙uh=a\odot u([128,12288][128,12288] FP32 = 6 MiB)+ 两个归一化加权输出(各 2 MiB),每层省 10 MiB;九层流水阶段从 135 MiB 降到 45 MiB。
  • 关键性质:重建只需一块工作区逐层复用(最大 6 MiB);而保留它要每个等待反向的 micro-batch 各留一份 —— 所以等待反向的 micro-batch 越多,重计算省得越多(四个并行等待就省 4×90=3604\times90=360 MiB)。
  • 代价:增加重建计算;只有它落在非关键路径上时才不延长每步时间。

方案 B:加到十六卡,把常驻状态从 15.3 GiB 降到约 7.6 GiB,总计降到约 17.6 GiB。

  • 代价:多八张卡;而且分片数改变会改变通信组(AllGather/ReduceScatter 的规模与拓扑变化),要重新验证通信能否被计算盖住。
  • 注意:加卡不会把峰值等比降下来 —— 降的是常驻那部分,执行时的完整权重缓冲区大小不变。

正确的决策顺序:先用容量筛出可行方案,再比这些方案的每步耗时。 时间有余量就用重计算省卡;时间紧张就加卡并重新核对通信预算。另外别忘了:如果显存已经足以保存所需张量,就不必再为省显存而增加额外工作 —— 这是第 10.6.2 节调优时的原则。

3. 有人主张「流水线用零气泡调度,因为它完成时间最短」。你会怎么回应?

答案

完成时间最短是对的,但这不是充分理由 —— 流水线调度要在两根轴上同时看。

同一算例(四阶段、8 个 micro-batch、前向 10 ms、反向 20 ms、边界传输 1 ms、参数更新 1 ms):

调度 完成时间 每卡空闲 阶段 0 激活峰值
填满排空 337 ms 96 ms 1,840 MiB(阶段 3 达 2,448)
1F1B 347 ms 106 ms 921 MiB
交错式 1F1B 298 ms 57 ms 1,329 MiB
零气泡 ZB-H1 283 ms 42 ms 921 MiB(阶段 3 达 1,226)
DualPipe 306 ms 65 ms 1,228 MiB(阶段 1、2 达 1,612)

零气泡的机制是把反向拆成两段:dXdX(输入梯度)在关键路径上、要尽早算;dWdW(权重梯度)只进本层累积,可以延后填进空隙。1F1B 气泡 (p−1)(TF+TB+TW)(p-1)(T_F+T_B+T_W) → ZB-H1 (p−1)(TF+TB−TW)(p-1)(T_F+T_B-T_W) → ZB-H2 (p−1)(TF+TB−2TW)(p-1)(T_F+T_B-2T_W)(本例 TF=TB=TWT_F=T_B=T_W 时 ZB-H2 下界为 0)。

代价就在那张表的最后一列:靠后阶段的 dWdW 比 dXdX 晚执行,那段激活要按完整一份保留 —— 阶段 3 的峰值从 1F1B 的 308 MiB 升到约 1,226 MiB(约 4 倍)。 所以:

  • 完成时间最短的是零气泡,最大单阶段峰值最低的是 1F1B —— 没有任何一种调度在两轴上同时占优;
  • 选择的第一步是确定约束在哪边:容量紧张时先按峰值排除不可行的调度,再比时间;容量充裕时零气泡/交错式的收益才拿得到;
  • 还要看 mm:micro-batch 翻倍到 16 时,1F1B 的气泡占比已从 37.5% 降到 18.8%,拆细带来的相对收益在缩小,而交错式增加的通信次数、零气泡增加的激活驻留、DualPipe 增加的参数量都不随 mm 缩小;
  • 最后还有链路争用这条边界:交错式每个 micro-batch 多出的四次跨界传输,在链路已被梯度归约占满时会直接延长关键路径。

一句话回应:「零气泡在时间轴上确实最好,但它把成本挪到了靠后阶段的激活驻留上 —— 我会先确认显存约束在哪一侧,再在可行集合里选时间最短的那个。」

4. checkpoint 该多久存一次?请给出模型、一个真实量级的算例,以及你会主动补充的三个细节。

答案

模型:设每次保存耗时 cc、保存之间完成 τ\tau 秒有用训练、作业故障率 λ\lambda、恢复耗时 rr,

L(τ)=cτ+λτ2+λr⟹τ∗=2cλL(\tau)=\frac{c}{\tau}+\lambda\frac{\tau}{2}+\lambda r\quad\Longrightarrow\quad \tau^*=\sqrt{\frac{2c}{\lambda}}

三项分别是保存频率的代价、故障平均落在间隔中点导致丢半个间隔、恢复次数 × 恢复时长;前两项一减一增,所以有最优点。

真实量级算例:Meta 统计的研究集群上,1024 卡作业平均 7.9 小时中断一次,且中断率与卡数成正比(≈每卡 337 天一次),任一卡故障即中断整个作业。115 GB 的 checkpoint 以 7 GB/s 保存,c≈16.4c\approx16.4 s;设 r=120r=120 s:

τ∗=2×16.41/(7.9×3600)≈965 s≈16 分钟\tau^*=\sqrt{\frac{2\times16.4}{1/(7.9\times3600)}}\approx965\ \mathrm{s}\approx16\ \text{分钟}

三档对照:300 s 合计附加 6.4%、900 s 合计 3.8%、1800 s 合计 4.5% —— 最低点平缓,可选附近好用的间隔。两个推论:卡数翻倍使 λ\lambda 翻倍 → 最优间隔缩短为 1/21/\sqrt2;保存速度翻倍使 cc 减半 → 同样缩短 1/21/\sqrt2。

主动补充的三个细节:

① 损失尖峰也回滚,要走同一个模型。 数值不稳定导致的 loss spike 不损坏硬件,但处理方式相同(丢参数、回快照),所以 λ→λhw+λspike\lambda\to\lambda_{\mathrm{hw}}+\lambda_{\mathrm{spike}}。翻转条件由两个发生率的相对大小给出:尖峰越频繁最优周期越短;λspike≫λhw\lambda_{\mathrm{spike}}\gg\lambda_{\mathrm{hw}} 时保存周期几乎只由数值稳定性决定,与硬件可靠性无关。

② 恢复点由「提交」决定,不是由保存调用返回决定。 异步保存时,快照复制到独立缓冲后训练就恢复,但只有写入并提交完成后这份 checkpoint 才能用于恢复。同一个 1 秒前台暂停,可以对应重做 30 s 或 10 s —— 差别全在后台写入何时完成(112 GB @ 7 GB/s 时第 50 s 故障只能恢复到第 20 s;换 20 GB/s 存储则可恢复到第 40 s)。另外元数据提交也有延迟(实测保存 API 约 7 ms 返回,恢复元数据约 48 ms 才提交;提交前终止进程,恢复程序会选上一份完整 checkpoint)。

③ checkpoint 不只是权重。 还要一阶/二阶矩、步数、学习率状态、随机状态、下一批数据的组成,以及打包器里剩余的 token。特别是最后一项:若打包器留着 2,000 个 token、下一份数据给 6,192 个,丢了这 2,000 就只能用后续数据补齐,从而改变这条序列的前缀、注意力关系和标签。同理还有预取与训练进度的错位(加载器派任务到第 108 个 batch、训练只完成第 100 个,恢复时若从 109 开始就跳过八个 batch)。

5. 一个 48 卡的训练方案,每步预算 67.2 s,实测每步 52.8 s。有人提议把 PCIe 换成更新的接口。你支持吗?还有,如果要在这个任务上省时间,你会先动哪里?

答案

先算清这套方案的时间构成(Qwen3-8B 全参、100B token、8,192 序列长、48 卡):

项 时间
每步单卡计算 52.2 s
每步 ZeRO-3 链路时间 12.0 s
每步实际通信等待(暴露部分) 0.08 s
每步输入等待 0.5 s
每步合计 52.8 s
每步上限 67.2 s(余量约 14.4 s)

① 换更快的 PCIe:不支持。 用 Amdahl 形式的判据 T′/T=1−f+f/rT'/T=1-f+f/r(ff 是该资源占原每步耗时的比例):暴露的通信只占 0.14%,所以 PCIe 带宽减半时每个 micro-batch 的链路时间从 1.50 s 升到约 3.0 s(仍短于该 micro-batch 6.53 s 的计算),暴露部分翻倍到约 0.15 s,每步从 52.8 只增到 52.9 s,几乎不变。反过来,这项变化的收益也几乎为零 —— 买更快的接口在这套方案上买不到时间。

② 先动哪里:先确认单卡计算效率,再核对网卡配置。

  • 单卡计算效率是本方案的真正瓶颈。 计算占 52.2 s / 52.8 s ≈ 99%。若效率从 40% 降到 30%,单卡计算变 69.6 s、每步约 70.2 s,直接超过 67.2 s 上限。所以任何影响 kernel 效率的工作(算子融合、精度格式、并行切法)都直接反映在完成时间上。
  • 其次是网卡配置的稳健性(不是速度,是冗余):12.0 s 链路时间的前提是每卡一张 200 Gbit/s 网卡。若一台主机的八张卡共用一张网卡,环形集合通信的割集要承载每卡全部收发,48 卡每步约 385 GB 都走这张 25 GB/s 的卡,链路时间约 15.4 s,超过 14.5 s 的通信上限;预取一旦失效,每步约 68.1 s,方案超期。两种配置的差别是:共用网卡把按期完成押在「重叠一定生效」上,每卡一张则不依赖重叠。
  • 最后才是输入等待(0.5 s)与故障恢复附加(1.08%)。

③ 更大的方向:这套方案还有 14.4 s/步 的余量没被用掉。 与其优化已经不构成瓶颈的项,不如把这笔余量换成更少的卡数或更短的基础训练时间 —— 例如回到 32 卡方案就需要把单卡效率从 40% 提到约 47%(已高于 Llama 3 公开报告的最高 43%),这比加两台主机难得多。这也是第 10.6.2 节的原则:先算清预算,再决定是增卡还是提效率,而不是先买硬件。