『跟着雨哥学AI』系列之五:快速上手趣味案例FashionMNIST

01/25 18:46
阅读数 5

点击左上方蓝字关注我们

课程简介:

“跟着雨哥学AI”是百度飞桨开源框架近期针对高层API推出的系列课。本课程由多位资深飞桨工程师精心打造,不仅提供了从数据处理、到模型组网、模型训练、模型评估和推理部署全流程讲解;还提供了丰富的趣味案例,旨在帮助开发者更全面清晰地掌握百度飞桨框架的用法,并能够举一反三、灵活使用飞桨框架进行深度学习实践。

前四节课我们已经完成对飞桨高层API的系统性学习,从这节课开始,我们就进入到实战项目--趣味案例的实现。

本次课程链接:

https://aistudio.baidu.com/aistudio/projectdetail/1439753

如果大家对本课程还存有疑问,欢迎在评论区留言,我们会一一回答。那接下来就开始动手实践今天的趣味案例吧。

1. 关于MNIST与FashionMNIST

1.1 什么是MNIST?

相信大部分同学在入门深度学习的时候都听过或使用过MNIST数据集,经典的MNIST数据集包含了大量的手写数字。十几年来,来自机器学习机器视觉人工智能深度学习领域的研究员们把这个数据集作为衡量算法的基准之一。你会在很多的会议,期刊的论文中发现这个数据集的身影。有人曾调侃道:"如果一个算法在MNIST不work, 那么它就根本没法用;而如果它在MNIST上work, 它在其他数据上也可能不work!"因此,MNIST数据集已经成为算法作者的必测的数据集之一,属于深度学习届的“Hello World”。

1.2 什么是FashionMNIST?

FashionMNIST是一个替代MNIST手写数字集的图像数据集,它是由Zalando(一家德国的时尚科技公司)旗下的研究部门提供。其涵盖了来自10种类别的共7万个不同商品的正面图片。FashionMNIST的大小、格式和训练集/测试集划分与原始的MNIST完全一致。60000/10000的训练测试数据划分,28x28的灰度图片。你可以直接用它来测试你的机器学习和深度学习算法性能,且不需要改动任何的代码。

1.3 FashionMNIST和MNIST有哪些区别?

MNIST是包含了从0-9共10个分类的单通道手写数字图片,而不同于MNIST手写数据集,FashionMNIST数据集包含了t-shirt(T恤)、trouser(牛仔裤)、pullover(套衫)、dress(裙子)、coat(外套)、sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)和ankle boot(短靴)共10个类别的单通道图像,从数据集上看包含的信息会比MNIST复杂。

1.4 为什么要使用FashionMNIST数据集?

  • MNIST太简单了

很多深度学习算法在MNIST测试集上的准确率已经达到99.6%!参考:"Most pairs of MNIST digits can be distinguished pretty well by just one pixel"(翻译:大多数MNIST只需要一个像素就可以区分开!)

  • MNIST被用烂了

参考:"Ian Goodfellow wants people to move away from mnist"(翻译:Ian Goodfellow希望人们不要再用MNIST了。)

  • MNIST不能代表现代计算机视觉任务

参考:"François Cholle: Ideas on MNIST do not transfer to real CV" (翻译:在MNIST上看似有效的想法没法迁移到真正的机器视觉问题上。)

2. 基于FashionMNIST数据集

使用LeNet卷积神经网络

完成图像分类

2.1 LeNet模型详解

LeNet是1998年提出的一种典型的卷积神经网络,它被用于数字识别并取得了巨大的成功。LeNet这个网络虽然很小,但是它包含了深度学习的基本模块:卷积层,池化层,全连接层,是其他深度学习模型的基础。这里我们详细讲解一下LeNet模型,对其进行深入分析,加深同学们对与卷积层和池化层的理解。

LeNet模型非常简单,包含7层,由2个卷积层,2个池化层3个全连接层组成,通过连续使用卷积层和池化层的组合提取图像特征,其模型架构图[1]如下所示:

  • 第一层,卷积层

这一层的输入就是原始的图像像素,LeNet-5 模型接受的输入层大小为32×32×1。第一个卷积层过滤器的尺寸为5×5,深度为6不使用全0填充,步长为1。因为没有使用全0填充,所以这一层的输出的尺寸为32-5+1=28,深度为6。这一个卷积层总共有5×5×1×6+6=156个参数,其中6个为偏置项参数。因为下一层的节点矩阵有28×28×6=4704个节点,每个节点和5×5=25个当前层节点相连,所以本层卷积层总共有4704×(25+1)=122304个连接 。

  • 第二层,池化层

