使用MNIST数据集训练手写数字识别模型


本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

一、MNIST 数据集介绍

发现 csdn 平台上似乎还没有一篇完整的介绍 MNIST 数据集、完整训练代码和训练好的模型文件,故发此文来对 MNIST 做一个详细的整合。
MNIST 数据集(手写数字数据集)是一个公开的公共数据集,任何人都可以免费获取它。目前,它已经是一个作为机器学习入门的通用性特别强的数据集之一,所以对于想要学习机器学习分类的、深度神经网络分类的、图像识别与处理的小伙伴,都可以选择 MNIST 数据集入门。

MNIST 数据集结构

MNIST 数据集包含 70000(60000+10000)个样本,其中有 60000 个训练样本和 10000 个测试样本,每个样本的像素大小为 28*28。

MNIST 数据集有多种下载方式,本文仅介绍两种。
方法一:
下载地址:http://yann.lecun.com/exdb/mnist/

可以直接下载这四个文件,这四个文件分别为:
①训练样本的图像(60000 个)
②对应训练样本上每一张图像上数字的标签(0~9)(60000 个)
③测试样本的图像(10000 个)
④对应测试样本上每一张图像上数字的标签(0~9)(10000 个)

方法二:
在 Keras 中已经内置了多种公共数据集,其中就包含 MNIST 数据集,如图所示。

所以可以直接调用 tf.keras.datasets.mnist,直接下载数据集。

二、模型训练思路

这是数字识别模型的神经网络结构,如图所示。
· 每个样本有 28_28 像素点,输入样本有 28_28=784 个像素值;
· 故输入层设置 784 个节点;
· 隐含层尽量设置成 2 的 n 次幂个节点,故选择 128 个节点,使用 ReLu 激活函数;
· 输出层用于输出数字识别结果 0~9,故输出层设置 10 个节点,

我们采用神经网络训练手写数字模型,分为以下几步:
注:这些分块的代码是用来分步讲解,不能单独运行,在结尾我会附上完整代码,可直接复制使用。

①加载数据

mnist = tf.keras.datasets.mnist
(train_x,train_y),(test_x,test_y) = mnist.load_data()
print('\n train_x:%s, train_y:%s, test_x:%s, test_y:%s'%(train_x.shape,train_y.shape,test_x.shape,test_y.shape)) 

运行结果

train_x:(60000, 28, 28), train_y:(60000,), test_x:(10000, 28, 28), test_y:(10000,)

从这里可以看出数据集的结构形状 train_x 存放了 60000 张 28*28 像素的数字图像,并有 train_y 有 60000 条标签值与之相对应,用于训练模型;test_x 和 test_y 同理,用于测试模型。

②数据预处理

#归一化、并转换为tensor张量,数据类型为float32.
X_train,X_test = tf.cast(train_x/255.0,tf.float32),tf.cast(test_x/255.0,tf.float32)     
y_train,y_test = tf.cast(train_y,tf.int16),tf.cast(test_y,tf.int16)

③建立模型

由于数据集比较简单,使用单层隐含层的神经网络就可以达到足够低的损失和较高的准确率。

model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=(28,28)))     #添加Flatten层说明输入数据的形状
model.add(tf.keras.layers.Dense(128,activation='relu'))     #添加隐含层,为全连接层,128个节点,relu激活函数
model.add(tf.keras.layers.Dense(10,activation='softmax'))   #添加输出层,为全连接层,10个节点,softmax激活函数
print('\n',model.summary())     #查看网络结构和参数信息

④配置模型训练方法

#adam算法参数采用keras默认的公开参数,损失函数采用稀疏交叉熵损失函数,准确率采用稀疏分类准确率函数
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['sparse_categorical_accuracy'])   

⑤训练模型

#批量训练大小为64,迭代5次,测试集比例0.2(48000条训练集数据,12000条测试集数据)
history = model.fit(X_train,y_train,batch_size=64,epochs=5,validation_split=0.2)

⑥评估模型

model.evaluate(X_test,y_test,verbose=2)     #每次迭代输出一条记录,来评价该模型是否有比较好的泛化能力

⑦保存模型

保存模型的格式可分为两种:
·HDF5 格式
·SavedModel 格式

