《神经网络和深度学习》系列文章三十九:梯度消失问题

出处: Michael Nielsen的《Neural Network and Deep Learning》,本节译者:朱小虎 、张广宇。

目录

1、使用神经网络识别手写数字

2、反向传播算法是如何工作的

3、改进神经网络的学习方法

4、神经网络可以计算任何函数的可视化证明

5、为什么深度神经网络的训练是困难的

  • 梯度消失问题
  • 什么导致了梯度消失问题?神经网络中的梯度不稳定性
  • 在更加复杂网络中的不稳定性梯度
  • 其他深度学习的障碍

6、深度学习

那么,在我们训练深度网络时究竟哪里出了问题?

为了回答这个问题,让我们重新看看使用单一隐藏层的神经网络示例。这里我们也是用MNIST 数字分类问题作为研究和实验的对象。

这里你也可以在自己的电脑上训练神经网络。或者就直接读下去。如果希望实际跟随这些步骤,那就需要在电脑上安装 Python 2.7,Numpy 和代码,可以通过下面的命令复制所需要的代码:

git clone https://github.com/mnielsen/neural-networks-and-deep-learning.git

如果你不使用 git,那么就直接从https://github.com/mnielsen/neural-networks-and-deep-learning/archive/master.zip 下载数据和代码。然后需要转入 src 子目录。

然后,在 Python 命令提示符中我们加载 MNIST 数据:

>>> import mnist_loader
>>> training_data, validation_data, test_data = \
… mnist_loader.load_data_wrapper()

设置我们的网络:

>>> import network2
>>> net = network2.Network([784, 30, 10])

这个网络拥有 784 个输入层神经元,对应于输入图片的  个像素点。我们设置隐藏层神经元为 30 个,输出层为 10 个神经元,对应于 10 个 MNIST 数字(‘0’, ‘1’, ‘2’, …, 9)。

让我们训练 30 个完整的 epochs,使用 minibatch 大小为 10, 学习率 ,规范化参数 。在训练时,我们也会在  validation_data  上监控分类的准确度。注意网络可能需要花费几分钟来训练,要看你机器的速度。所以如果你正在运行代码,你可能愿意继续阅读并稍后回来,而不是等待代码完成执行。

>>> 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%。这点令人兴奋:一点点的深度带来了效果。那么就再增加一层同样的隐藏层:

>>> net = network2.Network([784, 30, 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, 30, 30, 10])
>>> net.SGD(training_data, 30, 10, 0.1, lmbda=5.0,
… evaluation_data=validation_data, monitor_evaluation_accuracy=True)

分类准确度又下降了,96.53%。这可能不是一个统计显著地下降,但是会让人们觉得沮丧。

这里表现出来的现象看起非常奇怪。直觉地,额外的隐藏层应当让网络能够学到更加复杂的分类函数,然后可以在分类时表现得更好吧。可以肯定的是,事情并没有变差,至少新的层次增加上,在最坏的情形下也就是没有影响。事情并不是这样子的。

那么,应该是怎样的呢?假设额外的隐藏层的确能够在原理上起到作用,问题是我们的学习算法没有发现正确地权值和偏置。那么现在就要好好看看学习算法本身有哪里出了问题,并搞清楚如何改进了。

为了获得一些关于这个问题直觉上的洞察,我们可以将网络学到的东西进行可视化。下面,我画出了一部分  的网络,也就是包含两层各有 个隐藏神经元的隐藏层。图中的每个神经元有一个条形统计图,表示这个神经元在网络进行学习时改变的速度。更大的条意味着更快的速度,而小的条则表示变化缓慢。更加准确地说,这些条表示了 每个神经元上的,也就是代价函数关于神经元的偏置更变的速率。回顾第二章,我们看到了这个梯度的数值不仅仅是在学习过程中偏置改变的速度,而且也控制了输入到神经元权重的变量速度。如果没有回想起这些细节也不要担心:目前要记住的就是这些条表示了每个神经元权重和偏置在神经网络学习时的变化速率。

为了让图里简单,我只展示出来最上方隐藏层上的  个神经元。这里忽略了输入层神经元,因为他们并不包含需要学习的权重或者偏置。同样输出层神经元也忽略了,因为这里我们做的是层层之间的比较,所以比较相同数量的两层更加合理啦。在网络初始化后立即得到训练前期的结果如下1:

该网络是随机初始化的,因此看到了神经元学习的速度差异其实很大。而且,我们可以发现,第二个隐藏层上的条基本上都要比第一个隐藏层上的条要大。所以,在第二个隐藏层的神经元将学习得更加快速。这仅仅是一个巧合么,或者第二个隐藏层的神经元一般情况下都要比第一个隐藏层的神经元学习得更快? 为了确定我们的猜测,拥有一种全局的方式来比较学习速度会比较有效。我们这里将梯度表示为  在第  层的第  个神经元的梯度。我们可以将  看做是一个向量其中元素表示第一层隐藏层的学习速度, 则是第二层隐藏层的学习速度。接着使用这些向量的长度作为全局衡量这些隐藏层的学习速度的度量。因此, 就代表第一层隐藏层学习速度,而  就代表第二层隐藏层学习速度。 借助这些定义,在和上图同样的配置下, 而,所以这就确认了之前的疑惑:在第二层隐藏层的神经元学习速度确实比第一层要快。

