4. Batch Size 对训练的影响
Batch Size 这玩意儿,说白了就是每次往模型里塞多少样本。很多人刚开始调参时,觉得它就是个显存消耗器——大就慢、小就快。但实际搞过几次大规模训练后你会发现,Batch Size 对训练的影响远不止显存。
我个人的习惯是,拿到一个新模型,先不急着调学习率,而是先把 Batch Size 探一遍。为啥?因为它同时牵动着收敛速度、泛化能力和显存占用这三根神经。你动一个,另外两个也会跟着抖。
4.1 收敛速度:不是越大越快
很多人直觉上觉得,Batch Size 越大,一次看的样本越多,梯度越准,收敛应该更快。嗯,这个想法有一半是对的。
大 Batch 确实能提供更稳定的梯度估计。每个 step 的方差小,损失下降曲线更平滑。但问题来了——你一次处理那么多样本,每个 step 的时间也变长了。而且到后期,大 Batch 很容易卡在尖锐的局部最小值里,跳不出来。
我在项目中遇到过这种情况:把 Batch Size 从 32 翻到 256,每个 epoch 是快了,但收敛到同样的精度,反而多花了 30% 的时间。因为梯度太稳,模型丧失了“探索能力”。
小 Batch 呢?梯度噪声大,每次更新方向都有点抖。但正是这种“抖动”,有时候反而帮模型跳出了不好的局部点。尤其训练初期,小 Batch 的收敛速度往往更快。
一个常用的经验公式:
lr_new = lr_base * (batch_size_new / batch_size_base)
但这并不是万能的。我记得有一次调一个 Transformer 模型,Batch Size 从 64 涨到 512,学习率按比例提到原来的 8 倍,直接炸了。后来又调回 4 倍才稳住。所以最好还是先小范围试一下。
4.2 泛化能力:大 Batch 的隐藏代价
泛化能力,就是模型在没见过的数据上表现如何。说句实话,这是 Batch Size 选择里最容易被忽视的点。
研究表明,大 Batch 训练出来的模型,往往在测试集上比小 Batch 的差那么一截。为什么会这样?
- 尖锐 vs 平坦最小值:小 Batch 的梯度噪声会把模型推向平坦的谷底,那里泛化好;大 Batch 更容易落在尖锐的坑里,测试时一偏就掉下去。
- 隐式正则化:小 Batch 的随机性本身就有正则效果。你想想看,每次选哪几个样本都有随机性,模型自然学得更鲁棒。
- Batch Normalization 的统计量:BN 层在训练时用的是当前 batch 的均值和方差。Batch Size 太小,统计量不稳定;太大,统计量太准,推理时用全局统计反而有偏差。
我曾经在一个图像分类项目里踩过坑。用 Batch Size 1024 训了 100 个 epoch,训练损失降到 0.01,测试集准确率只有 81%。后来换回 128,训练损失 0.05,测试准确率反而涨到 84.5%。
那么什么时候可以用大 Batch?我个人觉得,当你数据量极大、训练时间有限,且对泛化精度要求不那么苛刻时,大 Batch 是划算的。或者配合学习率 warmup、梯度裁剪这些技巧,也能缓解泛化下降的问题。
4.3 显存占用:最直接也最可控
显存占用这块,反而是最没悬念的。Batch Size 翻一倍,显存占用几乎也翻一倍——主要就是多存了中间特征图和梯度。
但这里面有个细节很多人没注意:显存占用不是完全线性的。
| 组件 | Batch Size 影响 | 说明 |
|---|---|---|
| 模型参数 | 不变化 | 参数总量固定 |
| 中间激活 | 线性增长 | 每张图都要存一份特征图 |
| 梯度 | 线性增长 | 每个样本的梯度都需要累加 |
| 优化器状态 | 不变化 | Adam 这类只跟参数数量有关 |
| 临时缓冲区 | 亚线性增长 | CUDA kernel 调度有额外开销 |
你看,模型参数和优化器状态这部分是固定的,不受 Batch Size 影响。真正吃显存的大头是中间激活和梯度。所以你在调 Batch Size 时,可以重点关注这两块的峰值。
一个常用的估算公式:
显存 ≈ 模型参数 + 优化器状态 + batch_size * 单样本激活量 * (2~3)
那个 2~3 的系数是因为训练时既要保留前向的激活,又要存反向的梯度,有些框架还会保留两份。
我推荐一个实践方法:先用 Batch Size=1 跑一次,记录显存基线。然后逐步增加 Batch Size,观察显存增长的斜率。如果斜率突然变陡,很可能是遇到了框架的内存碎片化或缓存分配问题。
4.4 知识体系总览
为了帮你理清这三者的关系,我画了一张图。它把 Batch Size 对收敛速度、泛化能力和显存占用的影响,以及它们之间的相互牵制都标了出来。
4.5 实战建议
说了这么多,到底怎么选 Batch Size?我按场景给你列几条实用的:
- 显存紧张(显卡只有 8GB 甚至更小):Batch Size=4~16,配合梯度累积模拟更大的有效 Batch。我曾在 4GB 卡上训过一个 7B 模型,Batch Size=1,梯度累积 64 步,效果勉强能接受。
- 追求泛化精度(竞赛、上线产品):Batch Size=32~128。这个区间在收敛速度和泛化之间比较平衡。我个人习惯从 64 开始试。
- 追求训练吞吐(预训练、大规模数据):Batch Size=256~1024,配合学习率 warmup 和 Layer-wise Adaptive Rate Scaling。注意一定要监控测试集,发现泛化下降就降回来。
- 使用 Batch Normalization:Batch Size 别小于 16,否则 BN 统计量波动太大,训着训着就崩了。如果必须用小 Batch,考虑换成 Layer Norm 或 Group Norm。
最后送你一个命令,可以快速查看当前配置下的显存占用量:
python -c "
import torch
# 模拟一个简单模型
model = torch.nn.Linear(1024, 1024).cuda()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# 占位输入
x = torch.randn(64, 1024).cuda()
y = model(x)
loss = y.sum()
loss.backward()
optimizer.step()
print(f'当前显存占用: {torch.cuda.memory_allocated() / 1024**2:.0f} MB')
"
嗯,关于 Batch Size 就这些。记住一点:没有完美的 Batch Size,只有最适合你当前资源和目标的 Batch Size。