4、同步策略对比:同步SGD、异步SGD、梯度累积、混合精度训练中的同步问题
多卡训练,说白了就是一群人干活。但怎么分活、怎么汇总,这里面的门道可深了。我见过不少团队,卡买了不少,结果训练速度还不如单卡,问题就出在同步策略上。
今天咱们就把四种主流策略掰开揉碎讲清楚。同步SGD、异步SGD、梯度累积,还有混合精度训练里的同步坑,我一个一个说。
4.1 同步SGD:最稳,但也最慢
同步SGD的逻辑很简单:所有卡算完一个batch,大家一起同步梯度,再统一更新参数。
打个比方,就像小组作业,每个人都必须写完自己的部分,组长才收齐,然后统一交给老师。谁写得慢,全组都得等。
我在项目中遇到过这种情况:8张卡跑同步SGD,其中一张卡因为散热问题降频了,结果每轮迭代都要等它多花30%的时间。整体训练速度直接被拖垮。
θ(t+1) = θ(t) - η * (1/N) * Σ(g_i(t))其中N是卡数,g_i是第i张卡的梯度。所有卡梯度求平均,再更新。
优点很明显:收敛稳定,数学性质好。每个step的更新方向都是全局梯度的无偏估计,不会跑偏。
缺点也致命:木桶效应。最慢的那张卡决定了整个训练的速度。卡越多,等待时间越长。
4.2 异步SGD:快,但容易跑偏
异步SGD的思路是:各卡算完自己的梯度,直接更新参数,不用等别人。
还是小组作业的例子。异步SGD就像每个人写完自己的部分,直接交给老师,不用管别人写没写完。速度快了,但老师收到的作业可能是乱的——因为大家交的顺序和写的顺序不一样。
这就是异步SGD的核心问题:梯度陈旧性(Stale Gradient)。当一张卡算完梯度时,参数可能已经被其他卡更新了好几次。它拿到的梯度,对应的是旧版本的参数。
我记得有一次,我用异步SGD跑一个BERT-large模型,32张卡。训练速度确实快,但loss曲线像心电图一样上下乱跳。最后收敛效果比同步SGD差了将近2个点。
异步SGD适合什么场景?大规模稀疏模型,比如推荐系统。因为每个样本只更新少量参数,梯度冲突的概率低。
4.3 梯度累积:用时间换空间
梯度累积,说白了就是攒够一定数量的梯度再更新。
为什么要这么做?因为显存不够。比如你想用batch size=1024,但单卡显存只够塞batch size=128。怎么办?每张卡算8次,每次梯度都累加起来,攒够8次再更新一次参数。
公式长这样:
# 梯度累积伪代码
accumulation_steps = 8
optimizer.zero_grad()
for i in range(accumulation_steps):
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward() # 梯度累加,不清零
optimizer.step() # 攒够了,更新参数
optimizer.zero_grad() # 清零梯度
这里有个坑:Batch Normalization层的统计量。梯度累积时,每个micro-batch的BN统计量是独立的,和真正的large batch不一样。我建议用SyncBN或者干脆冻结BN层。
4.4 混合精度训练中的同步问题
混合精度训练(AMP)现在几乎是标配了。但很多人不知道,精度转换也会引入同步问题。
混合精度训练的核心是:前向和反向用FP16,权重更新用FP32。这样既省显存又保持精度。
但同步梯度时,问题来了:
- FP16梯度范围窄,容易溢出。梯度值太小会变成0,太大会变成inf。
- AllReduce操作在FP16下做,精度损失会累积。
我见过一个案例:某团队用8卡训练,开了AMP后,loss死活不降。排查了两天,发现是梯度同步时用了FP16 AllReduce,导致小梯度被截断为0。换成FP32 AllReduce后,问题解决。
- 梯度同步用FP32,不要省那点带宽。
- Loss Scaling要开,防止梯度下溢。
- 检查梯度分布,确保没有大量0梯度。
另外,混合精度训练和梯度累积一起用时,要特别注意。每次micro-step的梯度都是FP16,累积多次后再转FP32,精度损失会放大。我建议每步都做Loss Scaling,并且累积步数不超过4。
4.5 四种策略对比一览
| 策略 | 收敛速度 | 稳定性 | 显存占用 | 适用场景 |
|---|---|---|---|---|
| 同步SGD | 慢(受最慢卡限制) | 高 | 低 | 小规模集群、同构硬件 |
| 异步SGD | 快 | 低(梯度陈旧) | 低 | 大规模稀疏模型 |
| 梯度累积 | 中等 | 中等 | 低(等效大batch) | 显存受限场景 |
| 混合精度+同步 | 快(计算加速) | 中等(精度问题) | 低(FP16省显存) | 通用场景,需调参 |
4.6 知识体系总览
下面这张图,把四种策略的核心逻辑和关系画清楚了。你看一眼就能明白各自的位置。
嗯,四种策略各有千秋。同步SGD稳但慢,异步SGD快但容易飘,梯度累积是折中方案,混合精度训练则要额外关注精度转换的同步问题。
我个人建议:新手先从同步SGD+梯度累积入手,等摸透了再尝试异步。混合精度训练现在基本是必选项,但一定要做好梯度检查和Loss Scaling。
最后说一句:没有银弹。选哪种策略,取决于你的硬件、模型和数据。多试试,多对比,找到最适合自己的组合。