梯度下降算法时机器学习、深度学习中常用的找到最优参数的方法,由于梯度下降的参数更新方式的多种,也就产生了不同的梯度下降方法,这些方法中较好的方法能够更快的将参数迭代优化,下面我来介绍一下几种梯度下降的方法。
该算法的思想是独立地适应模型的每个参数:具有较大偏导的参数相应有一个较大的学习率,而具有小偏导的参数则对应一个较小的学习率。具体来说,每个参数的学习率会缩放各参数反比于其历史梯度平方值总和的平方根。
由于 W , b W,b W,b 更新的方式都一样的,我们用参数 θ \theta θ 才代替更新的参数。
d θ = 1 m ∑ i = 1 m L ( f ( x i ) ; θ ) r = r + ( d θ ) 2 θ = θ − ϵ δ + r d θ \begin{align} d\theta&=\frac{1}{m}\sum_{i=1}^mL(f(x_i);\theta) \notag \\ r &= r+(d\theta)^2 \notag \\ \theta &= \theta - \frac{\epsilon}{\delta+\sqrt{r}}d\theta \notag \end{align} dθrθ=m1i=1∑mL(f(xi);θ)=r+(dθ)2=θ−δ+rϵdθ其中 L ( f ( x ) ; θ ) L(f(x);\theta) L(f(x);θ) 为损失函数, r r r 为累积平方梯度,初始时 r = 0 r=0 r=0, ϵ \epsilon ϵ 为设置的全局学习率, δ \delta δ 为防止分母为 0 0 0 设置的小常数,一般是 1 0 − 7 10^{-7} 10−7。
AdaGrad 中学习率并没有更新,每次应用时将其进行了缩放,且缩放是单调递减的,训练后期学习率过小会导致训练困难,甚至提前结束。
有时需要画出数据的曲线,但是数据的波动又太过巨大,这时就需要使用一些方法来将数据的变化趋势变的更加明显,一般采用的是指数平滑的方式。
设数据为 Y = ( y 1 , y 2 , ⋯ , y n ) Y=(y_1,y_2, \cdots,y_n) Y=(y1,y2,⋯,yn),平滑后的数据为 θ = ( θ 0 , θ 1 , ⋯ , θ n ) \theta=(\theta_0,\theta_1, \cdots,\theta_n) θ=(θ0,θ1,⋯,θn),初始时有 θ 0 = 0 \theta_0=0 θ0=0,则采用以下公式来得到平滑后的数据
θ t = β θ t − 1 + ( 1 − β ) y t , 1 ≤ t ≤ n \theta_t= \beta \theta_{t-1}+(1-\beta)y_t,1 \le t \le n θt=βθt−1+(1−β)yt,1≤t≤n β \beta β 最常用的值是0.9,那么,为什么这种方法叫指数平均呢?
让我们将数据拆开来看,比如,取 β = 0.9 \beta=0.9 β=0.9 ,我们一层层的拆开 θ 3 \theta_3 θ3,可以得到:
θ 3 = 0.9 ∗ θ 2 + 0.1 ∗ y 3 = 0.9 ∗ ( 0.9 ∗ θ 1 + 0.1 ∗ y 2 ) + 0.1 ∗ y 3 \begin{align} \theta_3 &=0.9 *\theta_2+0.1*y_3 \notag \\ &=0.9*(0.9*\theta_1+0.1*y_2)+0.1*y_3 \notag \end{align} θ3=0.9∗θ2+0.1∗y3=0.9∗(0.9∗θ1+0.1∗y2)+0.1∗y3看,这样一层层的下去,前面的日期的影响是不是就以 0. 9 n 0.9^n 0.9n 的形式对下面的数据进行了影响,这就是指数的来历。
如果按照上面的公式进行加权平均的话,第一项数据由于没有前面的其他数据了,所以就只能成为 0.1 y 1 0.1y_1 0.1y1,这种偏差对于前面的数据来说是很大的,由此我们可以使用偏差修正。
偏差修正式由下面给出:
v t 1 − β t = β θ t − 1 + ( 1 − β ) y t , 1 ≤ t ≤ n \frac{v_{t}}{1- \beta^{t}}=\beta \theta_{t-1}+(1-\beta)y_t,1 \le t \le n 1−βtvt=βθt−1+(1−β)yt,1≤t≤n其中 t t t 也就是数据的个数,你会发现随着 t t t 增加, β t \beta^{t} βt 接近于0,所以当 t t t很大的时候,偏差修正几乎没有作用。
在机器学习中,在计算指数加权平均数的大部分时候,大家不在乎执行偏差修正,因为大部分人宁愿熬过初始时期,拿到具有偏差的估测,然后继续计算下去。如果你关心初始时期的偏差,在刚开始计算指数加权移动平均数的时候,偏差修正能帮助你在早期获取更好的估测。
动量梯度下降法就是在梯度下降法中,迭代点的更新方向由当前的负梯度方向和上一次的迭代更新方向加权组合形成,即
v t = β v t − 1 + ( 1 − β ) d W v_{{t}}= \beta v_{{t-1}} + \left( 1 - \beta \right)dW vt=βvt−1+(1−β)dW其中 d W dW dW 是计算出的该点要更新的梯度,如果不用动量梯度下降,按之前的方式,则使用的就是这个 d W dW dW,这跟我们之前所讲的指数加权有相似的地方,一样的,一般的地方采取的是 β = 0.9 \beta=0.9 β=0.9。
RMSprop 算法的全称是root mean square prop算法,它也可以加速梯度下降,它的参数更新如下所示:
S d W = β S d W + ( 1 − β ) ( d W ) 2 W = W − a d W S d W b = b − α d b S d b \begin{align} S_{dW}&= \beta S_{dW} + (1 -\beta) {(dW)}^{2} \notag \\ \notag \\ W&= W -a\frac{dW}{\sqrt{S_{dW}}} \notag \\ \notag \\ b&=b -\alpha\frac{db}{\sqrt{S_{db}}} \notag \end{align} SdWWb=βSdW+(1−β)(dW)2=W−aSdWdW=b−αSdbdb
Adam优化可以说是综合了RMSprop和动量梯度下降两者的算法,假设要优化的参数是 W , b W,b W,b,首先要初始化, v d W = 0 v_{dW} = 0 vdW=0, S d W = 0 S_{dW} =0 SdW=0, v d b = 0 v_{db} = 0 vdb=0, S d b = 0 S_{db} =0 Sdb=0,在第 t t t次迭代中,你要计算微分,用当前的mini-batch计算 d W dW dW, d b db db,一般你会用mini-batch梯度下降法。接下来计算Momentum指数加权平均数,所以 v d W = β 1 v d W + ( 1 − β 1 ) d W v_{dW}= \beta_{1}v_{dW} + ( 1 - \beta_{1})dW vdW=β1vdW+(1−β1)dW,使用Momentum时我们肯定会用这个公式,所以现在不叫它 β \beta β,而叫它 β 1 \beta_{1} β1。同样 v d b = β 1 v d b + ( 1 − β 1 ) d b v_{db}= \beta_{1}v_{db} + ( 1 -\beta_{1} ){db} vdb=β1vdb+(1−β1)db。
接着你用RMSprop进行更新,即用不同的超参数 β 2 \beta_{2} β2, S d W = β 2 S d W + ( 1 − β 2 ) ( d W ) 2 S_{dW}=\beta_{2}S_{dW} + ( 1 - \beta_{2}){(dW)}^{2} SdW=β2SdW+(1−β2)(dW)2,再说一次,这里是对整个微分 d W dW dW进行平方处理, S d b = β 2 S d b + ( 1 − β 2 ) ( d b ) 2 S_{db} =\beta_{2}S_{db} + \left( 1 - \beta_{2} \right){(db)}^{2} Sdb=β2Sdb+(1−β2)(db)2。
相当于Momentum更新了超参数 β 1 \beta_{1} β1,RMSprop更新了超参数 β 2 \beta_{2} β2。一般使用Adam算法的时候,要计算偏差修正, v d W corrected v_{dW}^{\text{corrected}} vdWcorrected,修正也就是在偏差修正之后,
v d W corrected = v d W 1 − β 1 t v_{dW}^{\text{corrected}}= \frac{v_{dW}}{1 - \beta_{1}^{t}} vdWcorrected=1−β1tvdW,
同样 v d b corrected = v d b 1 − β 1 t v_{db}^{\text{corrected}} =\frac{v_{db}}{1 -\beta_{1}^{t}} vdbcorrected=1−β1tvdb,
S S S也使用偏差修正,也就是 S d W corrected = S d W 1 − β 2 t S_{dW}^{\text{corrected}} =\frac{S_{dW}}{1 - \beta_{2}^{t}} SdWcorrected=1−β2tSdW, S d b corrected = S d b 1 − β 2 t S_{db}^{\text{corrected}} =\frac{S_{db}}{1 - \beta_{2}^{t}} Sdbcorrected=1−β2tSdb。
最后更新权重,所以 W W W更新后是 W = W − a v d W corrected S d W corrected + ε W= W - \frac{a v_{dW}^{\text{corrected}}}{\sqrt{S_{dW}^{\text{corrected}}} +\varepsilon} W=W−SdWcorrected+εavdWcorrected(如果你只是用Momentum,使用 v d W v_{dW} vdW或者修正后的 v d W v_{dW} vdW,但现在我们加入了RMSprop的部分,所以我们要除以修正后 S d W S_{dW} SdW的平方根加上 ε \varepsilon ε)。
根据类似的公式更新 b b b值, b = b − α v db corrected S db corrected + ε b=b - \frac{\alpha v_{\text{db}}^{\text{corrected}}}{\sqrt{S_{\text{db}}^{\text{corrected}}} +\varepsilon} b=b−Sdbcorrected+εαvdbcorrected。
在pytorch以及TensorFlow中,这些参数一般被设置为下面的值
α = 0.001 , β 1 = 0.9 , b e t a 2 = 0.999 , e p s i l o n = 1 0 − 8 \alpha=0.001, \beta_1=0.9, beta2=0.999, epsilon=10^{-8} α=0.001,β1=0.9,beta2=0.999,epsilon=10−8
目前优化神经网络的方法都是基于BP,即根据损失函数计算的误差通过梯度反向传播的方式,指导深度网络权值的更新优化。其中将误差从末层往前传递的过程需要链式法则的帮助,因此反向传播算法可以说是梯度下降在链式法则中的应用。
而链式法则是一个连乘的形式,所以当层数越深的时候,梯度将以指数形式传播。梯度消失问题和梯度爆炸问题一般随着网络层数的增加会变得越来越明显。在根据损失函数计算的误差通过梯度反向传播的方式对深度网络权值进行更新时,得到的梯度值接近0或特别大,也就是梯度消失或爆炸。这方面的例子可以参照以前挺火的一个例子,即 0.9 9 365 0.99^{365} 0.99365 和 1.0 1 365 {1.01^{365}} 1.01365 。
梯度爆炸的出现其实挺明显的,有以下的几个特点:
选取合适的激活函数
如果使用 s i g m o i d sigmoid sigmoid 作为损失函数,其梯度最大也只有 0.25 0.25 0.25 的,故梯度不可能超过 0.25 0.25 0.25,所以在深层网络中很容易发生梯度消失的现象。在这里建议的是不使用 s i g m o i d sigmoid sigmoid 函数,可以使用 R e L U ReLU ReLU 函数或者 L e a k y R e L U LeakyReLU LeakyReLU 函数, R e L U ReLU ReLU 函数解决了梯度消失和梯度爆炸的问题,计算方便计算速度快(梯度恒定为 0 0 0或 1 1 1),加速了网络的训练,但是由于负数部分恒为 0 0 0,导致一些神经元无法激活,所以建议可以多使用 L e a k y R e L U LeakyReLU LeakyReLU 激活函数。
梯度截断
梯度截断是一种比较简单的启发式方法,把梯度的模限定在一个区间,当梯度的模小于或大于这个区间时就进行截断.一般截断的方式有以下两种: