优化器(Optimizer)(SGD、Momentum、AdaGrad、RMSProp、Adam)

2023-05-16

文章目录

    • 3.1、传统梯度优化的不足(BGD,SGD,MBGD)
      • 3.1.1 一维梯度下降
      • 3.1.2 多维梯度下降
    • 3.2、动量(Momentum)
    • 3.3、AdaGrad算法
    • 3.4、RMSProp算法
    • 3.5、Adam算法

优化器在机器学习、深度学习中往往起着举足轻重的作用,同一个模型,因选择不同的优化器,性能有可能相差很大,甚至导致一些模型无法训练。所以了解各种优化器的基本原理非常重要。下面介绍各种常用优化器或算法的主要原理,及各自的优点或不足。

3.1、传统梯度优化的不足(BGD,SGD,MBGD)

BGD、SGD、MBGD分别为批量梯度下降算法、随机梯度下降算法、小批量梯度下降算法。BGD在训练的时候选用所有的训练集进行计算,SGD在训练的时候只选择一个数据进行训练,而MBGD在训练的时候只选择小部分数据进行训练。这三个优化算法在训练的时候虽然所采用的的数据量不同,但是他们在进行参数优化的时候是相同的。

在训练的时候一般都是使用小批量梯度下降算法,即选择部分数据进行训练,在此把这三种算法统称为传统梯度更新算法,因为他们在更新参数的时候采用相同的方式,而更优的优化算法从梯度方向学习率方面对参数更新方式进行优化。

传统梯度更新算法为最常见、最简单的一种参数更新策略。其基本思想是:先设定一个学习率 λ \lambda λ,参数沿梯度的反方向移动。假设需要更新的参数为 θ \theta θ,梯度为 g g g,则其更新策略可表示为:
θ ← θ − λ g \theta \leftarrow \theta-\lambda g θθλg
这种梯度更新算法简洁,当学习率取值恰当时,可以收敛到全面最优点(凸函数)或局部最优点(非凸函数)。但其还有很大的不足点:

  1. 对超参数学习率比较敏感(过小导致收敛速度过慢,过大又越过极值点)。
  2. 学习率除了敏感,有时还会因其在迭代过程中保持不变,很容易造成算法被卡在鞍点的位置。
  3. 在较平坦的区域,由于梯度接近于0,优化算法会因误判,在还未到达极值点时,就提前结束迭代,陷入局部极小值。

3.1.1 一维梯度下降

下面展示如何实现梯度下降以及上述这些不足点。为了简单,选用目标函数 f ( x ) = x 2 f(x)=x^2 f(x)=x2。尽管我们知道 x = 0 x=0 x=0 f ( x ) f(x) f(x)能取得最小值(这里x为模型参数)。

import numpy as np
import torch
from d2l import torch as d2l
%matplotlib inline

#目标函数
def f(x):
    return x**2

#目标函数的梯度(导数)
def f_grad(x):
    return 2*x

接下来,使用 x = 10 x=10 x=10作为初始值,并假设 η = 0.2 \eta=0.2 η=0.2。使用梯度下降迭代法迭代 x x x共10次,可以看到 x x x的值最终将接近最优解。

#进行梯度下降
def gd(eta,f_grad):
    x=10.0
    results=[x]
    for i in range(10):
        x-=eta*f_grad(x)
        results.append(float(x))
    print(f'epoch 10,x:{x:f}')
    return results
results = gd(0.2,f_grad)
epoch 10,x:0.060466

x x x优化的过程进行可视化。

def show_trace(results,f):
    n=max(abs(min(results)),abs(max(results)))
    f_line=torch.arange(-n,n,0.01)
    d2l.set_figsize()
    d2l.plot([f_line,results],[[f(x) for x in f_line],[f(x) for x in results]],'x','f(x)',fmts=['-','-o'])
show_trace(results,f)


svg

学习率

学习率决定了目标函数是否能够收敛到局部最小值,以及何时收敛到最小值。学习率 η \eta η可由算法设计者设置。请注意,如果使用的学习率太小,将导致 x x x的更新非常缓慢,需要更多的迭代。下面将学习率设置为0.05。如下图所示,尽管经历了10个步骤,我们仍然离最优解很远。

show_trace(gd(0.05,f_grad),f)
epoch 10,x:3.486784

svg

