如何理解BP算法:从错误中学习,在传递中进化(二)
梯度下降在深度神经网络中的应用存在哪些问题
在基础篇中我们以线性分类器为例解释了反向传播算法的工作原理,但是在实际应用中却很少直接使用这种最原始的梯度下降反向传播算法;主要由于对于大规模的深度网络来说求解每个节点的梯度是非常大的计算量,且在更加复杂的损失函数空间中也会遇到难以逼近极值的问题;
梯度的维度-计算量有多大?
由于梯度下降算法中梯度的维度是和参数维度完全相同的,所以我们先来感受参数量是一个什么样的概念,我们以一个全链接网络为例;下图表示了神经网络中某一层的一个感知机的计算过程(对应图中的红色矩形部分)
对于一个感知机来说,本层需要输出一个
最后对这个加权和 求一个激活函数
关于海量的参数量:
将每一个感知机的输出值看做一个变量的话,上一层有 n 个变量
对于多通道的图像、或one-hot特征的文本等高维度数据场景,输入层和隐藏层的节点数都是非常巨大的;比如一个1080p的RGB图片(当然实际训练不会直接用这么高像素的图片),就有两百万像素(1920 x 1080)再乘以3,我们就会得到一个输入层有600万个节点的网络,而这一层输入和隐藏层相关的参数W维度就是600w 乘以某个值;
在损失函数空间中梯度下降中遇到的问题:
- “Z字形”下降:
- 在参数维度方向下降对应的损失函数变化水平(敏感度)不一致,某一个参数方向敏感度很高,而另一个参数方向敏感度低时,就会出现梯度在敏感度高的方向正负方向反复横跳,在敏感度低的方向缓慢前进;而真实的梯度方向很有可能是敏感度低的方向,这就导致梯度的Z字形下降,学习效率很低;这个问题在高维空间更加普遍;
- 困在局部“最优”:
- 局部极小值点:这个在高维空间出现的概率很低,对于一个含有一亿个参数的高维空间,要求一个点对于一亿个点都是局部极小的,向任何一个方向前进较小的一步都会损失变大,这种情况非常稀少,假设某个点在某个维度上是局部最小的概率是P,那在整个参数空间(一亿个参数)这个点是局部最小点的概率是P ^(10^8);
- 而在低维空间这种情况就更加常见,损失函数在某个点的周围有一段凹陷,那么梯度计算周围一圈都是梯度为0,梯度下降算法在此处不执行更新,可以通过合理选择参数的初始值或者增加学习率来解决落入局部最小值的问题;
- 鞍点:鞍点是在某些维度上的局部最小值,而在某些维度上是局部最大值;鞍点问题在高维空间更加常见,鞍点处的梯度为0,高维空间中大部分梯度为0的点几乎都是鞍点,并非局部最小值点;同样,处于鞍点会使得基于梯度下降的优化算法在此处停滞,难以从鞍点“逃离”;在高维空间中如何避免梯度落入鞍点,也是一个待解决的问题;通常我们会通过在当前维度计算梯度时参考历史梯度下降的动量信息来绕开当前梯度为0的尴尬;
- 平原区域:由于深度神经网络的参数过大,可能会存在一定的冗余性,即每个参数只贡献一点点损失函数的优化值,这就导致出现损失函数出现“平坦的地形”,在这区域内梯度接近于0,而这又恰恰处于损失函数的“高原”地带,这就会得到非常差的优化结果;
- 局部极小值点:这个在高维空间出现的概率很低,对于一个含有一亿个参数的高维空间,要求一个点对于一亿个点都是局部极小的,向任何一个方向前进较小的一步都会损失变大,这种情况非常稀少,假设某个点在某个维度上是局部最小的概率是P,那在整个参数空间(一亿个参数)这个点是局部最小点的概率是P ^(10^8);
- 悬崖结构
- 当损失函数呈现悬崖结构,在某处梯度非常大,使用梯度下降算法更新可能导致参数发生很大的变化,从而产生梯度爆炸现象;可以用截断的方法解决这个问题,当梯度值超过某个规定的阈值时,就进行截断,这种方法叫做梯度截断(Gradiant Clipping)
- 随机性噪声影响
- 由于常规的梯度下降计算量过大,真正计算时我们通常采用随机梯度下降法,每次只对一个或者一小批的样本的损失值进行全局的梯度计算;由抽样带来随机性,通常会导致梯度收敛过程中变得曲折,增加收敛的时间;
梯度下降的演化和应用策略
梯度下降的优化方向沿着几个方向分别展开:参数量、梯度方向、学习率;这些不同方向的优化方法,由于角度的正交性(独立性)往往会一起使用来优化梯度下降算法,目前最常用的就是Adam算法。下图表示了不同优化算法之间的关系。
随机梯度下降:
首先,从最朴素的减少参数量的角度看,有了随机梯度下降(SGD),也就是每次随机挑一批数据训练(mini-batch)mini-batch梯度下降法的思想很简单,将样本总体分成多个mini-batch。例如100万的数据,分成10000份,每份包含100个数据的mini-batch-1到mini-batch-10000,每次梯度下降使用其中一个mini-batch进行训练,除此之外和梯度下降法没有任何区别。这样能够明显减少计算量,同时由于数据的冗余性,减少样本量的抽样对梯度的优化几乎没有影响,因此之后所有的优化方法几乎都会叠加这个方法一起使用。
牛顿法:
接下来,会提到一种理论上的方法–牛顿法,这种方法可以看做是优化参数更新方向的先驱,他为我们提供一种重要思路,但是由于会面临更复杂的计算所以只是一种理论模型。我们知道,梯度计算都是损失函数对参数的一阶偏导,而牛顿法对于一阶到是否是最优的下降方向提出了质疑。梯度是损失函数L(θ)在θ_0处的一阶偏导,梯度方向只能在很小的范围成立,在更大范围内并不成立,所以就不能在这个方向上前进太多,这就限制了找到更快优化目标函数的梯度方向的速度。
基于这个优化方向,牛顿法提出了对于损失函数L(θ)在θ_0处进行**二阶泰勒展开得到f(θ)**,针对二阶展式开来寻找优化的方向。而对于二阶泰勒展开式寻找优化方向就不再受到一阶导数小范围的限制,直接找到二阶展开的最小值(下图绿色抛物线顶点处)作为优化的值,意为二阶展开式的最小值点是当前点 θ_0 可以观测到最可能使目标函数快速下降方向。

