任务要求:从原始mnist测试数据集中,随机抽取出100张图片,测试下模型的分类准确率
目录
step1 导入相关库函数
import os
import random
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
step2 定义数据集读取器,按批次读取
def load_data(mode='train'):
# 读取数据文件
datafile = './work/mnist.json.gz'
print('loading mnist dataset from {} ......'.format(datafile))
data = json.load(gzip.open(datafile))
# 读取数据集中的训练集,验证集和测试集
train_set, val_set, eval_set = data
# 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLS
IMG_ROWS = 28
IMG_COLS = 28
# 根据输入mode参数决定使用训练集,验证集还是测试
if mode == 'train':
imgs = train_set[0]
labels = train_set[1]
elif mode == 'valid':
imgs = val_set[0]
labels = val_set[1]
elif mode == 'eval':
imgs = eval_set[0]
labels = eval_set[1]
# 获得所有图像的数量
imgs_length = len(imgs)
# 验证图像数量和标签数量是否一致
assert len(imgs) == len(labels), \
"length of train_imgs({}) should be the same as train_labels({})".format(
len(imgs), len(labels))
index_list = list(range(imgs_length))
# 读入数据时用到的batchsize
BATCHSIZE = 100
# 定义数据生成器
def data_generator():
# 训练模式下,打乱训练数据
if mode == 'train':
random.shuffle(index_list)
imgs_list = []
labels_list = []
# 按照索引读取数据
for i in index_list:
# 读取图像和标签,转换其尺寸和类型
img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')
label = np.reshape(labels[i], [1]).astype('int64')
imgs_list.append(img)
labels_list.append(label)
# 如果当前数据缓存达到了batch size,就返回一个批次数据
if len(imgs_list) == BATCHSIZE:
yield np.array(imgs_list), np.array(labels_list)
# 清空数据缓存列表
imgs_list = []
labels_list = []
# 如果剩余数据的数目小于BATCHSIZE,
# 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch
if len(imgs_list) > 0:
yield np.array(imgs_list), np.array(labels_list)
return data_generator
step3 定义网络结构
class MNIST(fluid.dygraph.Layer):
def __init__(self):
super(MNIST, self).__init__()
# 定义一个卷积层,使用relu激活函数
self.conv1 = Conv2D(num_channels=1, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
# 定义一个池化层,池化核为2,步长为2,使用最大池化方式
self.pool1 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
# 定义一个卷积层,使用relu激活函数
self.conv2 = Conv2D(num_channels=20, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
# 定义一个池化层,池化核为2,步长为2,使用最大池化方式
self.pool2 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
# 定义一个全连接层,输出节点数为10
self.fc = Linear(input_dim=980, output_dim=10, act='softmax')
# 定义网络的前向计算过程
def forward(self, inputs, label):
x = self.conv1(inputs)
x = self.pool1(x)
x = self.conv2(x)
x = self.pool2(x)
x = fluid.layers.reshape(x, [x.shape[0], 980])
x = self.fc(x)
if label is not None:
acc = fluid.layers.accuracy(input=x, label=label)
return x, acc
else:
return x
step4 加载数据集并开始训练,且加入正则化项,用GPU训练
#调用加载数据的函数
train_loader = load_data('train')
#在使用GPU机器时,可以将use_gpu变量设置成True
use_gpu = True
place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
with fluid.dygraph.guard(place):
model = MNIST()
model.train()
#四种优化算法的设置方案,可以逐一尝试效果
# optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
#optimizer = fluid.optimizer.MomentumOptimizer(learning_rate=0.01, momentum=0.9, parameter_list=model.parameters())
#optimizer = fluid.optimizer.AdagradOptimizer(learning_rate=0.01, parameter_list=model.parameters())
optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.01, regularization=fluid.regularizer.L2Decay(regularization_coeff=0.1), parameter_list=model.parameters())
EPOCH_NUM = 5
for epoch_id in range(EPOCH_NUM):
for batch_id, data in enumerate(train_loader()):
#准备数据
image_data, label_data = data
image = fluid.dygraph.to_variable(image_data)
label = fluid.dygraph.to_variable(label_data)
#前向计算的过程,同时拿到模型输出值和分类准确率
predict, acc = model(image, label)
#计算损失,取一个批次样本损失的平均值
loss = fluid.layers.cross_entropy(predict, label)
avg_loss = fluid.layers.mean(loss)
#每训练了200批次的数据,打印下当前Loss的情况
if batch_id % 200 == 0:
print("epoch: {}, batch: {}, loss is: {}, acc is {}".format(epoch_id, batch_id, avg_loss.numpy(), acc.numpy()))
#后向传播,更新参数的过程
avg_loss.backward()
optimizer.minimize(avg_loss)
model.clear_gradients()
#保存模型参数
fluid.save_dygraph(model.state_dict(), 'mnist')
训练结果如下图:
step5 加载测试数据集并随机抽取100张图片
# 调用api获取全部数据
trainset = paddle.dataset.mnist.test()
imgs = []
labels = []
test_imgs = []
test_labels = []
IMG_ROWS = 32
IMG_COLS = 32
for img, label in trainset():
imgs.append(img)
labels.append(label)
imgs_length = len(imgs)
# 定义数据集每个数据的序号,根据序号读取数据
index_list = list(range(imgs_length))
# 读入数据时用到的批次大小
BATCHSIZE = 100
# 随机打乱训练数据的索引序号
random.shuffle(index_list)
for i in index_list:
img = np.reshape(imgs[i], [1, 28, 28]).astype('float32')
label = np.reshape(labels[i], [1]).astype('int64')
test_imgs.append(img)
test_labels.append(label)
if len(test_imgs) == 100:
break
# 转换数据格式
test_imgs = np.array(test_imgs)
test_labels = np.array(test_labels)
print('图片个数为:', len(test_imgs))
输出如下:
step6 进行测试,输出acc
with fluid.dygraph.guard():
print('start evaluation .......')
#加载模型参数
model = MNIST()
model_state_dict, _ = fluid.load_dygraph('mnist')
model.load_dict(model_state_dict)
model.eval()
img = fluid.dygraph.to_variable(test_imgs)
label = fluid.dygraph.to_variable(test_labels)
prediction, acc = model(img, label)
loss = fluid.layers.cross_entropy(input=prediction, label=label)
for i in range(len(test_imgs)):
result = '正确' if test_labels[i] == np.argmax(prediction[i].numpy()) else '错误 ******************'
print('第 {} 张图片的真实标签为:{}, 预测标签为:[{}], loss = {}, \t预测 {}'.format(i, test_labels[i], np.argmax(prediction[i].numpy()), loss[i].numpy(), result))
avg_loss = fluid.layers.mean(loss)
print('\navg_loss = {}, acc = {}'.format(avg_loss.numpy(), acc.numpy()))
部分输出如下:
小结
从上面的结果可以看出,测试数据的精确度达到了0.96,而训练结果最后的精确度只有0.92,说明加入了正则化之后,确实可以缓解过拟合的问题。