self.gradient_accumulate_every
是一个超参数,用于控制 梯度累积 的次数。它的核心目的是通过累积多个小批次的梯度来模拟更大的批量训练,从而缓解显存不足的问题,同时获得大批量训练的优点。
1. 背景
在深度学习中,批量大小(batch size) 会显著影响训练过程:
- 小批量训练:由于显存限制,可能无法使用大批量训练,但梯度更新可能不够稳定,导致模型收敛较慢。
- 大批量训练:提高了梯度估计的稳定性,但需要更多显存。
梯度累积 的提出是为了解决显存限制问题,同时保留大批量训练的效果。
2. 梯度累积的原理
在常规训练中,每个批次的数据经过前向传播和反向传播后,计算得到参数梯度,并立即更新模型权重。
梯度累积的不同之处是:
- 将多个批次的梯度累加在一起,而不立即更新权重。
- 累积到指定次数后(
self.gradient_accumulate_every
),再一次性更新权重。
公式表示:
假设我们使用梯度累积进行训练,
- 小批量大小为 b b b
- 累积梯度次数为 k k k
- 等效大批量大小为 B = b × k B = b \times k B=b×k
梯度累积的过程:
-
每个小批次 i i i 计算梯度:
g i = ∂ L i ∂ θ g_i = \frac{\partial \mathcal{L}_i}{\partial \theta} gi=∂θ∂Li
其中 L i \mathcal{L}_i Li 是第 i i i 小批次的损失函数。 -
累积梯度:
G = ∑ i = 1 k g i G = \sum_{i=1}^{k} g_i G=i=1∑kgi -
使用累积的梯度更新权重:
θ ← θ − η ⋅ G \theta \leftarrow \theta - \eta \cdot G θ←θ−η⋅G
其中 η \eta η 是学习率。
3. self.gradient_accumulate_every
的作用
这个超参数控制累积的次数,即每次更新权重前要累积多少次梯度。
例如:
- 如果
self.gradient_accumulate_every = 1
,表示没有累积梯度,每次批次都立即更新权重(等同于常规训练)。 - 如果
self.gradient_accumulate_every = 4
,表示每计算 4 个小批次的梯度后,才进行一次权重更新,相当于模拟了 4 倍的批量大小。
4. 示例
假设:
- 小批量大小 b = 16 b = 16 b=16
- 累积次数 k = 4 k = 4 k=4
- 等效批量大小 B = b × k = 64 B = b \times k = 64 B=b×k=64
代码流程:
total_loss = 0.
for _ in range(self.gradient_accumulate_every): # 累积梯度
data = next(self.dl) # 取出一个小批次数据
with self.accelerator.autocast():
loss = self.model(data)
loss = loss / self.gradient_accumulate_every # 均分损失
total_loss += loss.item()
self.accelerator.backward(loss) # 累积梯度
self.opt.step() # 更新权重
self.opt.zero_grad() # 清空梯度
解释:
- 每次迭代计算一个小批次的损失,除以累积次数(避免梯度幅度过大)。
- 使用
self.accelerator.backward
累积梯度。 - 累积指定次数后,用
self.opt.step()
更新权重。