核心思想:
把“大 batch 的训练效果”拆成多个“小 batch 的梯度累加”,再一次性更新模型参数。
标准流程:
for micro_step in range(k):
loss = model(data_micro_batch)
loss = loss / k # 保持梯度尺度一致
loss.backward() # 梯度累加
optimizer.step() # 更新参数
optimizer.zero_grad() # 清空梯度等价于:
batch_size = k × micro_batch_size错误做法:
loss.backward()正确做法:
loss = loss / accumulation_steps
loss.backward()原因:
backward() 是累加梯度k 会导致梯度被放大 k 倍✅ 优化建议:
梯度累积 ≠ 直接等价大 batch,学习率需要重新考虑。
| Batch Size | 学习率 |
|---|---|
| 增大 k 倍 | 增大 √k ~ k 倍 |
建议:
⚠️ 注意:
梯度累积后,参数更新频率降低,训练动态会变化
在使用 torch.cuda.amp 时,梯度累积要特别注意:
scaler.scale(loss).backward()优化技巧:
optimizer.step() 前 scaler.unscale_()✅ 推荐写法:
if (step + 1) % accum_steps == 0:
scaler.unscale_(optimizer)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm)
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad()梯度累积会延迟裁剪时机,这是关键优化点。
❌ 错误:
loss.backward()
torch.nn.utils.clip_grad_norm_(params, max_norm)✅ 正确:
if (step + 1) % accum_steps == 0:
clip_grad_norm_(params, max_norm)原因:
with torch.no_grad():
# 评估、log、metric 计算optimizer.zero_grad() 的调用当累积步数很大时:
优化方法:
| 场景 | 优化方式 |
|---|---|
| DDP | 每个 rank 独立累积 |
| ZeRO | 累积后再 reduce |
| Pipeline Parallel | micro-batch 与 stage 对齐 |
⚠️ 注意:
DDP + 梯度累积 时 不要重复 all-reduce根据显存或 loss 波动动态调整 accum_steps:
if OOM:
accum_steps *= 2或:
if loss_variance > threshold:
reduce accum_steps✅ 解决:
✅ 调试:
print(optimizer.param_groups[0]['lr'])
print(global_step)✅ 适合:
❌ 不适合:
梯度累积的优化本质 = 正确的梯度缩放 + 合理的学习率调节 + 延迟但稳定的参数更新 + 与 AMP / 分布式 / BN 的协同设计。
如果你愿意,我也可以: