【PaddlePaddle】 mnist手写数字识别(卷积神经网络)

这篇文章主要讲解了卷积神经网络的使用。卷积神经网络可以用来提取图像特征,所以在计算机视觉上有很好的效果。

系统:ubuntu18.04
python版本:python2.7

训练模型

先把需要用到的模块导入进来。

#coding:utf-8
'''
created on February 10 23:18 2019

@author:lhy
'''
import numpy as np
import paddle as paddle
import paddle.dataset.mnist as mnist
import paddle.fluid as fluid
from PIL import Image
import matplotlib.pyplot as plt

图像识别方面,在卷积神经网络兴起之前,多层感知机一直是很流行的,下面代码就是一共有三层的感知机,前两层是全连接层,经过relu**函数,最后一层是输出层,经过softmax进行归一化,相当于一个分类器。

#多层感知机
def multilayer_perceptron(input):
	#第一个全连接层,**函数relu
	hidden1=fluid.layers.fc(input=input,size=100,act='relu')
	#第二个全连接层,**函数relu
	hidden2=fluid.layers.fc(input=hidden1,size=100,act='relu')
	#以softmax为**函数的全连接输出层,大小为label大小
	fc=fluid.layers.fc(input=hidden2,size=10,act='softmax')
	return fc

卷积神经网络普遍用在图像特征提取上,一些图像分类、目标检测、文字识别几乎都回使用到卷积神经网络作为图像的特征提取方式。卷积神经网络通常由卷积层、池化层和全连接层,有时还有Batch Normalization层和Dropout层。下面搭建一个简单的卷积神经网络,网络结构是:输入层-->卷积层-->最大池化层-->卷积层-->最大池化层-->输出层。我们可以通过调用PaddlePaddle的接口fluid.layers.conv2d()来做一次卷积操作,我们可以通过num_filters参数设置卷积核的数量,通过filter_size设置卷积核的大小,还有通过stride来设置卷积操作时移动的步长。使用fluid.layers.pool2d()接口做一次池化操作,通过参数pool_size可以设置池化的大小,通过参数pool_stride设置池化滑动的步长,通过参数pool_type设置池化的类型,目前有最大池化和平均池化,下面使用的时最大池化max,当值为avg时是平均池化。

#卷积神经网络
def convolutional_neural_network(input):
	#卷积层,卷积核大小为3*3,步长是1,一共有32个卷积核
	conv_1=fluid.layers.conv2d(input=input,num_filters=32,filter_size=3,stride=1)
	#池化层,池化核大小为2*2,步长是1,最大池化
	pool_1=fluid.layers.pool2d(input=conv_1,pool_size=2,pool_stride=1,pool_type='max')
	#第二个卷积层,卷积核大小为3*3,步长1,一共有64个卷积核
	conv_2=fluid.layers.conv2d(input=pool_1,num_filters=32,filter_size=3,stride=1)
	#第二个池化层,池化核大小是2*2,步长1,最大池化
	pool_2=fluid.layers.pool2d(input=conv_2,pool_size=2,pool_stride=1,pool_type='max')
	#以softmax为**函数的全连接输出层,大小为label的大小
	#softmax一般用于多分类问题最后一层输出层的**函数,作用是对输出归一化,这种情况下一般损失函数使用交叉熵
	fc=fluid.layers.fc(input=pool_2,size=10,act='softmax')
	return fc

接下来定义占位输入输出层、损失函数、优化器、reader、执行器、feeder等。

#定义占位输入层和标签层
#图像是28*28的灰度图,所以输入的形状是[1,28,28](灰度图是1通道,彩图3通道),理论上应该还有一个维度是Batch,PaddlePaddle帮我们默认设置,可以不设置Batch
image=fluid.layers.data(name='image',shape=[1,28,28],dtype='float32')
label=fluid.layers.data(name='label',shape=[1],dtype='int64')

#获取前向传播网络结果
#result=multilayer_perceptron(image)	#使用多层感知机
result=convolutional_neural_network(image) #使用卷积神经网络
	
