mnist手写数字识别是入门深度学习,或者学习一个新的深度学习框架时,最容易上手的程序。本文中,我将使用mxnet深度学习框架在mnist数据集上实现一个简单的手写数字识别模型。
整个程序的代码思路来源于GitHub 上别人的一个小项目,并使用mxnet框架复现了一遍:
https://gist.github.com/alexcpn/0683bb940cae510cf84d5976c1652abd
运行环境
运行软件
Python3
依赖包
- mxnet
- numpy
- pandas
- matplotlib
- time (默认自带)
- random (默认自带)
代码讲解
依赖包导入
from mxnet import gluon, init, nd, autograd from mxnet.gluon import nn, utils as gutils from mxnet.gluon import loss as gloss import mxnet as mx import time import numpy as np import random import matplotlib.pyplot as plt import pandas as pd
基础配置
np.random.seed(123) #设置随机数种子 use_gpu = False #指示是否使用GPU,当使用GPU时应设为True
导入数据
数据集的下载请点击这里(mnist.npz)
def load_data(path='mnist.npz'): # 读入本地文件 f = np.load(path) x_train, y_train = f['x_train'], f['y_train'] x_test, y_test = f['x_test'], f['y_test'] f.close() return (x_train, y_train), (x_test, y_test) # 将数据读入 (X_train, y_train), (X_test, y_test) = load_data() # 查看一下数据的格式,输入输出维度 print(X_train.shape,y_train.shape,X_test.shape,y_test.shape)
数据可视化
有时候,为了方便,或者更直观,我们往往需要将数据可视化。对于图像数据来说,可视化就是家常便饭了,而且也是最容易可视化的一类数据之一。这里我们使用到一个强大的绘图库matplotlib,同样,做过数学建模以及仿真的应该都用过这类库,尤其是用过matlab的话。
plt.subplot(221) print(y_train[4545],y_train[1],y_train[2],y_train[3]) plt.imshow(X_train[4545], cmap=plt.get_cmap('gray')) plt.subplot(222) plt.imshow(X_train[1], cmap=plt.get_cmap('gray')) plt.subplot(223) plt.imshow(X_train[2], cmap=plt.get_cmap('gray')) plt.subplot(224) plt.imshow(X_train[3], cmap=plt.get_cmap('gray')) # show the plot plt.show()
数据预处理
往往我们拿到的数据,都需要进行一些处理,在不同的场景下,有不同的处理方法。这里的mnist数据集是已经处理过的,我们只需要将其转为我们所需的输入格式即可。这里,我们需要将其转换为mxnet框架运算所需要的nd.array格式。对于数据的10个数字标签,由于mxnet框架提供的损失函数的特性,我们不需要像其他框架那样进行正交化处理。
# 调整shape X_train = X_train.reshape(X_train.shape[0], 1, 28, 28) X_test = X_test.reshape(X_test.shape[0], 1, 28, 28) # 调整数据类型 X_train = X_train.astype('float32') X_test = X_test.astype('float32') # 归一化 X_train /= 255 X_test /= 255 print(len(y_train),len(y_test)) # 从numpy.array转换为mxnet.nd.array Y _train = nd.array(y_train) Y _test = nd.array(y_test)
搭建神经网络模型
net = nn.Sequential() net.add(nn.Conv2D(channels=32,kernel_size=3,padding=1,strides=1)) net.add(nn.Conv2D(channels=32,kernel_size=3,padding=1,strides=1)) net.add(nn.MaxPool2D(pool_size=(2,2), strides=(2,2))) net.add(nn.Dense(units=128,activation='relu', flatten=True)) net.add(nn.Dense(units=10, activation='softrelu', flatten=True))
模型训练前的初始化设置
lr=0.001 # 学习率 num_epochs = 1 # 训练轮数 batch_size = 32 # batch大小 if(use_gpu == True): ctx = mx.gpu(0) else: ctx = mx.cpu(0) # 取一个与输入shape一样的随机值 X = nd.random.uniform(shape=(1, 1, 28, 28), ctx=ctx) # 网络结构初始化 net.initialize(init=init.MSRAPrelu(),ctx=ctx) # 进行一次正向传播测试 y = net(X) # 设置数据加载器 dataset_train = mx.gluon.data.ArrayDataset(X_train, Y_train) # ArrayDataset不需要从硬盘上加载数据 dataset_test = mx.gluon.data.ArrayDataset(X_test, Y_test) Train_data_loader = gluon.data.DataLoader(dataset_train, batch_size = batch_size) Test_data_loader = gluon.data.DataLoader(dataset_test, batch_size = batch_size) # 设置训练器,使用adam优化器 trainer = gluon.Trainer(net.collect_params(), 'adam', {'learning_rate': lr}) # 设置损失函数 loss = gloss.SoftmaxCrossEntropyLoss() Train_Loss = [] Train_Acc = [] Test_Acc = [] # 定义一些模型评估函数 def accuracy(y_hat, y): """Get accuracy.""" return (y_hat.argmax(axis=1) == y.astype('float32')).mean().asscalar() def evaluate_accuracy(data_loader, net, ctx): acc = nd.array([0], ctx=ctx) for X, y in data_loader: X, y = X.as_in_context(ctx), y.as_in_context(ctx) acc += accuracy(net(X), y) return acc.asscalar() / len(data_loader)
训练模型
for epoch in range(num_epochs): train_l_sum, train_acc_sum, start = 0, 0, time.time() count_datatrain = 0 for X, y in Train_data_loader: X, y = X.as_in_context(ctx), y.as_in_context(ctx) with autograd.record(): y_hat = net(X) l = loss(y_hat, y) l.backward() trainer.step(batch_size) train_l_sum += l.mean().asscalar() train_acc_sum += accuracy(y_hat, y) count_datatrain += batch_size if(count_datatrain % batch_size == 0): print('have train',count_datatrain,end='\r') print('have train',count_datatrain,end='\n') test_acc = evaluate_accuracy(Test_data_loader, net, ctx) print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, ' 'time %.1f sec' % (epoch + 1, train_l_sum / len(Train_data_loader), train_acc_sum / len(Train_data_loader), test_acc, time.time() - start)) Train_Acc.append(train_acc_sum / len(Train_data_loader)) Train_Loss.append(train_l_sum / len(Train_data_loader)) Test_Acc.append(test_acc)
保存模型参数
filename='mnist.model.mxnet' net.save_parameters(filename)
保存相关实验数据
Temp = [] Data = [] for i in range(len(Train_Acc)): Temp.append(Train_Loss[i]) Temp.append(Train_Acc[i]) Temp.append(Test_Acc[i]) Data.append(Temp) Temp = [] name=['loss','train_Acc','test_Acc'] test=pd.DataFrame(columns=name,data=Data)#数据有三列 print(test) test.to_csv('./mnist.csv',encoding='gbk')
实验结果
我们可以在文件目录中看到生成了两个新文件,一个是保存的模型参数文件“mnist.model.mxnet”,另一个是模型评估数据文件“mnist.csv”。
我们用Excel打开生成的mnist.csv文件,可以看到这几项数据:
loss | train_Acc | test_Acc | |
0 | 0.15305 | 0.9542 | 0.971046 |
模型在训练结束后,loss降为0.15305,训练集准确率为95.42%,测试集准确率为97.10%。
版权声明本博客的文章除特别说明外均为原创,本人版权所有。欢迎转载,转载请注明作者及来源链接,谢谢。本文地址: https://blog.ailemon.net/2019/06/06/deep-learning-mnist-handwritten-digit-recognition-by-mxnet/ All articles are under Attribution-NonCommercial-ShareAlike 4.0 |