MNIST手写数字数据库的训练集为60,000个示例,而测试集为10,000个示例。

一共4个文件,训练集、训练集标签、测试集、测试集标签

数据集下载链接

机器学习手写数字5特征分析总结 手写数字识别训练集_神经网络


MNIST的图像数据是28像素 × 28像素的灰度图像(1通道),各个像素

的取值在0到255之间。每个图像数据都相应地标有“7” “2” “1”等标签。

目录

  • 1、读入MNIST数据。
  • 2、进行训练显示图像
  • 3、神经网络的推理处理
  • 4、批处理


1、读入MNIST数据。

从下载MNIST数据集到将这些数据转换成NumPy数组

# coding: utf-8
try:
    import urllib.request
except ImportError:
    raise ImportError('You should use Python 3.x')
import os.path
import gzip
import pickle
import os
import numpy as np

# 下载数据
url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {
    'train_img':'train-images-idx3-ubyte.gz',
    'train_label':'train-labels-idx1-ubyte.gz',
    'test_img':'t10k-images-idx3-ubyte.gz',
    'test_label':'t10k-labels-idx1-ubyte.gz'
}

# 设置数据保存路径
dataset_dir = os.path.dirname(os.path.abspath(__file__))#
save_file = dataset_dir + "/mnist.pkl"

train_num = 60000
test_num = 10000
img_dim = (1, 28, 28)
img_size = 784


def _download(file_name):
    file_path = dataset_dir + "/" + file_name
    
    if os.path.exists(file_path):
        return

    print("Downloading " + file_name + " ... ")
    urllib.request.urlretrieve(url_base + file_name, file_path)
    print("Done")
    
def download_mnist():
    for v in key_file.values():
       _download(v)
        
def _load_label(file_name):
    file_path = dataset_dir + "/" + file_name
    
    print("Converting " + file_name + " to NumPy Array ...")
    with gzip.open(file_path, 'rb') as f:
            labels = np.frombuffer(f.read(), np.uint8, offset=8)
    print("Done")
    
    return labels

def _load_img(file_name):
    file_path = dataset_dir + "/" + file_name
    
    print("Converting " + file_name + " to NumPy Array ...")    
    with gzip.open(file_path, 'rb') as f:
            data = np.frombuffer(f.read(), np.uint8, offset=16)
    data = data.reshape(-1, img_size)
    print("Done")
    
    return data
    
def _convert_numpy():
    dataset = {}
    dataset['train_img'] =  _load_img(key_file['train_img'])
    dataset['train_label'] = _load_label(key_file['train_label'])    
    dataset['test_img'] = _load_img(key_file['test_img'])
    dataset['test_label'] = _load_label(key_file['test_label'])
    
    return dataset

def init_mnist():
    download_mnist()
    dataset = _convert_numpy()
    print("Creating pickle file ...")
    with open(save_file, 'wb') as f:
        pickle.dump(dataset, f, -1)
    print("Done!")

def _change_one_hot_label(X):
    T = np.zeros((X.size, 10))
    for idx, row in enumerate(T):
        row[X[idx]] = 1
        
    return T
    

def load_mnist(normalize=True, flatten=True, one_hot_label=False):
    """读入MNIST数据集
    
    Parameters
    ----------
    normalize : 将图像的像素值正规化为0.0~1.0
    one_hot_label : 
        one_hot_label为True的情况下,标签作为one-hot数组返回
        one-hot数组是指[0,0,1,0,0,0,0,0,0,0]这样的数组
    flatten : 是否将图像展开为一维数组
    
    Returns
    -------
    (训练图像, 训练标签), (测试图像, 测试标签)
    """
    if not os.path.exists(save_file):
        init_mnist()
        
    with open(save_file, 'rb') as f:
        dataset = pickle.load(f)
    
    if normalize:
        for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].astype(np.float32)
            dataset[key] /= 255.0
            
    if one_hot_label:
        dataset['train_label'] = _change_one_hot_label(dataset['train_label'])
        dataset['test_label'] = _change_one_hot_label(dataset['test_label'])
    
    if not flatten:
         for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].reshape(-1, 1, 28, 28)

    return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 


if __name__ == '__main__':
    init_mnist()
Python有pickle这个便利的功能。这个功能可以将程序运行中的对象保存为文件。
如果加载保存过的pickle文件,可以立刻复原之前程序运行中的对象。

2、进行训练显示图像

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定,从dataset中导入数据
import numpy as np
from dataset.mnist import load_mnist
from PIL import Image


def img_show(img):
    pil_img = Image.fromarray(np.uint8(img)) # 把保存为Numpy数组的图像数据转换为PIL用的数据对象
    pil_img.show()

# load_mnist的三个参数的意思
# 第 1 个 参 数normalize :设置是否将输入图像正规化为0.0~1.0的值。如果将该参数设置为 False ,则输入图像的像素会保持原来的0~255。
# 第2个参数 flatten 设置是否展开输入图像(变成一维数组):
# 如果将该参数设置为 False ,则输入图像为1 × 28 × 28的三维数组;若设置为 True ,则输入图像会保存为由784个元素构成的一维数组。
# 第3个参数 one_hot_label 设置是否将标签保存为one-hot表示(one-hot representation)。
# one-hot表示是仅正确解标签为1,其余皆为0的数组,就像 [0,0,1,0,0,0,0,0,0,0] 这样。
# 当 one_hot_label 为 False 时,只是像 7,2 这样简单保存正确解标签;
# 当 one_hot_label 为 True 时,标签则保存为one-hot表示。

(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)

img = x_train[0]
label = t_train[0]
print(label)  # 5

print(img.shape)  # (784,)
img = img.reshape(28, 28)  # 把图像的形状变为原来的尺寸
print(img.shape)  # (28, 28)

img_show(img)

3、神经网络的推理处理

神经网络的输入层有784个神经元,输出层有10个神经元。
输入层的784这个数字来源于图像大小的28 × 28 = 784,输出层的10这个数字来源于10类别分类(数字0到9,共10类别)。
此外,这个神经网络有2个隐藏层,第1个隐藏层有50个神经元,第2个隐藏层有100个神经元。这个50和100可以设置为任何值。

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import pickle
from dataset.mnist import load_mnist
from common.functions import sigmoid, softmax

# 获取数据:
def get_data():
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
    return x_test, t_test

# 初始化网络
# init_network()读入保存在pickle文件sample_weight.pkl中的学习到的权重参数。这个文件中以字典变量的形式保存了权重和偏置参数
def init_network():
    with open("sample_weight.pkl", 'rb') as f:
        network = pickle.load(f)
    return network

# 预测网络的权重和b
def predict(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = softmax(a3)

    return y

# 评价它的识别精度
x, t = get_data()  # 首先获得MNIST数据集,生成网络
network = init_network()
accuracy_cnt = 0
# 用 for 语句逐一取出保存在 x 中的图像数据,用 predict() 函数进行分类。
# predict() 函数以NumPy数组的形式输出各个标签对应的概率。
# 比如输出 [0.1, 0.3, 0.2, ... , 0.04] 的数组,该数组表示“0”的概率为0.1,“1”的概率为0.3,
for i in range(len(x)):
    y = predict(network, x[i])
    p= np.argmax(y) # 获取概率最高的元素的索引,作为预测结果
    if p == t[i]:
        accuracy_cnt += 1

print("Accuracy:" + str(float(accuracy_cnt) / len(x)))  # 比较神经网络所预测的答案和正确解标签,将回答正确的概率作为识别精度
# 会显示“Accuracy:0.9352”。这表示有93.52 %的数据被正确分类了

我们把 load_mnist 函数的参数 normalize 设置成了True 。将 normalize 设置成 True 后,函数内部会进行转换,将图像的各个像素值除以255,使得数据的值在0.0~1.0的范围内。
像这样把数据限定到某个范围内的处理称为正规化(normalization)。此外,对神经网络的输入数据进行某种既定的转换称为预处理(pre-processing)。这里,作为对输入图像的一种预处理,我们进行了正规化。

4、批处理

我们来关注输入数据和权重参数的“形状”。再看一下刚才的代码实现。

机器学习手写数字5特征分析总结 手写数字识别训练集_机器学习手写数字5特征分析总结_02


机器学习手写数字5特征分析总结 手写数字识别训练集_数据_03


通过上图的输入,进行下图的对比

机器学习手写数字5特征分析总结 手写数字识别训练集_数据_04


现在我们来考虑打包输入多张图像的情形。比如,我们想用 predict()

函数一次性打包处理100张图像。可以把x的形状改为100 × 784,将

100张图像打包作为输入数据。用图表示的话,如图所示。

机器学习手写数字5特征分析总结 手写数字识别训练集_机器学习手写数字5特征分析总结_05


输入数据的形状为100 × 784,输出数据的形状为100 × 10。这表示输入的100张图像的结果被一次性输出了。

比如, x[0] 和y[0] 中保存了第0张图像及其推理结果, x[1] 和 y[1] 中保存了第1张图像及其推理结果,等等。

这种打包式的输入数据称为批(batch)

代码:

机器学习手写数字5特征分析总结 手写数字识别训练集_数据_06


range() 函数:

range() 函数若指定为 range(start, end) ,则会生成一个由 start 到 end-1 之间的整数构成的列表。若像 range(start, end, step) 这样指定3个整数,则生成的列表中的下一个元素会增加 step 指定的值。

机器学习手写数字5特征分析总结 手写数字识别训练集_机器学习手写数字5特征分析总结_07


机器学习手写数字5特征分析总结 手写数字识别训练集_数组_08


通过 x[i:i+batch_size] 从输入数据中抽出批数据。

x[i:i+batch_n] 会取出从第 i 个到第 i+batch_n 个之间的数据。

本例中是像 x[0:100] 、 x[100:200] ……这样,从头开始以100为单位将数据提取为批数据。

通过 argmax() 获取值最大的元素的索引。

矩阵的第0维是列方向,第1维是行方向。

机器学习手写数字5特征分析总结 手写数字识别训练集_数组_09


最后,我们比较一下以批为单位进行分类的结果和实际的答案。

需要在NumPy数组之间使用比较运算符( == )生成由 True/False 构成的布尔型数组,并计算 True 的个数。

机器学习手写数字5特征分析总结 手写数字识别训练集_数组_10