#定义损失函数和准确率函数
cost=fluid.layers.cross_entropy(input=result,label=label)#交叉熵
avg_cost=fluid.layers.mean(cost)#整个Batch的平均值
accuracy=fluid.layers.accuracy(input=result,label=label)

#克隆主程序,获得一个预测程序
test_program=fluid.default_main_program().clone(for_test=True)

#定义优化器,使用Adam优化器
optimizer=fluid.optimizer.AdamOptimizer(learning_rate=0.001)
opts=optimizer.minimize(avg_cost)

#获取mnist数据集的reader,指定一个Batch的大小为128,一次喂入的张量shape是[128,1,28,28]
train_reader=paddle.batch(mnist.train(),batch_size=128)
test_reader=paddle.batch(mnist.test(),batch_size=128)

#定义执行器
place=fluid.CPUPlace()
exe=fluid.Executor(place)
#初始化参数
exe.run(fluid.default_startup_program())

#定义输入数据维度
feeder=fluid.DataFeeder(place=place,feed_list=[image,label])

接下来开始训练5个pass,每一个pass结束,就使用测试集进行测试。

#开始训练,5个pass
for pass_id in range(5):
	#开始训练
	for batch_id,data in enumerate(train_reader()):
		train_cost,train_acc=exe.run(program=fluid.default_main_program(),feed=feeder.feed(data),fetch_list=[avg_cost,accuracy])
		#每100个batch打印一次信息
		if batch_id%100==0:
			print('Pass:%d Batch:%d Cost:%0.5f Accuracy:%0.5f'%(pass_id,batch_id,train_cost[0],train_acc[0]))

	#每一个pass进行一次测试
	test_accs=[]
	test_costs=[]
	for batch_id,data in enumerate(test_reader()):
		test_cost,test_acc=exe.run(program=test_program,feed=feeder.feed(data),fetch_list=[avg_cost,accuracy])
		test_accs.append(test_acc[0])
		test_costs.append(test_cost[0])
	#求测试结果的平均值
	test_cost=(sum(test_costs)/len(test_costs))
	test_acc=(sum(test_accs)/len(test_accs))
	print('Test:%d Cost:%0.5f Accuracy:%0.5f'%(pass_id,test_cost,test_acc))

输出结果:

Pass:0 Batch:0 Cost:4.91017 Accuracy:0.07031
Pass:0 Batch:100 Cost:0.16996 Accuracy:0.94531
Pass:0 Batch:200 Cost:0.15141 Accuracy:0.96875
Pass:0 Batch:300 Cost:0.13084 Accuracy:0.96094
Pass:0 Batch:400 Cost:0.17776 Accuracy:0.96875
Test:0 Cost:0.09316 Accuracy:0.97063
Pass:1 Batch:0 Cost:0.13053 Accuracy:0.96875
Pass:1 Batch:100 Cost:0.10468 Accuracy:0.96875
Pass:1 Batch:200 Cost:0.08788 Accuracy:0.96875
Pass:1 Batch:300 Cost:0.09806 Accuracy:0.97656
Pass:1 Batch:400 Cost:0.10122 Accuracy:0.96875
Test:1 Cost:0.08290 Accuracy:0.97488
Pass:2 Batch:0 Cost:0.09273 Accuracy:0.98438
Pass:2 Batch:100 Cost:0.04793 Accuracy:0.98438
Pass:2 Batch:200 Cost:0.05122 Accuracy:0.98438
Pass:2 Batch:300 Cost:0.13064 Accuracy:0.97656
Pass:2 Batch:400 Cost:0.07900 Accuracy:0.96094
Test:2 Cost:0.10492 Accuracy:0.97340
Pass:3 Batch:0 Cost:0.06448 Accuracy:0.98438
Pass:3 Batch:100 Cost:0.02005 Accuracy:0.99219
Pass:3 Batch:200 Cost:0.07889 Accuracy:0.97656
Pass:3 Batch:300 Cost:0.09428 Accuracy:0.97656
Pass:3 Batch:400 Cost:0.08107 Accuracy:0.97656
Test:3 Cost:0.20494 Accuracy:0.95896
Pass:4 Batch:0 Cost:0.19759 Accuracy:0.96875
Pass:4 Batch:100 Cost:0.08347 Accuracy:0.97656
Pass:4 Batch:200 Cost:0.03714 Accuracy:0.97656
Pass:4 Batch:300 Cost:0.08429 Accuracy:0.97656
Pass:4 Batch:400 Cost:0.02875 Accuracy:0.98438
Test:4 Cost:0.10599 Accuracy:0.97577

