跳转至

第 9 章 长周期训练工程:数据、容错与 Goodput

开场问题:一次 54 天的训练,硬件层面发生了四百多次中断。如果每次都从上一个检查点重来,实际有多少时间在真正训练?

9.1 数据管线

从对象存储读取分片,解压、tokenize、shuffle、packing、预取,最后喂给 GPU。工业训练里 tokenize 通常是离线做好的,在线只做读取和组装——否则 CPU 必然成为瓶颈。

三个硬要求:数据顺序必须可复现(否则续训会改变训练轨迹);全局 shuffle 要在分片层面做好(同一个来源的数据扎堆会造成局部分布偏移);预取深度要够,GPU 不能等数据。判断数据是否成为瓶颈,看 profiler 里 step 开头有没有固定的空洞。

9.2 检查点里必须有什么

只存权重的 checkpoint 是不能续训的。完整的检查点包括:模型权重、优化器状态、学习率调度器的位置、随机数状态、数据读取到哪里了、以及当时的并行布局。

工程上的两个要点:异步保存(先把状态拷到主机内存,后台再落盘,把 GPU 停顿从几分钟压到几秒);以及分布式检查点要支持 resharding,否则改变卡数就无法恢复。

9.3 故障谱系

千卡跑几个月,硬件故障是必然事件而非意外。常见的几类,按排查难度递增:掉卡和进程崩溃(明确报错,最好处理);NCCL 超时(某张卡没进集合通信,需要定位是谁);ECC 错误和静默数据损坏(可能只表现为某张卡结果异常);慢节点(不报错,只是慢,最阴险)。

对应的检测手段是心跳、集合通信的超时记录、定期的健康检查和逐卡吞吐监控。

9.4 Goodput 才是真正的目标函数

MFU 衡量的是"GPU 正常跑的时候效率如何",但付钱是按 wall-clock 付的。Goodput = 真正在产出有效训练进度的时间 ÷ 总时间,它把故障停机、检测时间、重启时间、回滚重算和检查点停顿全部算进去。

这个视角直接给出一个可优化的问题:检查点存得越频繁,日常停顿越多;存得越稀疏,故障后重算越多。已知保存耗时和集群平均无故障时间,最优保存间隔是可以算出来的,而不该拍脑袋定"每 1000 步"。

弹性训练(动态增减节点)听起来很美,实际用得远没有宣传的多——大多数团队的做法仍是准备热备节点加快速重启。

9.5 监控什么

至少这些:loss、梯度范数、参数范数、学习率、每卡吞吐、MFU、goodput、通信耗时、数据读取进度、MoE 的专家负载分布。

只看 loss 会错过一整类问题:吞吐悄悄降了三成、某张卡在降频、某几个专家已经没有 token 流过、数据管线在重复播放同一批数据——这些都不会立刻反映在 loss 上,但都在烧钱。

梯度范数曲线尤其值得盯:它通常在 loss 尖刺之前就出现异常,是最有用的早期预警。

9.6 可复现与实验治理

代码版本、配置、镜像、数据版本、检查点必须能互相追溯。大规模训练最昂贵的失败往往不是 OOM,而是三周后拿到一个不错的权重,却说不清它是用哪版数据、哪个配置训出来的,于是无法复现也无法改进。

核心问答

Q:为什么检查点必须记录数据读取位置? 否则恢复后会从头重放数据,导致部分样本被训多次、部分从未见过,训练轨迹和预期不符。这也是一个不报错的静默问题。

Q:检查点多久存一次? 取决于保存耗时和集群平均无故障间隔,目标是最小化"保存开销 + 故障后期望重算时间"。集群越不稳定、保存越快,就应该存得越勤。

Q:MFU 很高,项目为什么还是延期? 因为 MFU 看不到停机。频繁故障、慢恢复、慢节点都体现在 goodput 上,不体现在 MFU 上。

Q:数据处理到底算第二册还是第三册? 预训练数据的存储格式、读取、shuffle、packing 属于训练基础设施,在本册;指令数据和偏好数据的构造属于第三册。