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则在特定场景中可以进行尝试和探索。同时,不同的优化器在不同的数据集和模型结构上的表现可能会有所不同,需要根据具体情况进行实验和调整。

相关推荐
qyz_hr几秒前
央国企人力资源穿透式监管的关键领域、核心机制与数智化路径研究
大数据·人工智能
打工仔折腾 AI2 分钟前
网易UU远程实测:手机控电脑做Python开发和Agent调试的真实体验
人工智能·后端·python·智能手机·性能优化·电脑·ai agent 实战
2601_962381133 分钟前
做城市历史视频时,AI 自动生成变迁动效的实现路径拆解
人工智能·音视频
知几蜗牛3 分钟前
从Holo4理解GUI Agent的坐标离散化、反映射与误差
人工智能
haliu3 分钟前
【FHE】(十三):位级可复现性——4 线程与 8 线程为什么算出不同的结果
人工智能·嵌入式·c·fhe·推理引擎·c11·边缘推理·同态加密推理
Token掘金室5 分钟前
MCP 怎么接大模型?Model Context Protocol 接入教程
人工智能
谢亮_vipxieliang5 分钟前
容器日志收集与管理:从 stdout 规范到 ELK/Loki 落地
运维·网络·人工智能·elk·docker·容器
YOLO数据集集合8 分钟前
风力发电机检测数据集 | 风机检测 电缆塔识别 风电运维 无人机巡检 9122期
运维·人工智能·目标检测·目标跟踪·无人机·风力发电·电力巡检
像风一样自由20208 分钟前
42.VueReactNextjs如何为AI应用设计前端交互
前端·人工智能·大模型·交互·rag·智能体