作者:薰风初入弦
链接:https://www.zhihu.com/question/323747423/answer/790457991
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
文章和论文看不懂,通常有三个原因:
- 对前置知识掌握不佳
- 没有结合理论与实践
- 没有对知识形象理解
Adam本质上实际是RMSProp+动量。但如果你对随机梯度下降SGD的知识,以及Adam之前的几个更新方法一无所知。那么当你看到一个“复杂的”Adam看了就头大(请不要嘲笑初学者,当年我也觉得深度学习各个地方都超复杂)。
即使你看了论文和文章,如果不结合代码与实现,知道的也只是别人口中的“xxx的本质其实是xxx”,但这种别人的本质对你自己了解帮助不大。
最后,如果有个动图,Demo,实例,那么对你的帮助当然就更大啦。
这里我就结合这三点,好好讲一讲什么是Adam
本文翻译&改编自CS231n课件,链接见参考文献部分
一、引言 Introduction
现在假设你对反向传播的计算梯度的内容比较了解(不了解的欢迎在评论留言,我考虑有时间写),一旦能使用反向传播计算解析梯度,梯度就能被用来进行参数更新了。
进行参数更新有好几种方法,接下来都会进行讨论。深度网络的最优化是现在非常活跃的研究领域。(具体的细节看论文,请)
二、前置知识(Adam有关的更新方法)
- 普通更新
最简单的沿着负梯度方向改变参数(梯度指向的是上升方向,但要最小化损失函数)。
假设有一个参数向量x及其梯度dx,那么最简单的更新的形式是:
# 普通更新
x += - learning_rate * dx
其中,learning_rate是一个超参数,它是一个固定常量。
当在整个数据集上进行计算时,只要学习率足够低,总是能在损失函数上得到非负的进展。
2. 动量(Momentum)更新
该方法从物理角度上对于最优化问题得到的启发。
损失值是山的高度(因此高度势能是
,所以有
)。最优化过程可以看做是模拟参数向量(即质点)在地形上滚动的过程。因为作用于质点的力与梯度潜在能量(
)有关,质点所受的力就是损失函数的(负)梯度。
重力势能,中学常识之一
还有,因为 F=ma,所以在这个观点下(负)梯度与质点的加速度成比例。
注意这个理解与随机梯度下降(SDG)不同,在SGD中,梯度影响位置。
而在这个版本的更新中,物理观点建议梯度只是影响速度,然后速度再影响位置:
# 动量更新
v = mu * v - learning_rate * dx # 与速度融合
x += v # 与位置融合
在这里引入了一个初始=0的 变量v 和一个超参数mu。变量 mu 在最优化的过程中被看做动量(一般值设为0.9),但其物理意义与摩擦系数更一致。
这个变量有效地抑制了速度,降低了系统的动能,不然质点在山底永远不会停下来。
通过交叉验证,mu通常设为[0.5,0.9,0.95,0.99]中的一个。和学习率随着时间退火(下文有讨论)类似,动量随时间变化能略微效果,其中动量在学习过程的后阶段会上升。
一个典型的设置是刚开始0.5,多个周期(epoch)中慢慢提升到0.99。
通过动量更新,参数向量会在任何有持续梯度的方向上增加速度。
3. Nesterov动量
与普通动量不同,最近比较流行。在理论上对于凸函数能更好的收敛,在实践中也表现更好。
核心思路 :
当参数向量位于某个位置 x 时,观察上面的动量更新公式,动量部分(momentum step)会通过mu * v改变参数向量。
因此,如要计算梯度,那么可以将未来的近似位置 x+mu*v看做是“向前看”,这个点在我们一会儿要停止的位置附近。因此,计算 x+mu*v的梯度而不是“旧”位置 x 的梯度就有意义了。
Nesterov动量
既然动量会把参数带到绿色箭头指向的点,那就不要在原点(红色点)那里计算梯度了。
使用Nesterov动量,我们就在这个“向前看”的地方计算梯度。
也就是说,添加一些注释后,实现代码如下:
x_ahead = x + mu * v
# 计算dx_ahead(在x_ahead处的梯度,而不是在x处的梯度)
v = mu * v - learning_rate * dx_ahead
x += v
上面的程序还得写 dx_ahead,就很麻烦。
但通过对 x_ahead = x + mu * v 使用变量变换进行改写是可以做到的,然后用x_ahead而不是x来表示上面的更新。
也就是说,实际存储的参数向量总是向前一步版本。x_ahead 的公式(将其重新命名为x)就变成了:
v_prev = v # 存储备份
v = mu * v - learning_rate * dx # 速度更新保持不变
x += -mu * v_prev + (1 + mu) * v # 位置更新变了形式
3. RMSprop
非常高效,但没发表的适应性学习率方法。有趣的是,使用这个方法的人论文中都引用自Geoff Hinton的Coursera课程的第六课的第29页PPT。他修改了Adagrad方法,让方法不那么激进。
具体说来,就是它使用了一个梯度平方的滑动平均:
cache = decay_rate * cache + (1 - decay_rate) * dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)
在上面的代码中,decay_rate是一个超参数,常用[0.9,0.99,0.999]。
x+=和Adagrad中是一样的,但是cache变量是不同的。因此,RMSProp仍然是基于梯度的大小来对每个权重的学习率进行修改,这同样效果不错。但其更新不会让学习率单调变小。
以上就是所有需要的前置知识,接下来就是正题了。
三、Adam
Adam是RMSProp的动量版。简化的代码是下面这样:
m = beta1*m + (1-beta1)*dx
v = beta2*v + (1-beta2)*(dx**2)
x += - learning_rate * m / (np.sqrt(v) + eps)
注意,这方法和RMSProp很像,除了使用的是平滑版的梯度m,而不是原始梯度dx。
推荐参数值eps=1e-8, beta1=0.9, beta2=0.999。
在实际操作中,推荐Adam作为默认算法,一般比RMSProp要好一点。
但是也可以试试SGD+Nesterov动量。
完整的Adam更新算法也包含了一个偏置(bias)矫正机制,因为m,v两个矩阵初始为0,在没有完全热身之前存在偏差,需要采取一些补偿措施。
不同最优化方法效果
注意基于动量的方法射偏了的情况,使得最优化过程看起来像是一个球滚下山的样子。
上图展示了一个马鞍状的最优化地形,其中对于不同维度它的曲率不同(一个维度下降另一个维度上升)。注意SGD很难突破对称性,一直卡在顶部。而RMSProp之类的方法能够看到马鞍方向有很低的梯度。
因为在RMSProp更新方法中的分母项,算法提高了在该方向的有效学习率,使得RMSProp能够继续前进。
参考文献:
http://cs231n.github.io/neural-networks-3/
转载于:
https://www.zhihu.com/question/323747423/answer/790457991