6.2 卷积神经网络在实际中的应用

我们现在已经明白了卷积神经网络后面的核心思想。让我们通过实现一些卷积网络,并将它们应用于 MNIST 数字分类问题,来看看它们如何在实践中工作。我们将使用的程序是network3.py,它是前面章节开发的 network.pynetwork2.py 的强化版本7。如果你想跟着学,代码可以从 GitHub 上下载。注意我们将在下一节中解决 network3.py 需要的代码。在这一节中,我们将把 network3.py 作为库来构建卷积网络。

程序 network.pynetwork2.py 是用 Python 和矩阵库 Numpy 实现的。这些程序从最初的原理工作,并致力于反向传播、随即梯度下降等细节。但是现在我们已经理解了这些细节,对于 network3.py 我们打算使用一个称为 Theano 的机器学习库8。使用 Theano 使得实现针对卷积神经网络的反向传播很容易,因为它自动计算涉及到的映射。Theano 也比我们前面代码更快(那些代码是为了容易理解,不是为了运行速度),这使它可实际用于训练更复杂的网络。特别地,Theano 的一个非常好的特性是它能够运行于 CPU 或者,如果可以,GPU 上。运行于 GPU上可以提供显著的增速,而且,有助于实际用于更复杂的网络。

如果你想要跟着学,你需要可运行在你的系统上的 Theano。按照项目主页上的说明来安装 Theano。接下来的例子使用 Theano 0.69 运行过。有些在没有 GPU 支持的 Mac OS X Yosemite 运行过。有些在有 NVIDIAGPU 支持的 Ubuntu 14.04 中运行过。有些实验在两个系统中都运行过。为了让 networks3.py 运行,你需要(适当地)把 networks3.py 源码中的 GPU 标志设置为 True 或者 False。此外,为了让 Theano 运行于 GPU 上,你可能会发现这份指导说明有帮助。互联网上也有教程,很容易用 Google 搜索到,同样能帮助你让 Theano 工作。如果你手上的系统没有可用的 GPU,那么你可能想要看下 Amazon Web Services EC2 G2实例类型。注意即使有 GPU 支持,代码仍然需要一些时间执行。许多实验要花费从几分钟到几个小时的时间来运行。在 CPU 上可能需要花费数天时间来运行最复杂的实验。正如前面章节里说的,我建议让程序运行着,同时继续阅读,偶尔回来检查下代码的输出。如果你用的是 CPU,你可能需要对更复杂的实验减少训练迭代期的数量,或者整个忽略它们。

为了取得一个基线,我们将从一个浅层架构开始,它仅仅使用一个隐藏层,包含 个隐藏神经元。我们会训练 迭代期,使用学习速率为:,小批量数据 大小为 ,没有规范化。这样运行10

>>> import network3
>>> from network3 import Network
>>> from network3 import ConvPoolLayer, FullyConnectedLayer, SoftmaxLayer
>>> training_data, validation_data, test_data = network3.load_data_shared()
>>> mini_batch_size = 10
>>> net = Network([
        FullyConnectedLayer(n_in=784, n_out=100),
        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1, 
            validation_data, test_data)

我获得的一个最好的分类准确率是 %。这是 test_data 上的分类准确率,在这个取值的训练迭代期的地方,我们在validation_data上得到了最好的分类准确率。使用验证数据来决定在何时对测试准确率估值有助于避免测试数据上的过度拟合(见前面关于验证数据使用的\hyperref[validation_explanation]{讨论})。我们将在下面遵循这个习惯。你的结构可能稍有不同,因为网络的权重和偏置是随机初始化的11

这个 % 的准确率接近于\hyperref[chap3_98_04_percent]{第三章}中获得的 % 的准确率,使用一个相似的网络架构和学习超参数。特别地,两个例子都使用一个浅层网络,具有单个包含有 个隐藏神经元的隐藏层。两者都训练 个迭代期,小批量数据大小为 ,学习速率 为

然而,在之前的网络中有两个不同的地方。首先,我们\hyperref[sec:overfitting_and_regularization]{规范化}了之前的网络,来帮助降低过度拟合带来的影响。规范化当前的网络确实可以提高准确率,但是得到的只是很小,所以我们将推迟到后面再来惦记规范化。第二,虽然之前的网络中的最终层使用了S型激活值和交叉熵代价函数,当前网络使用一个柔性最大值的最终层,以及对数似然代价函数。正如第三章中\hyperref[subsec:softmax]{解释}的,这不是一个大的改变。我没有为了任何特别深刻的原因来做出这样的改变 —— 主要是因为柔性最大值和对数似然代价在现代的图像分类网络中很常见。

我们能用一个更深的网络架构来做得比这些结果更好吗?

让我们从在网络开始位置的右边插入一个卷积层开始。我们将使用 局部感受野,跨距为 个特征映射。我们也会插入一个最大值混合层,它用一个 的混合窗口来合并特征。所以总体的网络架构看起来很像上一节讨论的架构,但是有一个额外的全连接层:

simple_conv

在这个架构中,我们可以把卷积和混合层看作是在学习输入训练图像中的局部感受野,而后面的全连接层则在一个更抽象的层次学习,从整个图像整合全局信息。这是一种常见的卷积神经网络模式。

让我们训练这样的一个网络,看看它表现怎样12

>>> net = Network([
        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 
                      filter_shape=(20, 1, 5, 5), 
                      poolsize=(2, 2)),
        FullyConnectedLayer(n_in=20*12*12, n_out=100),
        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1, 
            validation_data, test_data)

我们得到了 % 的准确率,这是相当大的改善,超过了我们以前结构的任何一个。事实上,我们已经减少了超过三分之一的错误率,这是一个很大的进步。

在指定网络结构时,我把卷积和混合层作为一个单一层对待。不管他们是被视为分开的层还是作为一个单一的层在一定程度上是一个个人喜好的问题。network3.py 视他们为单个层,因为它使得 network3.py 的代码更紧凑。然而,如果需要的话,很容易修改 network3.py 使得这些层可以单独指定。

练习

  • 如果你删除了全连接层,只使用卷积--混合层和柔性最大值层,你得到了什么样的分类准确率?全连接层的加入有帮助吗?

我们能改进 % 的分类准确率吗?

让我们试着插入第二个卷积--混合层。把它插在已有的卷积--混合层和全连接隐藏层之间。我们再次使用一个 局部感受野,混合 的区域。让我们看看用前面相似的超参数训练会发生什么:

>>> net = Network([
        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 
                      filter_shape=(20, 1, 5, 5), 
                      poolsize=(2, 2)),
        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 
                      filter_shape=(40, 20, 5, 5), 
                      poolsize=(2, 2)),
        FullyConnectedLayer(n_in=40*4*4, n_out=100),
        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1, 
            validation_data, test_data)

再一次,我们得到了改善:现在我们达到了 % 的分类准确率。

在这里有两个很自然想到的问题。第一个问题是:应用第二个卷积--混合层意味着什么?实际上,你可以认为第二个卷积--混合层输入 幅“图像”,其“像素”代表原始输入图像中特定的局部特征的存在(或不存在)。所以你可以认为这一层输入原始输入图像的一个版本。这个版本是经过抽象和凝缩过的,但是仍然有大量的空间结构,所以使用第二个卷积--混合层是有意义的。

这是一个令然满意的观点,但是引出了第二个问题。从前面层的输出涉及 个独立的特征映射,所以对第二个卷积--混合层有 个输入。就好像我们有 幅单独的图像输入给卷积--混合层,而不是第一个卷积--混合层情况下的单幅图像。第二个卷积--混合层里的神经元应该如何响应这些多重的输入图像呢?实际上,我们将允许这一层中的每个神经元从它的局部感受野中的所有 输入神经元学习。更非正式的:第二个卷积--混合层中的特征检测器可访问所有前面层的特征,但仅在其特定的局部感受野中13

