PyTorch不同优化器比较

常见优化器介绍

  • SGD(随机梯度下降):是最基本的优化器之一,通过在每次迭代中沿着损失函数的负梯度方向更新模型参数。在大规模数据集上计算效率高,对于凸问题和简单模型效果较好。但收敛速度慢,容易陷入局部最小值,对学习率的选择较为敏感,不合适的学习率可能导致训练无法收敛或收敛到较差的解。

  • Adagrad:为每个参数自适应地调整学习率,根据参数的历史梯度平方和来调整当前的学习率,使得在训练过程中,频繁更新的参数学习率逐渐减小,而不常更新的参数学习率相对较大,适合处理稀疏数据。但学习率会逐渐降低,导致训练后期学习非常慢,可能需要很长时间才能收敛。

  • Adadelta:解决了Adagrad学习率逐渐降低的问题,通过使用梯度平方的指数加权平均来代替全部梯度的平方和,动态地调整每个参数的学习率,不需要手动设置学习率。与Adam相比,在某些情况下收敛速度稍慢,但在一些特定场景中表现较好。

  • RMSprop:与Adadelta类似,通过将学习率除以梯度平方的指数加权平均来调整学习率,计算上更为简洁,收敛速度较快,在处理非平稳目标时表现较好,常用于循环神经网络等。但与Adam类似,在某些情况下可能需要更精细的超参数调整。

  • Adam:同时使用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率,一阶矩用来控制模型更新的方向,二阶矩控制步长,计算效率高,收敛速度快,自动调整学习率的特性使得它适用于大多数情况。但在某些情况下可能不如SGD及其变体具有好的泛化能力,需要调整超参数,如β1、β2、ε等。

  • AdamW(带权重衰减的Adam):在Adam的基础上增加了权重衰减项,有助于正则化模型,防止过拟合,对于大型模型训练和容易过拟合的任务效果较好。与Adam类似,需要调整超参数。

  • Adamax:是Adam的一种变体,将Adam的二范数(二阶矩估计)推广到无穷范数,具有更大的学习率范围和更好的稳定性。在某些情况下可能不如Adam或SGD表现得好,但在学习率选择较为困难时是一个不错的选择。

优化器比较实验

以下使用一个简单的线性回归模型在一个合成数据集上进行实验,对比不同优化器的收敛速度和性能。

import torch

import torch.optim as optim

import torch.nn as nn

import matplotlib.pyplot as plt

import numpy as np

生成合成数据集

x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)

y = x.pow(2) + 0.1 * torch.normal(torch.zeros(*x.size()))

定义线性回归模型

class Net(nn.Module):

def init(self):

super(Net, self).init()

self.linear = nn.Linear(1, 1)

def forward(self, x):

return self.linear(x)

定义不同的优化器

net_SGD = Net()

net_Adagrad = Net()

net_Adadelta = Net()

net_RMSprop = Net()

net_Adam = Net()

net_AdamW = Net()

net_Adamax = Net()

opt_SGD = optim.SGD(net_SGD.parameters(), lr=0.01)

opt_Adagrad = optim.Adagrad(net_Adagrad.parameters(), lr=0.01)

opt_Adadelta = optim.Adadelta(net_Adadelta.parameters(), lr=0.01)

opt_RMSprop = optim.RMSprop(net_RMSprop.parameters(), lr=0.01, alpha=0.9)

opt_Adam = optim.Adam(net_Adam.parameters(), lr=0.001, betas=(0.9, 0.99))

opt_AdamW = optim.AdamW(net_AdamW.parameters(), lr=0.001, betas=(0.9, 0.99))

opt_Adamax = optim.Adamax(net_Adamax.parameters(), lr=0.001, betas=(0.9, 0.99))

训练模型并记录损失

losses_SGD = []

losses_Adagrad = []

losses_Adadelta = []

losses_RMSprop = []

losses_Adam = []

losses_AdamW = []

losses_Adamax = []

for epoch in range(100):

SGD

optimizer_SGD.zero_grad()

output_SGD = net_SGD(x)

loss_SGD = nn.MSELoss()(output_SGD, y)

loss_SGD.backward()

optimizer_SGD.step()

losses_SGD.append(loss_SGD.item())

Adagrad

optimizer_Adagrad.zero_grad()

output_Adagrad = net_Adagrad(x)

loss_Adagrad = nn.MSELoss()(output_Adagrad, y)

loss_Adagrad.backward()

optimizer_Adagrad.step()

losses_Adagrad.append(loss_Adagrad.item())

Adadelta

optimizer_Adadelta.zero_grad()

output_Adadelta = net_Adadelta(x)

loss_Adadelta = nn.MSELoss()(output_Adadelta, y)

loss_Adadelta.backward()

optimizer_Adadelta.step()

losses_Adadelta.append(loss_Adadelta.item())

RMSprop

optimizer_RMSprop.zero_grad()

output_RMSprop = net_RMSprop(x)

loss_RMSprop = nn.MSELoss()(output_RMSprop, y)

loss_RMSprop.backward()

optimizer_RMSprop.step()

losses_RMSprop.append(loss_RMSprop.item())

Adam

optimizer_Adam.zero_grad()

output_Adam = net_Adam(x)

