本篇博文详细介绍了关于梯度下降算法的所有相关知识,具体包括:回归拟合问题、损失函数、梯度下降算法、随机梯度下降算法、动量随机梯度下降算法、AdaGrad算法、RMSProp算法、Adam算法。相信各位读者详读本篇博文后一定能学习到很多知识。关于梯度下降算法的相关知识点也是深度学习中的重要内容,所以本篇博文针对这些算法的原理进行了详细解释,并辅以一定量的图片进行说明。当然,由于我本人能力有限,可能有些地方写的不是特别准确,还请读者批评指正,谢谢!下面就开始本篇博文的全部内容!
如图1所示,若我们使用直线 y = w ^ x + b y=\widehat{w} x+b y=w x+b来拟合这三个点(假设斜率 w ^ \widehat{w} w 已知),该如何处理呢?
很明显,我们应该寻找一个最好的截距 b b b,使得直线与三个点的平均距离最短,如图2所示:
由图2可知,三个定点到直线(0的距离分别为 e 1 、 e 2 、 e 3 e_1、e_2、e_3 e1、e2、e3, e i ( 1 ≤ i ≤ 3 ) e_i(1≤i≤3) ei(1≤i≤3)也被称为定点到直线的距离误差,很明显,距离误差应该越小越好。我们可以把此距离误差写为最小二乘的形式:
L = 1 2 ∑ i = 1 3 ∣ e i ∣ 2 L=\frac{1}{2} \sum_{i=1}^{3}\left|e_{i}\right|^{2} L=21i=1∑3∣ei∣2
此时,函数 L L L就是我们俗称的“损失函数”,我们的目的就是让损失函数最小,这样直线才能距离定点的平均距离最小。那么如何使损失函数最小呢?经过刚才的分析,应该通过调整截距 b b b的值,使损失函数达到最小值。所以,我们可以将直线方程代入,可得损失函数为:
L = 1 2 ∑ i = 1 3 ∣ e i ∣ 2 = 1 2 ∑ i = 1 3 [ y i − ( w ^ x i + b ) ] 2 = L = 1 2 ∑ i = 1 3 b 2 + 2 ( w ^ x i − y i ) b + ( y i − w ^ x i ) 2 \begin{equation} \begin{aligned} L&=\frac{1}{2} \sum_{i=1}^{3}\left|e_{i}\right|^{2}\\ &=\frac{1}{2} \sum_{i=1}^{3}\left[y_{i}-\left(\widehat{w} x_{i}+b\right)\right]^{2}\\ &=L=\frac{1}{2} \sum_{i=1}^{3} b^{2}+2\left(\widehat{w} x_{i}-y_{i}\right) b+\left(y_{i}-\widehat{w} x_{i}\right)^{2} \end{aligned} \end{equation} L=21i=1∑3∣ei∣2=21i=1∑3[yi−(w xi+b)]2=L=21i=1∑3b2+2(w xi−yi)b+(yi−w xi)2
可以发现,损失函数 L L L就是一个关于 b b b的二次函数,其他参数都是已知的,假设此时损失函数 L L L的图像如下所示:
那么计算机如何通过迭代优化 b b b的值使 L L L最小呢?在深度学习中,我们可以随机初始化 b b b的值,然后令:
b = b − ε d L d b b=b-\varepsilon \frac{dL}{db} b=b−εdbdL
其中, ε \varepsilon ε就是我们常说的学习率,我们可以通过调整学习率去调整 b b b的更新程度(梯度下降的步长),而 d L d b \frac{dL}{db} dbdL,是损失函数 L L L在初始化 b b b的位置的斜率。此时, b b b就更新为如下所示,看起来随着 b b b的更新,损失函数 L L L变小了。
当 b b b更新后,直线 y = w ^ x + b y=\widehat{w} x+b y=w x+b也随之更新,因为 b b b影响直线的截距。可以发现,此时直线距离三个定点的平均距离变小了,说明上面的方法起作用了。
我们可以继续的进行这个过程,通过迭代优化去调整 b b b的值,从而找到损失函数 L L L最小的点。
当优化到最低点的时候,斜率等于0,说明 d L d b \frac{dL}{db} dbdL也是0,那么 b b b不会再被更新,说明已经找到了最优的 b b b值,使得损失函数 L L L最小,那么整个优化过程结束。然后将 b b b代入到直线 y = w ^ x + b y=\widehat{w} x+b y=w x+b中,可以发现,此时直线距离三个定点的平均距离最小。这就是梯度下降算法(Gradient Descent,GD),其中 d L d b \frac{dL}{db} dbdL就称为梯度值。
在更一般的情况下,我们的数据点可能有很多,且分布无规律,那么此时就不能使用直线去拟合这些数据点了,我们可以考虑使用曲线拟合这些数据点,如下图所示。
此时,我们需要使用非线性函数 y = f ( x , θ ) y=f(x,\theta) y=f(x,θ)来拟合这些数据,其中要优化的参数为 θ \theta θ,与上面所讲述的梯度下降算法一样,我们需要计算每个样本点的损失,所以定义第 i i i个样本点的损失函数为:
L ( f ( x i , θ ) , y i ) L\left(f\left(x_{i}, \theta\right), y_{i}\right) L(f(xi,θ),yi)
然后需要计算此样本点在 x i x_i xi处关于 θ \theta θ的梯度:
∇ θ L ( f ( x i , θ ) , y i ) \nabla_{\theta} L\left(f\left(x_{i}, \theta\right), y_{i}\right) ∇θL(f(xi,θ),yi)
因为样本点较多,所以还需要对所有样本点计算平均梯度:
g = 1 N ∇ θ ∑ i = 1 N L ( f ( x i , θ ) , y i ) g=\frac{1}{N} \nabla_{\theta} \sum_{i=1}^{N} L\left(f\left(x_{i}, \theta\right), y_{i}\right) g=N1∇θi=1∑NL(f(xi,θ),yi)
当计算好 θ \theta θ关于所有样本点的平均梯度 g g g后,就可以更新 θ \theta θ的值,使损失函数更小,其中 ε \varepsilon ε为学习率:
θ = θ − ε g \theta=\theta-\varepsilon g θ=θ−εg
虽然上面的方法确实可以帮助我们降低损失值,更好的拟合数据点,但是在整个计算过程中,我们需要计算每个样本点关于 θ \theta θ的梯度 ∇ θ L ( x i ) ( 1 ≤ i ≤ 8 ) \nabla_{\theta} L(x_{i})(1≤i≤8) ∇θL(xi)(1≤i≤8),这显然会带来两个问题:
现实情况通常会有非常多的数据需要我们去拟合,那该如何解决上面的问题呢?解决方法也很简单:每次从 n n n个样本里不重复地随机选择 m m m个样本,用这 m m m个样本去计算梯度:
g = 1 m ∇ θ ∑ i = 1 m L ( f ( x i , θ ) , y i ) g=\frac{1}{m} \nabla_{\theta} \sum_{i=1}^{m} L\left(f\left(x_{i}, \theta\right), y_{i}\right) g=m1∇θi=1∑mL(f(xi,θ),yi)
然后使用计算好的梯度更新参数:
θ = θ − ε g \theta=\theta-\varepsilon g θ=θ−εg
此种方法也被形象地称为随机梯度下降算法(Stochastic Gradient Descent,SGD),此算法可以有效解决上面提到的内存开销和迭代速度的问题。
看似随机梯度下降算法已经解决了所有问题,但是深度学习网络训练往往是一个非凸的优化过程,在损失函数的参数空间中通常分布各种的山脊和山谷,以一个山谷形状的损失函数的参数空间为例:
使用随机梯度下降的移动轨迹会在山谷两侧来回震荡,难以收敛到最低点(损失函数最小值)。那么该如何让运动轨迹变得更“平滑”一些呢?我们可以考虑利用上一次梯度来改变本次梯度下降的方向,如下图所示:
很明显,参数的运动来自上一次梯度力的作用,这个力也被称为动量(Moment),动量结合此次梯度的方向,并利用向量的加法规则,使参数的运动轨迹更加“平滑”,这就是动量随机梯度下降算法。那么如何使用数学公式表达此算法呢?梯度的计算方法仍与之前一致:
g = 1 m ∇ θ ∑ i = 1 m L ( f ( x i , θ ) , y i ) g=\frac{1}{m} \nabla_{\theta} \sum_{i=1}^{m} L\left(f\left(x_{i}, \theta\right), y_{i}\right) g=m1∇θi=1∑mL(f(xi,θ),yi)
引入的动量使用 v v v来表示:
v = α v − ε g v = \alpha v-\varepsilon g v=αv−εg
其中 α × v \alpha×v α×v就表示对原始梯度方向的保留量, α \alpha α的作用就是通过控制原始梯度(动量)对新梯度的影响程度。我们需要优化的参数仍是 θ \theta θ,此时的 θ \theta θ更新为:
θ = θ + v \theta=\theta+v θ=θ+v
通过此算法,我们就可以在复杂的损失函数的参数空间中找到最优的参数运动轨迹方向,从而使损失值达到最小,最终到达最优化参数的目的,这就是动量随机梯度下降算法的作用。
目前基本已经将所有情况都讨论了,但是还有一点我们忽略了,就是学习率 ε \varepsilon ε的设置对模型结果好坏的影响。我们清楚,学习率的作用是为了:
所以,我们通常手动设置一个学习率,随着模型的训练,学习率逐渐被调整的越来越小:
但是总是靠人工去调整学习率,不仅有一定的主观影响,而且还十分困难,导致最后的模型训练往往达不到想要的效果。如果可以每次结合梯度值,让神经网络在训练的过程中自适应的调整学习率,不仅可以解放人工操作,还可以提高模型训练精度。损失函数我们仍定义为:
g = 1 m ∇ θ ∑ i = 1 m L ( f ( x i , θ ) , y i ) g=\frac{1}{m} \nabla_{\theta} \sum_{i=1}^{m} L\left(f\left(x_{i}, \theta\right), y_{i}\right) g=m1∇θi=1∑mL(f(xi,θ),yi)
此时,我们引入参数 r r r:
r = r + g 2 r = r + g^{2} r=r+g2
r r r的含义就是梯度大小随时间的积累量。这样,我们在进行参数更新时,就可以利用每次梯度的影响,去自适应的调整学习率大小,从而让 θ \theta θ最优化。此时 θ \theta θ的更新公式为:
θ = θ − ε r + δ g \theta = \theta-\frac{\varepsilon}{\sqrt{r}+\delta} g θ=θ−r+δεg
其中, δ \delta δ是一个不为零的小量,作用是防止分母为零来稳定数值计算,可以手动初始化其参数大小。由以上公式可以发现:
这种可以自适应调整学习率的算法被称为AdaGrad算法。
虽然AdaGrad算法可以更好的调节学习率,但是这样学习率只和梯度有关,可能让学习率过早的变小而不好控制,最终导致模型训练效果不佳。所以应该让参数 r r r的变化不仅仅依赖于参数 g g g(梯度)。那么可以引入可以手动调节的参数$\rho $来控制优化过程。损失函数定义仍为:
g = 1 m ∇ θ ∑ i = 1 m L ( f ( x i , θ ) , y i ) g=\frac{1}{m} \nabla_{\theta} \sum_{i=1}^{m} L\left(f\left(x_{i}, \theta\right), y_{i}\right) g=m1∇θi=1∑mL(f(xi,θ),yi)
而此时的 r r r由于引入可以手动调节的参数 ρ \rho ρ,那么 r r r定义为:
r = ρ r + ( 1 − ρ ) g 2 r = \rho r+(1-\rho) g^{2} r=ρr+(1−ρ)g2
此时更新 θ \theta θ的公式如下,其余参数含义仍不变:
θ = θ − ε r + δ g \theta = \theta-\frac{\varepsilon}{\sqrt{r+\delta}} g θ=θ−r+δεg
这种算法中的 r r r不仅仅依赖参数 g g g(梯度),还依赖于可以手动调节的参数 ρ \rho ρ,这样得到的学习率对于参数优化过程更有效。这种自适应调整学习率的算法被称为RMSProp。
那么如果在参数更新时既考虑动量又考虑到参数对学习率的影响呢?假设损失函数仍定义为:
g = 1 m ∇ θ ∑ i = 1 m L ( f ( x i , θ ) , y i ) g=\frac{1}{m} \nabla_{\theta} \sum_{i=1}^{m} L\left(f\left(x_{i}, \theta\right), y_{i}\right) g=m1∇θi=1∑mL(f(xi,θ),yi)
首先考虑动量,为了更好的考虑到动量对参数更新的影响,我们也将其改造为自适应动量,也就是引入新的参数 ρ 1 \rho_{1} ρ1来控制动量对梯度的优化过程:
s = ρ 1 s + ( 1 − ρ 1 ) g s = \rho_{1} s+\left(1-\rho_{1}\right) g s=ρ1s+(1−ρ1)g
然后修正此参数,使之在训练之初比较大,帮助算法快速收敛。所以将 s s s修正为 s ^ \hat{s} s^:
s ^ = s 1 − ρ 1 t \hat{s} = \frac{s}{1-\rho_{1}^{t}} s^=1−ρ1ts
其次还要考虑学习率对参数更新的影响,此时仍需要通过自适应来调整学习率,所以参数 r r r的定义仍与RMSProp算法中的参数 r r r的定义一致:
r = ρ 2 r + ( 1 − ρ 2 ) g 2 r = \rho_{2} r+(1-\rho_{2}) g^{2} r=ρ2r+(1−ρ2)g2
对于参数 r r r来说,也需要修正,修正的目的与参数 s s s修正的目的一致,修正后的参数 r r r定义为 r ^ \hat{r} r^:
r ^ = r 1 − ρ 2 t \hat{r} = \frac{r}{1-\rho_{2}^{t}} r^=1−ρ2tr
由于此时既考虑动量又考虑到参数对学习率的影响,所以更新 θ \theta θ的公式此时为:
θ = θ − ε s ^ r ^ + δ g \theta = \theta-\frac{\varepsilon \hat{s}}{\sqrt{\hat{r}}+\delta} g θ=θ−r^+δεs^g
此时更新后的参数 θ \theta θ由于集各家之所长,所以此参数效果更好,损失值也非常低,从而最终模型的训练准确率也会有所提高。此种既考虑了动量又考虑到了自适应学习率的算法被称为Adam算法。
以上就是本篇博客的全部内容了,可以看到我对每个算法以及其原理都进行了详细的介绍与解释,希望读者可以学习到自己想要的内容。后面还会针对深度学习这方面的算法做更深入的研究,还请读者期待!