二、MLP算法实现手写数字识别
通过学习林大贵老师的《TensorFlow+Keras深度学习人工智能实践应用》,对图像处理的过程有了较浅薄的理解,在此与大家分享,同时由于上书中提供的代码下载页面失效,笔者按照书本中的内容手敲代码,如果有纰漏敬请指正。
此第二节为使用MLP算法进行神经网络构建以及手写数字的识别,有关MNIST数据的获取以及预处理的介绍可以参看笔者上一篇博客。
1.数据的预测处理以及导入
对图像数据的预处理,使图像数据的格式满足机器处理的要求,输入数据要整形成一维;同时,对输出结果进行独热编码更有利于结果匹配。
#导入所需模块
from keras.utils import np_utils
import numpy as np
np.random.seed(10)
#读取MNIST数据,同上篇讲解
from keras.datasets import mnist
(x_train_image,y_train_label),(x_test_image,y_test_label) = mnist.load_data()
#整形以及归一化
x_Train = x_train_image.reshape(60000,784).astype('float32')
x_Test = x_test_image.reshape(10000,784).astype('float32')
x_Train_nomalize = x_Train/255
x_Test_nomalize = x_Test/255
#One-Hot Encoding 转换
y_Train_OneHot = np_utils.to_categorical(y_train_label)
y_Test_OneHot = np_utils.to_categorical(y_test_label)
2.建立神经网络
2.1 先导知识:过拟合问题
过拟合问题及其解决:我们希望训练后找出最佳分类曲线(即实线),可是当训练时间太久(即训练数据太多)或者范例太少时,导致训练曲线过度适应数据中的细节特征(即虚线,然而这细节特征不具有普遍性,不代表数据整体)。其结果是虽然在训练时准确度高,但应用模型进行预测时,使用未知数据导致准确度低。
解决过拟合问题这里使用了Dropout模块,笔者的理解是通过随机丢弃部分范例而减少训练时间以及数据的特殊性。
查阅网络资料知:它的直接作用是减少中间特征的数量,从而减少冗余,即增加每层各个特征之间的正交性(数据表征的稀疏性观点也恰好支持此解释)。
2.2 导入建立神经网络所需要的模块
#导入所需模块
from keras.models import Sequential
from keras.layers import Dense
#导入Dropout模块解决过拟合问题
from keras.layers import Dropout
2.3 建立输入层以及隐含层
网络结构如下图所示:
以下为网络构建代码:
model = Sequential()
#建立隐含层1
model.add(Dense(units=1000, #隐含层1神经元个数为1000
input_dim=784, #输入层神经元个数为28*28=784
kernel_initializer='normal', #正态分布的随机数初始化权重
activation='relu')) #激活函数为relu
#dropout的直接作用是减少中间特征的数量,从而减少冗余,即增加每层各个特征之间的正交性(数据表征的稀疏性观点也恰好支持此解释)
model.add(Dropout(0.5)) #使用Dropout 解决过拟合,一般取值为0.5
#建立隐含层2
model.add(Dense(units=1000, #隐含层2神经元个数为 1000
kernel_initializer='normal',
activation='relu'))
model.add(Dropout(0.5))
#建立输出层
model.add(Dense(units=10, #识别结果共有10种,所以输出层10个神经元
kernel_initializer='normal',
activation='softmax')) #激活函数 使用softmax函数
3.进行训练
3.1 设置训练方式
-
定义训练方式,使用compile方法:
-
loss设置损失函数,这里使用交叉熵;
-
optimizer:使用 adam优化器,提高准确率和收敛速度;
-
metrics设置评价方式为准确率.
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
3.2 开始训练及画图显示
输入训练参数:
-
x_Train_nomalize为特征值;
-
y_Train_OneHot为真实值;
-
validation_split划分训练集(80%)和验证集(20%);
-
epoch代表训练周期,batch_size代表每一批次训练数据项数; verbose=2代表显示训练过程
#开始训练
train_history = model.fit(x=x_Train_nomalize,y=y_Train_OneHot,validation_split=0.2,epochs=10,batch_size=200,verbose=2)
以下为训练过程:
以下为画图显示训练过程:
这一部分不涉及算法,感兴趣的朋友可以自己学习一下相关画图内容,这里笔者不再注释解释,注意的一点是,在笔者使用的pycharm中,show_train_history(train_history,‘accuracy’,‘val_accuracy’) 参数分别为“ accuracy”与“val_accuracy”,而林老师书中是“acc”与“val_acc”,这一区别与版本有关。
#画图显示
import matplotlib.pyplot as plt
def show_train_history(train_history,train,validation):
plt.plot(train_history.history[train])
plt.plot(train_history.history[validation])
plt.title('Train History')
plt.ylabel(train)
plt.xlabel('Epoch')
plt.legend(['train','valization'],loc='upper left')
plt.show()
show_train_history(train_history,'accuracy','val_accuracy')
print(train_history.history.keys())
3.4 进行预测
首先建立评价机制,这里使用evaluate方法,将评价结果存放到scores中。
- x_Test_nomalize为测试数据的特征值
- y_Test_OneHot为测试数据的真实值
#评估模型精确率
scores =model.evaluate(x_Test_nomalize,y_Test_OneHot)
print()
#显示评价结果(测试集预测准确率)
print('accuracy=',scores[1])
之后使用上一章创建的函数显示预测结果 ,具体注释解释可以看上一篇博客,这里把代码复现。
def plot_images_labels_prediction(images,labels,prediction,idx,num=10):
fig = plt.gcf()
fig.set_size_inches(12,14)
if num>25 : num =25
for i in range(0,num):
ax=plt.subplot(5,5,i+1)
ax.imshow(images[idx],cmap='binary')
title = "label="+str(labels[idx])
if len(prediction)>0:
title+=",prediction="+str(prediction[idx])
ax.set_title(title,fontsize=10)
ax.set_xticks([]);ax.set_yticks([])
idx+=1
plt.show()
在最后的预测实施阶段,这里值得注意的一点是,笔者提供的代码与林老师的原文有一些的不同,林老师原文使用的是未正则化的数据,而笔者分别利用两种数据进行预测,大家可以自己测试一下预测效果。
prediction = model.predict_classes(x_Test) #未正则化数据
prediction1 = model.predict_classes(x_Test_nomalize) #使用的是正则化数据
plot_images_labels_prediction(x_test_image,y_test_label,prediction,idx=340)
#plot_images_labels_prediction(x_test_image,y_test_label,prediction1,idx=340)
预测结果如下:
4.小结
使用多层感知器模型来识别MNIST数据集中的手写数字,加入了Drop层以避免过度拟合,准确率接近0.98。对已有图像数据的格式重整,以及在已有框架内填入参数以适合本地数据是比较难以掌握的内容,希望大家多多沟通。