大规模分布式训练由于涉及多机器多设备,故障概率比单机训练高很多。常见故障包括:
大规模分布式训练集群跑一次可能需要几天甚至几周,中途任何节点故障都可能导致训练中断,如果没有 checkpoint 恢复,就要从头开始,浪费大量时间。
故障恢复的目标是:从最近保存的 checkpoint 恢复训练,尽可能减少损失。
存储间隔太小:频繁存储会占用大量IO和计算时间,降低训练效率 存储间隔太大:故障后需要重新训练很多step,浪费时间
可以通过公式推导最优间隔:
集群时间损失 = ckpt存储耗时 + 故障期望次数 × (ckpt间隔/2 + 恢复耗时)对间隔求导找到最小值,就能得到最优存储间隔。
能否异步存储checkpoint,掩盖存储时间?
1# 伪代码 - 这是错误的!
2ckpt_thread = Thread(target=save_ckpt)
3ckpt_thread.start()
4train_next_step() # 立刻开始下一轮训练这会导致内存踩踏:下一个step已经开始更新参数,异步存储还在拷贝数据,可能存进去一半更新一半不更新,checkpoint损坏。
完全异步不可行,但可以部分重叠:
如果任意节点故障,能否在退出前快速保存当前完整checkpoint?
可行条件:在多维并行(DP/TP/PP)场景中,只要每个PP stage的所有TP rank都完好,整网参数就是完整的,可以捕获错误后做临终存储。
这种做法能让ckpt interval趋近于0,故障只损失当前step。
不可行情况:
工业级框架一般会结合定期保存+临终遗言两种策略。
弹性训练允许训练过程中节点动态加入退出,不需要完全重启。比如:
问题描述:理论上DDP每个进程一个GPU,显存应该均匀,但实际发现0卡显存比其他卡高很多,容易OOM。
torch.load() 默认把数据加载到0卡:1# 错误写法
2checkpoint = torch.load("checkpoint.pth")
3model.load_state_dict(checkpoint["state_dict"])即使每个进程load一次,数据都会先到0卡再复制,导致0卡多占一块显存。
解决方法:把weights map到cpu再load:
1# 正确写法
2checkpoint = torch.load("checkpoint.pth", map_location=torch.device('cpu'))
3model.load_state_dict(checkpoint["state_dict"])问题描述:自定义数据接口,程序跑一个epoch正常,第一个epoch结束就卡住,没有报错。
问题定位:自研数据分配时,某张卡少一个batch,导致这张卡不执行all_reduce,其他所有卡都在等它,死锁。
解决方法:
排查步骤:
.deepspeed_env 文件,设置:NCCL_IB_DISABLE=1
NCCL_DEBUG=INFO
NCCL_SOCKET_IFNAME=eth0
NCCL_P2P_DISABLE=1如果没有IB(InfiniBand)网络,一定要禁用,否则NCCL会卡住。
原因:GPU显存不够,OOM被系统OOM killer杀死。
解决:按照之前的DeepSpeed调参步骤,一步步减小显存占用:
解决方法:DeepSpeed提供了转换脚本:
1python zero_to_fp32.py . pytorch_model.bin这个脚本会把分片的参数合并成一个完整的pytorch模型文件。
如果想保存的时候直接合并fp16,需要在配置中开启:
1"stage3_gather_16bit_weights_on_model_save": true常见原因:
排查:
原因:网络带宽打满,通信时间比计算时间还长。比如万兆网跑ZeRO3,每步通信量几百GB,当然很慢。
解决:
多节点训练需要保证:
exits with return code = 127或exits with return code = -6错误。解决方法:
1local_env = os.environ.copy()
2local_env["PATH"] = "/path/to/conda/bin:" + local_env["PATH"]
3os.environ.update(local_env)find_unused_parameters=True,否则DDP会报错找不到这些参数的梯度。开启会增加一点通信 overhead,所以静态图默认关。
A: 分布式训练涉及多台机器、多个GPU,需要网络通信。任何一个环节出问题(硬件故障、网络中断、软件bug)都会导致整个训练失败。故障概率和节点数量成正比,节点越多概率越高。所以需要checkpoint、故障恢复、弹性训练这些机制来容错。
A: 因为保存checkpoint需要拷贝参数,如果保存还没完成就开始下一个step训练,参数会被更新,导致保存过程中数据被修改,checkpoint文件损坏,一半旧一半新,无法恢复。所以必须等保存完成才能继续训练,或者用同步点保证数据一致性。
A: 弹性训练允许训练过程中动态增减节点,节点故障可以自动剔除,新节点可以加入。好处是:
torch.load默认把张量加载到0卡,即使每个进程都load一遍,0卡也会多保留一份。解决方法是加载时用map_location=torch.device('cpu'),把权重先读到CPU,再分发到各个GPU。A: 最常见原因是死锁:某个卡少一个batch,在all_reduce时,其他卡都在等这个卡,没人发消息也没人收,卡住不动。解决方法是保证每个卡每个epoch的batch数一致,使用DistributedSampler自动分配。
A:
NCCL_IB_DISABLE=1NCCL_DEBUG=INFO看日志输出,定位具体哪一步错了sqrt(2 * T_storage * MTTF),其中MTTF是平均故障间隔。简单说就是:故障越频繁,存的越频繁;存储越快,存的越频繁。