美文网首页
cs231n学习之参数更新(6)

cs231n学习之参数更新(6)

作者: Latet | 来源:发表于2019-08-24 15:43 被阅读0次

前言

本文旨在学习和记录,如需转载,请附出处https://www.jianshu.com/p/7b00149dc797

参数更新

神经网络学习的目标是找到一组使损失函数尽可能小的参数,这个过程一般称作最优化问题。
一般计算参数的梯度来更新参数,沿着梯度的方向进行参数更新,然后重复多次,直到收敛,这个最基础的参数更新方法叫做随机梯度下降(stochastic gradient descent, SGD)

  • 基于梯度的优化方法
    目标函数:最小化或最大化的函数,通常也称损失函数、误差函数。一般格式为x^{*}=argminf(x);
    梯度下降:对误差函数求导,往其导数的反方向移动一小步来减小误差函数;
    局部极小点:临界点小于周围所有的临近点,不可能通过无穷小的步长来减小f(x);
    局部极大点:与局部极小点相反;
    鞍点:临界点既不是最小也不是最大;
    在深度学习中,优化的函数可能不是非凸的,很可能存在许多全局极小点或者鞍点时,优化算法可能无法找到全局最小点;但是即使找到的不是全局最小点,他找到的局部极小点对应于误差函数显著低的值,通常代表网络收敛了。
  • 导数的导数
    一阶导数随输入的变化,也叫二阶导数,可以衡量曲率
    . 如果二阶导数为0,即没有曲率,就是平坦的线,用梯度就可以预测值,如果梯度为1,步长为\epsilon,则此时将下降\epsilon;
    . 如果二阶导数为正数,函数曲线向下凹陷,实际下降比\epsilon要少,比预期要下降慢;
    . 如果二阶导数为负数,函数曲线向上凹陷,实际下降比\epsilon要多,比预期要下降快;
  • Hessian矩阵:一个多元函数的二阶导数构成的方阵
    Hessian矩阵是实对称的,可以将其分解为一组实特征值和一组特征向量的正交基。特定方向d上的二阶导数可以写成d^THd,当d是H的一个特征向量时,这个方向的二阶导数就是对应的特征值;对于其他方向的二阶导数可以写成所以特征值的加权平均,且与d夹角越小的特征向量的权重越来越大。最大特征值确定为最大二阶导数。

f(x)在当前点x_{0}的近似二阶泰勒级数:f(x)\approx f(x_{0})+(x-x_{0})g+1/2(x-x_{0})^{2}H(x-x_{0}),其中g为梯度,H是x_{0}的Hessian值。
如果使用学习率\epsilon,则新的点为x_{0}- g\epsilon,带入二阶泰勒级数的近似,可以得到:
f(x_{0}-g\epsilon)\approx f(x_{0})-g^{T}g\epsilon +1/2\epsilon^{2}g^{T}Hg
上式三项分别为:函数的原始值、函数梯度导致的预期更新和函数曲率带来的校正。当最后一项较大的时候,梯度下降有可能是上升的。当第三项为0或者负数时,近似的泰勒展开表明梯度下降将一直下降。在实际情况上,泰勒级数不会在\epsilon大的时候也保持准确。所以当g^{T}Hg为正时,通过计算,使近似的泰勒级数下降的最多最优步长为:
\epsilon^{*}=\frac{g^{T}g}{g^{T}Hg}
最坏情况下,g与H最大特征值对应的特征向量对齐,最优步长为\frac{1}{最大特征值}
二阶导数的另一个作用:
二阶导数测试:确定一个临界点是否为局部极大值、局部极小值或者鞍点。
. 临界点导数都为0,如果其二阶导数大于0意味着导数随着右移动越来越大,则为局部极小值;
. 临界点导数都为0,如果其二阶导数小于0意味着导数随着右移动越来越小,则为局部极大值;
. 临界点的二阶导数为0,无法确定,鞍点或者平坦区域。
扩展到多维情况下,单个点上每个方向的二阶导数是不同的,Hessian矩阵的条件数会影响梯度下降,一个方向上导数增加的很快,另一方向却很慢,梯度下降无法判断这种变化,病态的条件会难以选择合适的步
长。步长不能太大,以免冲过了最小而具有较强正曲率方向上升;而且这步长太小,在其他较小曲率上进展会不明显。

image.png

一、SGD

标准的SGD更新策略:
X = X-lr*dx
SGD的缺点

  1. 优化效率低
    如果损失在一个方向上变换的较快而在另一方向上变换的较慢,它的梯度非常缓慢在慢的那个方向上,而在快的方向上抖动剧烈。这是因为梯度的方向并没有指定最小值的方向,梯度不知道网那个方向去下降,是病态的变换;
    cs231课件截图.png
  2. 难以跳出局部最小值或鞍点
    因为更新策略只考虑了梯度,在局部最小值和鞍点的梯度都为0,此时梯度下降将停滞不前;
  3. mini-batch容易引入噪声
    对m个训练样本的随机采样,其梯度并不会在极小点消失。而且随机采样的点并不能代表全局数据集。

二、带动量的SGD

momentum动量

v=\alpha v-lr*g
\theta = \theta+v
之前一般的SGD优化只考虑了梯度,步长只跟梯度和学习率有关;现在,步长需要考虑梯度序列的大小和排序,当很多梯度指向相同的方向的时候,步长最大,速度一直在-g方向一直加速,直到最大速度,步长大小为:
\frac{lr*||g||}{1-\alpha}