相反,当使用过高的学习率, x x x的迭代不能保证降低 f ( x ) f(x) f(x)的值,例如,当学习率为 η = 1.1 \eta=1.1 η=1.1时, x x x超出了最优解 x = 0 x=0 x=0并逐渐发散。

show_trace(gd(1.1,f_grad),f)
epoch 10,x:61.917364

svg

局部极小值

为了演示非凸函数的梯度下降,考虑函数 f ( x ) = x ⋅ c o s ( x ) f(x)=x\cdot cos(x) f(x)=xcos(x),其中 c c c为某常数。这个函数有无穷多个最小值。如果学习率选择不当,我们最终只会得到一个最优解。下面的例子说明了高学习率如何导致较差的局部最小值。
f ′ ( x ) = c o s ( c x ) − c ∗ x ∗ s i n ( c x ) f^{'}(x)=cos(cx)-c*x*sin(cx) f(x)=cos(cx)cxsin(cx)

c=torch.tensor(0.15*np.pi)

#目标函数
def f(x):
    return x*torch.cos(c*x)

#目标函数的梯度
def f_grad(x):
    return torch.cos(c*x)-c*x*torch.sin(c*x)

show_trace(gd(2,f_grad),f)
epoch 10,x:-1.528166

svg

3.1.2 多维梯度下降

在对单元梯度下降有了了解之后,下面看看多元梯度下降,即考虑 x = [ x 1 , x 2 , ⋯   , x d ] T x=[x_1,x_2,\cdots ,x_d]^T x=[x1,x2,,xd]T的情况。相应的它的梯度也是多元的,是一个由d个偏导数组成的向量:
∇ f ( x ) = [ ∂ f x ∂ x 1 , ∂ f x ∂ x 2 , ⋯   , ∂ f x ∂ x d ] T \nabla f(x)=[\frac{\partial f{x}}{\partial x_1},\frac{\partial f{x}}{\partial x_2},\cdots,\frac{\partial f{x}}{\partial x_d}]^T f(x)=[x1fx,x2fx,,xdfx]T

然后选择合适的学率进行梯度下降:
x ← x − η ∇ f ( x ) x \leftarrow x-\eta \nabla f(x) xxηf(x)

下面通过代码可视化它的参数更新过程。构造一个目标函数 f ( x ) = x 1 2 + 2 x 2 2 f(x)=x_1^2+2x_2^2 f(x)=x12+2x22,并有二维向量 x = [ x 1 , x 2 ] x=[x_1,x_2] x=[x1,x2]作为输入,标量作为输出。梯度由 ∇ f ( x ) = [ 2 x 1 , 4 x 2 ] T \nabla f(x)=[2x_1,4x_2]^T f(x)=[2x1,4x2]T给出。从初始位置[-5,-2]通过梯度下降观察x的轨迹。

首先需要定义两个辅助函数第一个是train_2d()函数,用指定的训练机优化2D目标函数;第二个是show_trace_2d(),用于显示x的轨迹。

#用指定的训练机优化2D目标函数
def train_2d(trainer,steps=20,f_grad=None):
    x1,x2,s1,s2=-5,-2,0,0
    results=[(x1,x2)]
    for i in range(steps):
        if f_grad:
            x1,x2,s1,s2=trainer(x1,x2,s1,s2,f_grad)
        else:
            x1,x2,s1,s2=trainer(x1,x2,s1,s2)
        results.append((x1,x2))
    print(f'epoch{i+1},x1:{float(x1):f},x2:{float(x2):f}')
    return results

#显示优化过程中2D变量的轨迹
def show_trace_2d(f,results):
    d2l.set_figsize()
    d2l.plt.plot(*zip(*results),'-o',color='#ff7f0e')
    x1,x2=torch.meshgrid(torch.arange(-5.5,1.0,0.1),
                         torch.arange(-3.0,1.0,0.1))
    d2l.plt.contour(x1,x2,f(x1,x2),colors='#1f77b4')
    d2l.plt.xlabel('x1')
    d2l.plt.ylabel('x2')

接下来,使用学习率为 η = 0.1 \eta=0.1 η=0.1时优化变量 x x x的轨迹。在经过20步时, x x x的值接近其位于[0,0]的最小值。

#目标函数 
def f_2d(x1,x2):
    return x1**2+2*x2**2

#目标函数的梯度
def f_2d_grad(x1,x2):
    return (2*x1,4*x2)
#SGD更新参数
def gd_2d(x1,x2,s1,s2,f_grad):
    g1,g2=f_grad(x1,x2)
    return (x1-eta*g1,x2-eta*g2,0,0)
eta=0.1
show_trace_2d(f_2d,train_2d(gd_2d,f_grad=f_2d_grad))
epoch20,x1:-0.057646,x2:-0.000073

svg

针对传统梯度优化算法的缺点,许多优化算法从梯度方向学习率两方面入手。有些从梯度方向入手,如动量更新策略;而有些从学习率入手,这涉及调参问题;还有从两方面同时入手,如自适应更新策略。

在pytorch中使用传统的梯度下降算法可以使用torch.optim.SGD其格式为:

torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False, *, maximize=False)

