本次分享pytorch中几种常用的优化器,并进行互相比较。
在 PyTorch 中,torch.optim
提供了多种优化器用于神经网络训练。每种优化器背后有不同的更新规则和机制,旨在适应不同的训练需求。以下是五种常见优化器(SGD、Momentum、AdaGrad、RMSprop、Adam)的原理、作用、优缺点及应用场景。
SGD 是最经典的优化算法,基于梯度下降的思想。每次参数更新时,SGD 使用当前参数的梯度对参数进行调整。其更新规则如下:
其中,
是学习率,
是当前参数点的梯度。
用于优化损失函数,更新神经网络中的权重参数。
Momentum 是对 SGD 的改进,通过引入动量项来加速梯度下降,尤其在面对陡峭的梯度或局部最小值时表现更好。动量项有助于保持一定的“惯性”,从而增加当前更新的速度。更新规则如下:
其中,
是动量参数,
是学习率。
加速收敛过程,尤其在梯度变化较小的方向上。
需要调节,最佳值依赖于具体问题。
AdaGrad 通过对每个参数使用不同的学习率,使得参数的更新速度自适应地调整。对于频繁出现的特征,AdaGrad 会减少学习率;对于稀疏特征,则增加学习率。具体来说,AdaGrad 会对梯度的历史平方和进行累加,动态调整每个参数的学习率:
其中,
是防止除零错误的小常数。
适用于具有稀疏特征的数据(如文本处理、推荐系统等),能够让模型快速适应不同特征的梯度变化。
RMSprop 是对 AdaGrad 的改进,通过引入衰减因子来防止学习率过快减小。它通过对梯度平方的指数加权平均来调整每个参数的学习率:
其中,
是衰减因子,
是学习率,
是防止除零错误的小常数。
适用于非平稳目标函数(例如递增或递减的动态任务)。特别适用于处理RNN(递归神经网络)和时间序列数据。
和学习率等超参数。
Adam 结合了 Momentum 和 RMSprop 的思想,通过计算梯度的一阶矩(动量)和二阶矩(梯度平方的均值)来进行自适应更新。更新规则如下:
其中,
和
是一阶和二阶矩的衰减率,
是学习率,
是防止除零的常数。
适用于各种类型的神经网络,尤其在大规模数据集上表现优异。
优化器 | 原理 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
SGD | 随机梯度下降 | 实现简单,计算开销小 | 收敛慢,容易震荡 | 基础任务,特别是小规模训练任务 |
Momentum | 加入动量 | 加速收敛,避免局部最小值 | 动量参数选择困难 | 适合梯度波动较大的任务 |
AdaGrad | 自适应调整每个参数的学习率 | 自动调整学习率,适合稀疏数据 | 学习率逐步减小,可能导致训练后期收敛缓慢 | 处理稀疏数据(如 NLP) |
RMSprop | 使用梯度平方的指数加权平均 | 防止学习率过早减小,适合动态任务 | 需要调节超参数 | 适用于非平稳目标函数,尤其是 RNN 和时间序列任务 |
Adam | 结合动量和自适应学习率 | 快速收敛,超参数调节简单 | 对学习率敏感,可能过拟合 | 适用于各种神经网络,尤其是大规模数据集训练 |
import torch
import torch.nn
import torch.utils.data as Data
import matplotlib
import matplotlib.pyplot as plt
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
#准备建模数据
x = torch.unsqueeze(torch.linspace(-1, 1, 500), dim=1)
y = x.pow(3)
#设置超参数
LR = 0.01
batch_size = 15
epoches = 5
torch.manual_seed(10)
#设置数据加载器
dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(
dataset=dataset,
batch_size=batch_size,
shuffle=True,
num_workers=2)
#搭建神经网络
class Net(torch.nn.Module):
def __init__(self, n_input, n_hidden, n_output):
super(Net, self).__init__()
self.hidden_layer = torch.nn.Linear(n_input, n_hidden)
self.output_layer = torch.nn.Linear(n_hidden, n_output)
def forward(self, input):
x = torch.relu(self.hidden_layer(input))
output = self.output_layer(x)
return output
#训练模型并输出折线图
def train():
net_SGD = Net(1, 10, 1)
net_Momentum = Net(1, 10, 1)
net_AdaGrad = Net(1, 10, 1)
net_RMSprop = Net(1, 10, 1)
net_Adam = Net(1, 10, 1)
nets = [net_SGD, net_Momentum, net_AdaGrad, net_RMSprop, net_Adam]
#定义优化器
optimizer_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
optimizer_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.6)
optimizer_AdaGrad = torch.optim.Adagrad(net_AdaGrad.parameters(), lr=LR, lr_decay=0)
optimizer_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
optimizer_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
optimizers = [optimizer_SGD, optimizer_Momentum, optimizer_AdaGrad, optimizer_RMSprop, optimizer_Adam]
#定义损失函数
loss_function = torch.nn.MSELoss()
losses = [[], [], [], [], []]
for epoch in range(epoches):
for step, (batch_x, batch_y) in enumerate(loader):
for net, optimizer, loss_list in zip(nets, optimizers, losses):
pred_y = net(batch_x)
loss = loss_function(pred_y, batch_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_list.append(loss.data.numpy())
plt.figure(figsize=(12,7))
labels = ['SGD', 'Momentum', 'AdaGrad', 'RMSprop', 'Adam']
for i, loss in enumerate(losses):
plt.plot(loss, label=labels[i])
plt.legend(loc='upper right',fontsize=15)
plt.tick_params(labelsize=13)
plt.xlabel('训练步骤',size=15)
plt.ylabel('模型损失',size=15)
plt.ylim((0, 0.3))
plt.show()
if __name__ == "__main__":
train()