可以看到,利用卷积神经网络,训练集的准确率达到了98.4%,而在测试集上的准确率达到了97.5%。

进行预测

首先我们需要对输入的图片进行预处理,首先转化成灰度图片(模式是’L’),然后改变图片大小成28*28,将图片变成数组,再进行归一化:
需要注意的是,输入的照片最好是黑底白字,因为mnist数据集的数据是黑底白字的照片转化成的数组。

#训练完成后,我们可以使用从主程序中克隆的test_program来预测自己的图像
#对自己的图像进行预处理,首先灰度化,然后压缩图像大小为28*28,接着将图像转换成一维向量,最后在对一维向量进行归一化处理
def load_image(file):
	im=Image.open(file).convert('L')
	im=im.resize((28,28),Image.ANTIALIAS)
	im=np.array(im).reshape(1,1,28,28).astype(np.float32)
	im=im/255.0 *2.0 -1.0#归一化
	#mnist数据集中的图片默认格式是黑底白字,所以如果有需要则要进行变换
	return im

我们放这样一张照片,显示的数字是3:
【PaddlePaddle】 mnist手写数字识别(卷积神经网络)
然后加载这张照片,并用克隆的主程序进行预测,label随便传入一个值(因为不需要用到损失函数,只需要用到result)。

#加载数据进行预测
img=load_image('3.jpeg')
results=exe.run(program=test_program,feed={'image':img,'label':np.array([[1]]).astype('int64')},fetch_list=[result])

#将预测标签概率最大的下标打印出来
lab=np.argsort(results)#np.argsort()返回数组值从小到大的索引值
print("图像预测的结果为:%d"%(lab[0][0][-1]))#选择值最大对应的索引值

预测结果:

图像预测的结果为:3

可见,训练的出的程序能正确得出一张图片的数字值。

完整代码

完整代码如下:

#coding:utf-8
'''
created on February 10 23:18 2019

@author:lhy
'''
import numpy as np
import paddle as paddle
import paddle.dataset.mnist as mnist
import paddle.fluid as fluid
from PIL import Image
import matplotlib.pyplot as plt

#多层感知机
def multilayer_perceptron(input):
	#第一个全连接层,**函数relu
	hidden1=fluid.layers.fc(input=input,size=100,act='relu')
	#第二个全连接层,**函数relu
	hidden2=fluid.layers.fc(input=hidden1,size=100,act='relu')
	#以softmax为**函数的全连接输出层,大小为label大小
	fc=fluid.layers.fc(input=hidden2,size=10,act='softmax')
	return fc

#卷积神经网络
def convolutional_neural_network(input):
	#卷积层,卷积核大小为3*3,步长是1,一共有32个卷积核
	conv_1=fluid.layers.conv2d(input=input,num_filters=32,filter_size=3,stride=1)
	#池化层,池化核大小为2*2,步长是1,最大池化
	pool_1=fluid.layers.pool2d(input=conv_1,pool_size=2,pool_stride=1,pool_type='max')
	#第二个卷积层,卷积核大小为3*3,步长1,一共有64个卷积核
	conv_2=fluid.layers.conv2d(input=pool_1,num_filters=32,filter_size=3,stride=1)
	#第二个池化层,池化核大小是2*2,步长1,最大池化
	pool_2=fluid.layers.pool2d(input=conv_2,pool_size=2,pool_stride=1,pool_type='max')
	#以softmax为**函数的全连接输出层,大小为label的大小
	#softmax一般用于多分类问题最后一层输出层的**函数,作用是对输出归一化,这种情况下一般损失函数使用交叉熵
	fc=fluid.layers.fc(input=pool_2,size=10,act='softmax')
	return fc

