深度学习实战
上QQ阅读APP看书,第一时间看更新

2.3 梯度下降法

上面的内容中,我们已经构造了两种代价函数,并且这两种函数都是凸函数,也就是都存在最小值。只要求出代价函数取最小值时的参数,我们的任务也就完成了。那函数的极值又怎么求呢?非常简单,当函数切线的斜率为0,也就是代价函数的导数为0时,也就是函数取得极值时(若自变量多于一个,导数也称为梯度)。但这并不好求,有时因为计算量太大,有时甚至根本找不到直接求解函数极值的公式,因此我们选择愚笨一些的方法“走一步,算一步”。

话说徒弟小飞与杨师傅已经抓到了小白兔,休息一会儿,就准备下山了,然天气大变,周围突然起了很浓的雾。此时小飞大叫,“师傅不好,有妖气!”哐当一下,小飞又被师傅一记长拳,“劣徒,休要胡闹,天气突变,恐有大雨,还未收衣,还不随为师下山。”于是师徒二人就急匆匆地下山了。但雾太浓密,早已分不清去路。师徒俩只能环顾四周,摸索一条下山最陡的路,然后走一段距离,再环顾四周,再往最陡的方向前进,到了傍晚,师徒俩终于回到了家中。如图2-2所示,我们根据当前数据,求出“下山”最快的方向(梯度),然后往梯度方向走一段距离,再重复相同步骤的迭代方法,就称为梯度下降法(Gradient Descent)[8]

图2-2 梯度下降示意图

梯度下降法,主要考虑两个问题:一是方向(梯度),二是步长(学习率α)。方向决定是否走在正确的道路上,而步长决定了要走多久才能到达目的地(错误率最低处)。对于第一个问题,主要集中在如何才能精准地找到最小值的方向。但很遗憾,我们并没上帝视角,因此我们只能找到当前的最佳梯度,但当前的梯度不一定是回家的路,我们却只能前行;对于第二个问题,主要集中在如何确定合理的步长,如果步子太小,则需要很长的时间才能到达目的地,如果步子过大,可能导致在目的地周围来回震荡。

  • 方向(梯度)

我们首先来看梯度,求梯度其实就是求多元函数相应变量的偏导数,若参数为一个,也就是数据为一维数据,那么其实就是在求复合函数的导数,如果代价函数为均方误差函数(为了计算简洁,在代价函数中乘以0.5),如式(2.13)所示。

我们的学习器为线性回归,并且数据为二维:f(x;w)=w1x1+w2x2,那么第一个参数w1的梯度就为式(2.14)(注意求导时的负号)。

同样地,w2的梯度就为式(2.15)。

我们所谓的沿着梯度方向上走一步,其实就是去修改

注意:本书是去减梯度,但有些资料中为加梯度,那是在梯度中藏着负号。也可以理解为正确的修改方向是负梯度方向。

虽然上述的推导比较烦琐,如果你能耐心看完,会发现其实非常简单,然后再看看算法2.1,你会发现我们的关键算法,其实用一行代码就可以完成。因为简单的一行代码,我们讲了一个故事,推导了一系列的公式,虽然有些烦琐,但这些代价都是值得的。

  • 步长(学习率α)

我们刚刚推导完了梯度,学会了如何找“下山的方向”,现在我们就来聊聊“下山的速度”。假如小飞是超人,一个奇怪的超人,他的步子可以任意长,但他有一个缺点,那就是比较“耿直”,只会沿着要走的方向直行。山谷那站着他喜欢的人小鱼,他要去见她。他离最低处4.5m,步长为1m,当他走到第5步时就出现了问题,那就是他多走了0.5m,接下来他会怎么做呢?根据上面的梯度求法,小飞知道现在的下山方向在他的后面,那他就往后再走1米,但接下来他就尴尬了,他就像一个可爱的钟摆一样,不停地在小鱼周围来回震荡,不管小飞走多久,他与小鱼始终相差着0.5m,此时耿直的小飞眼角都湿润了。你愿意帮帮他吗?请记住,我们从上帝视角知道小飞离小鱼的距离,但旁观者清,当局者迷,小飞是不知道距离的。

聪明的你可能有很多好的想法,而我就只能想出两个。首先就是提示小飞,“小伙子,欲速则不达,慢一点吧!”,于是小飞就减小了步长,一次走0.4m,那小飞离小鱼最近的距离也就缩短到0.1m。而尝到甜头的小飞就把自己的步长缩小到了0.04m,那他最终离最低点也就更近了。但这里就出现了一个问题,减小步长确实会离成功更近,但时间的代价,也会让小飞心碎。