在下图中可以看到,θ_0处的一阶梯度对应的目标函数优化值(橙色的Δy)比θ_0处的泰勒二阶展开找到的优化值更小,这也表示牛顿法可以更快的向损失函数优化的方向收敛;也可以看到二阶展开式比一阶导橙色线更接近优化方向的ground truth(灰色线)。牛顿法的迭代公式为:
其中,∇f(x_k) 是 f(x) 在点 x_k 处的梯度向量,∇^2f(x_k) 是f(x) 在点 x_k 处的 Hessian 矩阵;

这里非专业的同学不需要纠结Hessian矩阵。简单理解下,为什么需要hessian矩阵?① 黑塞矩阵实际上就是对于矩阵来说求解二阶偏导的工具:对点x的参数求两次偏导提供了一个精确的计算模型;② 求二次函数的最小值我们需要寻找其二阶导为0的点,偏导也是同理。

因此,迭代公式中的步长 (∇^2f(x_k))^−1 · ∇f(x_k) 可以看做是一个二次函数的最小值点与当前搜索点的差值。这里的 Hessian矩阵 也被称为牛顿步长。在牛顿法中,黑塞矩阵的作用就是提供了一个精确的二次函数模型,它描述了损失函数的局部曲率,使得在曲率小的时候能够大步长更新,曲率大的时候小步长更新,这可以解决梯度下降中“Z字型”下降的问题。在这个层面上,牛顿法在当前点上不仅考虑当前坡度是否足够大,还会进一步考虑迈出这一步后的坡度如何,更具有全局观,因此比梯度下降收敛的更快。
曲率和导数的差异:
曲率:在数学中,曲率通常被定义为曲线或曲面上某一点处的切线或切平面的弯曲程度。曲率值越大,表示该点处的曲线或曲面弯曲程度越大。可以理解为曲率是用来描述多维数据变化程度的指标;在一维数据中,曲率就等同于二阶导数。
曲率和黑塞矩阵的特征值:黑塞矩阵的特征值提供了评估函数曲线在某个点处弯曲程度和形状的重要指标
曲率可以被视为黑塞矩阵特征值的加权平均。更具体地讲,曲率的大小取决于黑塞矩阵特征值的大小及其相对比例。
在某个点处,如果所有黑塞矩阵的特征值都是正数,则函数在该点处是凸的,并且曲率在所有方向上都是正的。如果所有黑塞矩阵的特征值都是负数,则函数在该点处是凹的,并且曲率在所有方向上都是负的。如果黑塞矩阵的特征值中既有正数又有负数,则该点处可能是一个鞍点,因为曲率在不同方向上可能是正或负。
牛顿法的简易快速理解:牛顿法的「二阶优化思路」对于某个点的目标函数通过二级泰勒展开还原附近的曲线走势,并通过Hessian矩阵工具二阶求导,尝试找到他的加速度方向,针对这个加速度来决定优化的向量;
牛顿法这么牛,为什么我们不用牛顿法呢,除了在深度神经网络中黑塞矩阵的计算是内存的不可承受之重,另外这种方式也更容易陷入“鞍点”。由于深度网络有海量的参数,对应的损失函数空间可以想象为无规则起伏的山地,就是我们常说的是一个非凸问题,因此大概率存在很多鞍点。而在鞍点附近时,黑塞矩阵的特征值在不同维度上的符号并不相同,难以收敛,因此牛顿法得到的优化方向可能会指向函数的鞍点或局部极大值点,会向着错误的方向更新。
当然,以上的这些问题也得到了一定程度的解决。比如结合随机梯度下降法(SGD),牛顿法可以通过随机性来跨越一些鞍点。同时对于黑塞矩阵计算复杂度的问题,后来的学者也提出了优化方法–一系列的拟牛顿法。旨在通近似矩阵执行参数的更新,常用的包括DFP、BFGS、L-BFGS等。拟牛顿法只需要计算一阶导数,不需要计算黑塞矩阵就能快速收敛,同时也对存储部分做了优化,因此能够被应用在更大的网络上。
基于动量的优化方法:调整方向
动量法是更符合直觉的一种优化方法。在物理学中,动量描述了一个物体在它的运动方向上保持运动状态的趋势,表示为物体质量和他的速度的乘积。在动量法中,我们将参数的更新看做粒子的运动,当一个单位质量的粒子从一个小坡滑下来,首先有一个初始速度v_0,由于一个持续向下的力产生了加速度(F=ma)。小球的速度不断受到这个向下加速度影响,相比于每一个点的其他方向的梯度对速度的影响,这个向下的力产生的加速度是更大的,因此使得小球更快地落到最低处。即在每一点更新梯度时,除了当前点的梯度,还要参考(继承)历史梯度里面更有持续性、更显著的信息。比如一个小球在山坡下落的某个点遇到一个石块,但是因为受到重力产生的惯性,它不会立马沿着石块的切线方向横着走下一步,而是绕过石块继续下落,甚至有可能直接越过石块。
动量法
:没有达到停止准则 do 从训练集采集包含m个样本的小批量{x^i,…,x^m},对应目标为
计算梯度估计:
计算速度更新:
参数更新:
这里v表示更新后的速度 ,是参数在参数空间移动的反向和速率,一般初始化为0,
如何理解指数移动加权平均:
假设存在数列
令:
. . . . . . . . .
之所以叫指数衰减,下图展示了之前不同时刻的梯度对当前时刻的权重影响值,可以看到距离越近的梯度影响越大,权重影响是呈指数衰减的。