因为使用的是传统的梯度下降算法,则momentum参数和nesterov参数默认即可不需要设置。下面看一看它的用法。

import torch
#改代码不可运行
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
#梯度清零
optimizer.zero_grad()
loss_fn(model(input), target).backward()
#参数更新
optimizer.step()

3.2、动量(Momentum)

动量(Momentum)是模拟物理中动量的概念,具有物理上惯性的含义,一个物体在运动时具有惯性,把这个思想运用到梯度下降的计算中,可以增加算法的收敛速度和稳定性,具体实现如图所示:
img

动量算法每下降一步都是由前面下降方向的一个累积和当前点梯度方向组合而成。含动量的随机梯度下降算法,其更新方式如下:
更 新 梯 度 : g ^ ← 1 b a t c h _ s i z e ∑ i = 0 b a t c h _ s i z e ∇ θ L ( f ( x ( i ) ) , y ( i ) ) 计 算 梯 度 : v ← β v + g 更 新 参 数 : θ ← θ − η v 更新梯度:\hat{g} \leftarrow \frac{1}{batch\_size} \sum_{i=0}^{batch\_size}\nabla_{\theta}L(f(x^{(i)}),y^{(i)})\\ 计算梯度:v \leftarrow \beta v+g\\ 更新参数:\theta \leftarrow \theta-\eta v g^batch_size1i=0batch_sizeθL(f(x(i)),y(i))vβv+gθθηv

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

为了更好的观察动量带来的好处,使用一个新函数 f ( x ) = 0.1 x 1 2 + 2 x 2 2 f(x)=0.1x_1^2+2x_2^2 f(x)=0.1x12+2x22上使用不带动量的传统梯度下降算法观察下降过程。与上节的函数一样,f的最低水平为(0,0)。该函数在 x 1 x_1 x1方向上比较平坦,在此选择0.4的学习率。

import torch
from d2l import torch as d2l
%matplotlib inline

eta=0.4
#目标函数 
def f_2d(x1,x2):
    return 0.1*x1**2+2*x2**2
#sgd更新参数
def gd_2d(x1,x2,s1,s2):
    return (x1-eta*0.2*x1,x2-eta*4*x2,0,0)

d2l.show_trace_2d(f_2d,d2l.train_2d(gd_2d))
epoch 20, x1: -0.943467, x2: -0.000073

svg

从结果来看, x 2 x_2 x2方向的梯度比水平 x 1 x_1 x1方向的渐变高得多,变化快得多。因此就陷入了两个不可取的选择:如果选择较小的准确率。可以确保不会朝 x 2 x_2 x2方向发生偏离,但在 x 1 x_1 x1反向收敛会缓慢。如果学习率较高, x 1 x_1 x1方向会收敛很快,但在 x 2 x_2 x2方向就不会向最优点靠近。下面将学习率从0.4调整到0.6。可以看出在 x 1 x_1 x1方向会有所改善,但是整体解决方案会很差。

eta=0.6
d2l.show_trace_2d(f_2d,d2l.train_2d(gd_2d))
epoch 20, x1: -0.387814, x2: -1673.365109

svg

下面看一下使用动量算法在实践中的应用。

#动量法更新参数
def momentum_2d(x1,x2,v1,v2):
    v1=beta*v1+0.2*x1
    v2=beta*v2+4*x2
    return x1-eta*v1,x2-eta*v2,v1,v2
eta,beta=0.6,0.5
d2l.show_trace_2d(f_2d,d2l.train_2d(momentum_2d))
epoch 20, x1: 0.007188, x2: 0.002553

svg