#定义占位输入层和标签层
#图像是28*28的灰度图,所以输入的形状是[1,28,28](灰度图是1通道,彩图3通道),理论上应该还有一个维度是Batch,PaddlePaddle帮我们默认设置,可以不设置Batch
image=fluid.layers.data(name='image',shape=[1,28,28],dtype='float32')
label=fluid.layers.data(name='label',shape=[1],dtype='int64')

#获取前向传播网络结果
#result=multilayer_perceptron(image)	#使用多层感知机
result=convolutional_neural_network(image)
	
#定义损失函数和准确率函数
cost=fluid.layers.cross_entropy(input=result,label=label)#交叉熵
avg_cost=fluid.layers.mean(cost)#整个Batch的平均值
accuracy=fluid.layers.accuracy(input=result,label=label)

#克隆主程序,获得一个预测程序
test_program=fluid.default_main_program().clone(for_test=True)

#定义优化器,使用Adam优化器
optimizer=fluid.optimizer.AdamOptimizer(learning_rate=0.001)
opts=optimizer.minimize(avg_cost)

#获取mnist数据集的reader,指定一个Batch的大小为128,一次喂入的张量shape是[128,1,28,28]
train_reader=paddle.batch(mnist.train(),batch_size=128)
test_reader=paddle.batch(mnist.test(),batch_size=128)

#定义执行器
place=fluid.CPUPlace()
exe=fluid.Executor(place)
#初始化参数
exe.run(fluid.default_startup_program())

#定义输入数据维度
feeder=fluid.DataFeeder(place=place,feed_list=[image,label])

#开始训练,5个pass

for pass_id in range(5):
	#开始训练
	for batch_id,data in enumerate(train_reader()):
		train_cost,train_acc=exe.run(program=fluid.default_main_program(),feed=feeder.feed(data),fetch_list=[avg_cost,accuracy])
		#每100个batch打印一次信息
		if batch_id%100==0:
			print('Pass:%d Batch:%d Cost:%0.5f Accuracy:%0.5f'%(pass_id,batch_id,train_cost[0],train_acc[0]))

	#每一个pass进行一次测试
	test_accs=[]
	test_costs=[]
	for batch_id,data in enumerate(test_reader()):
		test_cost,test_acc=exe.run(program=test_program,feed=feeder.feed(data),fetch_list=[avg_cost,accuracy])
		test_accs.append(test_acc[0])
		test_costs.append(test_cost[0])
	#求测试结果的平均值
	test_cost=(sum(test_costs)/len(test_costs))
	test_acc=(sum(test_accs)/len(test_accs))
	print('Test:%d Cost:%0.5f Accuracy:%0.5f'%(pass_id,test_cost,test_acc))

#训练完成后,我们可以使用从主程序中克隆的test_program来预测自己的图像
#对自己的图像进行预处理,首先灰度化,然后压缩图像大小为28*28,接着将图像转换成一维向量,最后在对一维向量进行归一化处理
def load_image(file):
	im=Image.open(file).convert('L')
	im=im.resize((28,28),Image.ANTIALIAS)
	im=np.array(im).reshape(1,1,28,28).astype(np.float32)
	im=im/255.0 *2.0 -1.0#归一化
	#mnist数据集中的图片默认格式是黑底白字,所以如果有需要则要进行变换
	return im

#加载数据进行预测
img=load_image('3.jpeg')
results=exe.run(program=test_program,feed={'image':img,'label':np.array([[1]]).astype('int64')},fetch_list=[result])

#将预测标签概率最大的下标打印出来
lab=np.argsort(results)#返回数组值从小到大的索引值
print("图像预测的结果为:%d"%(lab[0][0][-1]))