这一层的输入为第一层的输出,是一个28×28×6的节点矩阵。本层采用的过滤器大小为 2×2,长和宽的步长均为2,所以本层的输出矩阵大小为14×14×6

  • 第三层,卷积层

本层的输入矩阵大小为14×14×6,使用的过滤器大小为5x5,深度为16。本层不使用全0填充,步长为1。本层的输出矩阵大小为10×10×16 。按照标准的卷积层,本层应该有5×5×6×16+16=2416个参数,10×10×16×(25+1)=41600个连接。

  • 第四层,池化层

本层的输入矩阵大小为10×10×16,采用的过滤器大小为2×2,步长为2。本层的输出矩阵大小为5×5×16

  • 第五层,全连接层

本层的输入矩阵大小为5×5×16,在 LeNet-5 模型的论文中将这一层称为卷积层,但是因为过滤器的大小就是 5×5,所以和全连接层没有区别,本层的输出节点个数为120,总共有5×5×16×120+120=48120 个参数。

  • 第六层,全连接层

本层的输入节点个数为120个,输出节点个数为84个,总共参数为120×84+84=10164

  • 第七层,全连接层

本层的输入节点个数为84个,输出节点个数为10个,总共参数为84×10+10=850个 。

2.2 模型实现过程

使用飞桨完成FashionMNIST识别模型任务的代码结构如下图所示。首先进行数据的处理,第二步为模型的设计,本次教程中我们使用LeNet作为识别模型,接下来进行模型的配置然后是模型的训练和测试。

2.2.1 数据集加载

在本次案例中,我们使用FashionMNIST数据集。该数据集在飞桨自带的paddle.vision.dataset接口中,我们可以通过调用该接口获取到该数据集并完成数据集的加载,并且通过paddle.vision.transforms接口可以对数据进行各种预处理操作。如下所示:

import paddle
paddle.__version__

'2.0.0-rc1'
import paddle.vision.transforms as T

transform = T.Compose([T.Normalize(mean=[127.5],
                                   std=[127.5],
                                   data_format='CHW')])
# 使用transform对数据集做归一化
print('Start download training data and load training data.')

# 加载FashionMNIST数据集
train_dataset = paddle.vision.datasets.FashionMNIST(mode='train', transform=transform)
test_dataset = paddle.vision.datasets.FashionMNIST(mode='test', transform=transform)
print('Finished.')

Start download training data and load training data.
Finished.

2.2.2 模型组网

LeNet模型是一种用于手写体字符识别的非常高效的卷积神经网络。上面的章节中我们已经对LeNet模型结构进行非常详细的讲解,我们使用飞桨paddle.nn下的API,如Conv2D、MaxPool2D、Linear完成LeNet的构建。

import paddle.nn.functional as F

class LeNet(paddle.nn.Layer):
    def __init__(self):
        super(LeNet, self).__init__()

        self.conv1 = paddle.nn.Conv2D(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=2)
        self.max_pool1 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
        self.conv2 = paddle.nn.Conv2D(in_channels=6, out_channels=16, kernel_size=5, stride=1)
        self.max_pool2 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
        self.linear1 = paddle.nn.Linear(in_features=16 * 5 * 5, out_features=120)
        self.linear2 = paddle.nn.Linear(in_features=120, out_features=84)
        self.linear3 = paddle.nn.Linear(in_features=84, out_features=10)

    def forward(self, inputs):
        y = self.conv1(inputs)
        y = F.relu(y)
        y = self.max_pool1(y)
        y = F.relu(y)
        y = self.conv2(y)
        y = self.max_pool2(y)
        y = paddle.flatten(y, start_axis=1, stop_axis=-1)
        y = self.linear1(y)
        y = F.relu(y)
        y = self.linear2(y)
        y = F.relu(y)
        y = self.linear3(y)

        return y

2.2.3 模型配置

首先通过使用paddle提供的Model构建实例,然后使用model.prepare接口进行模型的配置,比如优化器损失函数评价指等。

model = paddle.Model(LeNet())   # 用Model封装模型

# 配置模型
model.prepare(paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters()), 
              paddle.nn.CrossEntropyLoss(), 
              paddle.metric.Accuracy())

2.2.4 模型训练

模型配置完成后我们直接使用封装好的训练接口model.fit,快速完成模型训练,我们设置epochs为50batchsize为64

