本文将用卷积神经网络模型,对手写数字集minist进行分类识别,用的框架是keras。
MNIST是一个手写体数字的图片数据集,该数据集来由美国国家标准与技术研究所发起整理,一共统计了来自250个不同的人手写数字图片,其中50%是高中生,50%来自人口普查局的工作人员。该数据集的收集目的是希望通过算法,实现对手写数字的识别。
训练集一共包含了 60,000 张图像和标签,而测试集一共包含了 10,000 张图像和标签。测试集中前5000个来自最初NIST项目的训练集.,后5000个来自最初NIST项目的测试集。前5000个比后5000个要规整,这是因为前5000个数据来自于美国人口普查局的员工,而后5000个来自于大学生。
该数据集自1998年起,被广泛地应用于机器学习和深度学习领域,用来测试算法的效果,例如线性分类器(Linear Classifiers)、K-近邻算法(K-Nearest Neighbors)、支持向量机(SVMs)、神经网络(Neural Nets)、卷积神经网络(Convolutional nets)等等。
图1(minist部分手写数据集)
而Keras是一个由Python编写的开源人工神经网络库,可以作为Tensorflow、Microsoft-CNTK和Theano的高阶应用程序接口,进行深度学习模型的设计、调试、评估、应用和可视化 。
Keras在代码结构上由面向对象方法编写,完全模块化并具有可扩展性,其运行机制和说明文档有将用户体验和使用难度纳入考虑,并试图简化复杂算法的实现难度。Keras支持现代人工智能领域的主流算法,包括前馈结构和递归结构的神经网络,也可以通过封装参与构建统计学习模型。在硬件和开发环境方面,Keras支持多操作系统下的多GPU并行计算,可以根据后台设置转化为Tensorflow、Microsoft-CNTK等系统下的组件。因而本文用keras做为框架。
由于手写数字的输入集的长宽都是28像素,色彩空间是黑白的,所以不需要太过于复杂的结构。我将输入数据先进行两次卷积操作,卷积核大小为3×3,再进行一次池化操作,然后接着进行两次卷积操作,再进行一次池化操作,随后将数据展平,构造128维度的全连接层,最后输出10维的数组。在整个网络中,除了最后一次外所有层的激活函数都是relu函数,最后一层的激活函数使用的是softmax函数,损失函数用的是crossentropy函数。如图2所示。
图2 卷积神经网络示意图
图3 卷积神经网络示意图
网络构造的代码如下:
#构建卷积神经网络
def create_model():
model = keras.Sequential()
model.add(layers.Conv2D(5, (3, 3),activation='relu',input_shape=(28,28,1),padding = 'same'))
model.add(layers.Conv2D(5, (3, 3), activation='relu', padding = 'same'))
model.add(layers.MaxPooling2D(pool_size = (2,2)))
model.add(layers.Conv2D(10, (3, 3), activation='relu', padding = 'same'))
model.add(layers.Conv2D(10, (3, 3), activation='relu', padding = 'same'))
model.add(layers.MaxPooling2D(pool_size = (2,2)))
model.add(layers.Flatten())#将数据压缩成一维数组
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
return model
经过25个epoch的训练,最后网络的损失值降到了0.0307,准确率达到了0.99。
准确率的训练变化如图4所示,Train表示训练集的准确率变化,Test表示测试集的准确率变化。
损失值的训练变化如图5所示,Train表示训练集的损失值变化,Test表示测试集的损失值变化。
总训练过程的变化如图6所示。
图4模型准确率变化曲线
图5模型损失值变化曲线
图6 训练过程图
完整代码为:
#coding:gbk
from PIL import Image
import numpy as np
from keras import layers
import keras
import matplotlib.pyplot as plt
import glob
np.set_printoptions(threshold=np.inf)
wid = 28#定义图片的宽
hei = 28#定义图片的长
def process(preimg):#读取图片,将它转化成np.array的格式
imge = Image.open(preimg,'r')
imge = imge.convert('L')
imge = imge.resize((wid,hei))
return (np.asarray(imge))
trainset = []
trainexpe = []
testset = []
testexpe = []
train_src = glob.glob("train_images//*.jpg")#训练数据
test_src = glob.glob("test_images//*.jpg")#测试数据
for data in train_src:#数据填充到数组中
trainset.append(process(data))
datatem = [0] * 10
datatem[int(data [-5])] = 1
trainexpe.append(datatem)
for data in test_src:
testset.append(process(data))
datatem = [0] * 10
datatem[int(data [-5])] = 1
testexpe.append(datatem)
trainset = np.array(trainset).reshape((-1,wid,hei,1))
trainexpe = np.array(trainexpe)
testset = np.array(testset).reshape((-1,wid,hei,1))
testexpe = np.array(testexpe)
def create_model():
model = keras.Sequential()
model.add(layers.Conv2D(5, (3, 3), activation='relu', input_shape=(wid,hei,1),padding = 'same'))
'''''
filters 要去训练多少个卷积核
kernel_size: 卷积核大小
activation: 非线性化所需要去使用的激活函数
input_shape:输入数据的形状
padding:same表示填充一圈,valid表示不填充
strides表示滑动的步长
'''
model.add(layers.Conv2D(5, (3, 3), activation='relu', padding = 'same'))#第二层添加的时候就不需要 input_shape 这个参数了,因为默认是去根据上一层输出的形状进行计算。
model.add(layers.MaxPooling2D(pool_size = (2,2)))#默认maxpooling大小为(2,2)
model.add(layers.Conv2D(10, (3, 3), activation='relu', padding = 'same'))
model.add(layers.Conv2D(10, (3, 3), activation='relu', padding = 'same'))
model.add(layers.MaxPooling2D(pool_size = (2,2)))
#model.summary()#显示model信息
model.add(layers.Flatten())#将数据压缩成一维数组
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation='softmax'))
return model
model = create_model()#load_model("test.h5")
model.compile(optimizer="adam",loss= "categorical_crossentropy",metrics=['accuracy'])
history = model.fit(trainset,trainexpe,batch_size=60,epochs=25,verbose=2, validation_data=(testset,testexpe))
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
# 绘制训练 & 验证的损失值
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
model.save('test.h5')