当前位置: 首页 > article >正文

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


http://www.kler.cn/a/467516.html

相关文章:

  • VScode SSH 错误:Got bad result from install script 解決
  • Spark是什么?Flink和Spark区别
  • Vue 全局事件总线:Vue 2 vs Vue 3 实现
  • 云备份项目--服务端编写
  • Level DB --- BloomFilterPolicy
  • Uniapp中使用`wxml-to-canvas`开发DOM生成图片功能
  • 自动生成RESTful API——Spring Data Rest
  • C# 内置值类型
  • 基于Spring Boot的紧急物资管理系统
  • ARP攻击的原理和实现 (网络安全)
  • C#调用Lua
  • 监控易:确保统一运维管理平台长期稳定高效运行
  • 计算机网络基础(7)中科大郑铨老师笔记
  • 机器人领域的一些仿真器
  • 基于YOLOv8的道路缺陷检测系统
  • 【Golang 面试题】每日 3 题(十五)
  • 【Motion Builder】配置c++插件开发环境
  • 【赵渝强老师】MongoDB写入数据的过程
  • 【redisson】redisson分布式锁原理分析
  • 【深度学习】交叉熵:从理论到实践
  • 专业140+总分400+中国海洋大学819信号与系统考研综合考研经验中海大电子信息与通信工程,真题,。大纲,参考书。
  • 【go类库分享】go rate 原生标准限速库
  • 旷视科技Java面试题及参考答案
  • AWS IAM基础知识
  • ‘元素.style.样式名‘获取不到样式,应该使用Window.getComputedStyle()获取正真的样式
  • 什么是 AJAX ?