尝试创建只有两层的神经网络,一个784个神经元的输入层和一个10个神经元的输出层,没有隐含层。用随机梯度下降法来训练这个网络。你能取得多高的分类精度?

早些时候,我跳过了MNIST数据时如何被加载的细节。它相当的简单。为了完整性,这是代码。被用于存储MNIST数据的数据结构在文档注释中被说明。这是简单明了的事情,由Numpy的ndarray对象构成的元组和列表(如果你不熟悉ndarrays,将它们认为向量):

""" 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 cPickleimport 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 numpyndarray with 784 values, representing the 28 *28 = 784 pixelsin 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 modifythe 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.ndarrycontaining 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

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

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

《神经网络和深度学习》系列文章七:实现我们的神经网络来分类数字(下)
这表明使用训练数据来对每个数字0,1,2,…,9计算平均暗度。当面对一个新的图像,我们计算这个图像的暗度是多少,然后再猜测它最近哪个数字的平均暗度。这是一个很简单的程序,很容易编写,因此我不明确的写出代码。如果你感兴趣,它在GitHub仓库。但是这是相比于随机猜测的一个大的提升,在10,000个测试图像中识别正确2,225个,也就是22.25%的准确率。 找到能够准确率达到20%到50%范围的想法并不困难。如果你努力一点,你能达到50%以上。但是使用已有的机器学习算法能帮助你达到更高的准确率。让我们尝试使用最出名的机器学习算法之一,支持向量机(support vector machine,SVM)。如果你不熟悉SVM,不用担心,我们不需要了解SVM具体是怎么工作的。我们而是使用一个叫做scikit-learn的Python库,它提供一个被称为LIBSVM的基于C的快速SVM库的简单的Python接口。 如果我们用默认的设置来运行scikit-learn的SVM分类器,那么它会在10,000测试图像中正确识别9,435。(这里的代码是可用的。)这是相比于我们的朴素的基于图片暗度的分类方法有着巨大的提升。实际上,这意味着SVM与我们的神经网络表现接近,只差了一点。在后面的章节中,我们将介绍新的技术来提升我们的神经网络使得它比SVM表现的好更多。

然而这并不是故事的结尾。在scikit-learn中对于SVM的默认设置的结果是10,000中的9,435。SVM有大量的可调参数,而且可以搜索到能够取得更高准确率的参数。我不会做这个探究,但是如果你想了解更多的话,请你留意Andreas Mueller的这篇博客。Mueller对SVM的一些参数进行优化,取得98.5%的准确率。换句话说,一个精心调参后的SVM仅仅对一个数字错误识别了70次。这个结果相当不错了!神经网络可以做得更好吗?

实际上,神经网络可以做的更好。目前,解决MNIST数字识别问题上,一个精心设计的神经网络能够比其它任何技术(包括SVM)取得更好的结果。当前(2013年)的最高纪录是10,000个中正确识别了9,979个。这个纪录是由Li Wan,Matthew Zeiler,Sixin Zhang,Yann LeCun和Rob Fergus创造的。我们在本书的后面部分中会看到大多数他们所采用的技术。这个性能表现已经与人类的水平接近,甚至更好,因为相当多的MNIST图像对于人类来说是很难有信心识别的,例如:

《神经网络和深度学习》系列文章七:实现我们的神经网络来分类数字(下)
我相信你会同意这些是很难进行分类的!值得注意的是,在拥有像这样图像的MNIST数据集中,神经网络能够对于10,000个测试图像除了21个外都能正确分类。通常,我们认为像解决识别MNIST数字的复杂问题的程序需要一个复杂的算法。但尽管在Wan等人的论文中提及的神经网络和我们本章中所见到的算法有一些变化,但是也相当简单。所有复杂的事情都是可以从训练数据中自动学习的。在某种意义上,我们的结果和那些复杂论文中的结果表明了,在一些问题上:

复杂算法 ≤ 简单学习算法 + 好的训练数据

 

下一节我们将介绍“关于深度学习”,敬请关注!

 


    • “哈工大SCIR”公众号

    • 编辑部:郭江,李家琦,徐俊,李忠阳,俞霖霖

    • 本期编辑:俞霖霖