[pytorch]几种optimizer优化器的使用

[pytorch]几种optimizer优化器的使用

  • optimizer的构建
  • 梯度更新的过程
  • 几种optimizer
    • SGD+momentum
    • Adagrad
    • RMSProp
    • Adam

梯度下降的方法可以大致分为以下三大类:

  • 标准梯度下降方法:
    先计算所有样本汇总误差,然后根据总误差来更新权重

  • 随机梯度下降方法:
    随机选取一个样本来计算误差,然后更新权重

  • 批量梯度下降方法:
    从总的样本中选取一个batch,然后计算这个batch的总误差,根据其来更新权重

其中本文介绍pytorch为我们封装好的几种优化器,这些又可以两个大类:一大类方法是SGD及其改进(加Momentum);另外一大类是Per-parameter adaptive learning rate methods(逐参数适应学习率方法),包括AdaGrad、RMSProp、Adam等。

optimizer的构建

要构造一个Optimizer,你必须给它一个包含参数(必须都是Variable对象)进行优化。然后,您可以指定optimizer的参 数选项,比如学习率,权重衰减等。

optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)
optimizer = optim.Adam([var1, var2], lr = 0.0001)

如果我们想用不同的初始学习率更新不同的层,则要传进一个dict对象:

optim.SGD([
            {'params': model.base.parameters()},
            {'params': model.classifier.parameters(), 'lr': 1e-3}
            ], lr=1e-2, momentum=0.9)

梯度更新的过程

对于大多数优化器来说,我们只需要在loss.backward()之后用step更新权重:

for input, target in dataset:
    optimizer.zero_grad()
    output = model(input)
    loss = loss_fn(output, target)
    loss.backward()
    optimizer.step()

几种optimizer

SGD+momentum

在SGD中,gradient类比成速度(矢量),learning rate类比成时间。单纯的SGD就是直接利用梯度更新,但是因为更新比较频繁,会造成 cost function 有严重的震荡,最终停留在Local Minima或者Saddle Point处。(鞍点就是一阶导数为0但是不是局部最优值的点)。因此现在的SGD都会加上momentum。为了抑制SGD的震荡,SGDM认为梯度下降过程可以加入惯性。下坡的时候,如果发现是陡坡,那就利用惯性跑的快一些。SGDM全称是SGD with momentum,在SGD基础上引入了一阶动量::

> Momentum update
 v = mu * v - learning_rate * dx #这里的mu对应的就是momentum参数,是一个超参
 x += v 

如果我们把上式展开的话,会很容易发现一个现象:现在实际更新的梯度其实就是历史梯度的加权平均,这样就解释了怎么做到一直是下坡就跑的快一点。

class torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)

参数:

  • params (iterable) – 用于优化的可以迭代参数或定义参数组
  • lr (float) – 学习率
  • momentum (float, 可选) – 动量因子(默认:0)
  • weight_decay (float, 可选) – 权重衰减(L2范数)(默认:0)
  • dampening (float, 可选) – 动量的抑制因子(默认:0)
  • nesterov (bool, 可选) – 使用Nesterov动量(默认:False)

我们可以看到最后一个参数是nesterov,这个参数是确认是否使用一种加速化的Momentum: Nesterov Momentum。关于它的理解我们用以下公式理解:

v_t+1 = \mu*v_t = \alpha*d(x_t+\mu*v_t)
x_t+1 = x_t + v_t+1

我们可以看到相当于是在当前更新梯度的时候我们提前迈了一步,然后观察那个地方的梯度来更新。

Adagrad

此前我们都没有用到二阶动量。二阶动量的出现,才意味着“自适应学习率”优化算法时代的到来。SGD及其变种以同样的学习率更新每个参数,但深度神经网络往往包含大量的参数,这些参数并不是总会用得到(想想大规模的embedding)。或者来说对于SGD的不同参数我们是采用同样的方式梯度更新的,也就是所有的参数统一求导和下降的,但是由于实际数据中可能存在这样一种情况:有些参数已经近乎最优,因此只需要微调了,而另一些可能还需要很大的调整。这种情况可能会在样本较少的情况下出现,比如含有某一特征的样本出现较少,因此被代入优化的次数也较少,这样就导致不同参数的下降不平衡。Adagrad就是来处理这类问题的。

对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些;对于偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本身上多学一些,即学习速率大一些。怎么样去度量历史更新频率呢?那就是二阶动量——该维度上,迄今为止所有梯度值的平方和。

grad_squared = 0
while True:
    dx = compute_gradient(x)
    grad_squared += dx * dx
    x -= learning_rate * dx / (np.sqrt(grad_squared) + 1e-7)

grad_squared将用来归一化参数更新步长,归一化是逐元素进行的。变量grad_squared的尺寸和梯度矩阵的尺寸是一样的,还保持记录每个参数的梯度的平方和。注意,接收到较大梯度值的权重更新的学习率将减小,而接收到较小梯度值的权重的学习率将会变大。
有趣的是平方根的操作非常重要,如果去掉,算法的表现将会糟糕很多。用于平滑的式子eps(一般设为1e-4到1e-8之间)是防止出现除以0的情况。

class torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0)

参数:

  • params (iterable) – 用于优化的可以迭代参数或定义参数组
  • lr (float, 可选) – 学习率(默认: 1e-2)
  • lr_decay (float, 可选) – 学习率衰减(默认: 0)
  • weight_decay (float, 可选) – 权重衰减(L2范数)(默认: 0)

这一方法在稀疏数据场景下表现非常好。但也存在一些问题:因为历史梯度平方和是单调递增的,会使得学习率单调递减至0,可能会使得训练过程提前结束,即便后续还有数据也无法学到必要的知识。

RMSProp

由于AdaGrad单调递减的学习率变化过于激进,我们考虑一个改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度。这也就是AdaDelta名称中Delta的来历。修改的思路很简单。前面我们讲到,指数移动平均值大约就是过去一段时间的平均值,因此我们用这一方法来计算二阶累积动量。

grad_squared = 0
while True:
    dx = compute_gradient(x)
    grad_squared = decay_rate * grad_squared + (1 - decay_rate) * dx * dx
    x -= learning_rate * dx / (np.sqrt(grad_squared) + 1e-7)

RMSProp简单修改了Adagrad方法,它做了一个梯度平方的滑动平均。 decay_rate是一个超参数,常用的值是[0.9,0.99,0.999]。x+=和Adagrad中是一样的,但是cache变量是不同的。因此,RMSProp仍然是基于梯度的大小来对每个权重的学习率进行修改,这同样效果不错。但是和Adagrad不同,其更新不会让学习率单调变小。

class torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0,

Adam

谈到这里,Adam和Nadam的出现就很自然而然了——它们是前述方法的集大成者。我们看到,SGD-M在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。把一阶动量和二阶动量都用起来,就是Adam了——Adaptive + Momentum。Adam是RMSProp的momentum版本:

m = beta1*m + (1‐beta1)*dx
v = beta2*v + (1‐beta2)*(dx**2)
x += ‐ learning_rate * m / (np.sqrt(v) + eps)

class torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)[source]

参数:

  • params (iterable) – 用于优化的可以迭代参数或定义参数组
  • lr (float, 可选) – 学习率(默认:1e-3)
  • betas (Tuple[float, float], 可选) – 用于计算梯度运行平均值及其平方的系数(默认:0.9,0.999)
  • eps (float, 可选) – 增加分母的数值以提高数值稳定性(默认:1e-8)
  • weight_decay (float, 可选) – 权重衰减(L2范数)(默认: 0)

本篇部分引用知乎回答:优化算法

你可能感兴趣的:([pytorch]几种optimizer优化器的使用)