前言
本文旨在学习和记录,如需转载,请附出处https://www.jianshu.com/p/7b00149dc797
参数更新
神经网络学习的目标是找到一组使损失函数尽可能小的参数,这个过程一般称作最优化问题。
一般计算参数的梯度来更新参数,沿着梯度的方向进行参数更新,然后重复多次,直到收敛,这个最基础的参数更新方法叫做随机梯度下降(stochastic gradient descent, SGD)
-
基于梯度的优化方法
目标函数:最小化或最大化的函数,通常也称损失函数、误差函数。一般格式为;
梯度下降:对误差函数求导,往其导数的反方向移动一小步来减小误差函数;
局部极小点:临界点小于周围所有的临近点,不可能通过无穷小的步长来减小f(x);
局部极大点:与局部极小点相反;
鞍点:临界点既不是最小也不是最大;
在深度学习中,优化的函数可能不是非凸的,很可能存在许多全局极小点或者鞍点时,优化算法可能无法找到全局最小点;但是即使找到的不是全局最小点,他找到的局部极小点对应于误差函数显著低的值,通常代表网络收敛了。 -
导数的导数
一阶导数随输入的变化,也叫二阶导数,可以衡量曲率
. 如果二阶导数为0,即没有曲率,就是平坦的线,用梯度就可以预测值,如果梯度为1,步长为,则此时将下降;
. 如果二阶导数为正数,函数曲线向下凹陷,实际下降比要少,比预期要下降慢;
. 如果二阶导数为负数,函数曲线向上凹陷,实际下降比要多,比预期要下降快; -
Hessian矩阵:一个多元函数的二阶导数构成的方阵
Hessian矩阵是实对称的,可以将其分解为一组实特征值和一组特征向量的正交基。特定方向d上的二阶导数可以写成,当d是H的一个特征向量时,这个方向的二阶导数就是对应的特征值;对于其他方向的二阶导数可以写成所以特征值的加权平均,且与d夹角越小的特征向量的权重越来越大。最大特征值确定为最大二阶导数。
f(x)在当前点的近似二阶泰勒级数:,其中g为梯度,H是的Hessian值。
如果使用学习率,则新的点为,带入二阶泰勒级数的近似,可以得到:
上式三项分别为:函数的原始值、函数梯度导致的预期更新和函数曲率带来的校正。当最后一项较大的时候,梯度下降有可能是上升的。当第三项为0或者负数时,近似的泰勒展开表明梯度下降将一直下降。在实际情况上,泰勒级数不会在大的时候也保持准确。所以当为正时,通过计算,使近似的泰勒级数下降的最多最优步长为:
最坏情况下,g与H最大特征值对应的特征向量对齐,最优步长为。
二阶导数的另一个作用:
二阶导数测试:确定一个临界点是否为局部极大值、局部极小值或者鞍点。
. 临界点导数都为0,如果其二阶导数大于0意味着导数随着右移动越来越大,则为局部极小值;
. 临界点导数都为0,如果其二阶导数小于0意味着导数随着右移动越来越小,则为局部极大值;
. 临界点的二阶导数为0,无法确定,鞍点或者平坦区域。
扩展到多维情况下,单个点上每个方向的二阶导数是不同的,Hessian矩阵的条件数会影响梯度下降,一个方向上导数增加的很快,另一方向却很慢,梯度下降无法判断这种变化,病态的条件会难以选择合适的步
长。步长不能太大,以免冲过了最小而具有较强正曲率方向上升;而且这步长太小,在其他较小曲率上进展会不明显。
一、SGD
标准的SGD更新策略:
SGD的缺点:
-
优化效率低
如果损失在一个方向上变换的较快而在另一方向上变换的较慢,它的梯度非常缓慢在慢的那个方向上,而在快的方向上抖动剧烈。这是因为梯度的方向并没有指定最小值的方向,梯度不知道网那个方向去下降,是病态的变换;
-
难以跳出局部最小值或鞍点
因为更新策略只考虑了梯度,在局部最小值和鞍点的梯度都为0,此时梯度下降将停滞不前; -
mini-batch容易引入噪声
对m个训练样本的随机采样,其梯度并不会在极小点消失。而且随机采样的点并不能代表全局数据集。
二、带动量的SGD
momentum动量
之前一般的SGD优化只考虑了梯度,步长只跟梯度和学习率有关;现在,步长需要考虑梯度序列的大小和排序,当很多梯度指向相同的方向的时候,步长最大,速度一直在-g方向一直加速,直到最大速度,步长大小为:
对一个方向梯度较大和一个方向梯度较小的情况下,采用普通的SGD会很震荡,采用带momentum的SGD会缓和这种情况,因为在梯度较小的那个方向一些在累积梯度,而另外方向虽然梯度很大,但是一直震荡,累加起来就变小了,所以会更快的向最小点靠近。
Nesterov动量
令,则有
Nesterov动量和momentum动量之间的区别就在于计算梯度上。Nesterov动量中,梯度计算在施加当前速度之后,相当于momentum动量上添加了校正因子。利用速度更新把loss带到一定地方再计算梯度,然后混合速度得到实际的更新方向。
三、AdaGrad
AdaGrad算法为每个参数自适应的调整学习率.
AdaGrad保存了以前所有梯度的平方和,在更新参数时,除以了所有梯度平方和的根号,调整学习的尺度。对于变化较大的参数的学习率将变小,变化小的参数将会得到加速。
但是,如果一直学习下去,其梯度将为为0
四、RMSProp
RMSProp是改进的AdaGrad优化算法,它在计算梯度平方和时采用了加权,指数衰减平方来计算,逐步遗忘过去的梯度。
另外,使用Nesterov动量的RMSProp可以描述为:
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和动量方法的结合
修正
以下代码未写修正。
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
实验结果
总结
现在常用的优化算法有SGD,带动量的SGD(momentum/Nesterov),AdaGrad,RMSProp,Adam。其中,后三种是自适应学习率算法,这几种都是比较常用的,目前并没有一个统一的标准说哪一个算法更好,一般使用时都取决于是否对该方法比较熟悉或者以便调节超参数。