model.fit(train_dataset, epochs=50, batch_size=64, verbose=1)
The loss value printed in the log is the current step, and the metric is the average value of previous step.
Epoch 1/50
step  20/938 [..............................] - loss: 1.1545 - acc: 0.4984 - ETA: 22s - 24ms/ste

step  30/938 [..............................] - loss: 0.7657 - acc: 0.5630 - ETA: 17s - 19ms/step

……

Epoch 49/50
step 938/938 [==============================] - loss: 5.5819e-04 - acc: 0.9892 - 9ms/step    
Epoch 50/50
step 938/938 [==============================] - loss: 3.2794e-05 - acc: 0.9907 - 9ms/step      

2.2.5 模型评估

我们使用model.evaluate接口来进行模型的评估操作,来用测试集对我们的模型性能进行测试评价,如下代码所示:

eval_result = model.evaluate(test_dataset, verbose=1)

print(eval_result)
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 10000/10000 [==============================] - loss: 0.0016 - acc: 0.8873 - 2ms/step             
Eval samples: 10000
{'loss': [0.0015905361], 'acc': 0.8873}

2.2.6 模型预测

模型精度符合预期之后,我们直接调用model.predict接口进行模型的预测,这里我们没有另外构建预测的数据集,直接复用了验证数据集,我们实际应用时可以使用我们提前准备好的用于做模型预测验证的数据来做相关动作,具体代码如下所示:

predict_result = model.predict(test_dataset)
Predict begin...
step 10000/10000 [==============================] - 2ms/step        
Predict samples: 10000

2.2.7 模型保存

我们训练完模型后可以将模型的参数和训练过程中优化器的信息保存到指定的路径,以及推理所需的参数与文件。如果training=True,所有的模型参数都会保存到一个后缀为 .pdparams 的文件中。所有的优化器信息和相关参数,比如 Adam 优化器中的 beta1 , beta2 ,momentum 等,都会被保存到后缀为 .pdopt。如果优化器比如SGD没有参数,则该不会产生该文件。如果training=False,则不会保存上述说的文件。只会保存推理需要的参数文件和模型文件。

具体的信息可以参见:⬇️模型存储与载入⬇️

https://www.paddlepaddle.org.cn/documentation/docs/zh/2.0-rc1/guides/02_paddle2.0_develop/08_model_save_load_cn.html

model.save('checkpoint/test')  # save for training
model.save('inference_model', training=False)  # save for inference

总结

本节课和大家一起结合前面所学的知识完成了第一个趣味案例,即FashionMNIST快速上手,下节课我们将一起完成证件照的制作案例,大家是不是非常感兴趣呢?大家如果有什么其他感兴趣的趣味案例都可以在评论区留言,我们将会在后续的课程中给大家安排上哈,今天的课程到这里就结束了,我是雨哥,下节课再见啦~

有任何问题可以在本项目中评论或到飞桨Github仓库提交Issue。

同时欢迎扫码加入飞桨框架高层API技术交流群


回顾往期:

第一篇:『跟着雨哥学AI』系列:详解飞桨框架数据管道

第二篇:『跟着雨哥学AI』系列之二:详解飞桨框架模型组网

第三篇:『跟着雨哥学AI』系列之三:详解飞桨框架模型训练

第四篇:『跟着雨哥学AI』系列之四:详解飞桨框架高阶用法

如在使用过程中有问题,可加入官方QQ群进行交流:778260830。

如果您想详细了解更多飞桨的相关内容,请参阅以下文档。

·飞桨官网地址·

https://www.paddlepaddle.org.cn/

·飞桨开源框架项目地址·

GitHub: https://github.com/PaddlePaddle/Paddle 

Gitee: https://gitee.com/paddlepaddle/Paddle

飞桨(PaddlePaddle)以百度多年的深度学习技术研究和业务应用为基础,是中国首个开源开放、技术领先、功能完备的产业级深度学习平台,包括飞桨开源平台和飞桨企业版。飞桨开源平台包含核心框架、基础模型库、端到端开发套件与工具组件,持续开源核心能力,为产业、学术、科研创新提供基础底座。飞桨企业版基于飞桨开源平台,针对企业级需求增强了相应特性,包含零门槛AI开发平台EasyDL和全功能AI开发平台BML。EasyDL主要面向中小企业,提供零门槛、预置丰富网络和模型、便捷高效的开发平台;BML是为大型企业提供的功能全面、可灵活定制和被深度集成的开发平台。

END

本文同步分享在 博客“飞桨PaddlePaddle”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部