问题

  • 使用 tanh 激活函数 在本书前面我已经几次提起过\hyperref[subsec:other_models_of_artificial_neuron]{tanh 函数}可以是一个比 S型函数更好的激活函数。我们还没有实际采用过这些建议,因为我们已经用 S 型取得了大量进展。但现在让我们试试一些用 tanh 作为我们激活函数的实验。试着训练卷积和全连接层中具有 tanh 激活值的网络14。开始时使用 S 型网络中使用的相同的超参数,但是训练 个迭代期,而不是 个。你的网络表现得怎么样?如果你继续训练到 个迭代期会怎样?试着将tanh和 S型网络的每个迭代期的验证准确率都绘制出来,都绘制到 个迭代期。如果你的结果和我的相似,你会发现 tanh 网络训练得稍微快些,但是最终的准确率非常相似。你能否解释为什么 tanh 网络可以训练得更快?你能否用 S型取得一个相似的训练速度,也许通过改变学习速率,或者做些调整15?试着用五六个迭代学习超参数和网络架构,寻找 tanh 优于 S 型的方面。注意:这是一个开放式问题。就我个人而言,我并没有找到太多切换为 tanh 的优势,虽然我没全面地做过实验,也许你会找到一个方法。无论如何,我们马上会发现切换到修正线性激活函数的一个优势,所以我们不会去深入使用 tanh 函数。

使用修正线性单元: 到现在为止,我们开发的网络实际上是一篇开创性的 1998论文16中使用的众多网络中一种的变化形式,这个网络被称为 LeNet-5,并引入了 MNIST 问题。这为进一步实验并构筑理解和直观感受打下很好的基础。特别是,有很多种我们可以改变网络来改善结果的方式。

作为开始,让我们改变我们的神经元,我们使用\hyperref[sec:other_models_of_artificial_neuron]{修正线性单元}而不是 S 型激活函数。确切地说,我们将使用激活函数 。我们将训练 个迭代期,学习速率为 。我也发现用一些\hyperref[sec:overfitting_and_regularization]{L2 规范化}也有点帮助,使用规范化参数

>>> from network3 import ReLU
>>> net = Network([
        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 
                      filter_shape=(20, 1, 5, 5), 
                      poolsize=(2, 2), 
                      activation_fn=ReLU),
        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 
                      filter_shape=(40, 20, 5, 5), 
                      poolsize=(2, 2), 
                      activation_fn=ReLU),
        FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),
        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.03, 
            validation_data, test_data, lmbda=0.1)

我得到一个 99.23% 的分类准确率。它稍微超过了 S 型的结果(99.06)。然而,在我所有实验中我发现基于修正线性单元的网络,其性能始终优于基于 S 型激活函数的网络。似乎对于这个问题切换到修正线性单元确实有收益。

是什么使得修正线性激活函数好于 S 型或者 tanh 函数?目前,我们对这个问题的答案有一个很差的理解。实际上,修正线性单元只在过去几年才开始被广泛使用。最近才采用的原因是以经验为依据的:一些人经常基于直觉或者启发式的理由试着用修正线性单元17。他们在分类基准数据集时取得了很好的结果,并且其实践传播开了。在一个理想的世界中,我们有一个理论告诉我们为什么样的应用选择什么样的激活函数。但目前我们我们离这样的理想世界还有一条很长的路。如果通过选择一个更好的激活函数来取得了进一步的重大改进,我一点也不会感到惊讶,我还期待在未来的几十年里,一个强大的激活函数理论将被开发。今天,我们仍然不得不依靠单凭经验的不足的理解。

扩展训练数据: 另一种我们可能希望改进结果的方法是以算法形式扩展训练数据。扩展训练数据的一个简单的方法是将每个训练图像由一个像素来代替,无论是上一个像素,一个像素,左边一个像素,或右边一个像素。我们可以通过在 shell 提示符中运行程序 expand_mnist.py 来这样做18:\input{snippets/run_expand_mnist} % move to separate file to avoid syntax error caused by '$' in EMACS.

运行这个程序取得 幅 MNIST 训练图像并扩展为具有 幅训练图像的训练集。然后我们可以使用这些训练图像来训练我们的网络。我们将使用和上面一样的具有修正线性单元的网络。在我初始的实验中我减少了训练迭代期的数量 —— 这讲得通,因为我们在训练 倍的数据。但是实际上,扩展数据结果是相当多地减少了过度拟合的影响。所有,在做了一些实验后,我最终回到训练 个迭代期。不管怎样,让我们训练:

>>> expanded_training_data, _, _ = network3.load_data_shared(
        "../data/mnist_expanded.pkl.gz")
>>> net = Network([
        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 
                      filter_shape=(20, 1, 5, 5), 
                      poolsize=(2, 2), 
                      activation_fn=ReLU),
        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 
                      filter_shape=(40, 20, 5, 5), 
                      poolsize=(2, 2), 
                      activation_fn=ReLU),
        FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),
        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03, 
            validation_data, test_data, lmbda=0.1)

使用扩展后的训练数据我取得了一个 99.37% 的训练准确率。所以这个几乎是微不足道的改变在分类准确率上给出了一个显著的改进。事实上,正如我们\hyperref[sec:other_techniques_for_regularization]{前面所讨论的},这种以算法形式扩展数据的想法可以更进一步。提醒你一些早期讨论的结果: 在 2003 年,Simard,Steinkraus 和 Platt19 使用一个神经网络改进了他们的 MNIST 性能,达到了 %,这个网络以其它方式和我们的非常相似,使用两个卷积--混合层,跟着一个具有 个神经元的隐藏的全连接层。在他们的架构中有一些细节上的不同 —— 例如他们没有利用修正线性单元 —— 但是他们改进性能的关键是扩展训练数据。他们通过旋转,位移和扭曲 MNIST 训练图像来扩展。他们还开发了一个“弹性扭曲”的流程,一种模拟当一个人写字时手部肌肉随机振动的方式。通过组合所有这些流程,他们相当大地增加了训练数据的有效规模,而这就是他们如何达到 % 准确率的。

问题

  • 卷积层的想法是以一种横跨图像不变的方式作出反应。它看上去令人惊奇,然而,当我们做完所有输入数据的转换,网络能学习得更多。你能否解释为什么这实际上很合理?

插入一个额外的全连接层: 我们还能做得更好吗?一种可能性是使用和上面完全相同的程序,但是扩展全连接层的规模。我试过 个神经元,分别取得了 % 和 %。这很有趣,但对于前面的结果(99.37%)并不是一个令人信服的超越。

增加一个额外的全连接层怎样?让我们试着插入一个全连接层,这样我们就有两个 个隐藏神经元的全连接层:

>>> net = Network([
        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 
                      filter_shape=(20, 1, 5, 5), 
                      poolsize=(2, 2), 
                      activation_fn=ReLU),
        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 
                      filter_shape=(40, 20, 5, 5), 
                      poolsize=(2, 2), 
                      activation_fn=ReLU),
        FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),
        FullyConnectedLayer(n_in=100, n_out=100, activation_fn=ReLU),
        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03, 
            validation_data, test_data, lmbda=0.1)

这样做我取得一个 99.43% 的测试准确率。再一次,扩展后的网络并没有帮助太多。运行类似的试验,用包含 个隐藏神经元的全连接层产生 % 和 % 的结果。这是令人鼓舞的,但仍然缺乏一个真正决定性的胜利。

这里发生了什么事?扩展的,或者额外的全连接层真的对 MNIST 没帮助吗?或者说,我们的网络有能力做得更好,但我们在用错误的方式学习?例如,也许我们可以用更强有力的规范化技术来减小过度拟合的趋势。一种可能性是第三章介绍的\hyperref[sec:other_techniques_for_regularization]{弃权}技术。回想弃权的基本思想是在训练网络时随机地移除单独的激活值。这使得模型对单独依据的丢失更为强劲,因此不太可能依赖于训练数据的特质。让我们试着应用弃权到最终的全连接层:

