问题引入
假设你是⼀名⼯程师,接到⼀项从头开始设计计算机的任务。某天,你在⼯作室⼯作,设计 逻辑电路,构建AND⻔,OR⻔等等时,⽼板带着坏消息进来:客⼾刚刚添加了⼀个奇特的设 计需求:整个计算机的线路的深度必须只有两层:
也就是说,你必须要在两层网络上,实现一个无比强大的功能;
你惊呆了,跟⽼板说道:“这货疯掉了吧!” ⽼板说:“我也认为他们疯了,但是客⼾的需求⽐天⼤,我们要满⾜它。”
实际上,在某种程度上看,他们的客⼾并没有太疯狂。假设你可以使⽤某种特殊的逻辑⻔, 它让你对任意多的输⼊做AND运算。同样也能使⽤多输⼊的NAND⻔——可以对多个输⼊做 AND运算并取负的⻔。有了这类特殊的⻔,构建出来的两层深度的电路可以计算任何函数。
但是仅仅因为某件事是理论上可能的,并不代表这是⼀个好的想法。在实践中,在解决线路 设计问题(或者⼤多数的其他算法问题)时,我们通常考虑如何解决⼦问题,然后逐步地集成这 些⼦问题的解。换句话说,我们通过多层的抽象来获得最终的解答。例如:
最终的线路包含⾄少三层线路的基本部分。实际上,这个线路很可能会超过三层,因为我们 可以将⼦任务分解成⽐上述更⼩的单元。但是基本思想就是这样。
到现在为⽌,本书将神经⽹络看作是那个疯狂的客⼾。⼏乎我们遇到的所有的⽹络就只包括 ⼀层隐藏神经元(另外还有输⼊输出层)
这些简单的⽹络已经⾮常有⽤了:在前⾯的章节中,我们使⽤这样的⽹络可以进⾏准确率⾼ 达98%的⼿写数字的识别!⽽且,凭直觉地看,我们期望拥有更多隐藏层的神经⽹络能够变的 更加强⼤:
这样的⽹络可以使⽤中间层构建出多层的抽象,正如我们在布尔线路中做的那样。例如,如 果我们在进⾏视觉模式识别,
- 那么在第⼀层的神经元可能学会识别边,
- 在第⼆层的神经元可以 在边的基础上学会识别出更加复杂的形状,例如三⻆形或者矩形。
- 第三层将能够识别更加复杂 的形状。
依此类推。这些多层的抽象看起来能够赋予深度⽹络⼀种学习解决复杂模式识别问题 的能⼒。然后,正如线路的⽰例中看到的那样,存在着理论上的研究结果告诉我们深度⽹络在 本质上⽐浅层⽹络更加强⼤。
那我们如何训练这样的深度神经⽹络呢?当我们尝试使⽤我们犹如苦⼒般的学习算 法——基于反向传播的随机梯度下降——来训练深度⽹络时,发现在深度⽹络中,不同的层学习的 速度差异很⼤。尤其是,
- 在⽹络中后⾯的层学习的情况很好的时候,先前的层次常常会在训练 时停滞不变,基本上学不到东西。
- 这种停滞并不是因为运⽓不好。⽽是,有着更加根本的原因 是的学习的速度下降了,这些原因和基于梯度的学习技术相关。
- 当我们更加深⼊地理解这个问题时,发现相反的情形同样会出现:先前的层可能学习的⽐较 好,但是后⾯的层却停滞不变。
实际上,我们发现在深度神经⽹络中使⽤基于梯度下降的学习 ⽅法本⾝存在着内在不稳定性。
消失的梯度问题
在我们训练深度⽹络时究竟哪⾥出了问题?为了回答这个问题,让我们重新看看使⽤单⼀隐藏层的神经⽹络⽰例。
以之前的数字识别算法为例:
- 没有隐藏层时,分类的准确率为96.48%
- 增加另外⼀层隐藏层,最终的结果分类准确度提升了⼀点,96.90%。这点令⼈兴奋:⼀点点的深度带来了效果。
- 再增加一层,这⾥并没有什么提升,反⽽下降到了96.57%
- 再增加一层,分类准确度⼜下降了,96.53%。这可能不是⼀个统计显著地下降,但是会让⼈们觉得沮丧。
这⾥表现出来的现象看起⾮常奇怪。直觉地,额外的隐藏层应当让⽹络能够学到更加复杂的 分类函数,然后可以在分类时表现得更好吧。
假设额外的隐藏层的确能够在原理上起到作⽤,问题是我们的学习 算法没有发现正确地权值和偏置。那么现在就要好好看看学习算法本⾝有哪⾥出了问题,并搞 清楚如何改进了。
为了获得⼀些关于这个问题直觉上的洞察,我们可以将⽹络学到的东西进⾏可视化。
- 图中 的每个神经元有⼀个条形统计图,表⽰这个神经元在⽹络进⾏学习时改变的速度。更⼤的条意 味着更快的速度,⽽⼩的条则表⽰变化缓慢。
- 为了让图⾥简单,我只展⽰出来最上⽅隐藏层上的个神经元。
- 这⾥忽略了输⼊层神经元, 因为他们并不包含需要学习的权重或者偏置。同样输出层神经元也忽略了,因为这⾥我们做的 是层层之间的⽐较,所以⽐较相同数量的两层更加合理啦。
该⽹络是随机初始化的,因此看到了神经元学习的速度差异其实很⼤。⽽且,我们可以发现, 第⼆个隐藏层上的条基本上都要⽐第⼀个隐藏层上的条要⼤。所以,在第⼆个隐藏层的神经元 将学习得更加快速。
我们继续增加层次:
如图所⽰,两层在开始 时就有着不同的速度。然后两层的学习速度在触底前迅速下落。在最后,我们发现第⼀层的学 习速度变得⽐第⼆层更慢了。
如果是3个隐藏层,或者4个隐藏层:
这⾥,第⼀层的学习速 度和最后⼀层要差了两个数量级,也就是⽐第四层慢了倍。难怪我们之前在训练这些⽹络 的时候遇到了⼤⿇烦!
现在我们已经有了⼀项重要的观察结果:⾄少在某些深度神经⽹络中,在我们在隐藏层BP 的时候梯度倾向于变⼩。这意味着在前⾯的隐藏层中的神经元学习速度要慢于后⾯的隐藏层。 这⼉我们只在⼀个⽹络中发现了这个现象,其实在多数的神经⽹络中存在着更加根本的导致这 个现象出现的原因。这个现象也被称作是消失的梯度问题(vanishing gradient problem)。
为何消失的梯度问题会出现呢?我们可以通过什么⽅式避免它?还有在训练深度神经⽹络 时如何处理好这个问题?实际上,这个问题是可以避免的,尽管替代⽅法并不是那么有效,同 样会产⽣问题——在前⾯的层中的梯度会变得⾮常⼤!这也叫做激增的梯度问题(exploding gradient problem),这也没⽐消失的梯度问题更好处理。
更加⼀般地说,在深度神经⽹络中的梯 度是不稳定的,在前⾯的层中或会消失,或会激增。这种不稳定性才是深度神经⽹络中基于梯度 学习的根本问题。
深度神经⽹络中的梯度不稳定性
梯度消失问题
为了弄清楚为何会出现消失的梯度,来看看⼀个极简单的深度神经⽹络:每⼀层都只有⼀个 单⼀的神经元。下图就是有三层隐藏层的神经⽹络。
现在我们要来研究⼀下关联于第⼀个隐藏神经元梯度
。我们将会计算出
的 表达式,通过研究表达式来理解消失的梯度发⽣的原因。
经过一系列计算,得出:
梯度激增问题
现在看看梯度激增如何出现的吧。这⾥的例⼦可能不是那么⾃然:固定⽹络 中的参数,来确保产⽣激增的梯度。但是即使是不⾃然,也是包含了确定会产⽣爆炸梯度(⽽ ⾮假设的可能)的特质的。
不稳定的梯度问题
根本的问题其实并⾮是消失的梯度问题或者激增的梯度问题,⽽是在前⾯的层上的梯度是来⾃后⾯的层上项的乘积。当存在过多的层次时,就出现了内在本质上的不 稳定场景。唯⼀让所有层都接近相同的学习速度的⽅式是所有这些项的乘积都能得到⼀种平衡。 如果没有某种机制或者更加本质的保证来达成平衡,那⽹络就很容易不稳定了。简⽽⾔之,真实的问题就是神经⽹络受限于不稳定梯度的问题。所以,如果我们使⽤标准的基于梯度的学习 算法,在⽹络中的不同层会出现按照不同学习速度学习的情况。
在更加复杂⽹络中的不稳定梯度
现在已经研究了简单的⽹络,每⼀层只包含⼀个神经元。那么那些每层包含很多神经元的更 加复杂的深度⽹络呢
实际上,在这样的神经⽹络中,同样的情况也会发⽣。在前⾯关于反向传播的章节中,我们 看到了在⼀个共L层的第l层的梯度:
其它深度学习的障碍
本章我们已经聚焦在消失的梯度上,并且更加⼀般地,不稳定梯度——深度学习的⼀⼤障 碍。实际上,不稳定梯度仅仅是深度学习的众多障碍之⼀,尽管这⼀点是相当根本的。当前的研 究集中在更好地理解在训练深度神经⽹络时遇到的挑战。
“什么让训练深度⽹络⾮常困难”这个问题相当复杂。本章,我们已经 集中于深度神经⽹络中基于梯度的学习⽅法的不稳定性。结果表明了激活函数的选择,权重的 初始化,甚⾄是学习算法的实现⽅式也扮演了重要的⻆⾊。当然,⽹络结构和其他超参数本⾝ 也是很重要的。因此,太多因⼦影响了训练神经⽹络的难度,理解所有这些因⼦仍然是当前研 究的重点。尽管这看起来有点悲观,但是在下⼀章中我们会介绍⼀些好的消息,给出⼀些⽅法 来⼀定程度上解决和迂回所有这些困难。