2021SC@SDUSC
一、背景介绍
当我们学习编程的时候,编写的第一个程序一般是实现打印"Hello World"。而机器学习(或深度学习)的入门教程,一般都是 MNIST 数据库上的手写识别问题。原因是手写识别属于典型的图像分类问题,比较简单,同时MNIST数据集也很完备。MNIST数据集作为一个简单的计算机视觉数据集,包含一系列如图1所示的手写数字图片和对应的标签。图片是28x28的像素矩阵,标签则对应着0~9的10个数字。每张图片都经过了大小归一化和居中处理。
MNIST数据集是从 NIST 的Special Database 3(SD-3)和Special Database 1(SD-1)构建而来。由于SD-3是由美国人口调查局的员工进行标注,SD-1是由美国高中生进行标注,因此SD-3比SD-1更干净也更容易识别。Yann LeCun等人从SD-1和SD-3中各取一半作为MNIST的训练集(60000条数据)和测试集(10000条数据),其中训练集来自250位不同的标注员,此外还保证了训练集和测试集的标注员是不完全相同的。
MNIST吸引了大量的科学家基于此数据集训练模型,1998年,LeCun分别用单层线性分类器、多层感知器(Multilayer Perceptron, MLP)和多层卷积神经网络LeNet进行实验,使得测试集上的误差不断下降(从12%下降到0.7%)。在研究过程中,LeCun提出了卷积神经网络(Convolutional Neural Network),大幅度地提高了手写字符的识别能力,也因此成为了深度学习领域的奠基人之一。此后,科学家们又基于K近邻(K-Nearest Neighbors)算法、支持向量机(SVM)、神经网络和Boosting方法[8]等做了大量实验,并采用多种预处理方法(如去除歪曲、去噪、模糊等)来提高识别的准确率。
如今的深度学习领域,卷积神经网络占据了至关重要的地位,从最早Yann LeCun提出的简单LeNet,到如今ImageNet大赛上的优胜模型VGGNet、GoogLeNet、ResNet等,人们在图像分类领域,利用卷积神经网络得到了一系列惊人的结果。
二、模型概览
基于MNIST数据集训练一个分类器,在介绍本教程使用的三个基本图像分类网络前,我们先给出一些定义:
·X是输入:MNIST图片是28×28 的二维图像,为了进行计算,我们将其转化为784维向量,即X=(x0,x1,…,x783)。
·Y是输出:分类器的输出是10类数字(0-9),即Y=(y0,y1,…,y9),每一维yi代表图片分类为第i类数字的概率。
·Label是图片的真实标签:Label=(l0,l1,…,l9)也是10维,但只有一维为1,其他都为0。例如某张图片上的数字为2,则它的标签为(0,0,1,0,…,0)。
三、数据介绍
PaddlePaddle在API中提供了自动加载MNIST数据的模块paddle.dataset.mnist
。加载后的数据位于/home/username/.cache/paddle/dataset/mnist
下:
三、Fluid API 概述
演示将使用最新的 Fluid API。Fluid API是最新的 PaddlePaddle API。它在不牺牲性能的情况下简化了模型配置。
下面是 Fluid API 中几个重要概念的概述:
1.inference_program
:指定如何从数据输入中获得预测的函数, 这是指定网络流的地方。
2.train_program
:指定如何从 inference_program 和标签值中获取 loss 的函数, 这是指定损失计算的地方。
3.optimizer_func
: 指定优化器配置的函数,优化器负责减少损失并驱动训练,Paddle 支持多种不同的优化器。
四、配置说明
加载 PaddlePaddle 的 Fluid API 包。
from __future__ import print_function # 将python3中的print特性导入当前版本
import os
from PIL import Image # 导入图像处理模块
import matplotlib.pyplot as plt
import numpy
import paddle # 导入paddle模块
import paddle.fluid as fluid
Program Functions 配置
我们需要设置 inference_program
函数。我们想用这个程序来演示三个不同的分类器,每个分类器都定义为 Python 函数。 我们需要将图像数据输入到分类器中。Paddle 为读取数据提供了一个特殊的层 layer.data
层。 让我们创建一个数据层来读取图像并将其连接到分类网络。
·Softmax回归:只通过一层简单的以softmax为激活函数的全连接层,就可以得到分类的结果。
def softmax_regression():
"""
定义softmax分类器:
一个以softmax为激活函数的全连接层
Return:
predict_image -- 分类的结果
"""
# 输入的原始图像数据,大小为28*28*1
img = fluid.data(name='img', shape=[None, 1, 28, 28], dtype='float32')
# 以softmax为激活函数的全连接层,输出层的大小必须为数字的个数10
predict = fluid.layers.fc(
input=img, size=10, act='softmax')
return predict
·多层感知器:下面代码实现了一个含有两个隐藏层(即全连接层)的多层感知器。其中两个隐藏层的激活函数均采用ReLU,输出层的激活函数用Softmax。
def multilayer_perceptron():
"""
定义多层感知机分类器:
含有两个隐藏层(全连接层)的多层感知器
其中前两个隐藏层的激活函数采用 ReLU,输出层的激活函数用 Softmax
Return:
predict_image -- 分类的结果
"""
# 输入的原始图像数据,大小为28*28*1
img = fluid.data(name='img', shape=[None, 1, 28, 28], dtype='float32')
# 第一个全连接层,激活函数为ReLU
hidden = fluid.layers.fc(input=img, size=200, act='relu')
# 第二个全连接层,激活函数为ReLU
hidden = fluid.layers.fc(input=hidden, size=200, act='relu')
# 以softmax为激活函数的全连接输出层,输出层的大小必须为数字的个数10
prediction = fluid.layers.fc(input=hidden, size=10, act='softmax')
return prediction
·卷积池化层:在LeNet-5中会出现多个卷积-池化的操作,为避免代码重复书写,将串联的卷积-池化写成conv_pool函数。
def conv_pool(input, num_filters, filter_size, pool_size, pool_stride, act="relu"):
"""
定义卷积池化层:
含有一个卷积层和一个池化层
Args:
input —— 网络输入
num_filters —— 卷积核的个数
filter_size —— 卷积核的大小
pool_size —— 池化核的大小
pool_stride —— 池化的步长
act —— 卷积层的激活函数
Return:
out -- 经过卷积池化后的特征图
"""
conv_out = fluid.layers.conv2d(
input=input,
num_filters=num_filters,
filter_size=filter_size,
act=act)
out = fluid.layers.pool2d(
input=conv_out,
pool_size=pool_size,
pool_stride=pool_stride)
return out
·卷积神经网络LeNet-5: 输入的二维图像,首先经过两次卷积层到池化层,再经过全连接层,最后使用以softmax为激活函数的全连接层作为输出层。
def convolutional_neural_network():
"""
定义卷积神经网络分类器:
输入的二维图像,经过两个卷积-池化层,使用以softmax为激活函数的全连接层作为输出层
Return:
predict -- 分类的结果
"""
# 输入的原始图像数据,大小为28*28*1
img = fluid.data(name='img', shape=[None, 1, 28, 28], dtype='float32')
# 第一个卷积-池化层
# 使用20个5*5的滤波器,池化大小为2,池化步长为2,激活函数为Relu
conv_pool_1 = conv_pool(
input=img,
filter_size=5,
num_filters=20,
pool_size=2,
pool_stride=2,
act="relu")
conv_pool_1 = fluid.layers.batch_norm(conv_pool_1)
# 第二个卷积-池化层
# 使用50个5*5的滤波器,池化大小为2,池化步长为2,激活函数为Relu
conv_pool_2 = conv_pool(
input=conv_pool_1,
filter_size=5,
num_filters=50,
pool_size=2,
pool_stride=2,
act="relu")
# 以softmax为激活函数的全连接输出层,输出层的大小必须为数字的个数10
prediction = fluid.layers.fc(input=conv_pool_2, size=10, act='softmax')
return prediction
配置和训练见下一篇