批量大小(Batch Size)的调优策略

批量大小,也就是每个训练步骤喂给模型多少个样本,看起来是个简单参数。你可能会想:不就是设个32、64、128嘛?说实话,我在调参的头两年也没太当回事,直到有一次显存明明够,模型却怎么也收敛不了……后来发现,就是batch size搞的鬼。

批量大小直接影响三件事:收敛速度、梯度质量、显存占用。这三者之间互相牵制,你想想看,选大了显存爆炸,选小了训练慢得像蜗牛。所以调它,本质上是在找一个平衡点。

批量大小的三大影响

咱们先直观感受一下。每次迭代,模型拿一批数据算梯度,然后更新参数。数据量小,梯度噪声大,容易跳出局部最优,但也容易震荡不收敛。数据量大,梯度方向稳定,更新路径平滑,但计算代价高,而且可能让模型陷入尖锐的极小值。

我把它拆成三个维度来看:

维度 小批量(≤32) 大批量(≥256)
收敛速度 每步快,但总步数多 每步慢,但总步数少
梯度质量 噪声大,泛化性可能更好 方向稳定,容易过拟合
显存占用 低,可跑大模型 高,可能OOM
一个小提示:你跑CV任务时,batch size设32或64通常是个安全起点。NLP任务因为序列长度差异大,建议从16或32开始试。

常见调优策略

先说一个我踩过的坑。之前做图像分类,我直接把batch size设到512,心想显存够用总没错。结果训练了10个epoch,验证集损失不降反升。后来查资料才发现——学习率没跟着调。大批量需要更大的学习率,否则更新步长会被稀释。

这里有一条经验法则:

线性缩放法则:批量大小增大k倍,学习率也增大k倍。但要注意,这适用于一定的范围内(通常2~8倍),超出了容易导致训练炸掉。

具体怎么操作呢?我给出一个常见的调优流程:

  1. 先跑一个小批量(如32),找到能正常收敛的学习率。
  2. 按比例扩大batch size,同时按同样比例增大学习率。
  3. 观察损失曲线,如果出现震荡,回退学习率;如果收敛太慢,再适当提高。
  4. 如果显存不够,使用梯度累积模拟大批量。

梯度累积的代码示例(PyTorch):

accumulation_steps = 4  # 模拟 batch_size=32*4=128
optimizer.zero_grad()

for i, (inputs, labels) in enumerate(dataloader):
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    loss = loss / accumulation_steps  # 缩放loss
    loss.backward()

    if (i + 1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()

这段代码的意思就是:攒够4个mini-batch的梯度,再统一更新一次参数。效果等价于batch size扩大了4倍,但显存只占一个小batch。

注意:梯度累积时,记得把loss除以累积步数,否则梯度会爆炸。我刚开始就是这么写的,结果loss直接飞到NaN。

避坑指南:我曾经踩过的三个雷

  • 雷区一:盲目使用2的幂次。很多人觉得batch size必须设成32、64、128,其实没必要,能整除数据集就行。我曾经设过48,没什么问题。
  • 雷区二:batch size过大导致模型泛化能力下降。我记得有一篇论文说得好:大批量训练容易收敛到尖锐的极小值,泛化不如小批量。所以如果你发现测试集表现不佳,试试减小batch size。
  • 雷区三:忽略batch size对BN层的影响。Batch Normalization在小批量(比如≤8)时,统计量不稳定,训练效果很差。此时可以用LayerNorm代替,或者使用更大的batch size。

为什么会这样?简单说,BN层需要足够的样本数来估计均值和方差。小于8的时候,估计出来的均值和方差波动很大,相当于给模型引入了额外的噪声。

如何选择初始batch size?

我个人习惯先看显存上限,再往下压。比如你的GPU有8GB显存,先用最小的batch size试跑一个epoch,如果显存还有余量,就逐步翻倍,直到接近显存阈值的80%。留20%余量给中间激活和参数。

下面这张表给出了常见模型和任务的推荐初始值:

模型类型 典型输入尺寸 推荐初始batch size
ResNet-50 224×224 64
BERT-base 512 tokens 16 或 32
YOLOv8 640×640 16
GPT-2 small 1024 tokens 8

这些只是起点。你真正要做的,是在训练过程中观察损失曲线。如果loss震荡剧烈,就减小batch size或降低学习率;如果loss下降太慢,就增大batch size同时调高学习率。

我放一张图来帮你理清batch size调优的核心逻辑吧。

批量大小调优决策流程 选择初始batch size 小批量(32-64) 大批量(256+) 观察损失曲线 小批量->梯度噪声大,但泛化好 大批量->梯度稳,需调高学习率

从左边的选择开始,要么试小批量,要么试大批量。然后都汇到观察损失曲线上,再根据效果调整学习率或batch size。实际调参时经常要来回试两三轮才能找到最佳点。

总结几个要点

  • batch size和learning rate是铁搭档,改一个必须改另一个。
  • 显存够用的情况下,小批量通常泛化更好。
  • 梯度累积是没钱买大显存时的救命稻草。
  • BN层对小批量很敏感,注意不要低于8。
  • 别忘了看loss曲线——它比任何经验值都可靠。

好了,批量大小的调优基本就这回事。你回去可以拿自己的模型跑几个对比实验,很快就能找到手感。