cs231n学习之参数更新(6)

2019-08-24  本文已影响0人  Latet

前言

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

参数更新

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

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. 花书
上一篇 下一篇

猜你喜欢

热点阅读