4. 小批量梯度下降(MBGD):原理详解、优缺点分析、Batch Size的折中艺术

说实话,Batch Size 这个参数,我调了快十年了。

刚入行那会儿,我总觉得这玩意儿随便设个 32 或者 64 就行。直到有一次,我在一个图像分割项目里,模型怎么训都不收敛。折腾了三天,最后发现是 Batch Size 设得太大了。嗯,从那以后,我再也不敢小看这个超参数了。

今天咱们就来聊聊小批量梯度下降,也就是 MBGD。说白了,它就是 BGD 和 SGD 的「私生子」,但偏偏这个私生子,成了深度学习里最常用的优化方式。

4.1 什么是小批量梯度下降?

先回顾一下前两章的内容:

  • BGD(批量梯度下降):每次用全部数据算梯度。稳,但慢得要命。
  • SGD(随机梯度下降):每次只用一个样本算梯度。快,但抖得像过山车。

那有没有一种方法,既稳又快?

有。就是 MBGD。

它的核心思想很简单:每次从训练集中随机抽一小批样本,用这一批样本的平均梯度来更新参数。

核心公式:

θ = θ - η * (1/m) * Σ ∇θ J(θ, x^(i), y^(i))

其中 m 就是 Batch Size,也就是你每次抽多少个样本。

你想想看,这其实是个很自然的折中方案。用一批样本的平均梯度,既比单个样本的梯度更稳定,又比全量数据的梯度更高效。

4.2 MBGD 的完整流程

我习惯把 MBGD 的训练过程分成三步:

  1. 打乱数据:先把所有训练数据随机打乱。这一步很重要,不然模型可能会学到样本顺序里的假规律。
  2. 划分批次:按 Batch Size 把数据切成若干个小批次。比如你有 1000 个样本,Batch Size 设 100,那就切成 10 个批次。
  3. 逐批更新:每个批次算一次梯度,更新一次参数。所有批次跑完,算一个 Epoch。

这里有个细节我踩过坑——最后一个批次可能不够一个 Batch Size。比如 1000 个样本,Batch Size 设 128,最后一个批次只剩 104 个。怎么办?

我一般直接保留这个「小批次」,该算梯度就算梯度。也有人选择丢弃,但我个人不建议,毕竟数据来之不易。

💡 我的小技巧:

在 PyTorch 里,可以用 DataLoader 的 drop_last 参数来控制是否丢弃最后一个不完整的批次。我通常设成 False。

4.3 优缺点分析

咱们来掰扯掰扯 MBGD 的优缺点。这部分我尽量说人话。

优点

  • 训练速度快:比 BGD 快得多。你不需要等所有数据算完才更新一次参数。
  • 收敛稳定:比 SGD 稳。一批样本的平均梯度,方差更小,不会像 SGD 那样跳来跳去。
  • 硬件友好:可以利用 GPU 的并行计算能力。GPU 最喜欢的就是矩阵运算,一批数据正好喂给它。
  • 泛化能力好:相比 BGD,MBGD 引入了一定的随机性,反而有助于跳出局部最优。

缺点

  • Batch Size 需要调:这个最烦人。设大了像 BGD,设小了像 SGD。没有一个万能值。
  • 内存占用:Batch Size 越大,显存占用越高。我曾经在 8G 显存的卡上试着跑 Batch Size 512,直接 OOM。
  • 学习率需要配合调整:Batch Size 变了,学习率往往也得跟着变。这个后面会细说。

⚠️ 我曾经踩过的坑:

有一次我把 Batch Size 从 32 改到 128,没调学习率。结果模型直接炸了,Loss 变成了 NaN。后来才知道,Batch Size 变大,梯度方差变小,同样的学习率可能会导致梯度爆炸。

4.4 Batch Size 的折中艺术

说到 Batch Size 怎么选,这其实是个「艺术活」。我个人的经验是:

先看你的硬件条件。 显存有多大?能塞下多大的 Batch Size?这是硬约束。

再看你的任务类型。 图像分类?自然语言处理?还是强化学习?不同任务对 Batch Size 的敏感度不一样。

我整理了一个经验表格,供你参考:

任务类型 推荐 Batch Size 说明
图像分类(小数据集) 16 - 64 数据少,Batch Size 太大容易过拟合
图像分类(大数据集) 64 - 512 数据多,可以适当放大
自然语言处理 8 - 32 序列长度不一,太大容易内存爆炸
目标检测 2 - 16 每张图的信息量很大,小批次更稳
GAN 训练 32 - 128 需要平衡生成器和判别器的训练

你可能会问:「那到底选 32 还是 64?」

我的回答是:从 32 开始试。32 是个很安全的起点。如果显存够,试试 64。如果 Loss 曲线抖得厉害,试试 128。

核心原则:

Batch Size 越大,学习率可以越大。一般有个经验公式:lr_new = lr_old * sqrt(BatchSize_new / BatchSize_old)。但说实话,我更喜欢直接手动调。

4.5 知识体系结构图

下面这张图,是我自己整理的 MBGD 知识体系。你可以把它当作一个快速索引:

小批量梯度下降(MBGD)知识体系 MBGD 原理:每次用 m 个样本的平均梯度 流程:打乱数据 → 划分批次 → 逐批更新 优缺点 ✅ 训练快 · 收敛稳 · 硬件友好 · 泛化好 ❌ 需调参 · 内存高 · 学习率需配合 Batch Size 选择策略 从 32 开始 → 看显存 → 看任务 → 调学习率

4.6 代码示例:PyTorch 中的 MBGD

最后,咱们来看个实际的代码。这是我在一个图像分类项目里用的训练循环:

import torch
import torch.nn as nn
from torch.utils.data import DataLoader

# 假设 model, dataset 已经定义好了
# Batch Size 设为 64
batch_size = 64
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

model = MyModel()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

num_epochs = 10

for epoch in range(num_epochs):
    running_loss = 0.0
    
    # 每个 batch 就是一个批次
    for i, (inputs, labels) in enumerate(dataloader):
        # 前向传播
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        
        # 反向传播 + 参数更新
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        running_loss += loss.item()
        
        # 每 100 个 batch 打印一次
        if i % 100 == 99:
            print(f'Epoch {epoch+1}, Batch {i+1}, Loss: {running_loss/100:.3f}')
            running_loss = 0.0

你看,核心就两行:loss.backward() 算梯度,optimizer.step() 更新参数。PyTorch 帮我们处理了批次内的梯度平均,你只需要设好 Batch Size 就行。

💡 一个小建议:

刚开始调 Batch Size 的时候,别想太多。从 32 开始,跑一个 Epoch 看看 Loss 曲线。如果曲线太抖,加大 Batch Size;如果太慢,减小 Batch Size。就这么简单。

好了,关于 MBGD 的原理和 Batch Size 的折中艺术,今天就聊到这儿。记住一句话:没有最好的 Batch Size,只有最合适的 Batch Size。多试几次,你就能找到感觉。

可可精品资料库