第三节:批次处理的核心三板斧——Batch Size、Epoch、Iteration

搞深度学习,这几个概念你要是没搞清楚,代码跑出来都是糊涂账。

我做项目那会儿带过一个新人,他训练模型时设了个 epoch=100,结果跑到第10个 epoch 就过拟合了。我问他为什么不再设小一点?他反问我:"epoch 不是轮数吗?跟过拟合有什么关系?" 嗯,问题就在这里——概念没吃透。

Batch Size:一口吃多少

说白了,batch size 就是一次喂给模型多少个样本。

你给模型准备了一万个样本。如果 batch size=32,那模型就是每32个样本算一次梯度,更新一次权重。

关键公式:

每次更新权重所需的样本数 = batch size

那 batch size 设多大才合适?我个人的经验是——看你的显存和模型收敛速度。下面这张表是我平时常用的判断依据:

Batch Size 特点 适合场景
1 随机梯度下降(SGD),噪音大,收敛慢 在线学习、样本量极小
16-64 Mini-batch,平衡噪音与效率 绝大多数常见任务
128-512 大 batch,梯度估计更稳定 大规模训练、分布式场景
1024+ 超大 batch,需配合学习率调整 超大规模数据集、预训练

避坑提醒:我曾经在某个图像分类任务里,把 batch size 从 32 翻到 256,结果模型精度直接掉了 2 个点。后来发现,大 batch 会让梯度更新更单调,容易陷入「糟糕」的局部极小值。这种情况下,你一般得配合学习率 warmup 或者更大的学习率来救场。

Epoch:完整过一遍数据

Epoch 的定义其实很简单——全部训练样本都过完一遍,就叫一个 epoch。

你有一万张图片,batch size=32,那么一个 epoch 就包含 10000 ÷ 32 = 312.5 次迭代。嗯,实际代码里会取整,一般会补足到 313 次。

为什么 epoch 多了容易过拟合?你想想看,模型每看一遍数据,就相当于复习一次。复习次数太多,它就学会死记硬背了,泛化能力反而下降。我在做 NLP 任务时遇到过这情况,把 epoch 从 10 改到 5,测试集 F1 反倒涨了 0.03。

我的习惯:先跑 5 个 epoch 看看损失曲线。如果验证集损失还在下降,再试着加;如果已经开始走平或上翘,马上停。别傻乎乎地跑完预设的 100 个 epoch。

Iteration:一次更新算一步

Iteration 就是模型做一次前向传播 + 一次反向传播。

一次迭代 = 处理一个 batch 的样本 + 更新一次权重。

举个例子,你设 epoch=10,batch size=32,总样本 10000,那总的迭代次数就是:

总迭代次数 = epoch × (总样本数 ÷ batch_size)
            = 10 × (10000 ÷ 32)
            = 10 × 312.5
            ≈ 3130 次

这个数值你在训练日志里经常能看见,比如常见的输出:

Epoch [1/10], Iteration [100/313], Loss: 0.23
Epoch [1/10], Iteration [200/313], Loss: 0.19
...

这三者的关系,我用一张图说明白

下面这张 SVG 图把 batch size、epoch、iteration 的流程和关系给你梳理清楚了。我觉得这是理解整章知识体系最直观的方法。

Batch Size / Epoch / Iteration 核心流程关系图 Batch Size、Epoch、Iteration 关系图 完整数据集 分割 Batch 1 Batch 2 Batch 3 …… Batch N 遍历全部 batch 完成 1 Epoch 重复进行多个 epoch 1 Iteration 处理 1 个 batch 关系总结 1 Epoch = 所有 Batch 遍历完成一次 = (总样本数 ÷ Batch Size) 次 Iteration Batch Size 决定了一次 Iteration 看多少样本,Epoch 决定了模型把整个数据集看多少遍

实战中的参数设置

说了这么多理论,实际代码里怎么设?我习惯用 PyTorch,举个小例子:

from torch.utils.data import DataLoader

# 假设你的 dataset 有 10000 个样本
dataset = YourDataset()
# batch_size 设成 32
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

# epoch 设成 10
num_epochs = 10

for epoch in range(num_epochs):
    for i, (inputs, labels) in enumerate(dataloader):
        # 每次循环就是一次 iteration
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

经验之谈:写代码时你经常能看到这样的打印日志——

Epoch 5/10 - Iteration 1500/3130 - loss: 0.2134

你知道这代表什么意思吗?模型已经看了 5 遍全部数据,目前正在进行第 1501 次参数更新。这就是实时监控训练进度的基本功。

避坑要点

  • Epoch 太多不一定好:我在 CV 竞赛里吃过这个亏。光调 epoch 就跑崩了验证集,后来改用 early stopping 才稳住。
  • Batch size 太大会影响泛化:大 batch 的梯度噪音小,但容易收敛到尖锐的极小值。你可以试试学习率 warmup 或动态调整。
  • Iteration 的计数不要搞混:有些框架从 0 开始计,有些从 1 开始。你查日志时务必确认一下,不然容易误解训练进度。

说白了,batch size、epoch、iteration 就是三个决定模型怎么学、学多快、学几遍的参数。搞懂了它们,你写代码和调参时就不会——嗯,就不会两眼一抹黑了。