如果我们添加更多的隐藏层呢?如果我们有三个隐藏层,比如说在一个  的网络中,那么对应的学习速度就是 。这里前面的隐藏层学习速度还是要低于最后的隐藏层。假设我们增加另一个包含 30 个隐藏神经元的隐藏层。那么,对应的学习速度就是:。还是一样的模式:前面的层学习速度低于后面的层。

现在我们已经看到了训练开始时的学习速度,这是刚刚初始化之后的情况。那么这个速度会随着训练的推移发生什么样的变化呢?让我们看看只有两个隐藏层。学习速度变化如下:

为了产生这些结果,我在  个训练图像上进行了  轮 batch 梯度下降。这和我们通常训练方式还是不同的——我没有使用 minibatch,仅仅使用了  个训练图像,而不是全部的  幅图。我并不是想做点新鲜的尝试,或者蒙蔽你们的双眼,但因为使用 minibatch 随机梯度下降会在结果中带来更多的噪声(尽管在平均噪声的时候结果很相似)。使用我已经确定的参数可以对结果进行平滑,这样我们可以看清楚真正的情况是怎样的。 如图所示,两层在开始时就有着不同的速度。然后两层的学习速度在触底前迅速下落。在最后,我们发现第一层的学习速度变得比第二层更慢了。

那么更加复杂的网络是什么情况呢?这里是一个类似的实验,但是这次有三个隐藏层():

同样,前面的隐藏层要比后面的隐藏层学习的更慢。最后一个实验,就是增加第四个隐藏层(),看看这里会发生什么:

同样的情况出现了,前面的隐藏层的学习速度要低于后面的隐藏层。这里,第一层的学习速度和最后一层要差了两个数量级,也就是比第四层慢了  倍。难怪我们之前在训练这些网络的时候遇到了大麻烦!

现在我们已经有了一项重要的观察结果:至少在某些深度神经网络中,在我们在隐藏层 BP的时候梯度倾向于变小。这意味着在前面的隐藏层中的神经元学习速度要慢于后面的隐藏层。这儿我们只在一个网络中发现了这个现象,其实在多数的神经网络中存在着更加根本的导致这个现象出现的原因。这个现象也被称作是梯度消失问题(vanishing gradient problem)2。

为何消失的梯度问题会出现呢?我们可以通过什么方式避免它?还有在训练深度神经网络时如何处理好这个问题?实际上,这个问题是可以避免的,尽管替代方法并不是那么有效,同样会产生问题——在前面的层中的梯度会变得非常大!这也叫做梯度爆炸问题 ( exploding gradient problem),这也没比梯度消失问题更好处理。更加一般地说,在深度神经网络中的梯度是不稳定的,在前面的层中或会消失,或会激增。这种不稳定性才是深度神经网络中基于梯度学习的根本问题。这就是我们需要理解的东西,如果可能的话,采取合理的步骤措施解决问题。

一种有关消失的(不稳定的)梯度的看法是确定这是否确实是一个问题。此刻我们暂时转换到另一个话题,假设我们正要数值优化一个一元的函数 。如果其导数  很小,这难道不是一个好消息么?是不是意味着我们已经接近极值点了?同样的方式,在深度神经网络中前面隐藏层的小的梯度是不是表示我们不需要对权重和偏置做太多调整了?

当然,实际情况并不是这样的。想想我们随机初始网络中的权重和偏置。在面对任意的一种任务,单单使用随机初始的值就能够获得一个较好的结果是太天真了。具体讲,看看 MNIST问题的网络中第一层的权重。随机初始化意味着第一层丢失了输入图像的几乎所有信息。即使后面的层能够获得充分的训练,这些层也会因为没有充分的信息而很难识别出输入的图像。因此,在第一层不进行学习的尝试是不可能的。如果我们接着去训练深度神经网络,我们需要弄清楚如何解决梯度消失问题。

注:

1. 绘制的数据图形由程序 generate_gradient.py (https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/fig/generate_gradient.py)生成。同样的程序也用来生成本节后面引用的结果。


2. 参见 Gradient flow in recurrent nets: the difficulty of learning long-term dependencies (http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.24.7321),作者为 Sepp Hochreiter,Yoshua Bengio, Paolo Frasconi, 和 Jürgen Schmidhuber (2001)。这篇论文研究了递归神经网络,但是其本质现象和我们正在研究的前馈网络中的是一样的。还可看看 Sepp Hochreiter的早期的学位论文 Untersuchungen zu dynamischen neuronalen Netzen (http://www.idsia.ch/~juergen/SeppHochreiter1991ThesisAdvisorSchmidhuber.pdf, 1991,德语。

本文来源于哈工大SCIR

原文链接点击即可跳转


哈尔滨工业大学社会计算与信息检索研究中心

工程理论书籍神经网络深度学习梯度哈工大
1