MNIST数据集可通过链接http://yann.lecun.com/exdb/mnist/下载,它包含如下四个部分:
·训练集图像:train-images-idx3-ubyte.gz(9.9 MB,解压后47 MB,包含60000个样本)。
·训练集类标:train-labels-idx1-ubyte.gz(29 KB,解压后60 KB,包含60000个类标)。
·测试集图像:t10k-images-idx3-ubyte.gz(1.6 MB,解压后7.8 MB,包含10000个样本)。
·测试集类标:t10k-labels-idx1-ubyte.gz(5 KB,解压后10 KB,包含10000个类标)。
MNIST数据集基于美国国家标准与技术研究院(National Institute of Standards and Technology,NIST)的两个数据集构建而成。训练集中包含250个人的手写数字,其中50%的是高中生,另外50%来自人口调查局。测试集中的数字也是按照相同比例由高中生和人口调查局所抽选人员手写完成。
下载文件后,建议在UNIX/Linux的命令行终端窗口中,在MNIST文件所在目录,按如下命令使用gzip快速解压所下载文件:
如果读者使用的是Windows操作系统,则可以根据个人喜好选择合适的解压工具。数据集中的图像以字节形式存储,接下来,我们将其读入NumPy数组以训练和测试多层感知器模型:
load_minist函数返回两个数组,第一个为n×m维的NumPy数组(存储图像),其中n为样本数量,m为特征数量。训练数据集和测试数据集分别包含60000和10000个样本。MNIST数据集中的图像均为28×28个像素,每个像素用灰度强度值表示。在此,我们将28×28像素展开为1维行向量,并用此行向量来表示图像数组(每行或者说每个图像包含784个特征)。load_mnist函数返回的第二个数组(类标)包含对应的目标变量,即手写数字对应的类标(整数0~9)。
乍看起来,我们读取图像的方法好像有些奇怪:
为了解这两行代码是如何工作的,我们看一下MNIST网站上关于此数据集的介绍:
使用前面两行代码,在使用fromfile方法将后续字节读入NumPy数组之前,我们首先从文件缓冲区读入数据集的幻数,它是对文件协议的描述,同时读入的还有条目的数量。传递给struct.unpack函数中fmt参数的实参值:>II,此实参值包含两部分内容:
·>:这是代表大端字节序(定义多字节在计算机中的存储顺序),如果读者不熟悉大端字节序和小端字节序,可以参考维基百科中关于字节序的描述(https://en.wikipedia.org/wiki/Endianness)。
·I:代表这是一个无符号整数。
执行下列代码,我们将从解压后MNIST数据集所在目录mnist下读取60000个训练实例和10000个测试样本:
为了解MNIST数据集中图像的样子,我们通过将特征矩阵中的784像素向量还原为28×28图像,并使用matplotlib中的imshow函数将0~9数字的示例进行可视化展示:
现在我们可以看到,按照2×5方式排列的子图中显示了单个数字的图像:
此外,我们再绘制一下相同数字的多个示例,来看一下这些手写样本之间到底有多大差异:
执行上述代码后,可以看到数字7的前25个不同变体。
我们也可以选择将MNIST图像数据及其对应类标存储为CSV格式的文件,以方便不支持其原始特殊字节格式的程序使用。不过,我们应知道,CSV格式的文件会占用更多的存储空间,具体大小如下:
·train_img.csv:109.5 MB
·train_labels.csv:120 KB
·test_img.csv:18.3 MB
·test_labels:20 KB
在将MNIST数据加载到NumPy数组中后,我们可以在Python中执行如下代码,即可将数据存储为CSV格式文件:
对于已经保存过的CSV格式文件,我们可以使用NumPy的genfromtxt函数对其进行加载:
不过,加载CSV格式的MNIST数据需要更长的时间,因此建议读者尽可能使用原始的数据格式。