之前我们都是处理一层隐藏层的神经网络,但是在处理实际问题中,一层隐藏层往往不够,所以我们需要多层隐藏层:
比如,当我们在处理视觉模式识别问题时,第一层可能用来识别边缘,第二层可以识别稍微复杂的形状,比如三角形,第三层处理更加复杂的图形,以此类推,到最后我们可以识别非常复杂的物体。所以理论上来说,深度网络比浅层网络更好。这一章,作者主要讲了如何训练深度神经网络,用到的方法是之前讲到的随机梯度下降和后向传播算法。但是也会遇到深度网络并不比浅层网络效果好的情况。
会出现的一种情况是:不同层之间训练的速度不一样,后面的层训练的时候,前面的层会停止训练,这种learning slowdown问题跟我们基于梯度的训练方法有很大的关系。相反的情况也有可能会出现:有时候前面的层训练的很好,而后面的层却不再训练了。
之前的MNIST项目作者放到GitHub了,用下面的命令行可以看到:
git clone https://github.com/mnielsen/neural-networks-and-deep-learning.git
然后我们下载数据包,像之前一样训练:
import mnist_loader
training_data, validation_data, test_data = mnist_loader.load_data_wrapper()
import network2
net = network2.Network([784, 30, 10])
net.SGD(training_data, 30, 10, 0.1, lmbda=5.0, evaluation_data=validation_data, monitor_evaluation_accuracy=True)
我们得到的accuracy是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)
这次的accuracy稍微提高了一点:96.90%。如果我们再加一层隐层:[784, 30, 30, 30, 10],这次的准确率却变到了96.57%。如果再加一层,正确率96.53%。
假设问题并不是出在我们的算法上,也就是说网络找到了正确的权重和偏置。
上图是我们[784, 30, 30, 10]网络中间的两层隐藏层的一部分(只取了30个神经元之中的前六个),每一个神经元上面都有一个小条,代表了这个神经元变化的快慢。小条长表示权重和偏置改变的很快,短表示改变的慢。可以看到,第二层的bar比第一层的bar普遍要高,说明第二层比第一层学习速度要快。
记第l层第j个神经元梯度为
jl=
C/
bjl,我们可以把
l作为决定第l层神经元学习速度快慢的向量,将其模长作为这一层学习速度。比如,||
1||是第一层神经元学习的速度。我们可以计算出上面网络的每一层学习速度:||
1||=0.07,||
2||=0.31。如果我们训练了[784, 30, 30, 30, 10]的网络,那三层学习速度分别是0.012,0.060,0.283。再增加隐藏层还是同样的情况:前面的层比后面的层学习速度要慢。速度变化如下图:
四层隐层的时候,第一层比第四层慢了100倍,所以肯定会出现问题。一个重要的发现:在某些深度网络中,当我们反向传播的时候,梯度变小了。这个现象被称作梯度消失问题vanishing gradient problem。【梯度变大的问题叫exploding gradient problem
考虑每层只有一个神经元的神经网络:
根据后向传播算法,我们可以得到第一个神经元的偏置的偏导:
除了最后一项,这个式子都是形如wj
’(zj)的乘积,回忆
函数的导数图像:
导数最大在
’(0)处,值为1/4。我们初始化权重和偏置时常用的方法是均值为0,标准差为1的正态分布,所以权重满足|wj|<1,所以|wj
’(zj)|<1/4。所以乘积项越多,导数会以指数减少。越往前,偏导就越小,这是梯度消失最主要的原因。
如果权重在学习过程中变大,或者是刚开始我们不按照正常方法初始化,而是直接设定权重为很大的数,那么就不满足|wj
’(zj)|<1/4了,就会出现exploding gradient problem。
其实我们使用sigmoid函数作为激活函数的时候,大多数情况下出现的是vanish,因为即使w变大了,但是
’(z)还是基于w的:
’(z)=
’(wa+b),当w非常大时,
’(z)会趋近于0。不过最主要的问题不是vanishing还是exploding,重要的是前面的层权重是非常不稳定的,基于后面的许多层的权重、偏导,如果网络太深,我们很难把控前面的偏导。
当神经元变多之后,梯度变成:
‘(zl)是一个对角矩阵,元素为第l层的
’(z)。其他的分析与上面相同,都是由于sigmoid函数的导数性质导致的梯度消失。作者并没有在这一章展开描述如何处理这个问题,但是他给出的解决方法是选取其他的有效区间更大的激活函数或许可以解决这个问题。