可见使用和之前一样的学习率,也能够很好的收敛,下面看看当降低动量参数时会发生啥。虽然将其减半到 β = 0.25 \beta=0.25 β=0.25会导致一条几乎没有收敛的轨迹。但是也要比没有动力好很多。

eta,beta=0.6,0.25
d2l.show_trace_2d(f_2d,d2l.train_2d(momentum_2d))
epoch 20, x1: -0.126340, x2: -0.186632

svg

既然每一步都要将两个梯度方向(历史梯度、当前梯度)做一个合并再下降,因此可以按照前面一小步位置的“超前梯度”来做梯度合并。这样就可以先往前走一小步,在靠前一点的位置看到梯度,然后按照那个位置再来修正这一步的梯度方向,如下图所示。这样就得到动量算法的一种改进算法,称为Nesterov Accelerated Gradient,简称NAG算法。这种更新的算法能够防止大幅振荡,不会错过最小值,并会对参数更加敏感。
img

下面看看在pytorch中的使用:

torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False, *, maximize=False)

因为使用了动量,因此参数momentum就需要给定数值,nesterov设置为True时,将会使用NAG算法,它是动量算法的一种优化。

3.3、AdaGrad算法

AdaGrad算法是通过参数来调整合适的学习率,是能独立自动调整模型参数的学习率,对稀疏参数进行大幅更新和对频繁参数进行小幅更新,因此,AdaGrad方法非常适合处理稀疏数据。AdaGrad算法在某些深度学习模型上效果不错。但还是有些不足,可能是因其累积梯度平方导致学习率过早或过量的减少所致。以下是AdaGrad算法的更新步骤:
更 新 梯 度 : g ^ ← 1 b a t c h _ s i z e ∑ i = 0 b a t c h _ s i z e ∇ θ L ( f ( x ( i ) ) , y ( i ) ) 累 积 平 方 梯 度 : r ← r + g ^ ⊙ g ^ 计 算 参 数 : △ θ ← − λ δ + r ⊙ g ^ 更 新 参 数 : θ ← θ + △ θ 更新梯度:\hat{g} \leftarrow \frac{1}{batch\_size} \sum_{i=0}^{batch\_size}\nabla_{\theta}L(f(x^{(i)}),y^{(i)})\\ 累积平方梯度:r \leftarrow r+\hat{g} \odot \hat{g}\\ 计算参数:\triangle \theta \leftarrow - \frac{\lambda}{\delta+\sqrt{r}}\odot \hat{g}\\ 更新参数:\theta \leftarrow \theta+\triangle \theta g^batch_size1i=0batch_sizeθL(f(x(i)),y(i))rr+g^g^θδ+r λg^θθ+θ

其中 r r r为累积梯度变量,初始为0; λ \lambda λ为学习率; δ \delta δ为小参数,避免分母为0。

通过上述更新步骤可以看出:

  1. 随着迭代时间越长,累积梯度 r r r越大,导致学习速率 λ δ + r \frac{\lambda}{\delta+\sqrt{r}} δ+r λ随着时间较小,在接近目标值时,不会因为学习率过大而越过极值点。
  2. 不同参数之间的学习速率不同,因此,与之前固定学习率相比,不容易卡在鞍点。
  3. 如果梯度累积参数 r r r比较小,则速率会比较大,所以参数迭代的步长就会比较大。相反,如果梯度累积参数 r r r比较大,则速率会比较小,所以参数迭代的步长就会比较小。

下面使用和以前相同的问题:
f ( x ) = 0.1 x 1 2 + 2 x 2 2 f(x)=0.1x_1^2+2x_2^2 f(x)=0.1x12+2x22

将使用与之前相同的学习率来实施AdaGrad,即 η = 0.4 \eta=0.4 η=0.4

import math
import torch
from d2l import torch as d2l
%matplotlib inline

#adagrad更新参数
def adagrad_2d(x1,x2,s1,s2):
    eps=1e-6
    g1,g2=0.2*x1,4*x2
    s1+=g1**2
    s2+=g2**2
    x1-=eta/math.sqrt(s1+eps)*g1
    x2-=eta/math.sqrt(s2+eps)*g2
    return x1,x2,s1,s2
#目标函数
def f_2d(x1,x2):
    return 0.1*x1**2+2*x2**2
eta=0.4
d2l.show_trace_2d(f_2d,d2l.train_2d(adagrad_2d))
epoch 20, x1: -2.382563, x2: -0.158591

svg

