深度学习中的批处理与小批量训练

深度学习中的批处理与小批量训练

深度学习彻底改变了人工智能领域,它使机器能够从数据中获取更深入的信息。深度学习能够做到这一点,因为它模拟了我们大脑通过神经元突触逻辑运作的方式。训练深度学习模型最关键的方面之一是如何在训练过程中将数据输入模型。批处理和小批量训练正是为此而生。模型的训练方式将影响模型投入生产后的整体性能。在本文中,我们将深入探讨这些概念,比较它们的优缺点,并探索它们的实际应用。

深度学习训练过程

训练深度学习模型涉及最小化损失函数,该函数衡量每个周期后预测输出与实际标签之间的差异。换句话说,训练过程是前向传播和后向传播之间的双人舞蹈。这种最小化通常使用梯度下降来实现,这是一种优化算法,它会朝着减少损失的方向更新模型参数。

深度学习训练过程

因此,由于计算和内存限制,数据很少一次传递一个样本或一次性传递所有样本。相反,数据以称为“批次”的块形式传递。

数据以称为“批次”的块形式传递

Source: Medium

在机器学习和神经网络训练的早期阶段,有两种常见的数据处理方法:

1. 随机学习

这种方法每次使用单个训练样本更新模型权重。虽然它能够提供最快的权重更新速度,并且在流数据应用中非常有用,但它也存在一些明显的缺点:

  • 由于梯度噪声,更新过程极不稳定。
  • 这可能导致收敛效果不佳,并延长整体训练时间。
  • 不适合使用 GPU 并行处理。

2. 全批量学习

这种方法使用整个训练数据集来计算梯度,并对模型参数执行单次更新。它具有非常稳定的梯度和收敛行为,这是它的巨大优势。然而,说到它的缺点,以下是一些:

  • 内存占用极高,尤其是在大型数据集的情况下。
  • 由于需要等待处理整个数据集,因此每次迭代的计算速度很慢。
  • 对于动态增长的数据集或在线学习环境来说,它缺乏灵活性。

随着数据集越来越大,神经网络也越来越深,这些方法在实践中被证明效率低下。内存限制和计算效率低下迫使研究人员和工程师寻找一个折中方案:小批量训练。

现在,让我们尝试理解什么是批处理和小批量处理。

什么是批处理?

对于每个训练步骤,整个数据集都会一次性输入模型,这个过程称为批处理。这项技术的另一个名称是全批量梯度下降。

什么是批处理?

Source: Medium

主要特点:

  • 使用整个数据集计算梯度。
  • 每个迭代包含一次前向和后向迭代。
  • 占用大量内存。
  • 通常每个迭代速度较慢,但​​稳定。

使用场景:

  • 当数据集完全适合现有内存(适当拟合)时。
  • 当数据集较小时。

什么是小批量训练?

小批量训练是批量梯度下降和随机梯度下降之间的一种折衷方法。它使用数据的子集或部分,而不是整个数据集或单个样本。

主要特点:

  • 将数据集拆分成更小的组,例如 32、64 或 128 个样本。
  • 在每个小批量训练后执行梯度更新。
  • 实现更快的收敛和更好的泛化。

使用场景:

  • 适用于大型数据集。
  • 当 GPU/TPU 可用时。

让我们以表格形式总结上述算法:

类型 批量大小 更新频率 内存需求 收敛性 噪声
全量批(Full-Batch) 整个数据集 每轮(epoch)一次 稳定、慢
小批量(Mini-Batch) 例如 32 / 64 / 128 每个批次后 平衡
随机(Stochastic) 单样本 每个样本后 嘈杂、快

梯度下降的工作原理

梯度下降的工作原理是不断迭代更新模型参数,以最小化损失函数。在每一步中,我们计算损失函数相对于模型参数的梯度,并朝着梯度的反方向移动。

梯度下降的工作原理

Source: Builtin

更新规则:θ = θ − η ⋅ ∇θJ(θ)

其中:

  • θ 为模型参数
  • η 为学习率
  • ∇θJ(θ) 为损失函数的梯度

简单类比

想象一下,你蒙着眼睛,试图滑到游乐场滑梯的最低点。你用脚感受坡度,然后一步步小步地往下走。脚下坡度的陡峭程度决定了每一步的走向。由于我们是逐渐下降的,这类似于梯度下降。模型会朝着误差减少最多的方向移动。

全批量下降类似于使用巨大的滑梯地图来确定最佳行动方案。你问朋友你想去哪里,然后在随机下降中迈出一步。在行动之前,你会在小批量下降中与一小组人商量。

数学公式

设 X ∈ R n×d 为包含 n 个样本和 d 个特征的输入数据。

全批量梯度下降法

全批量梯度下降法 

小批量梯度下降法

小批量梯度下降法 

实际示例

假设尝试根据评论估算产品价格。