动量法就是让梯度更新的方向不至于那么剧烈,参考下图的绿线,由于积累了历史水平方向的更新向量,减少了在敏感维度上的震动幅度,因此比普通的梯度下降在接近极值点时,更新更快,同样的迭代次数下,更划算!
而Nestrov则是在动量法的基础上,又向前卷了一步,根据历史速度信息将参数向前更新一步,在新的(参数)位置上计算梯度更新,然后再根据这个梯度和速度信息更新历史参数。实验证明,Nestrov会更快收敛。
:没有达到停止准则 do 从训练集采集包含m个样本的小批量
,对应目标为 , 执行临时更新
计算梯度估计:
计算速度更新:
参数更新:
自适应学习率算法:调整步伐
学习率表示每次更新的幅度,在进行梯度更新时,开始需要比较大的学习率,提升学习速度,到接近极值点的地方就需要比较小的学习率来逼近,否则就会在极值点附近横跳。因此在学习率方面的优化需要一个合理的学习率随学习进行而衰减的算法, 因此又成为学习率退火;
学习率衰减策略可以分为两种:固定策略的学习率衰减和自适应学习率衰减,其中固定学习率衰减包括分段衰减、逆时衰减、指数衰减等,自适应学习率衰减包括AdaGrad、 RMSprop、 AdaDelta等。本质上都是依托一种数学表达来找到学习率衰减的方式。
AdaGrad算法
这里的ada指adaptive,就是相对于固定学习率这里会随着学习而自适应的意思。AdaGrad的思想很简单,类似于L^2正则化的思想,每一次对学习率β进行更新时,会用初始学习率α除以之前所有梯度平方的累加值;这样直接除以平方和的方式,对于出现Z字型的区域有很好的低效效果,会快速降低当前点的学习率,而对于波动比较小的维度(损失函数不敏感)学习率就会相对大一些,从这个意义上来看AdaGrad是一种对于剧烈波动维度的正则方法。
但是AdaGrad方法也存在一些问题,随着迭代次数K的增加由于背上了所有的历史包袱,更新的步长越来越小。对于凸函数,Ada算法表现良好,会在局部极小点附近慢下来并最终收敛;但对于非凸函数,Ada算法可能会导致学习率过早的减少,以至于还没有达到极值点就停滞不前了。
RMSProp算法
针对Ada存在的问题,Geoffery Hinton提出了另一种改进的自适应学习率的RMSProp算法,该方法以一种相对温和的方式调整学习率,优化了Ada过早衰减停滞的问题。总体思想是一样的,都是给初始学习率除以一个随迭代k次而变化的变量。只是将累加的梯度平方和变为历史梯度的指数衰减加权移动平均。其中,β为衰减率,一般为0.9。基于指数衰减加权的思路,给遥远的历史梯度平方很小的一个权重β^(k-1),这样参数的学习率便不会呈现衰减的趋势,有可能变小也可能变大,始终会受到当前梯度变化的影响。RMSProp算法已经作为一种有效的优化算法用于训练深层神经网络,并被广泛使用。
方法的融合–Adam方法
在计算考虑历史速度向量的梯度公式中有两部分,①对历史速度的参考,②对当前梯度的学习;动量法实际上是在第一部分进行修正,而学习率相关的方法则是调整对当前梯度学习比例,由于二者作用的方向并不冲突,如下图所示:
暂时无法在飞书文档外展示此内容
因此就有了Kingma D.P. 和 Ba J.提出的自适应动量估计法,Adam(Adaptive Moment Estimation)它融合了动量法和RMSPropd算法的优势,是一种非常好的优化方法,在很多网络上能得到很好的效果。
要选择几个对AI发展影响最深的算法,那BP算法绝对算其中基础性,灵魂性和哲理性的存在。
如果说多层网络的结构设计表现的是一种「世界观」。那么梯度下降就是一种「人生观」,这是一种在错误中学习,在迭代中进化的简单智慧。而通过梯度下降算法的不断演化的脉络,我们也看到了一条逐渐清晰的思路:如何更高效地在局部获得更加全局性信息来提高动态优化的效率,简单来说有三个方面:【提效】随机抽样减少信息量的冗余,【参考经验】参考历史和近期的重要的信息,【调整学习速度】在不同阶段调整学习内容的占比。
本文难免存在一些疏漏或不严谨的地方,但目标是希望可以让非专业的同学能够快速理解BP算法中看到的各种各样的名词,了解到它为什么是这样,是为了解决什么问题,甚至可以自己DIY新的用法。相信很多人接触深度学习都是从实用主义出发直接调pythorch,遇到优化瓶颈了在CSDN上搜一搜能用什么调参blahblah,然后就去试,效果好就是王道。当然这么做没有问题,但是这些工作人来做和机器有什么区别,属于更高智慧的直觉性经验和理解才是我们的天赋所在。
就像一个好的室内设计师,需要知道建筑哪里是承重墙,需要了解新中式和自然风格的元素差异,以及了解用户的性格和对室内环境的向往等等,以上所有信息佐之一些难以解释的随机的灵感,往往就能产生一个充满了元素混搭但却和谐而富有层次的作品。
相比模式化的套用,能够进行创造的理解才是我们学习的目的。当然这里设计师的例子也许有些过时,毕竟目前的AIGC显然已经可以80%的实现这种创造性。但是原理却不假,创造性上还有创造性,这是一条永无止尽的贪婪的生命进化之路。
参考:
论文:Learning representations by back-propagating errors D. Rumelhart, G. Hinton, and R. Williams. nature 323 (6088): 533–536 (1986)
书:《深度学习–从神经网络到深度强化学习的演进》