Neural Networks and Deep Learning读书笔记--神经网络应用:手写数字识别

神经网络应用于MNIST数据手写数字识别

加载MNIST数据,用下面所描述的一小段辅助程序mnist_loader.py来完成。用于存储MNIST数据的数据结构在文档字符串中进行了描述-它是简单的内容、元组和Numpy ndarray对象的列表(如果您不熟悉ndarray,可以将它们看作向量):

"""
mnist_loader
\~~~~~~~~~~~~

A library to load the MNIST image data.  For details of the data
structures that are returned, see the doc strings for ``load_data``
and ``load_data_wrapper``.  In practice, ``load_data_wrapper`` is the
function usually called by our neural network code.
"""

#### Libraries
# Standard library
import cPickle
import gzip

# Third-party libraries
import numpy as np

def load_data():
    """Return the MNIST data as a tuple containing the training data,
    the validation data, and the test data.

    The ``training_data`` is returned as a tuple with two entries.
    The first entry contains the actual training images.  This is a
    numpy ndarray with 50,000 entries.  Each entry is, in turn, a
    numpy ndarray with 784 values, representing the 28 * 28 = 784
    pixels in a single MNIST image.

    The second entry in the ``training_data`` tuple is a numpy ndarray
    containing 50,000 entries.  Those entries are just the digit
    values (0...9) for the corresponding images contained in the first
    entry of the tuple.

    The ``validation_data`` and ``test_data`` are similar, except
    each contains only 10,000 images.

    This is a nice data format, but for use in neural networks it's
    helpful to modify the format of the ``training_data`` a little.
    That's done in the wrapper function ``load_data_wrapper()``, see
    below.
    """
    f = gzip.open('../data/mnist.pkl.gz', 'rb')
    training_data, validation_data, test_data = cPickle.load(f)
    f.close()
    return (training_data, validation_data, test_data)

def load_data_wrapper():
    """Return a tuple containing ``(training_data, validation_data,
    test_data)``. Based on ``load_data``, but the format is more
    convenient for use in our implementation of neural networks.

    In particular, ``training_data`` is a list containing 50,000
    2-tuples ``(x, y)``.  ``x`` is a 784-dimensional numpy.ndarray
    containing the input image.  ``y`` is a 10-dimensional
    numpy.ndarray representing the unit vector corresponding to the
    correct digit for ``x``.

    ``validation_data`` and ``test_data`` are lists containing 10,000
    2-tuples ``(x, y)``.  In each case, ``x`` is a 784-dimensional
    numpy.ndarry containing the input image, and ``y`` is the
    corresponding classification, i.e., the digit values (integers)
    corresponding to ``x``.

    Obviously, this means we're using slightly different formats for
    the training data and the validation / test data.  These formats
    turn out to be the most convenient for use in our neural network
    code."""
    tr_d, va_d, te_d = load_data()
    training_inputs = [np.reshape(x, (784, 1)) for x in tr_d[0]]
    training_results = [vectorized_result(y) for y in tr_d[1]]
    training_data = zip(training_inputs, training_results)
    validation_inputs = [np.reshape(x, (784, 1)) for x in va_d[0]]
    validation_data = zip(validation_inputs, va_d[1])
    test_inputs = [np.reshape(x, (784, 1)) for x in te_d[0]]
    test_data = zip(test_inputs, te_d[1])
    return (training_data, validation_data, test_data)

def vectorized_result(j):
    """Return a 10-dimensional unit vector with a 1.0 in the jth
    position and zeroes elsewhere.  This is used to convert a digit
    (0...9) into a corresponding desired output from the neural
    network."""
    e = np.zeros((10, 1))
    e[j] = 1.0
    return e

在一个Python shell中执行下面的命令:

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

加载完MNIST数据之后,设置一个有30个隐层神经元的Network。导入如上所列的名字为network的Python程序:

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

使用随机梯度下降来从MNIST的training_data学习超过30次迭代,迷你块大小为10,学习率η=3.0:

>>> net.SGD(training_data, 30, 10, 3.0, test_data=test_data)
你可以通过减少迭代次数,减少隐层神经元次数或仅使用部分训练数据来提高速度。
一旦我们已经训练一个网络,它能在几乎任何的计算平台上快速的运行。例如,一旦我们对于一个网络学会了一组好的权重集和偏置集,它能很容易的移植到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

经过训练的网络给我们一个约95%的分类正确率,在峰值时为95.42%(“Epoch 28”)!

运行代码得到的结果不一定和我的完全一样,因为我们使用了(不同的)随机权重和偏置来初始化我们的网络。我采用了三次运行中的最优结果作为本章的结果。+

重新运行上面的实验,将隐层神经元数目改到100:

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

结果提升至96.59%。

读者的反馈表明本实验在结果上有相当多的变化,而且一些训练运行给出的结果相当糟糕。使用第三章所介绍的技术将对于我们的网络在不同的训练执行上大大减少性能变化。

为了区别于通过学习算法所学到的参数(权重和偏置),迭代次数,mini-batch大小和学习率在神经网络中被称为超参数。为了获得这些正确率,需要对超参进行选择,选择了较差的超参数会得到较差的结果。假设,例如我们选定学习率为η=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
从之前的实验中知道正确的做法是减小学习率。但是如果第一次遇到这样的问题,然而没有太多的输出来指导我们怎么做。我们可能不仅关心学习率,还要关心我们的神经网络中的其它每一个部分。我们可能想知道是否选择了让网络很难学习的初始化的权重和偏置?或者可能我们没有足够的训练数据来获得有意义的学习?或者我们没有进行足够的迭代次数?或者可能对于这种神经网络的结构,学习识别手写数字是不可能的?可能学习率太低?或者可能学习率太高?当你第一次遇到某问题,你通常抱有不了把握。
从这得到的教训是调试一个神经网络是一个琐碎的工作,就像日常的编程一样,它是一门艺术。你需要学习调试的艺术来获得神经网络更好的结果。更普通的是,我们需要启发式方法来选择好的超参数和好的结构。所有关于这些的内容,我们都会在本书中进行讨论,包括我之前是怎么样选择超参数的。

程序得到了很好的结果。这意味着什么?和什么比较很好?和一些简单的(非神经网络)baseline相比是非常有意义的,可以来理解什么样意味着表现好。当然,所有基准中最简单的是去随机的猜测数字,准确率大约是10%,我们做的比这好太多。

一个小的微不足道的baseline怎么样?让我们尝试一个极其简单的想法:我们来看看图片是如何的黑暗。例如一个的图片显然比一个的图片更黑,只是因为像下面示例中更多的像素点被涂黑:

Neural Networks and Deep Learning读书笔记--神经网络应用:手写数字识别_第1张图片

这表明使用训练数据来对每个数字计算平均暗度。计算一个新的图像的暗度是多少,然后再猜测它最近哪个数字的平均暗度。这是相比于随机猜测的一个大的提升,在10,000个测试图像中识别正确2,225个,也就是22.25%的准确率。找到能够准确率达到20%到50%范围的想法并不困难。如果你努力一点,你能达到50%以上。

使用已有的机器学习算法能帮助你达到更高的准确率。支持向量机(support vector machine,SVM),使用一个叫做scikit-learn的Python库,它提供一个被称为LIBSVM的基于C的快速SVM库的简单的Python接口。

如果我们用默认的设置来运行scikit-learn的SVM分类器,那么它会在10,000个测试图像中正确识别 9,435 个。这是相比于我们的朴素的基于图片暗度的分类方法有着巨大的提升。实际上SVM与我们的神经网络表现接近,只差了一点。在后面的章节中,我们将介绍新的技术来提升我们的神经网络使得它比SVM表现的好更多。

10,000个测试图像中正确识别 9,435 个的结果,是在scikit-learn中对于SVM的默认设置下得到的。SVM有大量的可调参数,而且可以搜索到能够取得更高准确率的参数。(Mueller对SVM的一些参数进行优化,取得98.5%的准确率。)换句话说,一个精心调参后的SVM仅仅对一个数字错误识别了70次。这个结果相当不错了!神经网络可以做得更好吗?
"""
mnist_svm
~~~~~~~~~
A classifier program for recognizing handwritten digits from the MNIST
data set, using an SVM classifier."""

#### Libraries
# My libraries
import mnist_loader 

# Third-party libraries
from sklearn import svm

def svm_baseline():
    training_data, validation_data, test_data = mnist_loader.load_data()
    # train
    clf = svm.SVC()
    clf.fit(training_data[0], training_data[1])
    # test
    predictions = [int(a) for a in clf.predict(test_data[0])]
    num_correct = sum(int(a == y) for a, y in zip(predictions, test_data[1]))
    print "Baseline classifier using an SVM."
    print "%s of %s values correct." % (num_correct, len(test_data[1]))

if __name__ == "__main__":
    svm_baseline()

实际上,神经网络可以做的更好。目前,解决MNIST数字识别问题上,一个精心设计的神经网络能够比其它任何技术(包括SVM)取得更好的结果。当前(2013年)的最高纪录是10,000个中正确识别了9,979个。这个纪录是由Li Wan,Matthew Zeiler,Sixin Zhang,Yann LeCun和Rob Fergus创造的。这个性能表现已经与人类的水平接近,甚至更好,因为相当多的MNIST图像对于人类来说是很难有信心识别的,例如,是很难进行分类的!:
Neural Networks and Deep Learning读书笔记--神经网络应用:手写数字识别_第2张图片
值得注意的是,在拥有像这样图像的MNIST数据集中,神经网络能够对于个测试图像除了个外都能正确分类。通常,我们认为像解决识别MNIST数字的复杂问题的程序需要一个复杂的算法。但尽管在Wan等人的论文中提及的神经网络和我们本章中所见到的算法有一些变化,但是也相当简单。所有复杂的事情都是可以从训练数据中自动学习的。在某种意义上,我们的结果和那些复杂论文中的结果表明了,在一些问题上:复杂算法<= 简单学习算法 + 好的训练数据

你可能感兴趣的:(Neural,Networks,and,Deep,Learning读书,神经网络,机器学习)