趋近智
一个实验比较了自适应优化算法(如AdaGrad、RMSprop和Adam)与基本方法(如SGD和带动量的SGD)在实际使用中的表现。实践指南将指导你设置并运行一个实验,在一个常见任务上比较这些优化器,使你能够亲眼观察它们对训练速度和模型性能的影响。
目的不仅是查看在特定问题上哪个优化器“胜出”,而是要明白它们不同的更新机制如何导致训练过程中可观察到的差异,例如收敛速度和稳定性。
我们将使用一个简单任务:对MNIST数据集中的手写数字进行分类。该数据集足够复杂,可以突显不同优化器之间的差异,但又足够简单,可以快速训练。我们将为此任务使用一个基本的多层感知机(MLP)。
首先,我们使用PyTorch定义神经网络架构:
import torch
import torch.nn as nn
import torch.nn.functional as F
class SimpleMLP(nn.Module):
def __init__(self, input_size=784, hidden_size=128, num_classes=10):
super(SimpleMLP, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_size, num_classes)
def forward(self, x):
# 展平图像
x = x.view(x.size(0), -1)
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
# 这里没有softmax,因为CrossEntropyLoss需要原始的logits
return out
# 定义输入大小(MNIST图像为28x28 = 784像素)
input_size = 784
hidden_size = 128
num_classes = 10
接下来,我们需要加载MNIST数据集。我们将为此使用torchvision。我们还将为训练和验证创建数据加载器。
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
# 数据变换
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)) # MNIST均值和标准差
])
# 加载MNIST数据集
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
val_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
# 创建数据加载器
batch_size = 64
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(dataset=val_dataset, batch_size=batch_size, shuffle=False)
我们将使用标准的交叉熵损失函数,适用于多类别分类。
criterion = nn.CrossEntropyLoss()
我们的实验将涉及使用四种不同的优化器,在MNIST训练数据上训练我们SimpleMLP模型的相同实例:
对于每个优化器,我们将:
SimpleMLP模型的新实例,以确保公平的起始条件。这是训练函数的概览。我们会将模型、数据加载器、损失函数和特定的优化器实例传递给此函数。
import torch.optim as optim
from collections import defaultdict
def train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=10):
"""训练模型并返回损失和准确度历史。"""
history = defaultdict(list)
print(f"正在使用优化器训练: {optimizer.__class__.__name__}")
for epoch in range(num_epochs):
model.train() # 将模型设置为训练模式
running_loss = 0.0
for i, (images, labels) in enumerate(train_loader):
# 将参数梯度归零
optimizer.zero_grad()
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播并优化
loss.backward()
optimizer.step()
running_loss += loss.item()
# 计算当前周期的平均训练损失
epoch_loss = running_loss / len(train_loader)
history['train_loss'].append(epoch_loss)
# 验证阶段
model.eval() # 将模型设置为评估模式
correct = 0
total = 0
val_loss = 0.0
with torch.no_grad():
for images, labels in val_loader:
outputs = model(images)
loss = criterion(outputs, labels)
val_loss += loss.item()
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
epoch_acc = 100 * correct / total
avg_val_loss = val_loss / len(val_loader)
history['val_loss'].append(avg_val_loss)
history['val_accuracy'].append(epoch_acc)
print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {epoch_loss:.4f}, Val Loss: {avg_val_loss:.4f}, Val Accuracy: {epoch_acc:.2f}%')
print("-" * 30)
return history
# --- 实验执行 ---
num_epochs = 10
learning_rate = 0.001
momentum = 0.9 # 用于带动量的SGD
optimizers_to_test = {
"SGD": lambda params: optim.SGD(params, lr=learning_rate),
"Momentum": lambda params: optim.SGD(params, lr=learning_rate, momentum=momentum),
"RMSprop": lambda params: optim.RMSprop(params, lr=learning_rate),
"Adam": lambda params: optim.Adam(params, lr=learning_rate)
}
results = {}
for name, optimizer_lambda in optimizers_to_test.items():
# 为每个优化器初始化一个全新的模型
model = SimpleMLP(input_size, hidden_size, num_classes)
optimizer_instance = optimizer_lambda(model.parameters())
history = train_model(model, train_loader, val_loader, criterion, optimizer_instance, num_epochs=num_epochs)
results[name] = history
# results 字典现在存储了每个优化器的训练/验证历史
运行训练循环后,results字典包含每个优化器每个周期的训练损失和验证准确度。让我们将这些结果可视化,以比较它们的表现。
我们将绘制训练损失曲线和验证准确度曲线。
比较不同优化器在10个周期内的训练损失曲线。
比较不同优化器在10个周期内的验证准确度曲线。
从图表(使用典型示例结果)中,我们可以看到一些模式:
重要注意事项:
这次实践练习显现了常见优化算法之间的具体差异。Adam和RMSprop等自适应方法通常提供更快的收敛速度,使它们成为许多深度学习任务的高效选项。带动量的SGD仍然是一个强劲的竞争者,特别是在仔细调整后,有时因其在某些情况下的潜在泛化优势而受到青睐。
通过实验了解这些行为有助于你在为自己的深度学习项目选择和调整优化器时做出明智的决定。请记住,虽然像Adam这样的默认设置在许多情况下都表现良好,但比较其他选项有时可以为你的特定问题带来更好的结果。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造