这里是原文的链接
为什么神经网络很难训练
想象你是一个工程师,然后你被叫去从头开始涉及一台电脑。有一天你在办公室里马不停蹄的工作,设计逻辑电路,与门,非门还有很多东西,这时你的老板带着一个坏消息走了进来。客户刚刚添加了一个惊人的设计要求:整个电脑的电路须只能有两层:
你开始慌了,你和你的老板说,这客户简直是疯了。
你的老板说,我也认为他们疯了,但是顾客想要什么,他们就能得到什么。
事实上,在某种程度上,他们的客户并不疯狂。假设,你有一种特殊的逻辑门让你的与门可以想输入多少就输入多少。同时,你也可以让一个非门支持很多的输入,这就是一种电路
可以与上多个输入然后无效这个输出。用这样特殊的电路结构,就可以证明用两层电路去实现任意的逻辑关系是可能的。
但是有些事情是可能的,但这并不是一个好主意。经过实践,当解决电路设计问题的时候(或者大多数种类的算法问题),我们经常会先搞清楚如何去解决基础的问题,然后再逐步的集合出解决方案。换句话来说我们通过多层抽象来建立一个解决方案。
例如,假设我们在设计基础的逻辑电路——让两个数相乘。我们很可能通过用类似于两数相加的方式去构建子电路。(多个加法的子电路的集合就可以完成乘法的运算)。构造加法的子电路又会被用来构建子电路的子电路比如两个比特的相加。粗糙的表示我们的思维会如下图
换言之,我们的终接电路包含了至少三个层次的电路基础。事实上,它很有可能包含更多的超过三个层次的电路,正如我们分支子任务称为更小的子任务,知道我们可能去表述。你大概知道了大体的意思。
所以,深层电路会让设计过程更加的简单。但是它并仅仅有益于设计。事实上,数学证明显示,对于一些函数,浅层电路需要的电路元件会比深层电路多的多。早在1980年的时候,就有一系列著名的论文指出,如果使用浅层电路,计算一组比特奇偶性需要指数级别数量的门。另一方面,如果里使用深层电路就可以用很少的电路去计算奇偶性,你只需要计算一组比特的奇偶性,然后用这个结果去计算下一组的比特如此往复,就可以快速的计算完全部的奇偶性。因此,深度电路从本质上比浅层电路更加的强大。
到目前为止,这本书像那些疯狂的客户一样,谈及了神经网络。(可能对这里会有一点困惑,这篇文章是一本书中其中的一章)几乎所有我们正在使用网络都只有一个隐藏层(包括输入层和输出层)
这些简单的网络已经非常有用了,在早期我们向这样去使用神经网络去进行手写识别,比传统的方式高98%的准确率。但是我们并不满足于此,我们还是直观的认为,有更多隐藏层的神经网络将会更加的强大。
就像布尔电路一样,这样的网络可以使用中间层去增强多层抽象。例如,如果我们准备做视觉模式辨别,那么视觉网络在第一层可能先学习识别边缘,在第二层网络可能学会识别更多复杂的形状,比如长方形和三角形,它们是由边缘构成的。在第三层会去识别更加复杂的形状,如此往复。这些抽象的多层结构好似给予了深度网络一个令人叹服的优势去解决模式识别问题。而且,只是已电路为例就可以从理论的结果证明深度网络在本质上比浅层网络更加强大。
我们应该如何去训练如此一个深层网络呢?在这一章,我们将会尝试使用我们的学习算法——随机梯度下降通过反向传播的方式去训练深层网络。但是我们会遇到一个麻烦,我们的深层网络并不比浅层网络表现的更好。
根据上面的讨论,这个失败看起来很让人惊讶。比起放弃深层网络,我们更会去发掘原因,尝试去理解是什么让深度网络那么的难以训练。当我们仔细观察的时候,我们将会发现,在深层网络中不同的层之间有着截然不同的学习速度。特别是,当后面的层已经训练的很好的时候,前面的层经常在训练中“堵住”,几乎什么都没有学到。这个现象不是简单的由于坏运气。当然了,我们发现了学习速度下降的基本原因和我们使用的梯度优化的技术有关。
当我们钻研到问题的深处的时候,我们将会发现,相反的情况也会出现:前面的层学习的很好,但是后面的层会被卡住。事实上,我们可以发现对多层的深度神经网络使用梯度下降的学习方法有一个固有的不稳定性。这个不稳定性导致了前面的层或后面的层在训练的过程中出现卡壳的现象。
这听起来都像是坏消息。但是同通过探讨这些难题,我们开始深刻的认识到在深度网络的训练中什么是被需要的且是高效的。同时这些研究已经在下一章中充当的叙述,我们在哪里会使用深度学习去解决图像识别的问题。
梯度消失问题
所以,我们在训练深度网络的时候到底哪里出了问题呢?
为了回答这个问题,让我们先向回到只有一个隐藏层的简单网络结果。照例,我们将会使用手写数字分类问题作为我们的案例去学习和实验
如果你像,你可以跟我一起在你的电脑上训练一个网络。当然,只是读也很好。如果你想要跟随我的话,你需要Python2.7 Numpy 和一些代码的副本(你可以从命令行里复制)
git clone https://github.com/mnielsen/neural-networks-and-deep-learning.git
然后从python Shell 里我们现在MNIST数据集
import mnist_loader
training_data, validation_data, test_data = \
... mnist_loader.load_data_wrapper()
搭建起我们的网络
import network2
net = network2.Netword([784,30,10])
这个网络在输入层有784个神经元与输入28 × 28 = 784 像素的图片一致。我们使用30个隐藏层和10个输出层网络和10中可能的输出结果一致(0,1,2,3,4,5,6,7,8,9)
让我们一次使用小批量的10个训练模型,学习率:η=0.1 ,,正则化参数:λ=5.0 去 训练这30层的网络。在训练的时候,我们将会监视有效数据的准确性
>>> net.SGD(training_data, 30, 10, 0.1, lmbda=5.0,
... evaluation_data=validation_data, monitor_evaluation_accuracy=True)
我们得到了96.48%的准确率(每次运行会有一定的偏差),和之前的结果几乎没有什么差别
现在让我们添加其他同样有30层的隐藏层,然后尝试用相同的超参数(在开始学习过程之前设置值的参数)训练
>>> net = network2.Network([784, 30, 30, 10])
>>> net.SGD(training_data, 30, 10, 0.1, lmbda=5.0,
... evaluation_data=validation_data, monitor_evaluation_accuracy=True)
这让分类的准确率增加到了96.90%。让人鼓舞的是,加深网络结构是有一定帮助的。让我们添加另外的30层隐藏层。
>>> net = network2.Network([784, 30, 30, 10])
>>> net.SGD(training_data, 30, 10, 0.1, lmbda=5.0,
... evaluation_data=validation_data, monitor_evaluation_accuracy=True)
这一点用都没有用。事实上,结果掉回到了96.57%,接近我们原来的浅层网络的分类准确率。加入我们插入更多的隐藏层
>>> net = network2.Network([784, 30, 30, 10])
>>> net.SGD(training_data, 30, 10, 0.1, lmbda=5.0,
... evaluation_data=validation_data, monitor_evaluation_accuracy=True)
分类的准确性又再次下降了,下降到了96.53%。这可能并不是一个显著的统计数据,但是这同样让了沮丧。
这个运行情况看起来很奇怪。直观的感受,额外的隐藏层应该让网络更加有能力去学习更多复杂的分类功能,因此可以更好的做好分类的工作。确实的,事情不应该变得那么糟糕,在最坏的情况下,额外的层可以什么都不做。但是,事实并非如此。
所以到底发送了什么?让我们来分析一下,额外的隐藏层在本质上应该可以帮助网络的学习,问题就是我们的学习算法没有找到正确的权重和偏置。我们想搞清楚我们的学习算法到底哪里出了问题和怎么去解决它。为了深入了解哪里出了问题,让我们先来了解网络是如何学习的。下面我已经画出了一部分【784,30,30,10】的网络。一个网络有两个隐藏层,每个包含30个隐藏神经元。每个图中的神经元都有一个小棒在上面。小棒代表神经元在网络学习中的改变速度。一个大的棒意味着神经元的权重和偏置改变的很快,一个小棒代表了神经元的权重和偏置改变的很慢。更精确的来说,棒表示了每个神经元的梯度∂C/∂b,相对于神经元的偏置和成本的变化率,在第二章我们看到梯度数量控制的不仅仅是偏置在学习中的改变速度,同样也控制了输入网络的权重的变化率。不要担心如果你无法回忆具体的细节,要记住的事情很简单就是那些棒显示了每个神经元的权重和偏置的改变速度。
为了让图更加的简单,我只展示了两个隐藏层里顶部的六个神经元,因为他们没有权重和偏置去学习。我同样省略了输出层,我们正在做逐层的对比,而且对比含有相同神经元的层数最有意义。结果一开始就画出来了,也就是在初始化网络后立即运行。给你下图:
网络的初始化是随意的,所以神经元的学习有那么多的变化并不奇怪。尽管如此,一件事情仍然跳了出来,第二层的棒比第一层的要大很多。这仅仅是一个巧合吗?或者总体上第二层网络的神经元好似都比第一层网络的神经元,学的更快。
要确定是否存在这种情况,可以使用全局方法来比较第一和第二隐藏层中的学习速度。为此,让我们用 δ j l = ∂ C / ∂ b j l δ^l_j=∂C/∂b^l_j δjl=∂C/∂bjl来表示第i层第j个神经元的梯度。我们可以把梯度 δ 1 δ^1 δ1当作确定第一个隐藏层变化的有多快的一个向量, δ 2 δ^2 δ2作为确定第二个隐藏层变化有多快的一个向量。之后我们使用这些向量的长度作为一层网络学习速度的综合评分。例如, ∣ ∣ δ 1 ∣ ∣ ||δ^1|| ∣∣δ1∣∣量度第一个隐藏层的学习速度, ∣ ∣ δ 2 ∣ ∣ ||δ^2|| ∣∣δ2∣∣量度第二个隐藏层的学习速度。
用这些定义以及上述同样的结构,可以得到 ∣ ∣ δ 1 ∣ ∣ ||δ^1|| ∣∣δ1∣∣=0.07, ∣ ∣ δ 2 ∣ ∣ ||δ^2|| ∣∣δ2∣∣=0.31 所以这个证明了我们之前的猜想,第二隐藏层的神经元确实要比第一层的神经元学习的更加快速。
在我们添加更多层数的时候到底发生了什么呢?如果我们有三个隐藏层在一个【784,30,30,30,10】的网络结构里,每一层的学习速度分别是0.012,0.030和0.283.又一次,前面的神经元比后面的神经元学习的更慢。假设我们在添加一层有三个神经元的隐藏层。在这种情况下,每层的学习速度分别是0.003,0.017,0.070和0.285。结果表明,前面的层比后面的层学习的更慢。
我们已经看到了开始训练时的学习速度,也就是网络被初始化的时候的学习速度。当我们在训练自己的网络的时候,速度又会如何变化呢?让我回过头去看只有两层网络时候。学习速度的改变情况如下图:
为了产生这些结果,我使用了批量梯度下降的算法,将只有1000张的训练图片数据训练了超过500个来回。这和我们以往普通的训练方式不同,我没有使用小批量,同时只是用了1000张图片作为训练数据,而不是完整的5000张图片的训练集。我不是想做一些鬼鬼祟祟的事情,也不是想蒙骗你,只是因为使用小批量的随机梯度下降算法会产生很多的干扰(尽管这些干扰平均后看起来不大)使用我选择的参数是一种平滑结果的简单方法,因此我们可以看到发生了什么。
无论如何,正如你所看到的两层网络以很大不同的速度学习(我们已经知道的)。在回升之前两层网络的学习速度都下降的很快。但即使是这样,第一个隐藏层的学习速度也要比第二层隐藏层慢很多。
对于更加复杂的网络又会怎么样呢?给你一个相似实验的结果,但是这次有三个隐藏层(一个【784,30,30,30,10】的网络)
又一次,前面的隐藏层比后面的隐藏层学习的更慢。最后让我们补充一个四个隐藏层的网络(一个【784,30,30,30,30,10】的网络结果)然后看一下在训练的时候会发生什么。
再一次的,前的隐藏层学习的比后面的隐藏层学习的更慢。在这种情况下,第一个隐藏层比最后一个隐藏层慢了将近100倍。难怪我们之前在训练的网络的时候遇到了麻烦。
我们在这里有一个重要的发现,至少在一些神经网络中,当我们通过隐藏层像后移动时候梯度会变的更小。这意味着在前面层的神经元比在后面层的神经元学习的更慢。虽然我们只在单个网络中看多过这种情况,但是这是为什么在多层网络中发生的基本原因。这种现象被称为梯度消失问题。
为什么梯度消失问题会发生呢?有什么方式是我们可以避免的吗?以及我们如何在训练深度神经网络的时候避免它。事实上,我们马上就会学习到,这是不可避免的,虽然代替方案不是很有吸引力,有时在前面的层中梯度会变的更大。这就是梯度爆炸问题,这并不是一个比梯度消失更好的消息。一般的来说,结果表明,在深度神经网络中,梯度会很不稳,在前面网络层中倾向于发生梯度爆炸和梯度消失。这种不稳定性是深度数据网络梯度优化中的一种最基本的问题。这是一些我们需要理解的东西,如果可能的话再采取一些应对措施。
首先要确认梯度消失是否真的是一个问题。暂时的不要想神经网络,想象我们正在尝试用数值方法去最小化一个单变量函数 f ( x ) f(x) f(x)。如果 f ( x ) ′ f(x)' f(x)′很小,这不是一个好消息吗?这难道不意味着我们已经接近一个极值了吗?同样地,在深度神经网络前面的层有很小的梯度值难道不意味着我们不需要对权重和偏置做太多调整?
当然,事实并非如此。回想一下,我们是随机的初始化了网络中的权重和偏置。这是极不可能的我们随机初始化的权重和偏置会在我们的网络中有很好的 效果。具体地考虑下手写数字识别问题中【784,30,30,30,10】的神经网络中第一层的权重。随机初始化权重意味着第一层丢掉了最多有关输入图片的信息。即使之后的网络层被有效率的训练了,它们依然会发现识别输入图片会非常困难,原因很简单:因为它们没有足够的信息。在第一层中不可能有太多的学习需要去做
如果我们想要去训练深度神经网络,我们需要去弄明白如何去处理梯度下降问题
什么导致了梯度消失问题?深度神经网络中不稳定的梯度
为什么搞清楚为什么梯度消失会发生,我们先考虑一下简单的深度神经网络:每一层只有一个神经元。下面是有三个隐藏层的神经网络
w
1
,
w
2
.
.
.
w_1,w_2...
w1,w2...是权重,
b
1
,
b
2
.
.
.
b_1,b_2...
b1,b2...是偏置,以及C是一些代价函数。只是为了提醒你这是怎么一回事,从
j
t
h
j^{th}
jth神经元
a
j
a_j
aj的输出是
σ
(
z
j
)
σ(z_j)
σ(zj)σ 是普通的sigmoid 激活函数,以及
z
j
=
w
j
a
j
−
1
+
b
j
z_j=w_ja_{j-1}+b_j
zj=wjaj−1+bj是输入神经元的权重。为了强调这是神经网络的输出,我已经在最后画上了一个C,
a
4
a_4
a4:如果神经网络的实际输出和预期输出很接近,代价就会很低,但是如果两者相差很远,代价就会很高
我们将要学习和第一个隐藏层相关的梯度 ∂ C / ∂ b 1 ∂C/∂b_1 ∂C/∂b1。我们会发现一个表示 ∂ C / ∂ b 1 ∂C/∂b_1 ∂C/∂b1的表达式,通过研究这个表达式,我们将会发现梯度消失产生的原因。
我会从向你简单的展示这个表达式开始。它看起来令人生畏,但它实际上有一个简单的结构,我一会儿会描述它。这是表达式(现在请忽略网络,注释中的σ′ 只是σ函数的导数)
表达式的结构如下:在表达式中对网络中的每个神经元有一个叫做
σ
′
(
z
j
)
σ′(z_j)
σ′(zj)d 的项;每层网络中的权重
w
j
w_j
wj,还有一个
∂
C
∂
a
4
\frac{∂C}{∂a_4}
∂a4∂C项对应最后的代价函数。请注意,我已经将表达式中的每个项置于网络的相应部分之上。所以神经网路本身就是这个表达式的助记符。
你可能把这个表达式当作理所当然的,然后跳过讨论它和梯度消失是如何产生联系的。这么做并没有什么坏处,因为表达式是我们前面讨论反向传播时的特殊情况。但是同样也有一个解释关于这个表达式为什么是正确的,而且来看看这个解释是很有趣的。
假设我们做了一个小小的改变,把
Δ
b
1
Δb_1
Δb1改为
b
1
b_1
b1的偏差。这将会引发神经网路中巨大的变化。第一,它改变了第一个隐藏层输出的
Δ
a
1
Δa_1
Δa1。这反过来又会导致,输入第二层隐藏层的权重
Δ
z
2
Δz_2
Δz2。
然后改变第二层隐藏层的输出
Δ
a
2
Δa_2
Δa2,等等,所有的方式通过改变输出的代价
Δ
C
ΔC
ΔC.我们可以得到
∂
C
∂
b
1
≈
Δ
C
Δ
b
1
.
\frac{\partial C}{\partial b_1} \approx \frac{\Delta C}{\Delta b_1}.
∂b1∂C≈Δb1ΔC.
这意味着我们可能通过仔细的跟踪这个级联中的每一步去计算出
∂
C
/
∂
b
1
∂C/∂b_1
∂C/∂b1的表达式。
为此,让我先想一想
Δ
b
1
Δb1
Δb1是如何导致第一层隐藏层神经元的输出
a
1
a_1
a1改变的。我们有
a
1
=
σ
(
z
1
)
=
σ
(
w
1
a
0
+
b
1
)
a_1 = \sigma(z_1) = \sigma(w_1 a_0 + b_1)
a1=σ(z1)=σ(w1a0+b1)所以
Δ
a
1
≈
∂
σ
(
w
1
a
0
+
b
1
)
∂
b
1
Δ
b
1
=
σ
′
(
z
1
)
Δ
b
1
Δa_1\approx \frac{∂σ(w_1a_0+b_1)}{∂b_1}Δb_1= σ′(z_1)Δb1
Δa1≈∂b1∂σ(w1a0+b1)Δb1=σ′(z1)Δb1
σ
′
(
z
1
)
σ′(z_1)
σ′(z1)这一项可能看起来比较眼熟,这是我们在声明
∂
C
/
∂
b
1
∂C/∂b_1
∂C/∂b1表达式中的第一项。直观的来看,这一项将偏置中的变化
Δ
b
1
Δb_1
Δb1转化为激活中的变化
Δ
a
1
Δa_1
Δa1.这个变化
Δ
a
1
Δa_1
Δa1有会反过来导致输入到第二个隐藏层的权重$z2=w2a1+b2 $的变化
Δ
z
2
≈
∂
z
2
∂
a
1
Δ
a
1
=
w
2
Δ
a
2
Δz_2\approx \frac{∂z_2}{∂a_1}Δa_1 = w_2Δa_2
Δz2≈∂a1∂z2Δa1=w2Δa2
结合我们对于
Δ
z
2
Δz_2
Δz2和
Δ
a
1
Δa_1
Δa1的表达,我们可以看到偏置
b
1
b_1
b1的改变是如何在神经网络的传播中改变
z
2
z_2
z2的
Δ
z
2
≈
σ
′
(
z
1
)
w
2
Δ
b
1
.
Δz_2\approx σ′(z_1)w_2Δb_1.
Δz2≈σ′(z1)w2Δb1.
同样,我们应该也感觉这个表达式很眼熟,这是我们在声明
∂
C
/
∂
b
1
∂C/∂b_1
∂C/∂b1表达式中的前两项。
我们可以以这种方式继续下去,跟踪变化传播到网络其余部分的方式。在每个神经元处,我们选取一个
σ
′
(
z
j
)
σ'(z_j)
σ′(zj)项,并且在每个权重中,我们选取一个
w
j
w_j
wj项。最终结果是将成本的最终变化
Δ
C
ΔC
ΔC与偏差的初始变化
Δ
b
1
Δb_1
Δb1相关联的表达式:
Δ
C
≈
σ
′
(
z
1
)
w
2
σ
′
(
z
2
)
…
σ
′
(
z
4
)
∂
C
∂
a
4
Δ
b
1
ΔC≈σ′(z_1)w_2σ′(z_2)…σ′(z_4)\frac{∂C}{∂a_4}Δb_1
ΔC≈σ′(z1)w2σ′(z2)…σ′(z4)∂a4∂CΔb1
将
Δ
b
1
Δb_1
Δb1除过去,我们就可以得到预期的表达式
∂
C
∂
b
1
=
σ
′
(
z
1
)
w
2
σ
′
(
z
2
)
…
σ
′
(
z
4
)
∂
C
∂
a
4
\frac{∂C}{∂b_1}=σ′(z_1)w_2σ′(z_2)…σ′(z_4)\frac{∂C}{∂a_4}
∂b1∂C=σ′(z1)w2σ′(z2)…σ′(z4)∂a4∂C
**为什么梯度消失的问题会发生:**为了理解为什么梯度消失会发生,让我们明确的写出梯度的完整我表达式。
∂
C
∂
b
1
=
σ
′
(
z
1
)
w
2
σ
′
(
z
2
)
w
3
σ
′
(
z
3
)
w
4
σ
′
(
z
4
)
∂
C
∂
a
4
\frac{∂C}{∂b_1}=σ′(z_1)w_2σ′(z_2)w_3σ′(z_3)w_4σ′(z_4)\frac{∂C}{∂a_4}
∂b1∂C=σ′(z1)w2σ′(z2)w3σ′(z3)w4σ′(z4)∂a4∂C
除最后一项外,此表达式是
w
j
σ
′
(
z
j
)
w_jσ'(z_j)
wjσ′(zj)形式的项的乘积。为了了解这些项是如何变化的,让我们来看一看
σ
′
σ′
σ′的图像。
导数的最大值为
σ
′
(
0
)
=
1
/
4
σ′(0)=1/4
σ′(0)=1/4.现在如果我们使用标准的方式去初始化网络中的权重和偏置,我们将使用平均值为0和标准差为1的高斯选择权重。所以权重通常会满足
∣
w
j
∣
<
1
|w_j|<1
∣wj∣<1.将这些观察结果放在一起,我们看到
w
j
σ
′
(
z
j
)
w_jσ'(z_j)
wjσ′(zj)项通常将满足
∣
w
j
σ
′
(
z
j
)
∣
<
1
/
4
|w_jσ'(z_j)| <1/4
∣wjσ′(zj)∣<1/4。当我们把那么多的项乘起来的时候,它的积会以指数级减少:项越多,积越小。这听起来像梯度消失的一个可能的解释。
-----------------------------------剩下的数学公式实在是晦涩难懂有兴趣的自己取看看吧--------------------------------------------------