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

【PyTorch入门】 PyTorch不同优化器的比较

本次分享pytorch中几种常用的优化器,并进行互相比较。

PyTorch 优化器原理及优缺点分析

在 PyTorch 中,torch.optim 提供了多种优化器用于神经网络训练。每种优化器背后有不同的更新规则和机制,旨在适应不同的训练需求。以下是五种常见优化器(SGD、Momentum、AdaGrad、RMSprop、Adam)的原理、作用、优缺点及应用场景。

1. SGD (Stochastic Gradient Descent) 随机梯度下降

原理:

SGD 是最经典的优化算法,基于梯度下降的思想。每次参数更新时,SGD 使用当前参数的梯度对参数进行调整。其更新规则如下:

θ t + 1 = θ t − η ⋅ ∇ θ J ( θ t ) \theta_{t+1} = \theta_t - \eta \cdot \nabla_{\theta} J(\theta_t) θt+1=θtηθJ(θt)

其中, η \eta η 是学习率, ∇ θ J ( θ t ) \nabla_{\theta} J(\theta_t) θJ(θt) 是当前参数点的梯度。

作用:

用于优化损失函数,更新神经网络中的权重参数。

优缺点:

  • 优点
    • 实现简单,计算资源消耗小。
    • 对于某些问题,收敛较快。
  • 缺点
    • 收敛缓慢:每次更新仅依赖单一样本或小批量数据,可能导致目标函数震荡,尤其在复杂的优化空间中。
    • 灵敏度高:学习率的选择非常关键,过大会导致发散,过小则收敛缓慢。

2. Momentum (带动量的梯度下降)

原理:

Momentum 是对 SGD 的改进,通过引入动量项来加速梯度下降,尤其在面对陡峭的梯度或局部最小值时表现更好。动量项有助于保持一定的“惯性”,从而增加当前更新的速度。更新规则如下:

v t + 1 = β v t + ( 1 − β ) ∇ θ J ( θ t ) v_{t+1} = \beta v_t + (1 - \beta) \nabla_{\theta} J(\theta_t) vt+1=βvt+(1β)θJ(θt)
θ t + 1 = θ t − η v t + 1 \theta_{t+1} = \theta_t - \eta v_{t+1} θt+1=θtηvt+1

其中, β \beta β 是动量参数, η \eta η 是学习率。

作用:

加速收敛过程,尤其在梯度变化较小的方向上。

优缺点:

  • 优点
    • 有助于突破局部最小值,优化过程中更加稳定,避免梯度震荡。
    • 相较于普通 SGD,收敛速度更快。
  • 缺点
    • 动量参数 β \beta β 需要调节,最佳值依赖于具体问题。
    • 动量可能导致跳过局部最优解,特别是在复杂的目标函数中。

3. AdaGrad (Adaptive Gradient Algorithm 自适应梯度算法)

原理:

AdaGrad 通过对每个参数使用不同的学习率,使得参数的更新速度自适应地调整。对于频繁出现的特征,AdaGrad 会减少学习率;对于稀疏特征,则增加学习率。具体来说,AdaGrad 会对梯度的历史平方和进行累加,动态调整每个参数的学习率:

G t + 1 = G t + ∇ θ J ( θ t ) 2 G_{t+1} = G_t + \nabla_{\theta} J(\theta_t)^2 Gt+1=Gt+θJ(θt)2
θ t + 1 = θ t − η G t + 1 + ϵ ∇ θ J ( θ t ) \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{G_{t+1} + \epsilon}} \nabla_{\theta} J(\theta_t) θt+1=θtGt+1+ϵ ηθJ(θt)

其中, ϵ \epsilon ϵ 是防止除零错误的小常数。

作用:

适用于具有稀疏特征的数据(如文本处理、推荐系统等),能够让模型快速适应不同特征的梯度变化。

优缺点:

  • 优点
    • 自动调整学习率,避免手动调整学习率的繁琐。
    • 对稀疏数据的收敛速度有显著提升。
  • 缺点
    • 随着训练进行,AdaGrad 的学习率会持续减小,导致训练后期更新过于缓慢。
    • 对于某些问题,可能导致过早收敛,特别是当参数梯度变化不大时。

4. RMSprop (Root Mean Square Propagation 均方根传播)

原理:

RMSprop 是对 AdaGrad 的改进,通过引入衰减因子来防止学习率过快减小。它通过对梯度平方的指数加权平均来调整每个参数的学习率:

v t + 1 = β v t + ( 1 − β ) ∇ θ J ( θ t ) 2 v_{t+1} = \beta v_t + (1 - \beta) \nabla_{\theta} J(\theta_t)^2 vt+1=βvt+(1β)θJ(θt)2
θ t + 1 = θ t − η v t + 1 + ϵ ∇ θ J ( θ t ) \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{v_{t+1} + \epsilon}} \nabla_{\theta} J(\theta_t) θt+1=θtvt+1+ϵ ηθJ(θt)

其中, β \beta β 是衰减因子, η \eta η 是学习率, ϵ \epsilon ϵ 是防止除零错误的小常数。

作用:

适用于非平稳目标函数(例如递增或递减的动态任务)。特别适用于处理RNN(递归神经网络)和时间序列数据。

优缺点:

  • 优点
    • 对梯度波动较大的问题表现更好,尤其适用于动态目标。
    • 相较于 AdaGrad,能够有效防止学习率过早减小。
  • 缺点
    • 需要调节 β \beta β 和学习率等超参数。
    • 可能需要针对具体问题进一步调整超参数,以获得最优性能。

5. Adam (Adaptive Moment Estimation 自适应矩估计)

原理:

Adam 结合了 Momentum 和 RMSprop 的思想,通过计算梯度的一阶矩(动量)和二阶矩(梯度平方的均值)来进行自适应更新。更新规则如下:

m t + 1 = β 1 m t + ( 1 − β 1 ) ∇ θ J ( θ t ) m_{t+1} = \beta_1 m_t + (1 - \beta_1) \nabla_{\theta} J(\theta_t) mt+1=β1mt+(1β1)θJ(θt)
v t + 1 = β 2 v t + ( 1 − β 2 ) ∇ θ J ( θ t ) 2 v_{t+1} = \beta_2 v_t + (1 - \beta_2) \nabla_{\theta} J(\theta_t)^2 vt+1=β2vt+(1β2)θJ(θt)2
m ^ t + 1 = m t + 1 1 − β 1 t + 1 , v ^ t + 1 = v t + 1 1 − β 2 t + 1 \hat{m}_{t+1} = \frac{m_{t+1}}{1 - \beta_1^{t+1}}, \quad \hat{v}_{t+1} = \frac{v_{t+1}}{1 - \beta_2^{t+1}} m^t+1=1β1t+1mt+1,v^t+1=1β2t+1vt+1
θ t + 1 = θ t − η v ^ t + 1 + ϵ m ^ t + 1 \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_{t+1}} + \epsilon} \hat{m}_{t+1} θt+1=θtv^t+1 +ϵηm^t+1

其中, β 1 \beta_1 β1 β 2 \beta_2 β2 是一阶和二阶矩的衰减率, η \eta η 是学习率, ϵ \epsilon ϵ 是防止除零的常数。

作用:

适用于各种类型的神经网络,尤其在大规模数据集上表现优异。

优缺点:

  • 优点
    • 结合了动量和自适应学习率,通常可以快速收敛。
    • 适用于非平稳目标函数和大规模数据集。
    • 超参数调整较为简单,少量调整即可获得较好的性能。
  • 缺点
    • 对小数据集或过于简单的任务,可能导致过拟合。
    • 对学习率较为敏感,可能需要根据具体问题进行微调。

总结

优化器原理优点缺点适用场景
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()

在这里插入图片描述

本次的分享就结束了,感谢大家观看。


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

相关文章:

  • 【Python】基于blind-watermark库添加图片盲水印
  • 解密序列建模:理解 RNN、LSTM 和 Seq2Seq
  • 【cuda学习日记】2.cuda编程模型
  • 新年感悟:2025年1月7日高铁随想
  • C++ constexpr(八股总结)
  • c#版本、.net版本、visual studio版本之间的对应关系
  • Netty中用了哪些设计模式?
  • 云计算安全需求分析与安全防护工程
  • windows下,golang+vscode+delve 远程调试
  • 安卓漏洞学习(十八):Android加固基本原理
  • PHP零基础入门笔记
  • Vue的后端之一,Django
  • 【大数据】(选修)实验4 安装熟悉HBase数据库并实践
  • 2台ubuntu之间scp
  • QPainter,QPen,QBrush详解
  • Ruby语言的数据结构
  • 微信小程序中调用阿里云 OSS(Object Storage Service)上传文件
  • 微软发布2025年AI预测:AI Agents将彻底改变工作方式
  • Ant Design Pro写项目的总结经验(react)
  • Redis中的Red Lock/Redis锁
  • K8s集群中,Worker 节点误执行了 kubeadm init
  • ip属地不是唯一的吗怎么改
  • Vue笔记-001-声明式渲染
  • macOS 安装 python3.11
  • Java基础知识-面试题
  • 智能服装推荐系统 协同过滤余弦函数推荐服装 Springboot Vue Element-UI前后端分离