如果您在做出选择之前阅读了全部 1000 条评论,则为全批量。仅阅读一条评论后做出决定是随机的。小批量是指您只阅读少量评论(例如 32 条或 64 条)然后估算价格。小批量在足够可靠以做出明智决策和足够快速以快速采取行动之间取得了良好的平衡。小批量提供了良好的平衡:它足够快以快速采取行动,并且足够可靠以做出明智的决策。

实际实施

我们将使用 PyTorch 演示批量处理和小批量处理之间的区别。通过这个实现,我们将能够了解这两种算法如何帮助我们收敛到最优的全局最小值。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import matplotlib.pyplot as plt
# Create synthetic data
X = torch.randn(1000, 10)
y = torch.randn(1000, 1)
# Define model architecture
def create_model():
return nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 1)
)
# Loss function
loss_fn = nn.MSELoss()
# Mini-Batch Training
model_mini = create_model()
optimizer_mini = optim.SGD(model_mini.parameters(), lr=0.01)
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)
mini_batch_losses = []
for epoch in range(64):
epoch_loss = 0
for batch_X, batch_y in dataloader:
optimizer_mini.zero_grad()
outputs = model_mini(batch_X)
loss = loss_fn(outputs, batch_y)
loss.backward()
optimizer_mini.step()
epoch_loss += loss.item()
mini_batch_losses.append(epoch_loss / len(dataloader))
# Full-Batch Training
model_full = create_model()
optimizer_full = optim.SGD(model_full.parameters(), lr=0.01)
full_batch_losses = []
for epoch in range(64):
optimizer_full.zero_grad()
outputs = model_full(X)
loss = loss_fn(outputs, y)
loss.backward()
optimizer_full.step()
full_batch_losses.append(loss.item())
# Plotting the Loss Curves
plt.figure(figsize=(10, 6))
plt.plot(mini_batch_losses, label='Mini-Batch Training (batch_size=64)', marker='o')
plt.plot(full_batch_losses, label='Full-Batch Training', marker='s')
plt.title('Training Loss Comparison')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset import matplotlib.pyplot as plt # Create synthetic data X = torch.randn(1000, 10) y = torch.randn(1000, 1) # Define model architecture def create_model(): return nn.Sequential( nn.Linear(10, 50), nn.ReLU(), nn.Linear(50, 1) ) # Loss function loss_fn = nn.MSELoss() # Mini-Batch Training model_mini = create_model() optimizer_mini = optim.SGD(model_mini.parameters(), lr=0.01) dataset = TensorDataset(X, y) dataloader = DataLoader(dataset, batch_size=64, shuffle=True) mini_batch_losses = [] for epoch in range(64): epoch_loss = 0 for batch_X, batch_y in dataloader: optimizer_mini.zero_grad() outputs = model_mini(batch_X) loss = loss_fn(outputs, batch_y) loss.backward() optimizer_mini.step() epoch_loss += loss.item() mini_batch_losses.append(epoch_loss / len(dataloader)) # Full-Batch Training model_full = create_model() optimizer_full = optim.SGD(model_full.parameters(), lr=0.01) full_batch_losses = [] for epoch in range(64): optimizer_full.zero_grad() outputs = model_full(X) loss = loss_fn(outputs, y) loss.backward() optimizer_full.step() full_batch_losses.append(loss.item()) # Plotting the Loss Curves plt.figure(figsize=(10, 6)) plt.plot(mini_batch_losses, label='Mini-Batch Training (batch_size=64)', marker='o') plt.plot(full_batch_losses, label='Full-Batch Training', marker='s') plt.title('Training Loss Comparison') plt.xlabel('Epoch') plt.ylabel('Loss') plt.legend() plt.grid(True) plt.tight_layout() plt.show()
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import matplotlib.pyplot as plt
# Create synthetic data
X = torch.randn(1000, 10)
y = torch.randn(1000, 1)
# Define model architecture
def create_model():
return nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 1)
)
# Loss function
loss_fn = nn.MSELoss()
# Mini-Batch Training
model_mini = create_model()
optimizer_mini = optim.SGD(model_mini.parameters(), lr=0.01)
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)
mini_batch_losses = []
for epoch in range(64):
epoch_loss = 0
for batch_X, batch_y in dataloader:
optimizer_mini.zero_grad()
outputs = model_mini(batch_X)
loss = loss_fn(outputs, batch_y)
loss.backward()
optimizer_mini.step()
epoch_loss += loss.item()
mini_batch_losses.append(epoch_loss / len(dataloader))
# Full-Batch Training
model_full = create_model()
optimizer_full = optim.SGD(model_full.parameters(), lr=0.01)
full_batch_losses = []
for epoch in range(64):
optimizer_full.zero_grad()
outputs = model_full(X)
loss = loss_fn(outputs, y)
loss.backward()
optimizer_full.step()
full_batch_losses.append(loss.item())
# Plotting the Loss Curves
plt.figure(figsize=(10, 6))
plt.plot(mini_batch_losses, label='Mini-Batch Training (batch_size=64)', marker='o')
plt.plot(full_batch_losses, label='Full-Batch Training', marker='s')
plt.title('Training Loss Comparison')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