>>> net = Network([
        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 
                      filter_shape=(20, 1, 5, 5), 
                      poolsize=(2, 2), 
                      activation_fn=ReLU),
        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 
                      filter_shape=(40, 20, 5, 5), 
                      poolsize=(2, 2), 
                      activation_fn=ReLU),
        FullyConnectedLayer(
            n_in=40*4*4, n_out=1000, activation_fn=ReLU, p_dropout=0.5),
        FullyConnectedLayer(
            n_in=1000, n_out=1000, activation_fn=ReLU, p_dropout=0.5),
        SoftmaxLayer(n_in=1000, n_out=10, p_dropout=0.5)], 
        mini_batch_size)
>>> net.SGD(expanded_training_data, 40, mini_batch_size, 0.03, 
            validation_data, test_data)

使用它,我们取得了 % 的准确率,这是一个显著的超越我们前面结果的进步,尤其是我们主要的基准,具有 个隐藏神经元的网络,其中我们达到了 %。

有两个值得注意的变化。

首先,我减少了训练迭代期的数量到 :弃权减少了过度拟合,所以我们学习得更快。

其次,全连接隐藏层有 个神经元,不是之前使用的 个。当然,在训练时弃权有效地忽略了很多神经元。所以一些扩充是可以预期的。实际上,我试过用 个隐藏神经元的实验,用 个隐藏神经元(非常略微地)取得了更好的验证性能。

使用一个组合的网络: 一个简单的进一步提高性能的方法是创建几个神经网络,然后让它们投票来决定最好的分类。例如,假设我们使用上述的方式训练了 个不同的神经网络,每个达到了接近于 % 的准确率。尽管网络都会有相似的准确率,他们很可能因为不同的随机初始化产生不同的错误。在这 个网络中进行一次投票来取得一个优于单个网络的分类,似乎是合理的。

这听上去太好了,不像是真的,但是这种组合的方式是神经网络和其它机器学习技术都惯用的伎俩。而它确实产生了更进一步的改善:我们最终得到了 99.67% 的准确率。换句话说,我们的网络组合正确分类了除了 个之外所有的 个测试图像。

剩余的测试集中的错误显示在下面。右上角的标签是按照 NMIST 数据的正确的分类,而右下角的标签是我们组合网络的输出。

ensemble_errors

值得去仔细看看这些。开头两个数字,一个 6 和一个 5,是我们的组合犯的真正的错误。然而,它们也是可以理解的错误,人类也会犯。那个 6 确实看上去更像一个 0,而那个 5看上去更像一个 3。第三幅图像,据称是一个 8,在我看来实际上更像一个 9。所以这里我站在网络组合这边:我认为它比最初画出这些数字的人做得更好。另一方面,第四幅图像,那个 6,确实看上去是我们网络分类错了。

如此等等。在大多数情况下我们网络的选择看上去至少是合理的,而在一些情况下我们比最初写这些数字的人做得更好。总体而言,我们的网络提供卓越的性能,特别是当你认为它们正确分类的 9,967 张图片,这是没有显示。在这种背景下,这里的不清晰的错误似乎是可以理解的。甚至一个细心的人也会偶尔犯错误。因此我认为只有一个非常细心和有条理的人才会做得更好。我们的网络正在接近人类的性能。

为什么我们只对全连接层应用弃权: 如果你仔细看上面的代码,你会注意到我们只在网络的全链接部分应用了弃权,而不是卷积层。原则上我们可以在卷积层上应用一个类似的程序。但是,实际上那没必要:卷积层有相当大的先天的对于过度拟合的抵抗。原因是共享权重意味着卷积滤波器被强制从整个图像中学习。这使他们不太可能去选择在训练数据中的局部特质。于是就很少有必要来应用其它规范化,例如弃权。

进一步: 仍然有可能在 MNIST 上提高性能。Rodrigo Benenson 汇编了一份信息汇总页面,显示这几年的进展,提供了论文的链接。这些论文许多使用深度卷积网络,与我们已经使用的网络相似。如果你深入挖掘这些论文你会发现许多有趣的技术,并且你可能乐于实现其中一些。如果你这样做,明智的做法是从一个简单的能被快速训练的网络开始实现,这将有助于你更快地了解正在发生的事。

