Python视觉识别--MNIST(一)

一、MNIST 数据集

(一)下载网址:
MNIST 数据集可在 http://yann.lecun.com/exdb/mnist/ 获取, 它包含了四个部分:

  • Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 包含 60,000 个样本)
  • Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 包含 60,000 个标签)
  • Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 包含 10,000 个样本)
  • Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 包含 10,000 个标签)

MNIST 数据集来自美国国家标准与技术研究所, National Institute of Standards and Technology (NIST). 训练集 (training set) 由来自 250 个不同人手写的数字构成, 其中 50% 是高中学生, 50% 来自人口普查局 (the Census Bureau) 的工作人员. 测试集(test set) 也是同样比例的手写数字数据.


image.png

图片是以字节的形式进行存储, 我们需要把它们读取到 NumPy array 中, 以便训练和测试算法.

(二)mnist数据集说明
MNIST数据集是一个手写体数据集,简单说就是一堆这样东西:

image.png

这个数据集由四部分组成,分别是:
image.png

也就是一个训练图片集,一个训练标签集,一个测试图片集,一个测试标签集;我们可以看出这个其实并不是普通的文本文件或是图片文件,而是一个压缩文件,下载并解压出来,我们看到的是二进制文件,其中训练图片集的内容部分如此:
image.png

这些二进制数据如何解释呢?在这里我们只针对官网的说法,对训练图片集和训练标签集进行解说,测试集是一样的道理。
针对训练标签集,官网上陈述有:
image.png

官网说法,训练集是有60000个用例的,也就是说这个文件里面包含了60000个标签内容,每一个标签的值为0到9之间的一个数;回到我们的训练标签集上,按上面说的,我们先解析每一个属性的含义,
offset代表了字节偏移量,也就是这个属性的二进制值的偏移是多少;
type代表了这个属性的值的类型;
value代表了这个属性的值是多少;
description是对这个的说明;
所以呢,这里对上面的进行一下说明,它的说法是“从第0个字节开始有一个32位的整数,它的值是0x00000801,它是一个魔数;从第4个字节开始有一个32位的整数,它的值是60000,它代表了数据集的数量;从第8个字节开始有一个unsigned byte,它的值是??,是一个标签值….”;我们现在针对我们看到的文件进行解说:
image.png

首先我们知道用sublime打开这个文件(是解压过的),是用十六进制表示的,也就是说里面的每一个数字代表了四个位,两个数字代表了一个字节;
我们首先看到偏移量为0字节处0000 0801它就是代表了魔数,它的值为00000801,这里补充说一下什么是魔数,其实它就是一个校验数,用来判断这个文件是不是MNIST里面的train-labels.idx1-ubyte文件;
接着往下看偏移量为4字节处0000 ea60,我们知道按照上面说过的这个应该是表示容量数,也就是60000,而60000的十六进制就是ea60,满足;
再看偏移量为8字节处05,它就表示我们的标签值了,也就是说第一个图片的标签值为5,后面的也是依此类推;接下来我们来看训练图片集,同样从官网上可以看到:


image.png

其解说与上面的标签文件类似,但是这里还要补充说明一下,在MNIST图片集中,所有的图片都是28×28的,也就是每个图片都有28×28个像素;
看回我们的上述图片,其表示,

  1. train-images-idx3-ubyte文件中偏移量为0字节处有一个4字节的数为0000 0803表示魔数;
  2. 接下来是0000ea60值为60000代表容量,接下来从第8个字节开始有一个4字节数,值为28也就是0000 001c,表示每个图片的行数;
  3. 从第12个字节开始有一个4字节数,值也为28,也就是0000001c表示每个图片的列数;
  4. 从第16个字节开始才是我们的像素值,用图片说话;
    而且每784个字节代表一幅图片:


    image.png

我们可以看到文件的二进制内容同我们分析的是一样的。补充说明:
在图示中我们可以看到有一个MSB first,其全称是”Most Significant Bit first”,相对称的是一个LSB first,“Least Significant Bit”; MSB first是指最高有效位优先,也就是我们的大端存储,而LSB对应小端存储;


image.png

常见问题:

  1. 错误from . import multiarray ImportError: DLL load failed: 找不到指定的模块。
    numpy版本太低
    (1) 在anaconda prompt下使用conda工具执行
    conda uninstall numpy


    image.png

    (如果还有numpy-base,也用此方法卸载)
    (2) 在anaconda prompt下使用pip工具执行
    pip intall numpy


    image.png

    本地安装:
    (base) C:\Users\Administrator>pip install numpy-1.16.2-cp37-cp37m-win_amd64.whl

(三)python代码解析二进制文件

'''
    使用python解析二进制文件
'''
import numpy as np
import struct


def loadImageSet(filename):
    binfile = open(filename, 'rb')  # 读取二进制文件
    buffers = binfile.read()

    head = struct.unpack_from('>IIII', buffers, 0)  # 取前4个整数,返回一个元组

    offset = struct.calcsize('>IIII')  # 定位到data开始的位置
    imgNum = head[1]
    width = head[2]
    height = head[3]

    bits = imgNum * width * height  # data一共有60000*28*28个像素值
    bitsString = '>' + str(bits) + 'B'  # fmt格式:'>47040000B'

    imgs = struct.unpack_from(bitsString, buffers, offset)  # 取data数据,返回一个元组

    binfile.close()
    imgs = np.reshape(imgs, [imgNum, width * height])  # reshape为[60000,784]型数组

    return imgs, head


def loadLabelSet(filename):
    binfile = open(filename, 'rb')  # 读二进制文件
    buffers = binfile.read()

    head = struct.unpack_from('>II', buffers, 0)  # 取label文件前2个整形数

    labelNum = head[1]
    offset = struct.calcsize('>II')  # 定位到label数据开始的位置

    numString = '>' + str(labelNum) + "B"  # fmt格式:'>60000B'
    labels = struct.unpack_from(numString, buffers, offset)  # 取label数据

    binfile.close()
    labels = np.reshape(labels, [labelNum])  # 转型为列表(一维数组)

    return labels, head


if __name__ == "__main__":
    file1 = 'C:/train-images.idx3-ubyte'
    file2 = 'C:/train-labels.idx1-ubyte'

    imgs, data_head = loadImageSet(file1)
    print('data_head:', data_head)
    print(type(imgs))
    print('imgs_array:', imgs)
    print(np.reshape(imgs[1, :], [28, 28]))  # 取出其中一张图片的像素,转型为28*28,大致就能从图像上看出是几啦

    print('----------我是分割线-----------')

    labels, labels_head = loadLabelSet(file2)
    print('labels_head:', labels_head)
    print(type(labels))
    print(labels)

print(np.reshape(imgs[4, :], [28, 28])) # 取出其中一张图片的像素,转型为28*28,数组第4个


image.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容