正常训练(无累积):
for each batch:
forward
backward → 得到梯度
optimizer.step() → 更新参数
optimizer.zero_grad()梯度累积训练:
accum_step = k
for i, batch in enumerate(dataloader):
forward
backward
if (i+1) % k == 0:
optimizer.step()
optimizer.zero_grad()等效 batch size =
batch_size_per_step × accum_step假设:
不使用累积:
使用梯度累积(k=8):
显存主要由三部分组成:
| 类型 | 是否受累积影响 |
|---|---|
| 模型参数 | ❌ 不变 |
| 优化器状态(Adam等) | ❌ 不变 |
| 激活值(activation) | ✅ 显著降低 |
| 梯度 | ⚠️ 略增(但很小) |
所以:
梯度累积主要节省的是 中间激活显存
| 方法 | 显存节省点 | 是否影响计算 |
|---|---|---|
| 梯度累积 | 激活值 | 变慢 |
| 混合精度 | 参数+激活 | 基本无 |
| 梯度检查点 | 激活值 | 重计算变慢 |
| 模型并行 | 参数+激活 | 通信开销 |
✅ 实际:
✅ 实际:
✅ 实际:
梯度累积通过把大 batch 拆成多个小 step 的“显存分期的训练方式”,在固定显存下实现大 batch 训练,从而节省显存成本,但以增加训练时间为代价。
如果你愿意,我也可以: