【第六章】简单网络实现手写数字分类-编程实现-运行测试

这个程序对手写数字的识别效果如何呢?好吧,让我们首先加载 MNIST 数据集。我将使用一个小辅助程序 mnist_loader.py 来完成这个任务,辅助程序稍后会进行描述。我们在 Python shell 中执行以下命令:

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

当然,这也可以在一个单独的 Python 程序中完成,但如果您正在跟随教程,最简单的方法可能是在 Python shell 中完成。

加载 MNIST 数据后,我们将设置一个具有 30 个隐藏神经元的网络。在这之前,我们需要导入上面列出的名为 network 的 Python 程序。

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

最后,我们将使用随机梯度下降算法在 MNIST 的训练数据上进行学习,迭代 30 个周期,每个周期使用小批量大小为 10,学习率为 η=3.0。

net.SGD(training_data, 30, 10, 3.0, test_data=test_data)

请注意,如果您在阅读过程中运行代码,执行可能需要一些时间 - 对于一台典型的机器(截至2015年),运行可能需要几分钟。我建议您启动代码运行,继续阅读,并定期检查代码的输出。如果您赶时间,可以通过减少周期数、减少隐藏神经元数量或仅使用部分训练数据来加快速度。请注意,生产代码会快得多:这些 Python 脚本旨在帮助您理解神经网络的工作原理,而不是成为高性能代码!当然,一旦我们训练了一个网络,它就可以在几乎任何计算平台上非常快速地运行。例如,一旦我们学习了一个网络的良好的权重和偏置集合,它可以轻松地移植到在 Web 浏览器中运行的 Javascript,或者作为移动设备上的本地应用程序运行。无论如何,这里是神经网络的一个训练运行的输出的部分。显示了每个训练周期后神经网络正确识别的测试图像数量。正如您所看到的,仅仅经过一个周期,这个数字已经达到了10,000个中的9,129个,并且这个数字继续增长。

Epoch 0: 9129 / 10000
Epoch 1: 9295 / 10000
Epoch 2: 9348 / 10000
...
Epoch 27: 9528 / 10000
Epoch 28: 9542 / 10000
Epoch 29: 9534 / 10000

也就是说,经过训练的网络在其峰值(“Epoch 28”)达到了约 95% 的分类率 - 95.42%!作为第一次尝试,这是相当令人鼓舞的。然而,我应该警告您,如果您运行代码,您的结果不一定会和我的完全相同,因为我们将使用(不同的)随机权重和偏置来初始化我们的网络。为了在本章中生成结果,我进行了三次运行中的最佳结果的选择。

让我们重新运行上面的实验,将隐藏神经元数量改为100个。和之前一样,如果您在阅读过程中运行代码,请注意它执行起来需要相当长的时间(在我的计算机上,每个训练周期大约需要几十秒),所以在代码执行时继续阅读是明智的。

>>> net = network.Network([784, 100, 10])
>>> net.SGD(training_data, 30, 10, 3.0, test_data=test_data)

确实,这将结果提高到了96.59%。至少在这种情况下,使用更多的隐藏神经元有助于我们获得更好的结果。当然,为了获得这些准确度,我必须对训练的周期数、小批量大小和学习率 η 做出特定选择。正如我上面提到的,这些被称为我们神经网络的超参数,以区别于我们学习算法学习到的参数(权重和偏置)。如果我们选择超参数不当,就会得到糟糕的结果。例如,假设我们选择的学习率是 η=0.001。

>>> net = network.Network([784, 100, 10])
>>> net.SGD(training_data, 30, 10, 0.001, test_data=test_data)

结果远没有那么令人鼓舞

Epoch 0: 1139 / 10000
Epoch 1: 1136 / 10000
Epoch 2: 1135 / 10000
...
Epoch 27: 2101 / 10000
Epoch 28: 2123 / 10000
Epoch 29: 2142 / 10000

然而,您可以看到随着时间的推移,网络的性能正在逐渐提高。这表明需要增加学习率,比如说将 η 增加到 0.01。如果我们这样做,就会得到更好的结果,这表明需要再次增加学习率。(如果进行了改变而使事情变得更好,尝试做更多!)如果我们多次这样做,最终我们将得到一个类似于 η=1.0(可能微调到 3.0)的学习率,这接近我们之前的实验。因此,即使我们最初选择了不良的超参数,至少我们得到了足够的信息来帮助我们改进超参数的选择。
总的来说,调试神经网络可能具有挑战性。特别是当初始超参数的选择产生的结果不比随机噪声好时。假设我们尝试之前成功的30个隐藏神经元网络架构,但将学习率更改为 η=100.0:

>>> net = network.Network([784, 30, 10])
>>> net.SGD(training_data, 30, 10, 100.0, test_data=test_data)

在这一点上,我们实际上已经走得太远了,学习率太高:

Epoch 0: 1009 / 10000
Epoch 1: 1009 / 10000
Epoch 2: 1009 / 10000
Epoch 3: 1009 / 10000
...
Epoch 27: 982 / 10000
Epoch 28: 982 / 10000
Epoch 29: 982 / 10000

现在想象一下,我们是第一次遇到这个问题。当然,我们从之前的实验中知道正确的做法是降低学习率。但是,如果我们是第一次遇到这个问题,那么输出中几乎没有任何东西可以指导我们要做什么。我们可能不仅担心学习率,还担心神经网络的每个其他方面。我们可能会想知道我们是否以一种使网络难以学习的方式初始化了权重和偏置?或者也许我们没有足够的训练数据来获得有意义的学习?也许我们还没有运行足够的周期?或者也许对于具有这种架构的神经网络来说,学习识别手写数字是不可能的?也许学习率太低了?或者,也许,学习率太高了?当你第一次遇到一个问题时,你并不总是确定的。

从中得出的教训是,调试神经网络并不是一件微不足道的事情,就像普通编程一样,这需要一种艺术。为了从神经网络中获得良好的结果,您需要学习调试的艺术。更一般地说,我们需要开发出选择好的超参数和好的架构的启发式方法。我们将在本书中详细讨论所有这些问题,包括我如何选择上述超参数。

上一篇:vue 重新渲染dom


下一篇:su: authentication failure 解决方法-解决办法: