查看公式请安装插件GitHub with MathJax
x += - learning_rate * dx
learning_rate是一个超参数, 只要学习率足够低那么总能够使得模型的损失值降低, 使得模型优化
# 动量更新
v = mu * v - learning_rate * dx # 与速度融合,mu小于1相当于摩擦力
x += v # 与位置融合,写成v*1更容易理解,x是路程,v是速度
损失值可以理解为山的高度,最优化过程可以看做是模拟参数向量(即质点)在地形上滚动的过程。质点所受的力就是损失函数的负梯度,由$F=ma$可知质点的加速度是成比例的,参数向量会在任何有持续梯度的方向上增加速度,像是在滚球。
x_ahead = x + mu * v
# evaluate dx_ahead (the gradient at x_ahead instead of at x)
v = mu * v - learning_rate * dx_ahead
x += v
当参数向量位于某个位置x时,为快速逼近最低点,应计算$x+mu∗vx+mu∗v$的梯度而不是“旧”位置$x$的梯度
在深度网络背景下,第二类常用的最优化方法是基于牛顿法的,其迭代如下: $$ x = x-[Hf(x)]^{-1}\nabla f(x) $$ 这里Hf(x)是Hessian矩阵,它是函数的二阶偏导数的平方矩阵,$\nabla f(x)$是梯度向量,这和梯度下降中一样。直观理解上,Hessian矩阵描述了损失函数的局部曲率,允许更高效的参数更新(直接知道最低值的路径),巨大的优势是没有超参数:学习率。但是Hessian矩阵操作非常耗费时间和空间,基于拟牛顿法的L-BFGS解决存储空间问题,但仍需要对整个训练集进行计算。
建议:L-BFGS不常用,如果能支持全批量计算并减去噪声源,使用L-BFGS会有不错效果。
如果学习率很高,系统的动能就过大,参数向量就会无规律地跳动,因此希望学习率越来越小,这样就能更加接近最优点而不是在最优点附近跳来跳去。
训练几个周期就根据一些模型表现来降低学习率, 典型的值是每过5个周期就将学习率减少一半, 或者每20个周期减少到之前的0.1, 但是这些数值的设定是严重依赖具体问题和模型的选择的。在实践中有一种经验做法:使用一个固定的学习率来进行训练的同时观察验证集错误率,每当验证集错误率停止下降,就乘以一个常数(比如0.5)来降低学习率。
学习率的退火公式为$α=α_0e^{−kt}$, 其中$α_0,k$都是超参数, t是迭代次数或者以周期为单位
学习率的退火公式为$α=α_0/(1+kt)$, 其中$α_0,k$都是超参数, tt是迭代次数或者以周期为单位
在实践中,我们发现随步数衰减的随机失活(dropout)更受欢迎,因为它使用的超参数(衰减系数和以周期为时间单位的步数)比k更有解释性。如果有足够的计算资源,可以让衰减更加缓慢一些,让训练时间更长些。
前面讨论的所有方法都是对学习率进行全局地操作,并且对所有的参数都是一样的操作, 接下来是自适应调整学习率的方法, 这些方法依然会引入一些超参数的设置, 但是这些方法确实有更好的表现
# 假设有梯度和参数向量x
cache += dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)
cache跟踪了每个参数的梯度的平方和,进行逐元素的归一化,初始时使得接收到高梯度值的权重更新的效果被减弱,而接收到低梯度值的权重的更新效果将会增强(累加的效果是使学习率逐渐下降,没毛病),eps一般设为1e-4到1e-8之间,是防止出现除以0的情况出现 缺点:在深度学习中单调的学习率通常过于激进且过早停止学习
#cache的计算不一样,decay_rate是一个超参数,常用的值是[0.9,0.99,0.999]
cache = decay_rate * cache + (1 - decay_rate) * dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)
用一种很简单的方式修改了Adagrad方法,让它不那么激进单调地降低了学习率,相当于梯度平方的滑动平均
#论文中推荐的参数值eps=1e-8, beta1=0.9, beta2=0.999
m = beta1*m + (1-beta1)*dx
v = beta2*v + (1-beta2)*(dx**2)
x += - learning_rate * m / (np.sqrt(v) + eps)
将RMSProp和momentum结合
推荐:Adam作为默认的算法,一般而言跑起来比RMSProp要好一点。但是也可以试试SGD+Nesterov动量。
神经网络最常设置的超参数:初始学习率、学习率衰减方式(例如一个衰减常量)、正则化强度(L2惩罚,随机失活强度),还有很多相对不那么敏感的超参数,比如在逐参数适应学习方法中,对于动量及其时间表的设置等。
实现:在代码中设计从程序去记录点中有各种各样的训练统计数据, 在文件名中最好包含验证集的算法表现,这样就能方便地查找和排序
注意的点:
- 比起交叉验证最好使用一个验证集
- 超参数范围, 在对数尺度上进行超参数搜索, 因为学习率和正则化强度参与乘法运算使得原尺寸改变效果不明显, 但是有一些参数(比如随机失活)还是在原始尺度上进行搜索
- 随机选择比网格化的选择更加有效
- 对于边界上的最优值要小心,它可能让你错过更好的其他搜索范围
- 从粗到细地分阶段搜索, 在实践中,先进行初略范围搜索,然后根据好的结果出现的地方,缩小范围进行搜索。进行粗搜索的时候,让模型训练一个周期就可以了, 第二个阶段就是对一个更小的范围进行搜索,这时可以让模型运行5个周期,而最后一个阶段就在最终的范围内进行仔细搜索,运行很多次周期。
- 贝叶斯超参数最优化是一整个研究领域,主要是研究在超参数空间中更高效的导航算法。其核心的思路是在不同超参数设置下查看算法性能时,要在探索和使用中进行合理的权衡。基于这些模型,发展出很多的库,比较有名的有:Spearmint,SMAC,Hyperopt。
在训练的时候训练几个独立的模型,然后在测试的时候平均它们预测结果。集成的模型数量增加,算法的结果也单调提升(但提升效果越来越少)。还有模型之间的差异度越大,提升效果可能越好。进行集成有以下几种方法:
- 同一个模型,不同的初始化。 使用交叉验证来得到最好的超参数,然后用最好的参数来训练不同初始化条件的模型, 这种方法的风险在于模型的多样性只来自于不同的初始化条件。
- 在交叉验证中发现最好的模型。 使用交叉验证来得到最好的超参数,然后取其中最好的几个模型来进行集成。这样就提高了集成的多样性,但风险在于可能会包含不够理想的模型。
- 一个模型设置多个记录点。 如果训练非常耗时,那就在不同的训练时间对网络留下记录点(比如每个周期结束),然后用它们来进行模型集成
- 在训练的时候跑参数的平均值。 在训练过程中,如果损失值相较于前一次权重出现指数下降时,就在内存中对网络的权重进行一个备份。这样你就对前几次循环中的网络状态进行了平均。你会发现这个"平滑"过的版本的权重总是能得到更少的误差。直观的理解就是目标函数是一个碗状的,你的网络在这个周围跳跃,所以对它们平均一下,就更可能跳到中心去。
Geoff Hinton:通过将集成似然估计纳入到修改的目标函数中,从一个好的集成中抽出一个单独模型。