image.png
对一个方向梯度较大和一个方向梯度较小的情况下,采用普通的SGD会很震荡,采用带momentum的SGD会缓和这种情况,因为在梯度较小的那个方向一些在累积梯度,而另外方向虽然梯度很大,但是一直震荡,累加起来就变小了,所以会更快的向最小点靠近。
image.png
Nesterov动量

v_{t+1} = \alpha*v_{t}-lr\bigtriangledown f(x_{t}+\alpha v_{t})
x_{t+1}=x_{t}+v_{t+1}

image.png

\hat x_{t}=x_{t}+\alpha v_{t},则有
v_{t+1}=\alpha*v_{t}-lr\bigtriangledown f(\hat x_{t})
\hat x_{t+1}=x_{t+1}+\alpha v_{t+1}=x_t+v_{t+1}+\alpha v_{t+1}
\hat x_{t+1}=x_t+v_{t+1}+\alpha v_{t+1}+\alpha v_{t}-\alpha v_{t}=\hat x_{t}+v_{t+1}+\alpha (v_{t+1}-v_{t})
Nesterov动量和momentum动量之间的区别就在于计算梯度上。Nesterov动量中,梯度计算在施加当前速度之后,相当于momentum动量上添加了校正因子。利用速度更新把loss带到一定地方再计算梯度,然后混合速度得到实际的更新方向。

三、AdaGrad

AdaGrad算法为每个参数自适应的调整学习率.
h = h+grad*grad
w=w-lr\frac{1}{\sqrt h}grad
AdaGrad保存了以前所有梯度的平方和,在更新参数时,除以了所有梯度平方和的根号,调整学习的尺度。对于变化较大的参数的学习率将变小,变化小的参数将会得到加速。
但是,如果一直学习下去,其梯度将为为0

四、RMSProp

RMSProp是改进的AdaGrad优化算法,它在计算梯度平方和时采用了加权,指数衰减平方来计算,逐步遗忘过去的梯度。
h =\alpha h+(1-\alpha )grad*grad
w=w-lr\frac{1}{\sqrt h}grad
另外,使用Nesterov动量的RMSProp可以描述为:
grad = \bigtriangledown f(w_{t}+\alpha v_{t})
h =\alpha h+(1-\alpha )grad*grad
v_{t+1} = \alpha*v_{t}-\frac {lr}{\sqrt {h}}*grad)
w_{t+1}=w_{t}+v_{t+1}

def rmsprop(w, dw, config=None):
    """
    Uses the RMSProp update rule, which uses a moving average of squared
    gradient values to set adaptive per-parameter learning rates.

    config format:
    - learning_rate: Scalar learning rate.
    - decay_rate: Scalar between 0 and 1 giving the decay rate for the squared
      gradient cache.
    - epsilon: Small scalar used for smoothing to avoid dividing by zero.
    - cache: Moving average of second moments of gradients.
    """
    if config is None: config = {}
    config.setdefault('learning_rate', 1e-2)
    config.setdefault('decay_rate', 0.99)
    config.setdefault('epsilon', 1e-8)
    config.setdefault('cache', np.zeros_like(w))

    next_w = None
    config['cache'] = config['decay_rate']*config['cache']+(1-config['decay_rate'])*dw*dw
    next_w = w-config['learning_rate']*dw/(np.sqrt(config['cache'])+1e-7)

    return next_w, config

五、Adam

Adam算法是AdaGrad/RMSProp和动量方法的结合
t = t+1
s=\beta_{1} s+(1-\beta_{1})grad
r =\beta_{2} s+(1-\beta_{2})grad *grad
\hat s = \frac{s}{1-\beta_{1}^t}修正
\hat r = \frac{r}{1-\beta_{2}^t}
w=w-lr*\frac{\hat s}{\sqrt{\hat r}}+eps
以下代码未写修正。

def adam(w, dw, config=None):
    """
    Uses the Adam update rule, which incorporates moving averages of both the
    gradient and its square and a bias correction term.

    config format:
    - learning_rate: Scalar learning rate.
    - beta1: Decay rate for moving average of first moment of gradient.
    - beta2: Decay rate for moving average of second moment of gradient.
    - epsilon: Small scalar used for smoothing to avoid dividing by zero.
    - m: Moving average of gradient.
    - v: Moving average of squared gradient.
    - t: Iteration number.
    """
    if config is None: config = {}
    config.setdefault('learning_rate', 1e-3)
    config.setdefault('beta1', 0.9)
    config.setdefault('beta2', 0.999)
    config.setdefault('epsilon', 1e-8)
    config.setdefault('m', np.zeros_like(w))
    config.setdefault('v', np.zeros_like(w))
    config.setdefault('t', 0)

    next_w = None
    config['m'] = config['m']*config['beta1']+(1-config['beta1'])*dw
    config['v'] = config['v']*config['beta2']+(1-config['beta2'])*dw*dw
    next_w = w - config['learning_rate']*config['m']/(np.sqrt(config['v']+config['epsilon']))
   
    return next_w, config

实验结果


loss.png
train accuracy.png
valid accuracy.png

总结

现在常用的优化算法有SGD,带动量的SGD(momentum/Nesterov),AdaGrad,RMSProp,Adam。其中,后三种是自适应学习率算法,这几种都是比较常用的,目前并没有一个统一的标准说哪一个算法更好,一般使用时都取决于是否对该方法比较熟悉或者以便调节超参数。

参考

  1. cs231课件
  2. 花书

相关文章

网友评论

      本文标题:cs231n学习之参数更新(6)

      本文链接:https://www.haomeiwen.com/subject/tzemsctx.html