二者的区别我会在后续的文章中介绍,我采用的是 HDF5 格式,这里不赘述。

保存模型的方式也可分为两种:
· 仅保存模型参数 model.save_weights()
· 保存整个模型 model.save()

二者的区别我也会在后续的文章中介绍,这两种方式都可以尝试一下,代码如下。

#保存模型参数
#model.save_weights('C:\\Users\\xuyansong\\Desktop\\深度学习\\python\\MNIST\\模型参数\\mnist_weights.h5')
#保存整个模型
model.save('C:\\Users\\xuyansong\\Desktop\\深度学习\\python\\MNIST\\整个模型\\mnist_weights.h5')

其中( )里的路径根据自己需要自行修改,若省去路径,默认保存到当前的工作路径。

#保存模型参数
#model.save_weights('mnist_weights.h5')
#保存整个模型
model.save('mnist_weights.h5')

⑧结果可视化

#结果可视化
print(history.history)
loss = history.history['loss']          #训练集损失
val_loss = history.history['val_loss']  #测试集损失
acc = history.history['sparse_categorical_accuracy']            #训练集准确率
val_acc = history.history['val_sparse_categorical_accuracy']    #测试集准确率

plt.figure(figsize=(10,3))

plt.subplot(121)
plt.plot(loss,color='b',label='train')
plt.plot(val_loss,color='r',label='test')
plt.ylabel('loss')
plt.legend()

plt.subplot(122)
plt.plot(acc,color='b',label='train')
plt.plot(val_acc,color='r',label='test')
plt.ylabel('Accuracy')
plt.legend()

#暂停5秒关闭画布,否则画布一直打开的同时,会持续占用GPU内存
#plt.ion()       #打开交互式操作模式
#plt.show()
#plt.pause(5)   
#plt.close()

plt.show()

运行结果

⑨使用模型

从测试集样本中随机抽取 10 张图像,并展示结果:

plt.figure()
for i in range(10):
    num = np.random.randint(1,10000)    #在1~10000之间生成随机整数

    plt.subplot(2,5,i+1)
    plt.axis('off')
    plt.imshow(test_x[num],cmap='gray')
    demo = tf.reshape(X_test[num],(1,28,28))
    y_pred = np.argmax(model.predict(demo))
    plt.title('标签值:'+str(test_y[num])+'\n预测值:'+str(y_pred))

如果对这块代码中的 demo 语句和 y_pred 这两条语句看不懂,可以看我发的这篇文章:
网址: https://blog.csdn.net/weixin_45954454/article/details/114437165?spm=1001.2014.3001.5501([使用 Keras 的 Sequential 框架搭建神经网络模型,在使用模型分类时报错…](https://blog.csdn.net/weixin_45954454/article/details/114437165?spm=1001.2014.3001.5501) )

三、代码实现——直接用

本人使用的是 Tensorflow2.2.0—gpu 版本,就目前已更新的版本来看,2.0.0 以上版本都可以直接使用。
如果你的 GPU 没有配置好,代码会在指定 GPU 那里报错的,如果是这种报错还不知道如何解决的话,就选择用 CPU 跑程序,即注释掉这两行代码:

gpus = tf.config.experimental.list_physical_devices('GPU')
tf.config.experimental.set_memory_growth(gpus[0],True)

注释掉后,基本就能跑通啦。

3.1 训练模型并保存模型

话不多说,完整代码附上:
再次强调:其中 model.save( ) 里的路径根据自己需要自行修改,若省去路径,默认保存到当前的工作路径。

########手写数字数据集##########
###########保存模型############
########1层隐含层(全连接层)##########
#60000条训练数据和10000条测试数据,28x28像素的灰度图像
#隐含层激活函数:ReLU函数
#输出层激活函数:softmax函数(实现多分类)
#损失函数:稀疏交叉熵损失函数
#输入层有784个节点,隐含层有128个神经元,输出层有10个节点
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

import time
print('--------------')
nowtime = time.strftime('%Y-%m-%d %H:%M:%S')
print(nowtime)

#指定GPU
#import os
#os.environ["CUDA_VISIBLE_DEVICES"] = "0"
gpus = tf.config.experimental.list_physical_devices('GPU')
tf.config.experimental.set_memory_growth(gpus[0],True)
#初始化
plt.rcParams['font.sans-serif'] = ['SimHei']

