PaddlePaddle2.0构建LeNet-5模型进行MNIST手写数字分类
一、完整代码
import paddle
import paddle.nn.functional as F
from paddle.vision.transforms import Compose, Normalize
transform = Compose([Normalize(mean=[127.5],
std=[127.5],
data_format='CHW')])
#导入MNIST数据
train_dataset=paddle.vision.datasets.MNIST(mode="train", transform=transform)
val_dataset=paddle.vision.datasets.MNIST(mode="test", transform=transform)
#定义模型
class LeNetModel(paddle.nn.Layer):
def __init__(self):
super(LeNetModel, self).__init__()
# 创建卷积和池化层块,每个卷积层后面接着2x2的池化层
#卷积层L1
self.conv1 = paddle.nn.Conv2D(in_channels=1,
out_channels=6,
kernel_size=5,
stride=1)
#池化层L2
self.pool1 = paddle.nn.MaxPool2D(kernel_size=2,
stride=2)
#卷积层L3
self.conv2 = paddle.nn.Conv2D(in_channels=6,
out_channels=16,
kernel_size=5,
stride=1)
#池化层L4
self.pool2 = paddle.nn.MaxPool2D(kernel_size=2,
stride=2)
#线性层L5
self.fc1=paddle.nn.Linear(256,120)
#线性层L6
self.fc2=paddle.nn.Linear(120,84)
#线性层L7
self.fc3=paddle.nn.Linear(84,10)
#正向传播过程
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.pool1(x)
x = F.relu(x)
x = self.conv2(x)
x = self.pool2(x)
x = paddle.flatten(x, start_axis=1,stop_axis=-1)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = F.relu(x)
out = self.fc3(x)
return out
model=paddle.Model(LeNetModel())
model.prepare(paddle.optimizer.Adam(parameters=model.parameters()),
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy())
model.fit(train_dataset,
epochs=5,
batch_size=64,
save_dir='my_model',
verbose=1)
#获取测试集的第一个图片
val_data0, val_label_0 = val_dataset[0][0],val_dataset[0][1]
val_data0 = val_data0.reshape([28,28])
plt.figure(figsize=(2,2))
#展示测试集中的第一个图片
# print(plt.imshow(test_data0, cmap=plt.cm.binary))
plt.imshow(val_data0, cmap=plt.cm.binary)
print('val_data0 的标签为: ' + str(val_label_0))
#模型预测
result = model.predict(val_dataset, batch_size=1)
#打印模型预测的结果
print('val_data0 预测的数值为:%d' % np.argsort(result[0][0])[0][-1])
测试结果为:
二、LeNet-5结构
注:手写数字数据集MINST的驶入图片大小为28*28。
数据集中图片的可视化:
import matplotlib.pyplot as plt
#看看数据集中的图片是什么样子的
train_data0, train_label_0 = train_dataset[0][0],train_dataset[0][1]
train_data0 = train_data0.reshape([28,28])
plt.figure(figsize=(2,2))
plt.imshow(train_data0)
print('train_data0 的标签为: ' + str(train_label_0))
三、详细程序解释
import paddle
import paddle.nn.functional as F
from paddle.vision.transforms import Compose, Normalize
paddle 目录下包含tensor、device、framework相关API以及某些高层API。
paddle.nn 目录下包含飞桨框架支持的神经网络层和相关函数的相关API。
paddle.vision 目录是飞桨在视觉领域的高层API,其中transforms中的:
Compose将用于数据集预处理的接口以列表的方式进行组合;
Normalize用于图像归一化处理,支持两种方式:
- 用统一的均值和标准差值对图像的每个通道进行归一化处理;
- 对每个通道指定不同的均值和标准差值进行归一化处理。
计算过程为:
output[channel] = (input[channel] - mean[channel]) / std[channel]
结果被限制在[-1,1]之间,将归一化后的图片像素数值可视化,可见(部分):
#导入MNIST数据
train_dataset=paddle.vision.datasets.MNIST(mode="train", transform=transform)
val_dataset=paddle.vision.datasets.MNIST(mode="test", transform=transform)
获取MINST数据分配给训练和测试两部分,并且将数据归一化。
self.conv1 = paddle.nn.Conv2D(in_channels=1,out_channels=6,kernel_size=5,stride=1)
表示输入是1组,输出是6组(也就是6组卷积核),卷积核的大小是5*5,每次移动的步长为1。
self.pool1 = paddle.nn.MaxPool2D(kernel_size=2,stride=2)
表示按照2*2的大小进行最大值池化,步长是2,也就是每次最大值池化没有重叠部分。
self.fc1=paddle.nn.Linear(256,120)
全连接层,输入是被展开成一列的16*(4*4),长度为256的tensor,输出为长度为120的tensor。
官方2.0版本的API后面是不接激活函数的,这样使用什么样的激活函数就比较灵活了,自己在方法中引用。
x = F.relu(x)
激活函数,用于增加网络的非线性。
model=paddle.Model(LeNetModel())
Model 对象是一个具备训练、测试、推理的神经网络。该对象同时支持静态图和动态图模式,飞桨框架默认为动态图模式,通过 paddle.enable_static() 来切换到静态图模式。需要注意的是,需要在实例化 Model 对象之前完成切换。
model.prepare(paddle.optimizer.Adam(parameters=model.parameters()),
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy())
在正式的训练之前定义优化器,损失函数,精确度度量形式。
model.fit(train_dataset,
epochs=5,
batch_size=64,
save_dir='my_model',
verbose=1)
verbose (int) 可视化的模型,必须为0,1,2。当设定为0时,不打印日志,设定为1时,使用进度条的方式打印日志,设定为2时,一行一行地打印日志。默认值:2。save_dir=‘my_model’,讲训练好的模型保存下来,方便之后的预测过程使用。