loss_Adam = nn.MSELoss()(output_Adam, y)

loss_Adam.backward()

optimizer_Adam.step()

losses_Adam.append(loss_Adam.item())

AdamW

optimizer_AdamW.zero_grad()

output_AdamW = net_AdamW(x)

loss_AdamW = nn.MSELoss()(output_AdamW, y)

loss_AdamW.backward()

optimizer_AdamW.step()

losses_AdamW.append(loss_AdamW.item())

Adamax

optimizer_Adamax.zero_grad()

output_Adamax = net_Adamax(x)

loss_Adamax = nn.MSELoss()(output_Adamax, y)

loss_Adamax.backward()

optimizer_Adamax.step()

losses_Adamax.append(loss_Adamax.item())

绘制损失曲线

plt.plot(losses_SGD, label='SGD')

plt.plot(losses_Adagrad, label='Adagrad')

plt.plot(losses_Adadelta, label='Adadelta')

plt.plot(losses_RMSprop, label='RMSprop')

plt.plot(losses_Adam, label='Adam')

plt.plot(losses_AdamW, label='AdamW')

plt.plot(losses_Adamax, label='Adamax')

plt.xlabel('Epoch')

plt.ylabel('Loss')

plt.title('Comparison of PyTorch Optimizers')

plt.legend()

plt.show()

实验结果分析

  • 收敛速度:在这个简单的实验中,Adam、RMSprop和Adamax在初期的收敛速度相对较快,能够在较少的迭代次数内使损失快速下降。而SGD的收敛速度相对较慢,需要更多的迭代次数才能达到类似的损失值。Adagrad在前期下降速度尚可,但由于学习率逐渐降低,后期收敛速度明显变慢。Adadelta的收敛速度较为稳定,但整体相对Adam等稍慢。

  • 稳定性:Adam、RMSprop和Adadelta在训练过程中相对稳定,损失值的波动较小。而SGD由于其随机性和对学习率的敏感性,损失值可能会出现较大的波动。Adagrad在后期由于学习率过小,可能会导致训练停滞不前,出现不稳定的情况。AdamW在稳定性上与Adam类似,但由于加入了权重衰减,在一定程度上可以防止模型在后期过拟合而导致的不稳定。

  • 泛化能力:一般来说,SGD及其变体在一些大规模的数据集和复杂模型上,如果调参得当,可能会具有较好的泛化能力。Adam等自适应学习率的优化器在大多数情况下能够快速收敛,但在某些特定的数据集和模型结构上,可能会出现过拟合的情况,导致泛化能力下降。不过,通过调整超参数和加入正则化项等方法,可以在一定程度上提高其泛化能力。

  • 超参数调整:SGD通常需要手动调整学习率和其他超参数,如动量等,对超参数的选择较为敏感。Adagrad、Adadelta和RMSprop等虽然在一定程度上自动调整学习率,但也可能需要根据具体情况调整一些超参数。Adam、AdamW和Adamax需要调整的超参数相对较多,如β1、β2、ε等,但通常在默认值附近进行微调就可以取得较好的效果。

优化器损失曲线对比

根据上述实验和分析,在实际应用中,对于简单模型和大规模数据集,SGD可能是一个不错的选择,如果对收敛速度有要求,可以尝试使用带动量的SGD。对于复杂模型和需要自动调整学习率的情况,Adam、RMSprop等自适应学习率的优化器通常表现较好。如果担心过拟合,可以选择AdamW。在处理稀疏数据时,Adagrad可能会更合适。而Adadelta和Adamax则在特定场景中可以进行尝试和探索。同时,不同的优化器在不同的数据集和模型结构上的表现可能会有所不同,需要根据具体情况进行实验和调整。

相关推荐
arbboter5 分钟前
【AI插件开发】Notepad++ AI插件开发1.0发布和使用说明
人工智能·大模型·notepad++·ai助手·ai插件·aicoder·notepad++插件开发
BB_CC_DD5 分钟前
四. 以Annoy算法建树的方式聚类清洗图像数据集,一次建树,无限次聚类搜索,提升聚类搜索效率。(附完整代码)
深度学习·算法·聚类
IT_Octopus17 分钟前
AI工程pytorch小白TorchServe部署模型服务
人工智能·pytorch·python
果冻人工智能22 分钟前
AI军备竞赛:我们是不是正在造一个无法控制的神?
人工智能
暴龙胡乱写博客27 分钟前
OpenCV---图像预处理(四)
人工智能·opencv·计算机视觉
程序员辣条35 分钟前
深度测评 RAG 应用评估框架:指标最全面的 RAGas
人工智能·程序员
curdcv_po36 分钟前
字节跳动Trae:一款革命性的免费AI编程工具完全评测
人工智能·trae
程序员辣条37 分钟前
为什么需要提示词工程?什么是提示词工程(prompt engineering)?为什么需要提示词工程?收藏我这一篇就够了!
人工智能·程序员·产品经理
孔令飞41 分钟前
Go:终于有了处理未定义字段的实用方案
人工智能·云原生·go
清流君1 小时前
【MySQL】数据库 Navicat 可视化工具与 MySQL 命令行基本操作
数据库·人工智能·笔记·mysql·ue5·数字孪生