#加载数据
mnist = tf.keras.datasets.mnist
(train_x,train_y),(test_x,test_y) = mnist.load_data()
print('\n train_x:%s, train_y:%s, test_x:%s, test_y:%s'%(train_x.shape,train_y.shape,test_x.shape,test_y.shape)) 

#数据预处理
#X_train = train_x.reshape((60000,28*28))
#Y_train = train_y.reshape((60000,28*28))       #后面采用tf.keras.layers.Flatten()改变数组形状
X_train,X_test = tf.cast(train_x/255.0,tf.float32),tf.cast(test_x/255.0,tf.float32)     #归一化
y_train,y_test = tf.cast(train_y,tf.int16),tf.cast(test_y,tf.int16)

#建立模型
model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=(28,28)))     #添加Flatten层说明输入数据的形状
model.add(tf.keras.layers.Dense(128,activation='relu'))     #添加隐含层,为全连接层,128个节点,relu激活函数
model.add(tf.keras.layers.Dense(10,activation='softmax'))   #添加输出层,为全连接层,10个节点,softmax激活函数
print('\n',model.summary())     #查看网络结构和参数信息

#配置模型训练方法
#adam算法参数采用keras默认的公开参数,损失函数采用稀疏交叉熵损失函数,准确率采用稀疏分类准确率函数
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['sparse_categorical_accuracy'])   

#训练模型
#批量训练大小为64,迭代5次,测试集比例0.2(48000条训练集数据,12000条测试集数据)
print('--------------')
nowtime = time.strftime('%Y-%m-%d %H:%M:%S')
print('训练前时刻:'+str(nowtime))

history = model.fit(X_train,y_train,batch_size=64,epochs=5,validation_split=0.2)

print('--------------')
nowtime = time.strftime('%Y-%m-%d %H:%M:%S')
print('训练后时刻:'+str(nowtime))
#评估模型
model.evaluate(X_test,y_test,verbose=2)     #每次迭代输出一条记录,来评价该模型是否有比较好的泛化能力

#保存模型参数
#model.save_weights('C:\\Users\\xuyansong\\Desktop\\深度学习\\python\\MNIST\\模型参数\\mnist_weights.h5')
#保存整个模型
model.save('mnist_weights.h5')

#结果可视化
print(history.history)
loss = history.history['loss']          #训练集损失
val_loss = history.history['val_loss']  #测试集损失
acc = history.history['sparse_categorical_accuracy']            #训练集准确率
val_acc = history.history['val_sparse_categorical_accuracy']    #测试集准确率

plt.figure(figsize=(10,3))

plt.subplot(121)
plt.plot(loss,color='b',label='train')
plt.plot(val_loss,color='r',label='test')
plt.ylabel('loss')
plt.legend()

plt.subplot(122)
plt.plot(acc,color='b',label='train')
plt.plot(val_acc,color='r',label='test')
plt.ylabel('Accuracy')
plt.legend()

#暂停5秒关闭画布,否则画布一直打开的同时,会持续占用GPU内存
#根据需要自行选择
#plt.ion()       #打开交互式操作模式
#plt.show()
#plt.pause(5)
#plt.close()

#使用模型
plt.figure()
for i in range(10):
    num = np.random.randint(1,10000)

    plt.subplot(2,5,i+1)
    plt.axis('off')
    plt.imshow(test_x[num],cmap='gray')
    demo = tf.reshape(X_test[num],(1,28,28))
    y_pred = np.argmax(model.predict(demo))
    plt.title('标签值:'+str(test_y[num])+'\n预测值:'+str(y_pred))
#y_pred = np.argmax(model.predict(X_test[0:5]),axis=1)
#print('X_test[0:5]: %s'%(X_test[0:5].shape))
#print('y_pred: %s'%(y_pred))

#plt.ion()       #打开交互式操作模式
plt.show()
#plt.pause(5)
#plt.close()

运行结果

卷积神经网络训练模型

关于卷积神经网络的介绍和使用,我写过写篇文章【神经网络与深度学习】CIFAR10 数据集介绍,并使用卷积神经网络训练图像分类模型——附完整代码训练好的模型文件——直接用https://blog.csdn.net/weixin_45954454/article/details/114519299?spm=1001.2014.3001.5501
对于 MNIST 数据集的使用方式与上文相似,如需 MNIST 数据集的卷积神经网络训练的源代码,在本文的结尾(第四章节)[四、训练好的模型文件——直接用] 下载。

3.2 加载训练好的模型

3.2.1 采用 model.save_weights() 方式保存的模型

采用 model.save_weights() 方式保存的模型,使用以下完整代码加载模型:

########手写数字数据集##########
###########加载模型参数方法############
########1层隐含层(全连接层)##########
#60000条训练数据和10000条测试数据,28x28像素的灰度图像
#隐含层激活函数:ReLU函数
#输出层激活函数:softmax函数(实现多分类)
#损失函数:稀疏交叉熵损失函数
#输入层有784个节点,隐含层有128个神经元,输出层有10个节点
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

import time
print('--------------')
nowtime = time.strftime('%Y-%m-%d %H:%M:%S')
print(nowtime)

#指定GPU
#import os
#os.environ["CUDA_VISIBLE_DEVICES"] = "0"
gpus = tf.config.experimental.list_physical_devices('GPU')
tf.config.experimental.set_memory_growth(gpus[0],True)
#初始化
plt.rcParams['font.sans-serif'] = ['SimHei']

#加载数据
mnist = tf.keras.datasets.mnist
(train_x,train_y),(test_x,test_y) = mnist.load_data()
print('\n train_x:%s, train_y:%s, test_x:%s, test_y:%s'%(train_x.shape,train_y.shape,test_x.shape,test_y.shape)) 

#数据预处理
#X_train = train_x.reshape((60000,28*28))
#Y_train = train_y.reshape((60000,28*28))       #后面采用tf.keras.layers.Flatten()改变数组形状
X_train,X_test = tf.cast(train_x/255.0,tf.float32),tf.cast(test_x/255.0,tf.float32)        #归一化
y_train,y_test = tf.cast(train_y,tf.int16),tf.cast(test_y,tf.int16)

#建立模型
model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=(28,28)))     #添加Flatten层说明输入数据的形状
model.add(tf.keras.layers.Dense(128,activation='relu'))     #添加隐含层,为全连接层,128个节点,relu激活函数
model.add(tf.keras.layers.Dense(10,activation='softmax'))   #添加输出层,为全连接层,10个节点,softmax激活函数
print('\n',model.summary())     #查看网络结构和参数信息

#配置模型训练方法
#adam算法参数采用keras默认的公开参数,损失函数采用稀疏交叉熵损失函数,准确率采用稀疏分类准确率函数
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['sparse_categorical_accuracy'])   

#加载模型参数
history = model.load_weights('C:\\Users\\xuyansong\\Desktop\\深度学习\\python\\MNIST\\模型参数\\mnist_weights.h5') #路径根据文件实际位置修改,不然会报错

#评估模型
model.evaluate(X_test,y_test,verbose=2)     #每次迭代输出一条记录,来评价该模型是否有比较好的泛化能力

#使用模型
plt.figure()
for i in range(10):
    num = np.random.randint(1,10000)

    plt.subplot(2,5,i+1)
    plt.axis('off')
    plt.imshow(test_x[num],cmap='gray')
    demo = tf.reshape(X_test[num],(1,28,28))
    y_pred = np.argmax(model.predict(demo))
    plt.title('标签值:'+str(test_y[num])+'\n预测值:'+str(y_pred))
#y_pred = np.argmax(model.predict(X_test[0:5]),axis=1)
#print('X_test[0:5]: %s'%(X_test[0:5].shape))
#print('y_pred: %s'%(y_pred))

plt.ion()       #打开交互式操作模式
plt.show()
plt.pause(5)
plt.close()

运行结果:

3.2.2 采用 model.save() 方式保存的模型

采用 model.save() 方式保存的模型,使用以下完整代码加载模型:

########手写数字数据集##########
###########加载整个模型方法############
########1层隐含层(全连接层)##########
#60000条训练数据和10000条测试数据,28x28像素的灰度图像
#隐含层激活函数:ReLU函数
#输出层激活函数:softmax函数(实现多分类)
#损失函数:稀疏交叉熵损失函数
#输入层有784个节点,隐含层有128个神经元,输出层有10个节点
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

import time
print('--------------')
nowtime = time.strftime('%Y-%m-%d %H:%M:%S')
print(nowtime)

#指定GPU
#import os
#os.environ["CUDA_VISIBLE_DEVICES"] = "0"
gpus = tf.config.experimental.list_physical_devices('GPU')
tf.config.experimental.set_memory_growth(gpus[0],True)
#初始化
plt.rcParams['font.sans-serif'] = ['SimHei']

#加载数据
mnist = tf.keras.datasets.mnist
(train_x,train_y),(test_x,test_y) = mnist.load_data()
print('\n train_x:%s, train_y:%s, test_x:%s, test_y:%s'%(train_x.shape,train_y.shape,test_x.shape,test_y.shape)) 

#数据预处理
#X_train = train_x.reshape((60000,28*28))
#Y_train = train_y.reshape((60000,28*28))       #后面采用tf.keras.layers.Flatten()改变数组形状
X_train,X_test = tf.cast(train_x/255.0,tf.float32),tf.cast(test_x/255.0,tf.float32)     #归一化
y_train,y_test = tf.cast(train_y,tf.int16),tf.cast(test_y,tf.int16)

#加载整个模型 
model = tf.keras.models.load_model('C:\\Users\\xuyansong\\Desktop\\深度学习\\python\\MNIST\\整个模型\\mnist_weights.h5')    #路径根据文件实际位置修改,不然会报错
model.summary()     #查看摘要

#评估模型
model.evaluate(X_test,y_test,verbose=2)     #每次迭代输出一条记录,来评价该模型是否有比较好的泛化能力

#使用模型
plt.figure()
for i in range(10):
    num = np.random.randint(1,10000)

    plt.subplot(2,5,i+1)
    plt.axis('off')
    plt.imshow(test_x[num],cmap='gray')
    demo = tf.reshape(X_test[num],(1,28,28))
    y_pred = np.argmax(model.predict(demo))
    plt.title('标签值:'+str(test_y[num])+'\n预测值:'+str(y_pred))
#y_pred = np.argmax(model.predict(X_test[0:5]),axis=1)
#print('X_test[0:5]: %s'%(X_test[0:5].shape))
#print('y_pred: %s'%(y_pred))

plt.ion()       #打开交互式操作模式
plt.show()
plt.pause(5)
plt.close()

运行结果

:对比这三个运行的结果,细心的你会注意到:为什么我展示的 loss 损失函数和 sparse_categorical_accuracy 准确率的结果都不一样?



这是因为在每次训练模型时,由于神经网络参数初始值随机,神经网络在学习的过程中即使使用的方法相同,学习的过程和结果也会不同。不信你可以查看每次训练的结果都会有细微的差别(即每次训练出的都不是同一个模型),这就是深度学习的魅力所在。
由于我在保存这两种模型时,并不是在同一个训练模型下保存的,所以会有模型结果会有差别。但如果你是在同一个训练模型下保存的这两种模型(即保存了模型参数,这个模型就定下来了),只要测试集不变,其测试结果一定是相同的,且永远不会变。

四、训练好的模型文件——直接用

全连接神经网络模型

csdn 资源下载链接:使用 MNIST 数据集训练手写数字识别模型——附完整代码训练好的模型文件——直接用.:https://download.csdn.net/download/weixin_45954454/15621509?spm=1001.2014.3001.5501

卷积神经网络模型

csdn 资源下载链接:【神经网络与深度学习】MNIST 数据集介绍,并使用卷积神经网络训练手写数字识别模型——附完整代码训练好的模型文件——直接用:https://download.csdn.net/download/weixin_45954454/15650033?spm=1001.2014.3001.5503

< 本文主要参考于《神经网络与深度学习 - Tensorflow 实践》>

出现问题可私信我解决,不定期查看。

<后续还会继续整理【神经网络与深度学习】相关内容,如果需要,可持续关注我哦~>

<整理不易,留个赞或评论支持一下我吧 ^^>

如有疑问,欢迎批评指正 ^^

声明:HEUE NOTE|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA 4.0协议进行授权

转载:转载请注明原文链接 - 使用MNIST数据集训练手写数字识别模型