我不会概述这份近期成果的大部分内容。但是我忍不住有一个例外。它是一篇 Cireșan、Meier、 Gambardella、 和 Schmidhuber 所著的 2010 年论文20。我喜欢这篇论文的地方是它是如此简单。其中的网络是一个许多层的神经网络,仅使用全连接层(没有卷积层)。他们最成功的网络有分别包含有 神经元的隐藏层。他们使用和 Simard等人类似的想法来扩展他们的训练数据。除了这些,他们没有使用其它的技巧,包括没有卷积层:这是一个清晰的,简单的网络,这样的网络如果有足够的耐心,可以在 80 年代将其训练(如果 MNIST 数据集已经有了),假设那时有足够的计算能力。他们达到了一个99.65% 的分类准确率,或多或少和我们一样。其关键是使用一个非常大,非常深的网络,并且使用一块 GPU 来加速训练。这让他们训练了很多个迭代期。他们也利用了他们的长训练时间来逐渐地将学习速率从 减小到 。试着用一个相似的架构来匹配他们的结果是个很有趣的联系。

为什么我们能够训练? 我们在\hyperref[ch:WhyHardToTrain]{上一章}看到了深的、多层的神经网络中的基本障碍。特别是,我们看到的梯度往往是相当不稳定的:当我们从输出层移动到前面层,梯度趋于消失(消失的梯度问题)或爆炸(爆炸的梯度问题)。由于梯度是我们用来训练的动机,这会导致问题。

我们如何避免这些结果?

当然,答案是我们没有回避这些结果。相反,我们已经做了一些事情,帮助我们继续进行。特别地:(1)使用卷积层极大地减少了这些层中的参数的数目,使学习的问题更容易;(2) 使用更多强有力的规范化技术(尤其是弃权和卷积层)来减少过度拟合,否则它在更复杂的网络中是更多的问题;(3)使用修正线性单元而不是 S 型神经元,来加速训练 —— 依据经验通常是 -- 倍;(4)使用 GPU 并愿意长时间的训练。特别是,在我们最后的实验中,我们训练了 个迭代期,使用一个 倍于未经处理的 MNIST 训练数据的数据集。在本书前面,我们主要用原始训练数据训练了 个迭代期。结合因素(3)和 (4),仿佛我们训练了比以前 倍长的时间。

你的反应可能是:“就这样?这就是我们为了训练深度网络所要做的全部事情?为什么要小题大做?”

