梯度下降的优化算法

通过之前的学习我们知道通过梯度下降法,我们可以优化模型的参数。 但是如果按照我们之前推断的数学方式直接进行实现的话, 以目前的算力几乎是不可能实现的,甚至说在可见的未来也不太可能。 所以说梯度下降法在上个世纪七十年代就提出来了,直到2012年Alexnet出现才得以应用。 在这期间,科学家们的想法主要是对梯度下降法进行优化。 那么我们先不说科学家是怎么想的,我们自己先想一想如何对梯度下降法进行优化。 一个方向是优化计算梯度的时间,另一个方向是优化梯度下降的路径。 我们先来看一看如何优化计算梯度的时间:

随机梯度下降法

假设我们这里使用的是最小交叉熵, 我们可以看到公式: \[ \mathscr{L}\left(\hat{y}, y\right) = -\sum\left(y\log\hat{y} + \left(1-y\right)\log\left(1-\hat{y}\right)\right) \] 在反向传播过程中,我们需要对每一个输入数据的参数都要求梯度,那么在一个特别大的模型或者说最近非常流行的一些深度网络中,数据量是极其庞大的,并且网络的宽度也是极其大的,每一个数据或者参数可能都有非常高的维度, 因此在工程上要直接对所有的输入数据求梯度几乎是不可能的, 即使可以实现也是不实用的。 那我们就可以思考一下,假设我们要知道全国男性的平均身高我们不可能去采集所有男性的身高,因为这很不现实。那这种情况我们会怎么做呢?我们是不是会考虑对部分地区进行采样,对一小块采样数据进行建模,他可以大致模拟全国的数据分布。那么在机器学习领域我们是否也可以使用这种想法呢? 数学家已经证明了我们确实可以使用类似的方法,我们对某个特定的数据求梯度其实可以大致模拟整个数据的梯度下降的方向。当然在真实情况下,我们不可能只选其中一个数据求梯度,我们会选取一个小批量的数据来作为样本去求它的梯度来 模拟整个数据集的梯度方向。 这个方法又被称为批量梯度下降法(Batch Gradient Descent |mini-batch)。 那么这种方法真的可以加快梯度下降的速度吗? 数学家们已经证明了在凸问题下经过\(k\)次迭代之后,随机梯度下降算法和梯度下降算法的误差会在\(\frac{1}{\sqrt{k}}\)这个量级(\(f(x^{(k)}) - f^* = O(\frac{1}{\sqrt{k}})\) )。如果是强凸问题的话他的收敛好会更快一些,误差会在\(\frac{1}{k}\) (\(f(x^{(k)}) - f^* = O(\frac{1}{k})\) )。


考虑完第一个优化的方向之后我们就思考第二个方向,是否可以优化梯度下降的路径来优化算法呢? 我们知道梯度的方向已经指向了数据下降最快的方向那么对路径的优化还有必要吗?当然是有的,如果我们使用随机梯度下降法的话,因为我们选取的数据是随机的那么他的下降路径一定和真实的路径就是有偏差的那么这就给我们带来了优化的空间。即使我们使用的是梯度下降法它仍然是有优化空间的,因为我们每一次求得的梯度只是在当时的那个点梯度下降的最快的方向。对于全局来说他仍然是有偏差的。通过下图我们可以看到:

当我们固定步长时, \(A\)点在梯度方向上移动会到达\(A^{’}\) 而在\(B\)点时梯度方向却发生了改变,那么为了达到极致点的话就会需要得到更多的步数。

我们当然也是可以直接计算它的最优路径的,那就是我们把他的步长增加到无限小那次就是微积分的概念了,我们就能求得他在极限下的路径但是随之迭代的次数也会增加。 那么有没有一种方法可以在不改变步长的情况下,使得他的路径更平滑呢?

牛顿法

我们先在二维实用一个一维变量举个例子