由结果来看,参数更新的过程变得平稳,但是由于梯度累积的越来越大,学习率持续下降,因此参数在后期阶段移动的不会那么多。现在我们适当的提高学习率到2,看看结果怎么样。

eta=2
d2l.show_trace_2d(f_2d,d2l.train_2d(adagrad_2d))
epoch 20, x1: -0.002295, x2: -0.000000

svg

可以看出,在越接近最优点附近,学习率越来越小,参数更新变得更慢,以至于不会错过最优点的位置。

下面看看在Pytorch中如何使用AdaGrad优化算法,在Pytorch中的格式为:

torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0, initial_accumulator_value=0, eps=1e-10)

各个参数的功能为:

  1. params – 要优化的参数。
  2. lr (float, optional) – 学习率 (默认: 1e-2)
  3. lr_decay (float, optional) – 学习率衰减 (默认: 0)
  4. weight_decay (float, optional) – 权重衰减 (L2 penalty) (默认: 0)
  5. eps (float, optional) – 为提高数字稳定性,在分母上添加了该项 (默认: 1e-10)

3.4、RMSProp算法

RMSProp算法通过修改AdaGrad得来,其目的是在非凸背景下效果更好。针对梯度平方和累计越来越大的问题,RMSProp指数加权的移动平均代替梯度平方和。RMSProp为了使用移动平均,还引入了一个新的超参数 ρ \rho ρ,用来控制移动平均的长度范围。以下是RMSProp算法的更新步骤:

更 新 梯 度 : g ^ ← 1 b a t c h _ s i z e ∑ i = 0 b a t c h _ s i z e ∇ θ L ( f ( x ( i ) ) , y ( i ) ) 累 积 平 方 梯 度 : r ← ρ r + ( 1 − ρ ) g ^ ⊙ g ^ 计 算 参 数 更 新 : △ θ ← − λ δ + r ⊙ g ^ 更 新 参 数 : θ ← θ + △ θ 更新梯度:\hat{g} \leftarrow \frac{1}{batch\_size} \sum_{i=0}^{batch\_size}\nabla_{\theta}L(f(x^{(i)}),y^{(i)})\\ 累积平方梯度:r \leftarrow \rho r+ (1- \rho) \hat{g} \odot \hat{g}\\ 计算参数更新:\triangle \theta \leftarrow - \frac{\lambda}{\delta+\sqrt{r}}\odot \hat{g}\\ 更新参数:\theta \leftarrow \theta+\triangle \theta g^batch_size1i=0batch_sizeθL(f(x(i)),y(i))rρr+(1ρ)g^g^θδ+r λg^θθ+θ

RMSProp算法在实践中已被证明是一种有效且实用的深度神经网络优化算法,因而在深度学习中得到了广泛应用。

和之前一样,使用二次函数 f ( x ) = 0.1 x 1 2 + 2 x 2 2 f(x)=0.1x_1^2+2x_2^2 f(x)=0.1x12+2x22来观察RMSProp的轨迹。在使用学习率为0.4的AdaGrad的时候,参数在算法的后期阶段移动的越来越慢,因为学习率下降太快。由于 η \eta η是单独控制的,RMSProp不会发生这种情况。

import math
from d2l import torch as d2l
#rmsprop更新参数
def rmsprop_2d(x1,x2,s1,s2):
    g1,g2,eps=0.2*x1,4*x2,1e-6
    s1=gamma*s1+(1-gamma)*g1**2
    s2=gamma*s2+(1-gamma)*g2**2
    x1-=eta/math.sqrt(s1+eps)*g1
    x2-=eta/math.sqrt(s2+eps)*g2
    return x1,x2,s1,s2
#目标函数
def f_2d(x1,x2):
    return 0.1*x1**2+2*x2**2
eta,gamma=0.4,0.9
d2l.show_trace_2d(f_2d,d2l.train_2d(rmsprop_2d))
epoch 20, x1: -0.010599, x2: 0.000000

svg

在pytorch中,RMSProp算法的格式为:

torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)

alpha为平滑常数,momentum为动量。

3.5、Adam算法

Adam本质上是带有动量项的RMSProp,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使参数比较稳定。

Adam是一种学习速率自适应的深度神经网络方法,他利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam算法的更新步骤如下:
t ← t + 1 计 算 梯 度 : g t ← ∇ θ f t ( θ t − 1 ) 更 新 有 偏 一 阶 矩 估 计 : m t ← β 1 ⋅ m t − 1 + ( 1 − β 1 ) ⋅ g t 更 新 有 偏 二 阶 矩 估 计 : v t ← β 2 ⋅ v t − 1 + ( 1 − β 2 ) ⋅ g t 2 计 算 偏 差 校 正 的 一 阶 矩 估 计 : m t ^ ← m t 1 − β 1 t 计 算 偏 差 校 正 的 二 阶 矩 估 计 : v t ^ ← v t 1 − β 2 t 更 新 参 数 : θ t ← θ t − 1 − α ⋅ m t ^ ϵ + v t ^ t \leftarrow t+1\\ 计算梯度:g_t \leftarrow \nabla_{\theta} f_t(\theta_{t-1})\\ 更新有偏一阶矩估计:m_t \leftarrow \beta_1 \cdot m_{t-1} + (1-\beta_1)\cdot g_t\\ 更新有偏二阶矩估计:v_t \leftarrow \beta_2 \cdot v_{t-1} + (1-\beta_2)\cdot g_t^2\\ 计算偏差校正的一阶矩估计:\hat{m_t} \leftarrow \frac{m_t}{1-\beta_1^t}\\ 计算偏差校正的二阶矩估计:\hat{v_t} \leftarrow \frac{v_t}{1-\beta_2^t}\\ 更新参数:\theta_t \leftarrow \theta_{t-1}-\alpha \cdot \frac{\hat{m_t}}{\epsilon+\sqrt{\hat{v_t}}} tt+1gtθft(θt1)mtβ1mt1+(1β1)gtvtβ2vt1+(1β2)gt2mt^1β1tmtvt^1β2tvtθtθt1αϵ+vt^ mt^
下面看看每个步骤的含义是:

首先,计算梯度的指数移动平均数, m 0 m_0 m0初始化为0。类似于Momentum算法,综合考虑之前时间步的梯度动量。 β 1 \beta_1 β1系数为指数衰减率,控制权重分配(动量与当前梯度),通常取接近于1的值。默认为0.9
m t ← β 1 ⋅ m t − 1 + ( 1 − β 1 ) ⋅ g t m_t \leftarrow \beta_1 \cdot m_{t-1} + (1-\beta_1)\cdot g_t mtβ1mt1+(1β1)gt
其次,计算梯度平方的指数移动平均数, v 0 v_0 v0初始化为0。 β 2 \beta_2 β2系数为指数衰减率,控制之前的梯度平方的影响情况。类似于RMSProp算法,对梯度平方进行加权均值。默认为0.999
v t ← β 2 ⋅ v t − 1 + ( 1 − β 2 ) ⋅ g t 2 v_t \leftarrow \beta_2 \cdot v_{t-1} + (1-\beta_2)\cdot g_t^2 vtβ2vt1+(1β2)gt2
第三,由于 m 0 m_0 m0初始化为0,会导致 m t m_t mt偏向于0,尤其在训练初期阶段。所以,此处需要对梯度均值 m t m_t mt进行偏差纠正,降低偏差对训练初期的影响。
m t ^ ← m t 1 − β 1 t \hat{m_t} \leftarrow \frac{m_t}{1-\beta_1^t} mt^1β1tmt
第四,与 m 0 m_0 m0类似,因为 v 0 v_0 v0初始化为0导致训练初始阶段 v t v_t vt偏向0,对其进行纠正。
v t ^ ← v t 1 − β 2 t \hat{v_t} \leftarrow \frac{v_t}{1-\beta_2^t} vt^1β2tvt
最后,更新参数,初始的学习率 α \alpha α乘以梯度均值与梯度方差的平方根之比。其中默认学习率 α = 0.001 \alpha=0.001 α=0.001 ϵ = 1 0 − 8 \epsilon=10^{-8} ϵ=108,避免除数变为0。由表达式可以看出,对更新的步长计算,能够从梯度均值及梯度平方两个角度进行自适应地调节,而不是直接由当前梯度决定。
θ t ← θ t − 1 − α ⋅ m t ^ ϵ + v t ^ \theta_t \leftarrow \theta_{t-1}-\alpha \cdot \frac{\hat{m_t}}{\epsilon+\sqrt{\hat{v_t}}} θtθt1αϵ+vt^ mt^