考虑到刚才的情况,我又有了一个不错的想法。开始时就勇敢地大步往前迈,走到一定步数后就缩小步长。在实际的应用中,步长(学习率α)的选择是一个很有技巧性的活,具体情况还要具体分析。总体情况就是α过大,代价函数就会在最低值附近较大震荡,而α较小代价函数的震荡也会较小,但到达震荡区间的时间也会变长,这也是一个鱼与熊掌的问题。

2.3.1 批量梯度下降法

批量梯度下降(Batch Gradient Descent,BGD)[9]又称最速梯度下降,使用“批量”一词可能会产生歧义,“批量”一词应该是“一批一批处理”的意思,如管道和缓冲器一般,但这里的“批量”其实指的是“全部一起处理”的意思。我们要处理什么呢?我们要处理的其实是数据,前文中说到我们要找“下降最快的方向”,因此就进行了求导,但其实我们只是找到了下降的方向,还没找到下降最快的方向。如算法2.2所示,才是下降最快的方向,不知道你喜不喜欢玩“大家来找茬”游戏,我们现在就找找算法2.1与算法2.2的茬吧!

可以发现,我们把算法2.1中的m换成了算法2.2中的N,那N是什么呢?N表示的是数据个数,我们计算一次梯度时,需要计算完所有数据中的误差梯度,然后累加之后再取平均值,这就是我们要找的最速下降梯度,也就是我们上文提到的一个信息“环顾四周”。

事物具有两面性,最速梯度下降也具有,优点是准确,我们找到了最正确的方向;缺点就是慢,每计算一次梯度都要遍历所有数据,考虑到如今的大数据时代,这就成了巨大的困难。

2.3.2 随机梯度下降法

随机梯度下降(Stochastic Gradient Descent,SGD)[10]是批量梯度下降的一个极端版本,如算法2.3所示,是随机梯度下降的算法描述。

算法优点很明显,那就是快,只要见到一条数据,就计算其梯度,然后调整参数w。也许你会说缺点也很明显,那就是不可靠。但其真的“不可靠”吗?

我们先来聊聊SGD算法的优点。使用SGD算法,最大的好处就是我们不需要考虑数据集的尺寸,我们看见一条数据就修改一次参数。很自然地,我们的学习器就可以边学习边工作,也就是现在流行的在线学习(Online learning)[11]模式,更为重要的是社会是在发展变化的,同时数据也在不停地更新,就像3年前流行的穿衣风格,3年后就可能彻底变了,而我们使用3年前最流行的穿衣风格数据来预测当前的穿衣风格,这就落伍了。

我们现在来谈谈SGD算法的“不可靠”,诚然,随机梯度下降使用的梯度是带有噪声的梯度,我们的下降方向也总是曲折的,但在这种曲折中,我们还是跌跌撞撞地游到了最小值附近,然后就在最小值周围震荡。虽然我们没有得到最优解,但我们至少得到了一个不错的解。并且在深度学习中,我们恰恰会避开最优解,而选择一个还不错的解,这其中有两个重要的原因。

其一就是我们的数据本身就不可靠,数据是带有噪声的,产生数据噪声的原因有很多,有人为造成的,也有天然的,噪声是不可避免的。为了解决这一问题,一个简单而又重要的想法呼之欲出,就是我们在噪声中学习。之前我们提到降噪任务就是在数据中加入噪声学习的一种学习方式,而随机梯度下降也可以认为是在梯度中加入噪声学习,这样训练出来的学习器就可能拥有更强的抗噪能力。

其二就是我们不要学得“太好”,这可能有点难以理解。在深度学习中,由于我们模型能力很强大,因此很容易就在已知数据中学得很“完美”,但在未知数据中就会表现得水土不服。深度学习中一个重要的方法就是使用早停(Early Stopping)[12],也就是学到一定程度就终止了学习,这种方式没有在已知数据中表现得“完美”,反而在未知数据中表现得还不错。而随机梯度下降也没有在数据中学得“完美”,而在未知数据中,往往表现还不错。

当然如果噪声太大,我们也无法学习。取批量梯度下降与随机梯度下降的一个折中,如最早的算法2.1所示,我们称之为最小批量梯度下降(Mini-Batch Gradient Descent)[9]。假如全部数据有一百条,那我们可以随机选择10条数据求出其梯度误差,然后累加之后再取平均值。但如何选择最小批量的大小,也是需要在实际应用中根据实际情况进行选择的。