我们可以看到橙色的线就是这个点的切线在高维来说就是梯度,灰色的线就是最优路径。我们会发现切线方向上固定步长\(\Delta x\)的位置和真实方向上差距很大。那我们就思考是否可以使用一个抛物线来拟合函数曲线,使得他的极值点更接近我们的最优路径的方向呢?牛顿就把这个公式写了出来: \[ W = W - \frac{J'(W)}{J''(W)} \] 那我这个公式是如何得出来的呢?我们假设\(J(x)\)为损失函数,我们对他进行二阶的泰勒展开,那么 \[ J(x) \approx J(a_0) + J'(a_0)(x - a_0) + \frac{1}{2}J''(a_0)(x - a_0)^2 \] 如果我们把这个泰勒展开在坐标系中画出来就是这个绿色的曲线,我们可以用\(f(x)\)来表示。那么这个函数最接近真实方向的点就是这个函数的极值点。我们令\(f'(x) = 0\) \[ \begin{align} f'(x) &= J'(a_0) + J''(a_0)(x - a_0) = 0 \\ x &= a_0 - \frac{J'(a_0)}{J''(a_0)} \\ W &= W - \cancel{\eta\cdot} \frac{J'(W)}{J''(W)} \end{align} \] 因为我们这里直接使用的是泰勒展开之后的极值点,所以不需要学习率来固定步长。

在更高的维度来说,其实也是一样的。通过下图可以看到,橙色的线是我们正常使用梯度下降的方向,绿色线是我们使用牛顿法构造出来的方向,而灰色显示真实的方向。

\[ W = W - \nabla^2J(W)^{-1} \cdot \nabla J(W) \] 其中\(\nabla^2J(W)\) 也就是Hessian矩阵\(H(W)\), \(\nabla J(W)\) 是Jacobian矩阵


牛顿法虽然已经提出了一个很好的思路,但是每次计算的时候我们都还要计算两个梯度矩阵,这个计算量仍是非常大的依旧无法实现。但是既然牛顿法我们可以在所有维度上的梯度统一考虑去寻找一个最优的路径,那为什么不尝试在不同的维度上分别考虑呢?

动量法(冲量法)

首先我们来看一个比较简单的情况。在这里所有的橙色线表示每一步沿着梯度方向走的路径,我们可以看到分别在横轴和纵轴上有两个分量,我们可以很明显的看到影响路径震荡的原因是纵轴的分量过大。因此为了获得更优的路径我们可以考虑减小每一步纵轴的分量大小并且增加横轴的分量大小,使得他可以更快的到达极值点。就会变成下图这样:

我们可以把权重每一步的变换写出来就是这样: \[ \begin{align} W_{(t)} &= W_{(t-1)} - \eta \cdot \nabla J \\ \begin{bmatrix} b_{(t)}\\ W_{(t)1} \\ W_{(t)2} \\ \cdots \\ W_{(t)i} \end{bmatrix} &= \begin{bmatrix} b_{(t-1)}-\eta \cdot \frac{\partial J}{\partial b} \\ W_{(t-1)1} - \eta \cdot \frac{\partial J}{\partial W_1} \\ W_{(t-1)2} - \eta \cdot \frac{\partial J}{\partial W_2} \\ \cdots \\ W_{(t-1)i} - \eta \cdot \frac{\partial J}{\partial W_i} \end{bmatrix} \end{align} \] 为了方便后面的计算我们这里定义一个新的变量 \(\Delta W_{(t)i} = \frac{\partial J(W_{(t-1)i})}{\partial W_i}\), 则: \[ W_{(t)i} = W_{(t-1)i} - \eta \cdot \Delta W_{(t)i} \] 这里定义的\(\Delta W_{(t)i}\) 表示的是这个系数在这个维度下应该调整多少,他其实跟原本的本质上是没有区别的,原本的公式是在不同的分量上沿着梯度方向调整,我们只不过为了后面的方便写成这个样子。本来我们有了\(\Delta W_{(t)i}\)后就可以直接带入计算了, 但这里我们增加了一个中间过程。 \[ \begin{align} V_{(t)} &= V_{(t-1)} + \Delta W_{(t)i} \\ W_{(t)i} &= W_{(t-1)i} - \eta \cdot V_{(t)} \end{align} \] 我们定义了一个新的变量 \(V_{(t)}\), 这里他其实是一个递归的定义。我们可以看出来其实这里的\(V_{(t-1)}\) 就是把历史上前面所有的\(\Delta W_{(t)i}\)的和。通过这种方法我们就可以把历史的信息考虑进来,通过历史的信息对现在的分量调整。我们把历史上所有的\(\Delta W_{(t)i}\)求和其实也很容易理解,当当前的某个分量跟过去的分量方向不同的时候分量相加会得到一个折中的方向,当分量相同的时候我们知道这个方向是我们可以更快到达极值点的方向所有分量相加会更大。但是使用这种方法会有一个问题,就是当如果我们步数足够多的话,每一个过去的信息都会一视同仁全部相加。但可能在过去很远的信息对现在的影响相对较小, 以相同的权重进行相加的话可能就不太合适了。所以到这里我们相当于只是给动量法提供了一个思路,真正实现的过程应该是这样的 \[ V_{(t)} = \beta \cdot V_{(t-1)} + (1 - \beta) \cdot \Delta W_{(t)i} \] 这里相当于就是对所有的信息进行一个加权求和, 但为什么这里用额\(\beta\) 和 \((1-\beta)\)呢?其实这也就用了一个数学上的灰尘常用的一个方法叫指数加权移动平均法(EWMA)。通过这种方式我们就可以以最近的步作为基础,更远的步我们给予更小的权重。


其实对梯度下降法进行优化,我们不仅可以去参考过去的数据,甚至可以超前去参考未来的数据。

Nesterov

在讲Nesterov之前我们先来看一下基本的动量法是怎么做的

这里的橙色虚线是当前位置的梯度方向,绿色虚线是动量方向,而橙色的实线就是动量法中我们下降的方向。

那如果我们再进一步的进行动量法会求得下一个点,而这一个点的方向,显然好于我们之前的方向。那我们有什么方法可以超前获得未来这个点的方向呢? 这就是Nesterov算法的精妙之处了。 他其实是对原本动量法中的\(\Delta W_{(t)i}\)进行了一些修改 \[ \Delta W_{(t)i} = \frac{\part J(W_{(t-1)i}) + \gamma V_{(t-1)}}{\part W_i} \] 我们最开始\(W_{(t)i} = W_{(t-1)i} - \eta \cdot V_{(t)}\) 求的是第一个灰色点和第二个灰色点的方向, 而这里的\(\Delta W_{(t)i}\) 其实是绿色箭头指向的位置的梯度方向,也就是红色这条线

当我们求出来这个方向后把他向上平移,然后再修正参数

此时获得的梯度方向会更加接近于真实的方向。

AdaGrad

除了上述的方法,其实我们也认为学习率不应该是一个固定的值,因为如果学习率太大的话很可能越过极值点, 在极值点附近一直震荡,如果学习率过小的话可能需要过多的步数才能到达极值点。因此一种解决方法就是使用自适应的调整学习率。 AdaGrad算法通过给学习率添加一个参数,让他可以考虑过去梯度的影响从而动态的调整学习率。 \[ W_{(t)i} = W_{(t-1)i} - \frac{\eta}{\sqrt{S_{(t)}} + \varepsilon } \cdot \Delta W_{(t)i} \] 其中 \(S_{(t)} = S_{(t-1)} + \Delta W_{(t)i}\cdot \Delta W_{(t)i}\), 这里的 \(\varepsilon\) 是个极小值为了防止除0。这里我们可以理解为对梯度的内积开方,学习到的梯度是真实梯度除以梯度内积的开方。 由于梯度在各个分量上对量级不一致会导致震荡,因此AdaGrad本质是解决各方向导数数值量级的不一致而将梯度数值归一化。

AdaGrad算法对稀疏数据表现良好,什么叫稀疏数据呢,就是当数据之间特征的类型不同而不是程度不同的时候我们称之为稀疏数据(比如,区分人和狗)。对于稀疏数据我们相当于给定每一个特征的数据量较少,对于每一个维度特征的梯度变化较大,因此使用AdaGrad就能很好的避免这种情况产生的震荡。 但是对于有“平台期”的数据可能就会导致即使经过了平台期梯度依然下降的很慢,因为他需要考虑到历史上所有的信息。

RMSProp

RMSProp其实是对AdarGrad的一种优化方法。他基于动量法的思想对使用EWMA的方法对AdaGrad进行优化 \[ \begin{align} S_{(t)} &= \beta S_{(t-1)} + (1-\beta)\Delta W_{(t)i}\cdot \Delta W_{(t)i} \\ W_{(t)i} &= W_{(t-1)i} - \frac{\eta}{\sqrt{S_{(t)}} + \varepsilon } \cdot \Delta W_{(t)i} \end{align} \]

Adam

Adam算法其实就是将动量法和RMSProp进行结合 \[ \begin{align} V_{(t)} &= \beta_1 \cdot V_{(t-1)} + (1 - \beta_1)\cdot \Delta W_{(t)i} \\ S_{(t)} &= \beta_2 S_{(t-1)} + (1-\beta_2)\Delta W_{(t)i}\cdot \Delta W_{(t)i} \\ W_{(t)i} &= W_{(t-1)i} - \frac{\eta}{\sqrt{S_{(t)}} + \varepsilon } \cdot V_{(t)} \end{align} \]

References

https://www.bilibili.com/video/BV1r64y1s7fU?spm_id_from=333.999.0.0

https://en.wikipedia.org/wiki/Moving_average


知识来源作者为b站UP主王木头学科学

猜你喜欢

转载自blog.csdn.net/Kevin_Carpricron/article/details/124123153