Adam主要包含以下几个显著的优点:

  1. 实现简单,计算高效,对内存需求少
  2. 参数的更新不受梯度的伸缩变换影响
  3. 超参数具有很好的解释性,且通常无需调整或仅需很少的微调
  4. 更新的步长能够被限制在大致的范围内(初始学习率)
  5. 能自然地实现步长退火过程(自动调整学习率)
  6. 很适合应用于大规模的数据及参数的场景
  7. 适用于不稳定目标函数
  8. 适用于梯度稀疏或梯度存在很大噪声的问题

和之前一样,使用二次函数 𝑓(𝑥)=0.1𝑥21+2𝑥22 来观察Adam的轨迹。使用学习率为0.16的AdaGrad并迭代50次。

import math
from d2l import torch as d2l
%matplotlib inline
#针对Adam改造原来的优化目标函数
def train_2d_adam(trainer,steps=20,f_grad=None):
    x1,x2,m1,m2,v1,v2=-5,-2,0,0,0,0
    results=[(x1,x2)]
    for i in range(steps):
        x1,x2,m1,m2,v1,v2=trainer(x1,x2,m1,m2,v1,v2)
        results.append((x1,x2))
    print(f'epoch{i+1},x1:{float(x1):f},x2:{float(x2):f}')
    return results
#Adam更新参数过程
def rmsprop_2d(x1,x2,m1,m2,v1,v2):
    g1,g2,eps=0.2*x1,4*x2,1e-8
    m1=beta1*m1+(1-beta1)*g1
    m2=beta1*m2+(1-beta1)*g2
    v1=beta2*v1+(1-beta2)*g1**2
    v2=beta2*v2+(1-beta2)*g2**2
    m_hat_1=m1/(1-beta1)
    m_hat_2=m2/(1-beta1)
    v_hat_1=v1/(1-beta2)
    v_hat_2=v2/(1-beta2)
    x1-=alpha*(m_hat_1/(eps+math.sqrt(v_hat_1)))
    x2-=alpha*(m_hat_2/(eps+math.sqrt(v_hat_2)))
    return x1,x2,m1,m2,v1,v2
#目标函数
def f_2d(x1,x2):
    return 0.1*x1**2+2*x2**2
alpha,beta1,beta2=0.16,0.9,0.999
d2l.show_trace_2d(f_2d,train_2d_adam(rmsprop_2d))
epoch20,x1:0.131652,x2:0.447787

svg

在pytorch中Adam的使用格式为

torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False, *, maximize=False)

参数betas β 1 \beta_1 β1 β 2 \beta_2 β2的集合,分别控制权重分配和之前的梯度平方的影响情况。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

优化器(Optimizer)(SGD、Momentum、AdaGrad、RMSProp、Adam) 的相关文章

