Gradient Accumulation
梯度累积会先对多个微批次的梯度求和,再更新一次权重,从而在 GPU 显存有限时实现更大的批次效果。
梯度累积是一种训练技术:先将多个称为微批次的小批次依次进行前向传播和反向传播,累加它们的梯度,然后再更新模型权重。这种更新近似于在一个包含所有微批次样本的大批次上执行单步训练,同时 GPU只需容纳一个微批次的激活值。这样一来,受内存限制的设备也能使用训练方案要求的批次大小,而无需降低图像分辨率或缩小模型。有效批次大小等于微批次大小乘以累积步数,再乘以设备数量;因此,在 4 个 GPU 上将每批 4 张图像累积 8 步,其效果相当于批次大小为 128。
梯度累积的工作原理#
在 PyTorch 和大多数其他框架中,每次调用 backward() 都会将新梯度加到参数中已有的梯度上,而不是替换已有梯度。累积直接利用了这一特性:对每个微批次执行前向和反向传播,达到设定的微批次数量后调用一次优化器,然后清除梯度并开始下一个累积窗口:
大多数损失函数会对批次中的样本求平均,因此每个微批次的损失都要除以累积步数,才能将梯度和转换成平均值。经过这样的缩放,4 个每批 8 个样本的微批次会产生与一个 32 个样本批次相同的梯度:
import torch
from torch import nn
torch.manual_seed(0)
model = nn.Linear(16, 1)
x, y = torch.randn(32, 16), torch.randn(32, 1)
loss_fn = nn.MSELoss()
# Reference: one batch of 32 samples
loss_fn(model(x), y).backward()
batch_grad = model.weight.grad.clone()
model.zero_grad()
# Four micro-batches of 8: backward() adds into .grad, and dividing each loss by 4 turns the sum into a mean
steps = 4
for x_micro, y_micro in zip(x.chunk(steps), y.chunk(steps)):
(loss_fn(model(x_micro), y_micro) / steps).backward()
print("Matches the full-batch gradient:", torch.allclose(model.weight.grad, batch_grad, atol=1e-6))
print(f"Largest difference: {(model.weight.grad - batch_grad).abs().max():.1e}")它会打印 Matches the full-batch gradient: True,最大差异为 1.6e-07;这是由于加数顺序不同而产生的浮点舍入误差。
累积与大批次的不同之处#
对于对独立样本求平均的损失,两者结果一致;但在以下三个常见情况下并非如此:
- 批归一化:会在每个微批次上计算均值和方差,因此 4 张图像的统计量会比 128 张图像的统计量更不稳定。
- 可变长度序列:如果语言模型对每个微批次中的词元求平均损失,那么词元较少的微批次会与词元较多的微批次权重相同。
- 梯度裁剪:应在累积梯度上、优化器执行更新前进行一次;对每个微批次分别裁剪会改变结果。
Hugging Face 和 Unsloth 于 2024 年 10 月修复了词元数量不匹配问题:在对大语言模型(LLM)进行微调时,将损失总和除以整个累积窗口中的词元总数。梯度累积也不会带来加速:相同数量的样本仍要一次通过一个微批次来处理,而小微批次对 GPU 的利用效率更低。在分布式训练中,PyTorch 的 DistributedDataParallel提供了 no_sync() 上下文,可跳过除最后一个微批次以外每个微批次的梯度全归约,从而减少通信。有效批次通常越大,学习率也应越高:Facebook 研究人员通过按批次大小线性缩放学习率并逐步预热,在一小时内使用 256 个 GPU 和 8,192 张图像的批次训练了 ImageNet 上的 ResNet-50。
相关技术#
- 梯度检查点:在反向传播时重新计算激活值,而不是将其存储起来,从而节省每个微批次的内存,代价是增加计算量。
- 混合精度:以 16 位格式存储激活值,从而为更大的微批次腾出空间。其损失缩放器会在最后一个微批次结束后对梯度反缩放一次。
- 流水线并行:将每个批次拆分为多个微批次,让它们依次经过不同 GPU 上的模型阶段,并在执行一次优化器更新前累积其梯度。
Ultralytics YOLO26默认会累积梯度:在训练模式下,训练器每经过 round(nbs / batch) 个批次就执行一次优化器更新,因此默认的 batch=16 和名义批次大小 nbs=64 会让每次更新累积 4 个批次,而 batch=8 则会累积 8 个批次。累积数量会在预热阶段从 1 逐渐增加,权重衰减会按 batch × accumulation / nbs 缩放,因此为 64 张图像批次设置的默认超参数,在一次只能容纳几张图像的 GPU 上仍然适用。