当然,我们也已经使用了其它主意:利用充分大的数据集(为了避免过度拟合);使用正确的代价函数(为了避免\hyperref[sec:the_cross-entropy_cost_function]{学习减速}); 使用\hyperref[how_to_choose_a_neural_network's_hyper-parameters]{好的权重初始 化}(也是为了避免因为神经元饱和引起的学习减速);\hyperref[sec:other_techniques_for_regularization]{以算法形式扩展训练数据}。我们在前面章节中讨论了这些和其它想法,并在本章中的大部分已经可以重用这些想法了,而不需要太多注解。

有了这样说法,这真的是一套相当简单的想法。在组合使用时简单,但功能强大。入门深度学习变得非常容易!

这些网络有多深? 把卷积--混合层算作一个层,我们最终的架构有 个隐藏层。这样的一个真的应该被称为一个深度网络吗?当然, 个隐藏层远远多于我们前面学习的浅层网络。那些网络大部分只有一个隐藏层,或者偶尔有 个隐藏层。另一方面,2015年使用最先进技术的深度网络有时候有几十个隐藏层。我偶尔听到有人采取“更比你更深”的态度,认为如果你没有跟上在隐层数目方面的攀比,那么你真的没有在做深度学习。我不赞同这样的态度,部分因为它使得深度学习的定义像是时刻就有结果的事。深度学习中实际的突破是认识到它超过浅的 层的网络是切实可行的,这样的浅层网络直到 2000 年中都占据优势。这确实是一个重大的突破,开启了更多有特殊意义的模型的探索。但除这之外,层的数目并不是主要的基本利益关系。更确切地说,使用更深层的网络是一种用来帮助实现其他目标工具 —— 例如更好的分类精确率。

一些按部就班的话: 在这一节中,我们从单个隐藏层的浅层网络顺利转换到多层卷积网络。这一切似乎很容易!我们做了一个改变,其中大部分,我们得到了改进。如果你开始尝试,我可以保证事情不会总是那么顺利。原因是,我呈现的是一个清理过的叙述,省略了许多实验 —— 包括许多失败的实验。这个清理过的叙述,希望能帮助你清楚认识基本思想。但它也有风险,传达着不完整的感觉。取得一个好的,可工作的网络会涉及到大量的试验和错误,偶尔有挫折。在实践中,你应该预计会处理相当多的实验。为了加快这一进程,你可能会发现回顾第三章关于\hyperref[sec:how_to_choose_a_neural_network's_hyper-parameters]{如何选择一个神经网络的超参数}的讨论会有帮助,或许也看一些那一小节中的进一步阅读的建议。


7. 注意 network3.py 包含了源自 Theano 库文档中关于卷积神经网络(尤其是 LeNet-5 的实现),Misha Denil 的弃权的实现,以及 Chris Olah 的概念。
8. 参见 Theano: A CPU and GPU Math Expression Compiler in Python,作者为 James Bergstra, Olivier Breuleux, Frederic Bastien, Pascal Lamblin, Ravzan Pascanu, Guillaume Desjardins, Joseph Turian, David Warde-Farley, 和 Yoshua Bengio (2010)。 Theano 也是流行的 Pylearn2Keras 神经网络库的基础。其它在本文写作时流行的神经网路库包括 CaffeTorch
9. 当我发布这一章时,Theano 的当前版本变成了 0.7。我实际上已经在 Theano 0.7 版本中重新运行过这些例子并取得了和文中非常相似的结果。
10. 本节中的实验代码可以在这个脚本中找到。注意,脚本中的代码只是简单地重复并相对于本节中的讨论。
11. 实际上,在这个实验中我其实对这个架构的网络运行了三次独立的训练。然后我从这三次运行中报告了对应于最佳验证准确率的测试准确率。利用多次运行有助于减少结果中的变动,它在比较许多个架构时是很有用的,正如我们正在做的。除非明确指出,我在下面已经遵循了这个程序。在实践中,它对于所获得的结果不会带来什么区别。
12. 这里我继续使用一个大小为 的小批量数据。正如我们\hyperref[mini_batch_size]{前面讨论过的},使用更大的小批量数据可能提高训练速度。我继续使用相同的小批量数据,主要是为了和前面章节中的实验保持一致。
13. 如果输入图像是有颜色的,这个问题会在第一层中出现。在这种情况下,对于每一个像素我们会有 3 个输入特征,对应于输入图像中的红色、绿色和蓝色通道。因此我们将允许特征检测器可访问所有颜色信息,但仅仅在一个给定的局部感受野中。
14. 注意你可以将 activation_fn=tanh 作为一个参数传递给 ConvPoolLayerFullyConnectedLayer 类。
15. 你也许可以回想 来找灵感。
16. "Gradient-based learning applied to document recognition",作者为 Yann LeCun, Léon Bottou, Yoshua Bengio, 和 Patrick Haffner (1998)。细节上有很多不同,但大体上讲,我们的网络和论文中描述的网络非常相似。
17. 一个通常的理由是 取最大极限时不会饱和,不像 S 型神经元,而这有助于修正线性单元持续学习。到目前为止,这一辩解很好,但不是一个详细的理由,更多的是一个“就这样”的故事。注意我们在\hyperref[saturation]{第二章}里讨论过饱和的问题。
18. expand_mnist.py 的代码可以从这里获取。
19. Best Practices for Convolutional Neural Networks Applied to Visual Document Analysis,作者为 Patrice Simard, Dave Steinkraus, 和 John Platt (2003)。
20. Deep, Big, Simple Neural Nets Excel on Handwritten Digit Recognition,作者为 Dan Claudiu Cireșan, Ueli Meier,Luca Maria Gambardella, 和 Jürgen Schmidhuber (2010)。

results matching ""

    No results matching ""