随机推荐

  • Golang实现小型CMS内容管理功能(二):前端接入百度ueditor富文本编辑器

    当我们把接口都做好以后 xff0c 我们需要去开发前端界面 添加文章功能里面 xff0c 最重要的就是文章内容部分 xff0c 需要配置上富文本编辑器 xff0c 这样才能给我们的内容增加样式 下载ueditor代码 ueditor已经很久
  • 网络分析中数据包结构(含七层模型)

    七层模型 xff1a 包 Packet 是TCP IP协议通信传输中的数据单位 xff0c 一般也称 数据包 有人说 xff0c 局域网中传输的不是 帧 Frame 吗 xff1f 没错 xff0c 但是TCP IP协议是工作在OSI模型第
  • ubuntu下PyCharm遇到问题

    第三方库没有自动补全功能 xff08 autocomplete xff09 190921补充 xff1a 这个问题就是环境配置的问题当初真是无知 原因 xff1a PyCharm的人工编译环境和程序的运行环境不是同一个 xff08 说的太不
  • 用java简单的实现单链表的基本操作

    此代码仅供参考 xff0c 如有疑问欢迎评论 xff1a package com tyxh link 节点类 public class Node protected Node next 指针域 protected int data 数据域
  • 算法:海量日志数据,提取出某日访问百度次数最多的那个IP

    首先是这一天 xff0c 并且是访问百度的日志中的IP取出来 xff0c 逐个写入到一个大文件中 注意到IP是32位的 xff0c 最多有个2 32个IP 同样可以采用映射的方法 xff0c 比如模1000 xff0c 把整个大文件映射为1
  • 使用JUnit测试预期异常

    开发人员常常使用单元测试来验证的一段儿代码的操作 xff0c 很多时候单元测试可以检查抛出预期异常 expected exceptions 的代码 在Java语言中 xff0c JUnit是一套标准的单元测试方案 xff0c 它提供了很多验
  • BlockingQueue深入分析

    1 BlockingQueue 定义的常用方法如下 抛出异常特殊值阻塞超时插入add e offer e put e offer e time unit 移除remove poll take poll time unit 检查element
  • 聚合类新闻客户端产品功能点详情分析

    产品功能点 功能 今日头条 百度新闻 鲜果 ZAKER 媒体订阅 个性化内容推荐 个性化订阅 RSS 视频新闻 评论盖楼 搜索新闻 离线下载 地方新闻 一键分享 收藏 推送 天气 夜间模式 线上活动 主题设置 感兴趣 语音读文章 字体设置
  • 聚合类新闻客户端初体验

    初体验的产品 xff1a 今日头条 ios3 6 百度新闻 ios4 4 0 ZAKER ios4 4 5 鲜果 ios3 8 7 中搜搜悦 ios4 0 1 Flipboard ios2 3 9 1 Flipboard 一款国外很火的ap
  • 聚合类新闻客户端的改进

    zaker和鲜果是最早的聚合类新闻产品 xff0c 前几年发展很快 xff0c 迅速占领了市场 xff0c 但近两年发展变得缓慢 xff0c 而今日头条自发布以来才两年 xff0c 用户量就迅速超过了zaker和鲜果 xff0c 使用起来非
  • 单例模式优缺点

    主要优点 xff1a 1 提供了对唯一实例的受控访问 2 由于在系统内存中只存在一个对象 xff0c 因此可以节约系统资源 xff0c 对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能 3 允许可变数目的实例 主要缺点 xff
  • 适配器模式优缺点

    优点 xff1a 1 将目标类和适配者类解耦 2 增加了类的透明性和复用性 xff0c 将具体的实现封装在适配者类中 xff0c 对于客户端类来说是透明的 xff0c 而且提高了适配者的复用性 3 灵活性和扩展性都非常好 xff0c 符合开
  • Oracle 的 Round函数

    Round函数用法 xff1a 截取数字 格式如下 xff1a ROUND xff08 number decimals xff09 其中 xff1a number 待做截取处理的数值 decimals 指明需保留小数点后面的位数 可选项 x
  • Ubuntu安装卸载软件

    VMware 1 首先 xff0c 官网下载 vmware 虚拟机 2 转到下载目录下 给vmware升权限 sudo chmod 43 x VMware Workstation Full 15 1 0 13591040 x86 64 bu
  • eclipse报错:Failed to load the JNI shared library

    电脑自装系统以来 xff0c 好久没有写java代码了 xff0c 所以一直也没用 eclipse IDE xff0c 今天将eclipse打开 xff0c 报了个问题 xff0c Failed to load the JNI shared
  • ACM 鸡兔同笼 线性代数linear algebra

    想模仿线性代数变化的步骤写程序但总感觉失去了灵魂 java Scanner sc 61 new Scanner System in int head 61 sc nextInt int leg 61 sc nextInt int arr 6
  • 使用Example_where_Cause出现 Column 'goods_id' in where clause is ambiguous解决办法

    改写SSM项目https www bilibili com video BV18J411k7SF from 61 search amp seid 61 7715680395343362130出现 Column 39 goods id 39
  • 在CLI中打印表格----gotable使用介绍

    目录 介绍 获取gotable 在github中获取 下载源码 git clone go mod API 创建table 从结构体中创建空table 获取版本信息 获取版本列表 打印表格 给表格添加行 给表格添加多个行 给表格添加列 介绍
  • 抽象类和普通类

    包含抽象方法的类称为抽象类 xff0c 但并不意味着抽象类中只能有抽象方法 xff0c 它和普通类一样 xff0c 同样可以拥有成员变量和普通的成员方法 注意 xff0c 抽象类和普通类的主要有三点区别 xff1a 1 抽象方法必须为pub
  • 优化器(Optimizer)(SGD、Momentum、AdaGrad、RMSProp、Adam)

    文章目录 3 1 传统梯度优化的不足 BGD SGD MBGD 3 1 1 一维梯度下降3 1 2 多维梯度下降 3 2 动量 Momentum 3 3 AdaGrad算法3 4 RMSProp算法3 5 Adam算法 优化器在机器学习 深