第四章:批量大小与梯度累积

说到 Batch Size,我脑子里第一个蹦出来的画面,是几年前调一个图像分类模型。当时显存只有 8G,我硬是把 Batch Size 从 64 一路降到 8,结果模型死活不收敛。后来我才明白,这玩意儿不是越大越好,也不是越小越灵。

今天咱们就聊聊 Batch Size 对收敛的影响、梯度累积的原理,以及显存和速度怎么权衡。

一、Batch Size 对收敛的影响

先问个问题:Batch Size 到底在控制什么?

说白了,它控制的是每次参数更新时,你用多少样本去估计梯度。

  • Batch Size 太大:梯度估计更准,收敛更稳定。但容易陷入尖锐的局部极小值,泛化能力反而下降。我见过有人把 Batch Size 设到 1024,训练 loss 降得飞快,测试集上直接崩了。
  • Batch Size 太小:梯度噪声大,收敛路径曲折。但好处是能跳出一些不好的局部点,泛化性往往更好。不过训练时间会拉长,显存利用率也低。

我个人习惯的做法是:先试一个中等值,比如 32 或 64。如果 loss 震荡太厉害,就往上加;如果收敛太慢,就往下减。你想想看,这其实是个经验活。

核心结论:Batch Size 和学习率需要联动调整。增大 Batch Size 时,通常也要适当增大学习率,否则收敛速度会变慢。

二、梯度累积原理

显存不够怎么办?梯度累积就是来救场的。

它的原理很简单:把一个大 Batch 拆成几个小 Batch,分别计算梯度,然后累加起来,最后统一更新参数。

举个例子:你想用 Batch Size = 64,但显存只够跑 16。那就跑 4 个 step,每个 step 用 16 个样本算梯度,累加 4 次后再更新参数。效果上,等价于一次用 64 个样本更新。

# 梯度累积伪代码
accumulation_steps = 4
optimizer.zero_grad()

for i, (inputs, labels) in enumerate(dataloader):
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    loss = loss / accumulation_steps  # 注意这里要除
    loss.backward()
    
    if (i + 1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()

注意:loss 要除以 accumulation_steps,否则梯度会放大 accumulation_steps 倍,导致训练不稳定。我曾经在这上面吃过亏,调了半天才发现是忘了除。

三、显存与速度的权衡

这里有个常见的误区:梯度累积能省显存,但省不了时间。

为什么?因为计算量没变。你原来一次算 64 个样本,现在分 4 次算 16 个,总计算量是一样的。而且多了梯度累加和参数更新的开销,实际耗时反而可能增加。

那梯度累积的价值在哪?

  • 突破显存瓶颈:让你能在有限硬件上跑更大的有效 Batch Size
  • 稳定训练:当单卡 Batch Size 太小导致梯度噪声过大时,累积可以平滑梯度

我一般这样权衡:

场景 推荐做法
显存够用,追求速度 直接用大 Batch Size,不用累积
显存不够,但需要大 Batch 梯度累积,accumulation_steps 尽量小
单卡 Batch Size 太小(如 2、4) 梯度累积到 16 或 32,减少梯度噪声

小技巧:如果你的模型用了 Batch Normalization,梯度累积时要小心。BN 的统计量是基于每个 micro-batch 计算的,累积不会改变这一点。所以有效 Batch Size 还是 micro-batch 的大小,只是梯度更新频率降低了。

四、知识体系图

下面这张图帮你理清 Batch Size 和梯度累积的核心逻辑:

Batch Size 与梯度累积 Batch Size 大小的影响 大 Batch Size 梯度准,收敛稳 小 Batch Size 梯度噪声大,泛化好 梯度累积原理 小 Batch 分步计算 梯度累加后统一更新 省显存但耗时不变 适合显存受限场景

五、实战建议

最后给几条实在的建议:

  1. 先定 Batch Size,再调学习率。我习惯用学习率预热 + 余弦退火的组合,对 Batch Size 变化比较鲁棒。
  2. 梯度累积步数不要太大。超过 8 步的话,参数更新间隔太长,训练效率会下降。我一般控制在 2-4 步。
  3. 混合精度训练 + 梯度累积是绝配。显存能省一半,速度还能提升。不过要注意 loss scaling 的调整。
  4. 如果条件允许,优先增大单卡 Batch Size,而不是堆累积步数。因为 BN 的统计量会更准确。

一句话总结:Batch Size 控制梯度质量,梯度累积解决显存瓶颈。两者配合好,才能在有限硬件上跑出好模型。

嗯,今天就聊到这儿。这些经验都是真金白银换来的,你试试看就知道了。