梯度累积(Gradient Accumulation)是一种在显存受限情况下,实现大批次(large batch)训练的常用技术。它的核心思想是:把多次小批次(mini-batch)的梯度先累加起来,再统一做一次参数更新。
在深度学习中,训练通常按如下步骤进行:
通常:
于是出现矛盾:
想用大 batch,但显存放不下
梯度累积就是解决这个问题的方案。
假设真实想用的 batch size 是 B,但显存只能放 b(b < B)。
正常训练一次更新:
for batch in dataloader:
loss = model(batch)
loss.backward()
optimizer.step()
optimizer.zero_grad()把大 batch 拆成 k = B / b 个小 batch:
.grad 中k 次后,再统一更新等价于:
用 B 个样本算一次梯度,再更新一次参数假设损失函数是:
[
mathcal{L} = frac{1}{B} sum_{i=1}^{B} ell(x_i)
]
显存限制下,我们分成 k 步:
[
g_t = frac{1}{b} sum_{i in text{batch}_t} nabla ell(x_i)
]
梯度累积时:
[
G = sum_{t=1}^{k} g_t
]
最终更新:
[
theta leftarrow theta - eta cdot G
]
B 个样本算梯度是数学等价的(不考虑数值误差)accumulation_steps = 4 # 累积 4 次
optimizer.zero_grad()
for i, (x, y) in enumerate(dataloader):
pred = model(x)
loss = criterion(pred, y)
# 梯度归一化(很重要)
loss = loss / accumulation_steps
loss.backward()
if (i + 1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()optimizer.step()zero_grad() 在 step 后调用| 对比项 | 大 Batch | 梯度累积 |
|---|---|---|
| 梯度计算 | 一次 | 多次累加 |
| 显存占用 | 高 | 低 |
| 数值结果 | 几乎一致 | 几乎一致 |
| BN 行为 | 不同 | 可能不同 |
⚠️ Batch Normalization 是例外
✅ 解决方案:
✅ 非常适合:
❌ 不太适合:
梯度累积的本质是:在显存受限的情况下,通过多次前向-反向传播“攒梯度”,最终模拟一次大 batch 的参数更新。
如果你愿意,我也可以: