【神经网络与深度学习】使用MNIST数据集训练手写数字识别模型——[附完整训练代码]

  • 一、MNIST数据集介绍
  • MNIST数据集结构
  • 二、模型训练思路
  • ①加载数据
  • ②数据预处理
  • ③建立模型
  • ④配置模型训练方法
  • ⑤训练模型
  • ⑥评估模型
  • ⑦保存模型
  • ⑧结果可视化
  • ⑨使用模型
  • 三、代码实现——直接用
  • 3.1训练模型并保存模型
  • 卷积神经网络训练模型
  • 3.2加载训练好的模型
  • 3.2.1采用model.save_weights()方式保存的模型
  • 3.2.2采用model.save()方式保存的模型
  • 四、训练好的模型文件——直接用
  • 全连接神经网络模型
  • 卷积神经网络模型


一、MNIST数据集介绍

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

MNIST数据集结构

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

机器学习实现数字识别_机器学习实现数字识别


MNIST数据集有多种下载方式,本文仅介绍两种。

方法一:

下载地址:http://yann.lecun.com/exdb/mnist/

机器学习实现数字识别_机器学习实现数字识别_02


可以直接下载这四个文件,这四个文件分别为:

①训练样本的图像(60000个)

②对应训练样本上每一张图像上数字的标签(0~9)(60000个)

③测试样本的图像(10000个)

④对应测试样本上每一张图像上数字的标签(0~9)(10000个)方法二:

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

机器学习实现数字识别_深度学习_03


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

二、模型训练思路

这是数字识别模型的神经网络结构,如图所示。

·每个样本有2828像素点,输入样本有2828=784个像素值;
·故输入层设置784个节点;
·隐含层尽量设置成2的n次幂个节点,故选择128个节点,使用ReLu激活函数;
·输出层用于输出数字识别结果0~9,故输出层设置10个节点,

机器学习实现数字识别_tensorflow_04

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

①加载数据

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()

运行结果

机器学习实现数字识别_神经网络_05

⑨使用模型

从测试集样本中随机抽取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这两条语句看不懂,可以看我发的这篇文章:
网址:(使用Keras的Sequential框架搭建神经网络模型,在使用模型分类时报错… )

三、代码实现——直接用

本人使用的是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()

运行结果

机器学习实现数字识别_深度学习_06

卷积神经网络训练模型

关于卷积神经网络的介绍和使用,我写过写篇文章【神经网络与深度学习】CIFAR10数据集介绍,并使用卷积神经网络训练图像分类模型——附完整代码训练好的模型文件——直接用:
对于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()

运行结果:

机器学习实现数字识别_tensorflow_07

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()

运行结果

机器学习实现数字识别_tensorflow_08

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

机器学习实现数字识别_神经网络_09


机器学习实现数字识别_tensorflow_10


机器学习实现数字识别_神经网络_11


这是因为在每次训练模型时,由于神经网络参数初始值随机,神经网络在学习的过程中即使使用的方法相同,学习的过程和结果也会不同。不信你可以查看每次训练的结果都会有细微的差别(即每次训练出的都不是同一个模型),这就是深度学习的魅力所在。

由于我在保存这两种模型时,并不是在同一个训练模型下保存的,所以会有模型结果会有差别。但如果你是在同一个训练模型下保存的这两种模型(即保存了模型参数,这个模型就定下来了),只要测试集不变,其测试结果一定是相同的,且永远不会变。