unnamed

在这里,我们可以将两种策略的训练损失随时间的变化可视化,以观察差异。我们可以观察到:

  1. 小批量训练通常表现出更平滑、更快的初始进度,因为它更新权重的频率更高。

训练损失随时间的变化可视化

  1. 全批次训练可能更新较少,但其梯度更稳定。

在实际应用中,通常优先使用小批量,以获得更好的泛化能力和计算效率。

如何选择批次大小?

我们设置的批次大小是一个超参数,需要根据模型架构和数据集大小进行实验。确定最佳批次大小的有效方法是实施交叉验证策略。

下表可以帮助您做出此决定:

特性 全量批训练(Full-Batch) 小批量训练(Mini-Batch)
梯度稳定性
收敛速度
内存占用
并行化程度
训练时间 较低(优化)
泛化能力 可能过拟合 较好

注意:如上所述,batch_size 是一个超参数,需要根据模型训练进行微调。因此,有必要了解较低和较高批次大小值的性能。

小批次大小

较小的批次大小值通常介于 1 到 64 之间。由于梯度更新频率更高(每批次),模型可以更早地开始学习,并快速更新权重,因此更新速度更快。恒定的权重更新意味着一个周期需要更多次迭代,这会增加计算开销,从而延长训练时间。

梯度估计中的“噪声”有助于避免尖锐的局部最小值和过拟合,通常可以提高测试性能,从而展现出更好的泛化能力。此外,由于这些噪声的存在,收敛可能会不稳定。如果学习率很高,这些噪声梯度可能会导致模型超调并发散。

将小批次大小想象成朝着目标迈出频繁但不稳定的步伐。你可能不会走直线,但你可能会发现一条总体上更好的路径。

大批量

可以考虑使用 128 及以上的大批量。较大的批量可以实现更稳定的收敛,因为每个批量包含的样本越多,平均梯度就越平滑,越接近损失函数的真实梯度。如果梯度平滑,模型可能无法摆脱平坦或尖锐的局部最小值。

这样,完成一个迭代周期所需的迭代次数就越少,从而可以加快训练速度。大批量需要更多内存,这需要 GPU 来处理这些巨大的数据块。虽然每个迭代周期的速度更快,但由于更新步长较小且梯度噪声较少,可能需要更多迭代周期才能收敛。

大批量就像按照预先规划好的步数稳步朝着目标前进,但有时你可能会因为没有探索所有其他路径而陷入困境。

整体差异

这里有一个比较全批量和小批量训练的综合表格。

维度 全量批训练(Full-Batch Training) 小批量训练(Mini-Batch Training)
优点 Pros • 梯度稳定且精确
• 损失计算精确
• 由于频繁更新,训练速度更快
• 支持 GPU/TPU 并行
• 噪声有助于提升泛化能力
缺点 Cons • 内存消耗高
• 每轮训练速度慢
• 不适合大规模数据
• 梯度更新噪声大
• 需要调节批大小
• 稳定性略差
适用场景 Use Cases • 内存可容纳的小型数据集<br>• 需要高度可复现的实验 • 大规模数据集
• 在 GPU/TPU 上的深度学习
• 实时/流式训练管线

实用建议

在批量训练和小批量训练之间进行选择时,请考虑以下几点:

  • 如果数据集较小(少于 10,000 个样本)且内存不成问题,则全批量梯度下降法可能是可行的,因为它具有稳定性和精确的收敛性。
  • 对于中大型数据集(例如,100,000 个以上样本),批量大小在 32 到 256 之间的小批量训练通常是最佳选择。
  • 在小批量训练的每个 epoch 之前使用 shuffle 操作,以避免学习数据顺序模式。
  • 使用学习速率调度或自适应优化器(例如,Adam、RMSProp 等)来帮助缓解小批量训练中的噪声更新。

小结

批处理和小批量训练是深度学习模型优化中必须掌握的基础概念。虽然全批量训练能够提供最稳定的梯度,但正如本文开头所述,由于内存和计算能力的限制,它很少适用于现代大规模数据集。另一方面,小批量训练则实现了恰到好处的平衡,借助 GPU/TPU 加速,提供了良好的速度、泛化能力和兼容性。因此,它已成为大多数实际深度学习应用的事实上的标准。

选择最佳批量大小并非一刀切的决策。它应该以数据集的大小以及现有的内存和硬件资源为指导。此外,还需要考虑优化器的选择以及所需的泛化能力和收敛速度,例如 learning_rate 和 decay_rate。通过理解这些动态因素,并利用学习速率调度、自适应优化器(如 ADAM)和批量大小调整等工具,我们可以更快、更准确、更高效地创建模型。

评论留言