近年来,AI大潮汹涌澎湃,包括机器学习,深度学习,计算机视觉,自然语言处理在内的许多领域都在迅速发展,并取得了显著的成果。每年都会有很多优秀经典的算法提出来,适用于不同的场景。However,现在网络的训练主要还是梯度下降以及从中衍生出的优化算法。想要入门机器学习和深度学习,梯度下降是你绕不过的坑,但今天本文的任务就是要引领大家彻底搞懂梯度下降,在学习中避免掉坑!
训练神经网络的过程实质上是一个优化的过程,我们会定义一个成本函数,比如最小均方差,交叉熵等。本文拿最小均方误差举例子:
在我们的成本函数中,自变量为神经网络的权重,我们的网络在训练过程中就是要不断的根据系统输出进行权重参数的更新,最终寻找到使得成本函数得到最小值的权重集合。随着成本函数的降低,我们的网络的精度随之升高,而权重参数就是使用梯度下降法完成的。梯度下降的原理:学过高数我们都知道,梯度是一个向量,其方向就是函数上升最快的方向,梯度下降就是利用这个性质,在梯度的方向上更新自变量,从而以最快的速度到达函数的极小值点。就简单地拿一个开口像上的二次函数举例,当前点如果处于对称轴左端的话,此时梯度的方向是向下的,为负,我们应用梯度下降法
可以很明显的看到自变量是向右移动的,即函数最小值的方向;反之,当前点位于对称轴右端的时候亦然。以此类推到多维的复杂函数,道理相似。
现在大概有三种梯度下降算法框架,它们不同之处在于每次学习(更新参数)所需的样本数,每次更新使用的样本数不同会导致学习时间和学习精度也各有差异。
批量梯度下降法在训练过程中,每次迭代都要将所有的样本数据输入系统,计算得到总误差之后,将误差经过反向传播一步步地传递到上游,然后缓存每一层的梯度矩阵,进行各层权重参数的更新,附上代码:
在这里插入代码片
使用BGD对网络进行训练,会占用很多我们的计算资源,训练的时间也会变得很长,那我们为什么还要使用BGD呢?这肯定是有道理的,前边把BGD说的一无是处,但其实不尽然,BGD还是有很大的优点的。由于我们每次参数的迭代都要计算所有的样本数据,所以我们计算出来的误差和梯度都是最真实的,这可以确保我们的每次梯度下降都是沿着最符合成本函数的梯度方向进行的。
上面说到BGD的计算量太大,下面即将上场的SGD就是来解决这个问题的。SGD每次迭代都是随机输入一个样本,然后对所有的权重进行梯度下降,更新一次,这就大大减少了我们的计算量,但是这样却不能保证我们每次更新的梯度都是沿着正常的方向进行的。因为真正的梯度方向是所有的权重梯度的矢量和,然而我们每次只是随机输入了一个样本,这一个样本并不能适合所有的梯度,所以SGD的梯度下降会出现很多曲折,就相当于我们在走下山的路,但是老是辨不清方向,一会向上走,一会向下走,虽然最终会走到目的地,但是会浪费很长时间。不能采用较大的学习率在此附上SGD的Python代码:
在这里插入代码片
讲了上述两种梯度下降的框架之后,相信你已经对梯度下降有了一个大致的了解。但是前两种方法的优点和缺点都很明显,有没有一种方法可以中和一下他们呢?of course,MBGS(mini-batch gradient descent)就是来解决这个问题的。结合BGD和SGD,MBGD每次学习都会从样本数据中随机选取一小批输入网络进行计算(选取的样本数目是根据你要解决的问题而定的,一般而言选取范围在[50,256]),然后运行梯度下降算法进行参数更新,这样既减轻了SGD的随机性,又减少了相当一部分的计算量。看到这相信你会说完美,但是世界上又有什么东西是完美的呢。不可避免的,MBGD也有自身的局限性,或者说是传统的梯度下降算法之间普遍存在的问题。下面我们就来说一下:
参数的初始化对于模型的训练至关重要。参数初始化的符号和大小都是需要注意的问题。如果我们的参数全部初始化为正数或者负数的话,那么所以参数每次梯度下降的方向都是一样的,失去了学习的多样性,这样我们训练出来的网络过于简单,效果很差。如果我们初始化参数太小,会出现神经元无法激活,因为每往下传递一层,就要乘一次权重,如此下去,只会越来越小,导致大片的神经元变成死区,造成网络瘫痪;如果太大的话会导致网络不收敛,也不能得到训练结果。
学习率的选择关系到模型训练的速度和质量,选取的学习速率太小的话,你的模型就会收敛的很慢;选取的太大的话,又很容易发生振荡,导致模型无法收敛,所以我们每次训练的时候都会选取一个合适的范围(一般而言是1e-3~1e-5),每次训练完成然后缩小范围再次训练,直到找出针对问题最好的学习速率。
因为我们的模型拟合的函数都是比较复杂的,所以损失函数的形式也会相对复杂,不一定是凸优化,大多部分时候都是非凸的,这个时候,我们的损失函数可能有好多局部极小值点和鞍点,在局部极小值点和鞍点处的梯度都是零,因此学习过程中很可能就卡在局部极小值点和鞍点上,导致我们与最优解失之交臂。
上边罗列了一堆问题,现在是时候解决了。毕竟问题不解决,他永远是个问题。很久以前大牛们就已经提出了各种优化算法来解决上述问题,让我们一起来看一下。
上面我们讲到了批量梯度下降和小批量梯度下降,以及随机梯度下降,了解了它们的优缺点。但是没有提到的一个它们共有的缺点,就是当它们遇到局部极小值和鞍点的时候就会卡住,因为此时梯度为零,带动量的梯度下降,顾名思义,就是给梯度加上一个动量,这样即使碰到了局部极小值点和鞍点这种梯度为零的情况,也会因为动量的存在而使得参数可以正常更新。(指数加权平均)公式如下:
当我们的调试点跑到局部极小值点或者鞍点的时候,这个时候相当于我们不想跑了,想停下来,但是由于我们有一个初速度的动量,我们还是会继续往前跑一段时间帮助我们跳过局部极小值点和鞍点。除此之外,带动量的随机梯度下降还能有效防止振荡。当前梯度与上一次的梯度方向相同时,梯度更新的跨度就会比较大;方向相反时,又会起到一个抵消的作用,不会产生一个急转弯,而是产生一个小角度的转变,尽量往一条直线上拉(我们可以将动量理解为惯性。当你开始跑步的时候,如果之前你有一个速度,这个时候由于惯性,你跑起来会比较轻松;当你想要往反方向跑的时候,你的惯性又会“拽”着你,让你显得比较吃力)。其数学原理如图所示:
NAG相比较于上面的动量法,变得更加聪明和具有前瞻性了。其数学原理如图所示:
我们可以看到NAG不是在当前点进行学习了,而是预测到下一点的梯度方向,然后结合当前点的梯度方向,得到我们应该进行更新的方向。
上边我们所说的梯度下降方法,每次学习用的都是一样的学习率,但是在训练过程中每个参数的重要性肯定是不同的,这个时候我们如果继续用同样的学习率进行更新的话,就会出现误差。这时候我们就需要使用不同的学习率动态地更新参数。接下来我们看一下Adagrad算法,其数学原理如下:
其中,r是储存梯度平方和的变量,δ是为了避免r=0时分母为零的情况(一般取值1e-7),分子为全局的学习率。从上述式子中可以看出,随着学习的过程,学习率是一直在动态的变化,对于梯度较大的参数,学习率较小,反之亦然。除此之外还是随着r的不断增大而减小。这也就造成了Adagrad的一个缺点,就是随着学习的深入,激励收敛变为惩罚收敛,最终学习率会变得非常小,收敛的很慢,不利于训练。
[1] https://study.163.com/my#/smarts
[2]: https://blog.csdn.net/program_developer/article/details/80756008
[3]: https://blog.csdn.net/tsyccnh/article/details/76673073
[4]: https://blog.csdn.net/Solo95/article/details/84837711