Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
book
提交
c367cc7d
B
book
项目概览
PaddlePaddle
/
book
通知
16
Star
4
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
40
列表
看板
标记
里程碑
合并请求
37
Wiki
5
Wiki
分析
仓库
DevOps
项目成员
Pages
B
book
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
40
Issue
40
列表
看板
标记
里程碑
合并请求
37
合并请求
37
Pages
分析
分析
仓库分析
DevOps
Wiki
5
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
c367cc7d
编写于
1月 29, 2019
作者:
T
tink2123
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
update recognize_digits.md
上级
4adeb85b
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
300 addition
and
112 deletion
+300
-112
02.recognize_digits/README.cn.md
02.recognize_digits/README.cn.md
+150
-56
02.recognize_digits/index.cn.html
02.recognize_digits/index.cn.html
+150
-56
未找到文件。
02.recognize_digits/README.cn.md
浏览文件 @
c367cc7d
#
识别数字
#
数字识别
本教程源代码目录在
[
book/recognize_digits
](
https://github.com/PaddlePaddle/book/tree/develop/02.recognize_digits
)
,初次使用请您参考
[
Book文档使用说明
](
https://github.com/PaddlePaddle/book/blob/develop/README.cn.md#运行这本书
)
。
...
...
@@ -12,26 +12,28 @@
MNIST数据集是从
[
NIST
](
https://www.nist.gov/srd/nist-special-database-19
)
的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位不同的标注员,此外还保证了训练集和测试集的标注员是不完全相同的。
Yann LeCun早先在手写字符识别上做了很多研究,并在研究过程中提出了卷积神经网络(Convolutional Neural Network),大幅度地提高了手写字符的识别能力,也因此成为了深度学习领域的奠基人之一。如今的深度学习领域,卷积神经网络占据了至关重要的地位,从最早Yann LeCun提出的简单LeNet,到如今ImageNet大赛上的优胜模型VGGNet、GoogLeNet、ResNet等(请参见
[
图像分类
](
https://github.com/PaddlePaddle/book/tree/develop/03.image_classification
)
教程),人们在图像分类领域,利用卷积神经网络得到了一系列惊人的结果
。
MNIST吸引了大量的科学家基于此数据集训练模型,1998年,LeCun分别用单层线性分类器、多层感知器(Multilayer Perceptron, MLP)和多层卷积神经网络LeNet进行实验,使得测试集上的误差不断下降(从12%下降到0.7%)
\[
[
1
](
#参考文献
)
\]
。在研究过程中,LeCun提出了卷积神经网络(Convolutional Neural Network),大幅度地提高了手写字符的识别能力,也因此成为了深度学习领域的奠基人之一。此后,科学家们又基于K近邻(K-Nearest Neighbors)算法
\[
[
2
](
#参考文献
)
\]
、支持向量机(SVM)
\[
[
3
](
#参考文献
)
\]
、神经网络
\[
[
4-7
](
#参考文献
)
\]
和Boosting方法
\[
[
8
](
#参考文献
)
\]
等做了大量实验,并采用多种预处理方法(如去除歪曲、去噪、模糊等)来提高识别的准确率
。
有很多算法在MNIST上进行实验。1998年,LeCun分别用单层线性分类器、多层感知器(Multilayer Perceptron, MLP)和多层卷积神经网络LeNet进行实验,使得测试集上的误差不断下降(从12%下降到0.7%)
\[
[
1
](
#参考文献
)
\]
。此后,科学家们又基于K近邻(K-Nearest Neighbors)算法
\[
[
2
](
#参考文献
)
\]
、支持向量机(SVM)
\[
[
3
](
#参考文献
)
\]
、神经网络
\[
[
4-7
](
#参考文献
)
\]
和Boosting方法
\[
[
8
](
#参考文献
)
\]
等做了大量实验,并采用多种预处理方法(如去除歪曲、去噪、模糊等)来提高识别的准确率
。
如今的深度学习领域,卷积神经网络占据了至关重要的地位,从最早Yann LeCun提出的简单LeNet,到如今ImageNet大赛上的优胜模型VGGNet、GoogLeNet、ResNet等(请参见
[
图像分类
](
https://github.com/PaddlePaddle/book/tree/develop/03.image_classification
)
教程),人们在图像分类领域,利用卷积神经网络得到了一系列惊人的结果
。
本教程中,我们从简单的模型Softmax回归开始,带大家入门手写字符识别,并逐步进行模型优化。
本教程中,我们从简单的Softmax回归模型开始,带大家了解手写字符识别,并向大家介绍如何改进模型,利用多层感知机(MLP)和卷积神经网络(CNN)优化识别效果。
## 模型概览
基于MNIST数据训练一个分类器,在介绍本教程使用的三个基本图像分类网络前,我们先给出一些定义:
基于MNIST数据
集
训练一个分类器,在介绍本教程使用的三个基本图像分类网络前,我们先给出一些定义:
-
$X$是输入:MNIST图片是$28
\t
imes28$ 的二维图像,为了进行计算,我们将其转化为$784$维向量,即$X=
\l
eft ( x_0, x_1,
\d
ots, x_{783}
\r
ight )$。
-
$Y$是输出:分类器的输出是10类数字(0-9),即$Y=
\l
eft ( y_0, y_1,
\d
ots, y_9
\r
ight )$,每一维$y_i$代表图片分类为第$i$类数字的概率。
-
$L
$是图片的真实标签:$L=
\l
eft ( l_0, l_1,
\d
ots, l_9
\r
ight )$也是10维,但只有一维为1,其他都为0。
-
$L
abel$是图片的真实标签:$Label=
\l
eft ( l_0, l_1,
\d
ots, l_9
\r
ight )$也是10维,但只有一维为1,其他都为0。例如某张图片上的数字为2,则它的标签为$(0,1,0,
\d
ot, 0)$
### Softmax回归(Softmax Regression)
最简单的Softmax回归模型是先将输入层经过一个全连接层得到
的特征,然后直接通过softmax 函数进行多分类
\[
[
9
](
#参考文献
)
\]
。
最简单的Softmax回归模型是先将输入层经过一个全连接层得到
特征,然后直接通过 softmax 函数计算多个类别的概率并输出
\[
[
9
](
#参考文献
)
\]
。
输入层的数据$X$传到输出层,在激活操作之前,会乘以相应的权重 $W$ ,并加上偏置变量 $b$ ,具体如下:
...
...
@@ -39,24 +41,26 @@ $$ y_i = \text{softmax}(\sum_j W_{i,j}x_j + b_i) $$
其中 $
\t
ext{softmax}(x_i) =
\f
rac{e^{x_i}}{
\s
um_j e^{x_j}} $
图2为softmax回归的网络图,图中权重用蓝线表示、偏置用红线表示、+1代表偏置参数的系数为1。
<p
align=
"center"
>
<img
src=
"https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/image/softmax_regression.png?raw=true"
width=
400
><br/>
图2. softmax回归网络结构图
<br/>
</p>
对于有 $N$ 个类别的多分类问题,指定 $N$ 个输出节点,$N$ 维结果向量经过softmax将归一化为 $N$ 个[0,1]范围内的实数值,分别表示该样本属于这 $N$ 个类别的概率。此处的 $y_i$ 即对应该图片为数字 $i$ 的预测概率。
在分类问题中,我们一般采用交叉熵代价损失函数(cross entropy loss),公式如下:
$$ L_{cross-entropy}(label, y) = -
\s
um_i label_ilog(y_i) $$
图2为softmax回归的网络图,图中权重用蓝线表示、偏置用红线表示、+1代表偏置参数的系数为1。
<p
align=
"center"
>
<img
src=
"https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/image/softmax_regression.png?raw=true"
width=
400
><br/>
图2. softmax回归网络结构图
<br/>
</p>
### 多层感知
器
(Multilayer Perceptron, MLP)
### 多层感知
机
(Multilayer Perceptron, MLP)
Softmax回归模型采用了最简单的两层神经网络,即只有输入层和输出层,因此其拟合能力有限。为了达到更好的识别效果,我们考虑在输入层和输出层中间加上若干个隐藏层
\[
[
10
](
#参考文献
)
\]
。
1.
经过第一个隐藏层,可以得到 $ H_1 =
\p
hi(W_1X + b_1) $,其中$
\p
hi$代表激活函数,常见的有
sigmoid、tanh或ReLU
等函数。
1.
经过第一个隐藏层,可以得到 $ H_1 =
\p
hi(W_1X + b_1) $,其中$
\p
hi$代表激活函数,常见的有
[
sigmoid、tanh或ReLU
](
#常见激活函数介绍
)
等函数。
2.
经过第二个隐藏层,可以得到 $ H_2 =
\p
hi(W_2H_1 + b_2) $。
3.
最后,再经过输出层,得到的$Y=
\t
ext{softmax}(W_3H_2 + b_3)$,即为最后的分类结果向量。
...
...
@@ -73,7 +77,7 @@ Softmax回归模型采用了最简单的两层神经网络,即只有输入层
在多层感知器模型中,将图像展开成一维向量输入到网络中,忽略了图像的位置和结构信息,而卷积神经网络能够更好的利用图像的结构信息。
[
LeNet-5
](
http://yann.lecun.com/exdb/lenet/
)
是一个较简单的卷积神经网络。图4显示了其结构:输入的二维图像,先经过两次卷积层到池化层,再经过全连接层,最后使用softmax分类作为输出层。下面我们主要介绍卷积层和池化层。
<p
align=
"center"
>
<img
src=
"https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/image/cnn.png?raw=true"
width=
"
4
00"
><br/>
<img
src=
"https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/image/cnn.png?raw=true"
width=
"
6
00"
><br/>
图4. LeNet-5卷积神经网络结构
<br/>
</p>
...
...
@@ -86,7 +90,9 @@ Softmax回归模型采用了最简单的两层神经网络,即只有输入层
图5. 卷积层图片
<br/>
</p>
图5给出一个卷积计算过程的示例图,输入图像大小为$H=5,W=5,D=3$,即$5
\t
imes 5$大小的3通道(RGB,也称作深度)彩色图像。这个示例图中包含两(用$K$表示)组卷积核,即图中滤波器$W_0$和$W_1$。在卷积计算中,通常对不同的输入通道采用不同的卷积核,如图示例中每组卷积核包含($D=3)$个$3
\t
imes 3$(用$F
\t
imes F$表示)大小的卷积核。另外,这个示例中卷积核在图像的水平方向($W$方向)和垂直方向($H$方向)的滑动步长为2(用$S$表示);对输入图像周围各填充1(用$P$表示)个0,即图中输入层原始数据为蓝色部分,灰色部分是进行了大小为1的扩展,用0来进行扩展。经过卷积操作得到输出为$3
\t
imes 3
\t
imes 2$(用$H_{o}
\t
imes W_{o}
\t
imes K$表示)大小的特征图,即$3
\t
imes 3$大小的2通道特征图,其中$H_o$计算公式为:$H_o = (H - F + 2
\t
imes P)/S + 1$,$W_o$同理。 而输出特征图中的每个像素,是每组滤波器与输入图像每个特征图的内积再求和,再加上偏置$b_o$,偏置通常对于每个输出特征图是共享的。输出特征图$o[:,:,0]$中的最后一个$-2$计算如图5右下角公式所示。
图5给出一个卷积计算过程的示例图,输入图像大小为$H=5,W=5,D=3$,即$5
\t
imes 5$大小的3通道(RGB,也称作深度)彩色图像。
这个示例图中包含两(用$K$表示)组卷积核,即图中$Filter W_0$ 和 $Filter W_1$。在卷积计算中,通常对不同的输入通道采用不同的卷积核,如图示例中每组卷积核包含($D=3)$个$3
\t
imes 3$(用$F
\t
imes F$表示)大小的卷积核。另外,这个示例中卷积核在图像的水平方向($W$方向)和垂直方向($H$方向)的滑动步长为2(用$S$表示);对输入图像周围各填充1(用$P$表示)个0,即图中输入层原始数据为蓝色部分,灰色部分是进行了大小为1的扩展,用0来进行扩展。经过卷积操作得到输出为$3
\t
imes 3
\t
imes 2$(用$H_{o}
\t
imes W_{o}
\t
imes K$表示)大小的特征图,即$3
\t
imes 3$大小的2通道特征图,其中$H_o$计算公式为:$H_o = (H - F + 2
\t
imes P)/S + 1$,$W_o$同理。 而输出特征图中的每个像素,是每组滤波器与输入图像每个特征图的内积再求和,再加上偏置$b_o$,偏置通常对于每个输出特征图是共享的。输出特征图$o[:,:,0]$中的最后一个$-2$计算如图5右下角公式所示。
在卷积操作中卷积核是可学习的参数,经过上面示例介绍,每层卷积的参数大小为$D
\t
imes F
\t
imes F
\t
imes K$。在多层感知器模型中,神经元通常是全部连接,参数较多。而卷积层的参数较少,这也是由卷积层的主要特性即局部连接和共享权重所决定。
...
...
@@ -96,6 +102,8 @@ Softmax回归模型采用了最简单的两层神经网络,即只有输入层
通过介绍卷积计算过程及其特性,可以看出卷积是线性操作,并具有平移不变性(shift-invariant),平移不变性即在图像每个位置执行相同的操作。卷积层的局部连接和权重共享使得需要学习的参数大大减小,这样也有利于训练较大卷积神经网络。
关于卷积的更多内容可
[
参考阅读
](
http://ufldl.stanford.edu/wiki/index.php/Feature_extraction_using_convolution#Convolutions
)
。
#### 池化层
<p
align=
"center"
>
...
...
@@ -105,8 +113,9 @@ Softmax回归模型采用了最简单的两层神经网络,即只有输入层
池化是非线性下采样的一种形式,主要作用是通过减少网络的参数来减小计算量,并且能够在一定程度上控制过拟合。通常在卷积层的后面会加上一个池化层。池化包括最大池化、平均池化等。其中最大池化是用不重叠的矩形框将输入层分成不同的区域,对于每个矩形框的数取最大值作为输出层,如图6所示。
更详细的关于卷积神经网络的具体知识可以参考
[
斯坦福大学公开课
](
http://cs231n.github.io/convolutional-networks/
)
和
[
图像分类
](
https://github.com/PaddlePaddle/book/tree/develop/03.image_classification
)
教程。
更详细的关于卷积神经网络的具体知识可以参考
[
斯坦福大学公开课
](
http://cs231n.github.io/convolutional-networks/
)
、
[
Ufldl
](
http://ufldl.stanford.edu/wiki/index.php/Pooling
)
和
[
图像分类
](
https://github.com/PaddlePaddle/book/tree/develop/03.image_classification
)
教程。
<a
name=
"常见激活函数介绍"
></a>
### 常见激活函数介绍
-
sigmoid激活函数: $ f(x) = sigmoid(x) =
\f
rac{1}{1+e^{-x}} $
...
...
@@ -132,18 +141,18 @@ PaddlePaddle在API中提供了自动加载[MNIST](http://yann.lecun.com/exdb/mni
## Fluid API 概述
演示将使用最新的
`Fluid API`
。Fluid API是最新的 PaddlePaddle API。它在不牺牲性能的情况下简化了模型配置。
我们建议使用 Fluid API,
因为它更容易学起来
。
演示将使用最新的
[
Fluid API
](
http://paddlepaddle.org/documentation/docs/zh/1.2/api_cn/index_cn.html
)
。Fluid API是最新的 PaddlePaddle API。它在不牺牲性能的情况下简化了模型配置。
我们建议使用 Fluid API,
它易学易用的特性将帮助您快速完成机器学习任务。
。
下面是
快速的 Fluid API 概述。
下面是
Fluid API 中几个重要概念的概述:
1.
`inference_program`
:指定如何从数据输入中获得预测的函数
。
1.
`inference_program`
:指定如何从数据输入中获得预测的函数
,
这是指定网络流的地方。
2.
`train_program`
:指定如何从
`inference_program`
和
`标签值`
中获取
`loss`
的函数
。
2.
`train_program`
:指定如何从
`inference_program`
和
`标签值`
中获取
`loss`
的函数
,
这是指定损失计算的地方。
3.
`optimizer_func`
:
“指定优化器配置的函数。优化器负责减少损失并驱动培训。
Paddle 支持多种不同的优化器。
3.
`optimizer_func`
:
指定优化器配置的函数,优化器负责减少损失并驱动培训,
Paddle 支持多种不同的优化器。
4.
`Trainer`
:PaddlePaddle Trainer 管理由
`train_program`
和
`optimizer`
指定的训练过程。
通过
`event_handler`
回调函数,用户可以监控培训的进展。
...
...
@@ -151,31 +160,40 @@ PaddlePaddle在API中提供了自动加载[MNIST](http://yann.lecun.com/exdb/mni
5.
`Inferencer`
:Fluid inferencer 加载
`inference_program`
和由 Trainer 训练的参数。
然后,它可以推断数据和返回预测。
在
这个演示
中,我们将深入了解它们。
在
下面的代码示例
中,我们将深入了解它们。
## 配置说明
加载 PaddlePaddle 的 Fluid API 包。
```
python
import
os
from
PIL
import
Image
from
PIL
import
Image
# 导入图像处理模块
import
matplotlib.pyplot
as
plt
import
numpy
import
paddle
import
paddle
# 导入paddle模块
import
paddle.fluid
as
fluid
from
__future__
import
print_function
from
__future__
import
print_function
# 将python3中的print特性导入当前版本
```
### Program Functions 配置
我们需要设置
“推理程序”
函数。我们想用这个程序来演示三个不同的分类器,每个分类器都定义为 Python 函数。
我们需要将图像数据
馈送到分类器
。Paddle 为读取数据提供了一个特殊的层
`layer.data`
层。
我们需要设置
`inference_program`
函数。我们想用这个程序来演示三个不同的分类器,每个分类器都定义为 Python 函数。
我们需要将图像数据
输入到分类器中
。Paddle 为读取数据提供了一个特殊的层
`layer.data`
层。
让我们创建一个数据层来读取图像并将其连接到分类网络。
-
Softmax回归:只通过一层简单的以softmax为激活函数的全连接层,就可以得到分类的结果。
```
python
def
softmax_regression
():
"""
定义softmax分类器:
一个以softmax为激活函数的全连接层
Return:
predict_image -- 分类的结果
"""
# 输入的原始图像数据,大小为28*28*1
img
=
fluid
.
layers
.
data
(
name
=
'img'
,
shape
=
[
1
,
28
,
28
],
dtype
=
'float32'
)
# 以softmax为激活函数的全连接层,输出层的大小必须为数字的个数10
predict
=
fluid
.
layers
.
fc
(
input
=
img
,
size
=
10
,
act
=
'softmax'
)
return
predict
...
...
@@ -185,6 +203,15 @@ def softmax_regression():
```
python
def
multilayer_perceptron
():
"""
定义多层感知机分类器:
含有两个隐藏层(全连接层)的多层感知器
其中前两个隐藏层的激活函数采用 ReLU,输出层的激活函数用 Softmax
Return:
predict_image -- 分类的结果
"""
# 输入的原始图像数据,大小为28*28*1
img
=
fluid
.
layers
.
data
(
name
=
'img'
,
shape
=
[
1
,
28
,
28
],
dtype
=
'float32'
)
# 第一个全连接层,激活函数为ReLU
hidden
=
fluid
.
layers
.
fc
(
input
=
img
,
size
=
200
,
act
=
'relu'
)
...
...
@@ -199,8 +226,17 @@ def multilayer_perceptron():
```
python
def
convolutional_neural_network
():
"""
定义卷积神经网络分类器:
输入的二维图像,经过两个卷积-池化层,使用以softmax为激活函数的全连接层作为输出层
Return:
predict -- 分类的结果
"""
# 输入的原始图像数据,大小为28*28*1
img
=
fluid
.
layers
.
data
(
name
=
'img'
,
shape
=
[
1
,
28
,
28
],
dtype
=
'float32'
)
# 第一个卷积-池化层
# 使用20个5*5的滤波器,池化大小为2,池化步长为2,激活函数为Relu
conv_pool_1
=
fluid
.
nets
.
simple_img_conv_pool
(
input
=
img
,
filter_size
=
5
,
...
...
@@ -210,6 +246,7 @@ def convolutional_neural_network():
act
=
"relu"
)
conv_pool_1
=
fluid
.
layers
.
batch_norm
(
conv_pool_1
)
# 第二个卷积-池化层
# 使用20个5*5的滤波器,池化大小为2,池化步长为2,激活函数为Relu
conv_pool_2
=
fluid
.
nets
.
simple_img_conv_pool
(
input
=
conv_pool_1
,
filter_size
=
5
,
...
...
@@ -232,13 +269,27 @@ def convolutional_neural_network():
```
python
def
train_program
():
"""
配置train_program
Return:
predict -- 分类的结果
avg_cost -- 平均损失
acc -- 分类的准确率
"""
# 标签层,名称为label,对应输入图片的类别标签
label
=
fluid
.
layers
.
data
(
name
=
'label'
,
shape
=
[
1
],
dtype
=
'int64'
)
# predict = softmax_regression() # uncomment for Softmax回归
# predict = multilayer_perceptron() # uncomment for 多层感知器
predict
=
convolutional_neural_network
()
# uncomment for LeNet5卷积神经网络
# predict = softmax_regression() # 取消注释将使用 Softmax回归
# predict = multilayer_perceptron() # 取消注释将使用 多层感知器
predict
=
convolutional_neural_network
()
# 取消注释将使用 LeNet5卷积神经网络
# 使用类交叉熵函数计算predict和label之间的损失函数
cost
=
fluid
.
layers
.
cross_entropy
(
input
=
predict
,
label
=
label
)
# 计算平均损失
avg_cost
=
fluid
.
layers
.
mean
(
cost
)
# 计算分类准确率
acc
=
fluid
.
layers
.
accuracy
(
input
=
predict
,
label
=
label
)
return
predict
,
[
avg_cost
,
acc
]
...
...
@@ -246,7 +297,7 @@ def train_program():
#### Optimizer Function 配置
在下面的
`Adam optimizer`
,
`learning_rate`
是
训练的速度,
与网络的训练收敛速度有关系。
在下面的
`Adam optimizer`
,
`learning_rate`
是
学习率,它的大小
与网络的训练收敛速度有关系。
```
python
def
optimizer_program
():
...
...
@@ -262,14 +313,15 @@ def optimizer_program():
`batch`
是一个特殊的decorator,它的输入是一个reader,输出是一个batched reader。在PaddlePaddle里,一个reader每次yield一条训练数据,而一个batched reader每次yield一个minibatch。
```
python
# 一个minibatch中有64个数据
BATCH_SIZE
=
64
# 每次读取训练集中的500个数据并随机打乱,传入batched reader中,batched reader 每次 yield 64个数据
train_reader
=
paddle
.
batch
(
paddle
.
reader
.
shuffle
(
paddle
.
dataset
.
mnist
.
train
(),
buf_size
=
500
),
batch_size
=
BATCH_SIZE
)
# 读取测试集的数据,每次 yield 64个数据
test_reader
=
paddle
.
batch
(
paddle
.
dataset
.
mnist
.
test
(),
batch_size
=
BATCH_SIZE
)
```
...
...
@@ -282,23 +334,24 @@ test_reader = paddle.batch(
#### Event Handler 配置
我们可以在训练期间通过调用一个handler函数来监控培训进度。
我们将在这里演示两个
`event_handler`
程序。请随意修改 Jupyter
笔记本
,看看有什么不同。
我们将在这里演示两个
`event_handler`
程序。请随意修改 Jupyter
Notebook
,看看有什么不同。
`event_handler`
用来在训练过程中输出训练结果
```
python
def
event_handler
(
pass_id
,
batch_id
,
cost
):
# 打印训练的中间结果,训练轮次,batch数,损失函数
print
(
"Pass %d, Batch %d, Cost %f"
%
(
pass_id
,
batch_id
,
cost
))
```
```
python
from
paddle.
v2
.plot
import
Ploter
from
paddle.
utils
.plot
import
Ploter
train_prompt
=
"Train cost"
test_prompt
=
"Test cost"
cost_ploter
=
Ploter
(
train_prompt
,
test_prompt
)
#
event_handler to plot a figure
#
将训练过程绘图表示
def
event_handler_plot
(
ploter_title
,
step
,
cost
):
cost_ploter
.
append
(
ploter_title
,
step
,
cost
)
cost_ploter
.
plot
()
...
...
@@ -316,31 +369,49 @@ def event_handler_plot(ploter_title, step, cost):
`feed_order`
用于将数据目录映射到
`train_program`
创建一个反馈训练过程中误差的
`train_test`
训练完成后,模型参数存入
`save_dirname`
中
定义网络结构:
```
python
# 该模型运行在单个CPU上
use_cuda
=
False
#
set to True if training with GPU
use_cuda
=
False
#
如想使用GPU,请设置为 True
place
=
fluid
.
CUDAPlace
(
0
)
if
use_cuda
else
fluid
.
CPUPlace
()
# 调用train_program 获取预测值,损失值,
prediction
,
[
avg_loss
,
acc
]
=
train_program
()
# 输入的原始图像数据,大小为28*28*1
img
=
fluid
.
layers
.
data
(
name
=
'img'
,
shape
=
[
1
,
28
,
28
],
dtype
=
'float32'
)
# 标签层,名称为label,对应输入图片的类别标签
label
=
fluid
.
layers
.
data
(
name
=
'label'
,
shape
=
[
1
],
dtype
=
'int64'
)
# 告知网络传入的数据分为两部分,第一部分是img值,第二部分是label值
feeder
=
fluid
.
DataFeeder
(
feed_list
=
[
img
,
label
],
place
=
place
)
# 选择Adam优化器
optimizer
=
fluid
.
optimizer
.
Adam
(
learning_rate
=
0.001
)
optimizer
.
minimize
(
avg_loss
)
```
PASS_NUM
=
5
设置训练过程的超参:
```
python
PASS_NUM
=
5
#训练5轮
epochs
=
[
epoch_id
for
epoch_id
in
range
(
PASS_NUM
)]
# 将模型参数存储在名为 save_dirname 的文件中
save_dirname
=
"recognize_digits.inference.model"
```
```
python
def
train_test
(
train_test_program
,
train_test_feed
,
train_test_reader
):
# 将分类准确率存储在acc_set中
acc_set
=
[]
# 将平均损失存储在avg_loss_set中
avg_loss_set
=
[]
# 将测试 reader yield 出的每一个数据传入网络中进行训练
for
test_data
in
train_test_reader
():
acc_np
,
avg_loss_np
=
exe
.
run
(
program
=
train_test_program
,
...
...
@@ -348,17 +419,30 @@ def train_test(train_test_program,
fetch_list
=
[
acc
,
avg_loss
])
acc_set
.
append
(
float
(
acc_np
))
avg_loss_set
.
append
(
float
(
avg_loss_np
))
#
get test acc and loss
#
获得测试数据上的准确率和损失值
acc_val_mean
=
numpy
.
array
(
acc_set
).
mean
()
avg_loss_val_mean
=
numpy
.
array
(
avg_loss_set
).
mean
()
# 返回平均损失值,平均准确率
return
avg_loss_val_mean
,
acc_val_mean
```
创建执行器:
```
python
exe
=
fluid
.
Executor
(
place
)
exe
.
run
(
fluid
.
default_startup_program
())
```
设置 main_program 和 test_program :
```
python
main_program
=
fluid
.
default_main_program
()
test_program
=
fluid
.
default_main_program
().
clone
(
for_test
=
True
)
```
开始训练:
```
python
lists
=
[]
step
=
0
for
epoch_id
in
epochs
:
...
...
@@ -366,12 +450,12 @@ for epoch_id in epochs:
metrics
=
exe
.
run
(
main_program
,
feed
=
feeder
.
feed
(
data
),
fetch_list
=
[
avg_loss
,
acc
])
if
step
%
100
==
0
:
if
step
%
100
==
0
:
#每训练100次 打印一次log
print
(
"Pass %d, Batch %d, Cost %f"
%
(
step
,
epoch_id
,
metrics
[
0
]))
event_handler_plot
(
train_prompt
,
step
,
metrics
[
0
])
step
+=
1
#
test for epoch
#
测试每个epoch的分类效果
avg_loss_val
,
acc_val
=
train_test
(
train_test_program
=
test_program
,
train_test_reader
=
test_reader
,
train_test_feed
=
feeder
)
...
...
@@ -380,19 +464,25 @@ for epoch_id in epochs:
event_handler_plot
(
test_prompt
,
step
,
metrics
[
0
])
lists
.
append
((
epoch_id
,
avg_loss_val
,
acc_val
))
# 保存训练好的模型参数用于预测
if
save_dirname
is
not
None
:
fluid
.
io
.
save_inference_model
(
save_dirname
,
[
"img"
],
[
prediction
],
exe
,
model_filename
=
None
,
params_filename
=
None
)
# find the best
pass
# 选择效果最好的
pass
best
=
sorted
(
lists
,
key
=
lambda
list
:
float
(
list
[
1
]))[
0
]
print
(
'Best pass is %s, testing Avgcost is %s'
%
(
best
[
0
],
best
[
1
]))
print
(
'The classification accuracy is %.2f%%'
%
(
float
(
best
[
2
])
*
100
))
```
训练过程是完全自动的,event_handler里打印的日志类似如下所示:
训练过程是完全自动的,event_handler里打印的日志类似如下所示。
Pass表示训练轮次,Batch表示训练全量数据的次数,cost表示当前pass的损失值。
每训练完一个Epoch后,计算一次平均损失和分类准确率。
```
Pass 0, Batch 0, Cost 0.125650
...
...
@@ -417,7 +507,7 @@ Test with Epoch 0, avg_cost: 0.053097883707459624, acc: 0.9822850318471338
### 生成预测输入数据
`infer_3.png`
是数字 3 的一个示例图像。把它变成一个 numpy 数组以匹配数据
馈送
格式。
`infer_3.png`
是数字 3 的一个示例图像。把它变成一个 numpy 数组以匹配数据
feed
格式。
```
python
def
load_image
(
file
):
...
...
@@ -427,7 +517,7 @@ def load_image(file):
im
=
im
/
255.0
*
2.0
-
1.0
return
im
cur_dir
=
cur_dir
=
os
.
getcwd
()
cur_dir
=
os
.
getcwd
()
tensor_img
=
load_image
(
cur_dir
+
'/image/infer_3.png'
)
```
...
...
@@ -436,20 +526,23 @@ tensor_img = load_image(cur_dir + '/image/infer_3.png')
```
python
inference_scope
=
fluid
.
core
.
Scope
()
with
fluid
.
scope_guard
(
inference_scope
):
# Use fluid.io.load_inference_model to obtain the inference program desc,
# the feed_target_names (the names of variables that will be feeded
# data using feed operators), and the fetch_targets (variables that
# we want to obtain data from using fetch operators).
# 使用 fluid.io.load_inference_model 获取 inference program desc,
# feed_target_names 用于指定需要传入网络的变量名
# fetch_targets 指定希望从网络中fetch出的变量名
[
inference_program
,
feed_target_names
,
fetch_targets
]
=
fluid
.
io
.
load_inference_model
(
save_dirname
,
exe
,
None
,
None
)
#
Construct feed as a dictionary of
{feed_target_name: feed_target_data}
#
and results will contain a list of data corresponding to fetch_targets.
#
将feed构建成字典
{feed_target_name: feed_target_data}
#
结果将包含一个与fetch_targets对应的数据列表
results
=
exe
.
run
(
inference_program
,
feed
=
{
feed_target_names
[
0
]:
tensor_img
},
fetch_list
=
fetch_targets
)
lab
=
numpy
.
argsort
(
results
)
# 打印 infer_3.png 这张图片的预测结果
img
=
Image
.
open
(
'image/infer_3.png'
)
plt
.
imshow
(
img
)
print
(
"Inference result of image/infer_3.png is: %d"
%
lab
[
0
][
0
][
-
1
])
```
...
...
@@ -457,11 +550,11 @@ with fluid.scope_guard(inference_scope):
### 预测结果
如果顺利,预测结果输入如下:
`Inference result of image/infer_3.png is: 3`
`Inference result of image/infer_3.png is: 3`
, 说明我们的网络成功的识别出了这张图片!
## 总结
本教程的softmax回归、多层感知
器
和卷积神经网络是最基础的深度学习模型,后续章节中复杂的神经网络都是从它们衍生出来的,因此这几个模型对之后的学习大有裨益。同时,我们也观察到从最简单的softmax回归变换到稍复杂的卷积神经网络的时候,MNIST数据集上的识别准确率有了大幅度的提升,原因是卷积层具有局部连接和共享权重的特性。在之后学习新模型的时候,希望大家也要深入到新模型相比原模型带来效果提升的关键之处。此外,本教程还介绍了PaddlePaddle模型搭建的基本流程,从dataprovider的编写、网络层的构建,到最后的训练和预测。对这个流程熟悉以后,大家就可以用自己的数据,定义自己的网络模型,并完成自己的训练和预测任务了。
本教程的softmax回归、多层感知
机
和卷积神经网络是最基础的深度学习模型,后续章节中复杂的神经网络都是从它们衍生出来的,因此这几个模型对之后的学习大有裨益。同时,我们也观察到从最简单的softmax回归变换到稍复杂的卷积神经网络的时候,MNIST数据集上的识别准确率有了大幅度的提升,原因是卷积层具有局部连接和共享权重的特性。在之后学习新模型的时候,希望大家也要深入到新模型相比原模型带来效果提升的关键之处。此外,本教程还介绍了PaddlePaddle模型搭建的基本流程,从dataprovider的编写、网络层的构建,到最后的训练和预测。对这个流程熟悉以后,大家就可以用自己的数据,定义自己的网络模型,并完成自己的训练和预测任务了。
<a
name=
"参考文献"
></a>
## 参考文献
...
...
@@ -479,3 +572,4 @@ with fluid.scope_guard(inference_scope):
<br/>
<a
rel=
"license"
href=
"http://creativecommons.org/licenses/by-sa/4.0/"
><img
alt=
"知识共享许可协议"
style=
"border-width:0"
src=
"https://i.creativecommons.org/l/by-sa/4.0/88x31.png"
/></a><br
/><span
xmlns:dct=
"http://purl.org/dc/terms/"
href=
"http://purl.org/dc/dcmitype/Text"
property=
"dct:title"
rel=
"dct:type"
>
本教程
</span>
由
<a
xmlns:cc=
"http://creativecommons.org/ns#"
href=
"http://book.paddlepaddle.org"
property=
"cc:attributionName"
rel=
"cc:attributionURL"
>
PaddlePaddle
</a>
创作,采用
<a
rel=
"license"
href=
"http://creativecommons.org/licenses/by-sa/4.0/"
>
知识共享 署名-相同方式共享 4.0 国际 许可协议
</a>
进行许可。
02.recognize_digits/index.cn.html
浏览文件 @
c367cc7d
...
...
@@ -40,7 +40,7 @@
<!-- This block will be replaced by each markdown file content. Please do not change lines below.-->
<div
id=
"markdown"
style=
'display:none'
>
#
识别数字
#
数字识别
本教程源代码目录在[book/recognize_digits](https://github.com/PaddlePaddle/book/tree/develop/02.recognize_digits),初次使用请您参考[Book文档使用说明](https://github.com/PaddlePaddle/book/blob/develop/README.cn.md#运行这本书)。
...
...
@@ -54,26 +54,28 @@
MNIST数据集是从 [NIST](https://www.nist.gov/srd/nist-special-database-19) 的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位不同的标注员,此外还保证了训练集和测试集的标注员是不完全相同的。
Yann LeCun早先在手写字符识别上做了很多研究,并在研究过程中提出了卷积神经网络(Convolutional Neural Network),大幅度地提高了手写字符的识别能力,也因此成为了深度学习领域的奠基人之一。如今的深度学习领域,卷积神经网络占据了至关重要的地位,从最早Yann LeCun提出的简单LeNet,到如今ImageNet大赛上的优胜模型VGGNet、GoogLeNet、ResNet等(请参见[图像分类](https://github.com/PaddlePaddle/book/tree/develop/03.image_classification) 教程),人们在图像分类领域,利用卷积神经网络得到了一系列惊人的结果
。
MNIST吸引了大量的科学家基于此数据集训练模型,1998年,LeCun分别用单层线性分类器、多层感知器(Multilayer Perceptron, MLP)和多层卷积神经网络LeNet进行实验,使得测试集上的误差不断下降(从12%下降到0.7%)\[[1](#参考文献)\]。在研究过程中,LeCun提出了卷积神经网络(Convolutional Neural Network),大幅度地提高了手写字符的识别能力,也因此成为了深度学习领域的奠基人之一。此后,科学家们又基于K近邻(K-Nearest Neighbors)算法\[[2](#参考文献)\]、支持向量机(SVM)\[[3](#参考文献)\]、神经网络\[[4-7](#参考文献)\]和Boosting方法\[[8](#参考文献)\]等做了大量实验,并采用多种预处理方法(如去除歪曲、去噪、模糊等)来提高识别的准确率
。
有很多算法在MNIST上进行实验。1998年,LeCun分别用单层线性分类器、多层感知器(Multilayer Perceptron, MLP)和多层卷积神经网络LeNet进行实验,使得测试集上的误差不断下降(从12%下降到0.7%)\[[1](#参考文献)\]。此后,科学家们又基于K近邻(K-Nearest Neighbors)算法\[[2](#参考文献)\]、支持向量机(SVM)\[[3](#参考文献)\]、神经网络\[[4-7](#参考文献)\]和Boosting方法\[[8](#参考文献)\]等做了大量实验,并采用多种预处理方法(如去除歪曲、去噪、模糊等)来提高识别的准确率
。
如今的深度学习领域,卷积神经网络占据了至关重要的地位,从最早Yann LeCun提出的简单LeNet,到如今ImageNet大赛上的优胜模型VGGNet、GoogLeNet、ResNet等(请参见[图像分类](https://github.com/PaddlePaddle/book/tree/develop/03.image_classification) 教程),人们在图像分类领域,利用卷积神经网络得到了一系列惊人的结果
。
本教程中,我们从简单的模型Softmax回归开始,带大家入门手写字符识别,并逐步进行模型优化。
本教程中,我们从简单的Softmax回归模型开始,带大家了解手写字符识别,并向大家介绍如何改进模型,利用多层感知机(MLP)和卷积神经网络(CNN)优化识别效果。
## 模型概览
基于MNIST数据训练一个分类器,在介绍本教程使用的三个基本图像分类网络前,我们先给出一些定义:
基于MNIST数据
集
训练一个分类器,在介绍本教程使用的三个基本图像分类网络前,我们先给出一些定义:
- $X$是输入:MNIST图片是$28\times28$ 的二维图像,为了进行计算,我们将其转化为$784$维向量,即$X=\left ( x_0, x_1, \dots, x_{783} \right )$。
- $Y$是输出:分类器的输出是10类数字(0-9),即$Y=\left ( y_0, y_1, \dots, y_9 \right )$,每一维$y_i$代表图片分类为第$i$类数字的概率。
- $L
$是图片的真实标签:$L=\left ( l_0, l_1, \dots, l_9 \right )$也是10维,但只有一维为1,其他都为0。
- $L
abel$是图片的真实标签:$Label=\left ( l_0, l_1, \dots, l_9 \right )$也是10维,但只有一维为1,其他都为0。例如某张图片上的数字为2,则它的标签为$(0,1,0, \dot, 0)$
### Softmax回归(Softmax Regression)
最简单的Softmax回归模型是先将输入层经过一个全连接层得到
的特征,然后直接通过softmax 函数进行多分类
\[[9](#参考文献)\]。
最简单的Softmax回归模型是先将输入层经过一个全连接层得到
特征,然后直接通过 softmax 函数计算多个类别的概率并输出
\[[9](#参考文献)\]。
输入层的数据$X$传到输出层,在激活操作之前,会乘以相应的权重 $W$ ,并加上偏置变量 $b$ ,具体如下:
...
...
@@ -81,24 +83,26 @@ $$ y_i = \text{softmax}(\sum_j W_{i,j}x_j + b_i) $$
其中 $ \text{softmax}(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}} $
图2为softmax回归的网络图,图中权重用蓝线表示、偏置用红线表示、+1代表偏置参数的系数为1。
<p
align=
"center"
>
<img
src=
"https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/image/softmax_regression.png?raw=true"
width=
400
><br/>
图2. softmax回归网络结构图
<br/>
</p>
对于有 $N$ 个类别的多分类问题,指定 $N$ 个输出节点,$N$ 维结果向量经过softmax将归一化为 $N$ 个[0,1]范围内的实数值,分别表示该样本属于这 $N$ 个类别的概率。此处的 $y_i$ 即对应该图片为数字 $i$ 的预测概率。
在分类问题中,我们一般采用交叉熵代价损失函数(cross entropy loss),公式如下:
$$ L_{cross-entropy}(label, y) = -\sum_i label_ilog(y_i) $$
图2为softmax回归的网络图,图中权重用蓝线表示、偏置用红线表示、+1代表偏置参数的系数为1。
<p
align=
"center"
>
<img
src=
"https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/image/softmax_regression.png?raw=true"
width=
400
><br/>
图2. softmax回归网络结构图
<br/>
</p>
### 多层感知
器
(Multilayer Perceptron, MLP)
### 多层感知
机
(Multilayer Perceptron, MLP)
Softmax回归模型采用了最简单的两层神经网络,即只有输入层和输出层,因此其拟合能力有限。为了达到更好的识别效果,我们考虑在输入层和输出层中间加上若干个隐藏层\[[10](#参考文献)\]。
1. 经过第一个隐藏层,可以得到 $ H_1 = \phi(W_1X + b_1) $,其中$\phi$代表激活函数,常见的有
sigmoid、tanh或ReLU
等函数。
1. 经过第一个隐藏层,可以得到 $ H_1 = \phi(W_1X + b_1) $,其中$\phi$代表激活函数,常见的有
[sigmoid、tanh或ReLU](#常见激活函数介绍)
等函数。
2. 经过第二个隐藏层,可以得到 $ H_2 = \phi(W_2H_1 + b_2) $。
3. 最后,再经过输出层,得到的$Y=\text{softmax}(W_3H_2 + b_3)$,即为最后的分类结果向量。
...
...
@@ -115,7 +119,7 @@ Softmax回归模型采用了最简单的两层神经网络,即只有输入层
在多层感知器模型中,将图像展开成一维向量输入到网络中,忽略了图像的位置和结构信息,而卷积神经网络能够更好的利用图像的结构信息。[LeNet-5](http://yann.lecun.com/exdb/lenet/)是一个较简单的卷积神经网络。图4显示了其结构:输入的二维图像,先经过两次卷积层到池化层,再经过全连接层,最后使用softmax分类作为输出层。下面我们主要介绍卷积层和池化层。
<p
align=
"center"
>
<img
src=
"https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/image/cnn.png?raw=true"
width=
"
4
00"
><br/>
<img
src=
"https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/image/cnn.png?raw=true"
width=
"
6
00"
><br/>
图4. LeNet-5卷积神经网络结构
<br/>
</p>
...
...
@@ -128,7 +132,9 @@ Softmax回归模型采用了最简单的两层神经网络,即只有输入层
图5. 卷积层图片
<br/>
</p>
图5给出一个卷积计算过程的示例图,输入图像大小为$H=5,W=5,D=3$,即$5 \times 5$大小的3通道(RGB,也称作深度)彩色图像。这个示例图中包含两(用$K$表示)组卷积核,即图中滤波器$W_0$和$W_1$。在卷积计算中,通常对不同的输入通道采用不同的卷积核,如图示例中每组卷积核包含($D=3)$个$3 \times 3$(用$F \times F$表示)大小的卷积核。另外,这个示例中卷积核在图像的水平方向($W$方向)和垂直方向($H$方向)的滑动步长为2(用$S$表示);对输入图像周围各填充1(用$P$表示)个0,即图中输入层原始数据为蓝色部分,灰色部分是进行了大小为1的扩展,用0来进行扩展。经过卷积操作得到输出为$3 \times 3 \times 2$(用$H_{o} \times W_{o} \times K$表示)大小的特征图,即$3 \times 3$大小的2通道特征图,其中$H_o$计算公式为:$H_o = (H - F + 2 \times P)/S + 1$,$W_o$同理。 而输出特征图中的每个像素,是每组滤波器与输入图像每个特征图的内积再求和,再加上偏置$b_o$,偏置通常对于每个输出特征图是共享的。输出特征图$o[:,:,0]$中的最后一个$-2$计算如图5右下角公式所示。
图5给出一个卷积计算过程的示例图,输入图像大小为$H=5,W=5,D=3$,即$5 \times 5$大小的3通道(RGB,也称作深度)彩色图像。
这个示例图中包含两(用$K$表示)组卷积核,即图中$Filter W_0$ 和 $Filter W_1$。在卷积计算中,通常对不同的输入通道采用不同的卷积核,如图示例中每组卷积核包含($D=3)$个$3 \times 3$(用$F \times F$表示)大小的卷积核。另外,这个示例中卷积核在图像的水平方向($W$方向)和垂直方向($H$方向)的滑动步长为2(用$S$表示);对输入图像周围各填充1(用$P$表示)个0,即图中输入层原始数据为蓝色部分,灰色部分是进行了大小为1的扩展,用0来进行扩展。经过卷积操作得到输出为$3 \times 3 \times 2$(用$H_{o} \times W_{o} \times K$表示)大小的特征图,即$3 \times 3$大小的2通道特征图,其中$H_o$计算公式为:$H_o = (H - F + 2 \times P)/S + 1$,$W_o$同理。 而输出特征图中的每个像素,是每组滤波器与输入图像每个特征图的内积再求和,再加上偏置$b_o$,偏置通常对于每个输出特征图是共享的。输出特征图$o[:,:,0]$中的最后一个$-2$计算如图5右下角公式所示。
在卷积操作中卷积核是可学习的参数,经过上面示例介绍,每层卷积的参数大小为$D \times F \times F \times K$。在多层感知器模型中,神经元通常是全部连接,参数较多。而卷积层的参数较少,这也是由卷积层的主要特性即局部连接和共享权重所决定。
...
...
@@ -138,6 +144,8 @@ Softmax回归模型采用了最简单的两层神经网络,即只有输入层
通过介绍卷积计算过程及其特性,可以看出卷积是线性操作,并具有平移不变性(shift-invariant),平移不变性即在图像每个位置执行相同的操作。卷积层的局部连接和权重共享使得需要学习的参数大大减小,这样也有利于训练较大卷积神经网络。
关于卷积的更多内容可[参考阅读](http://ufldl.stanford.edu/wiki/index.php/Feature_extraction_using_convolution#Convolutions)。
#### 池化层
<p
align=
"center"
>
...
...
@@ -147,8 +155,9 @@ Softmax回归模型采用了最简单的两层神经网络,即只有输入层
池化是非线性下采样的一种形式,主要作用是通过减少网络的参数来减小计算量,并且能够在一定程度上控制过拟合。通常在卷积层的后面会加上一个池化层。池化包括最大池化、平均池化等。其中最大池化是用不重叠的矩形框将输入层分成不同的区域,对于每个矩形框的数取最大值作为输出层,如图6所示。
更详细的关于卷积神经网络的具体知识可以参考[斯坦福大学公开课]( http://cs231n.github.io/convolutional-networks/ )
和
[图像分类]( https://github.com/PaddlePaddle/book/tree/develop/03.image_classification )教程。
更详细的关于卷积神经网络的具体知识可以参考[斯坦福大学公开课]( http://cs231n.github.io/convolutional-networks/ )
、[Ufldl](http://ufldl.stanford.edu/wiki/index.php/Pooling) 和
[图像分类]( https://github.com/PaddlePaddle/book/tree/develop/03.image_classification )教程。
<a
name=
"常见激活函数介绍"
></a>
### 常见激活函数介绍
- sigmoid激活函数: $ f(x) = sigmoid(x) = \frac{1}{1+e^{-x}} $
...
...
@@ -174,18 +183,18 @@ PaddlePaddle在API中提供了自动加载[MNIST](http://yann.lecun.com/exdb/mni
## Fluid API 概述
演示将使用最新的
`Fluid API`
。Fluid API是最新的 PaddlePaddle API。它在不牺牲性能的情况下简化了模型配置。
我们建议使用 Fluid API,
因为它更容易学起来
。
演示将使用最新的
[Fluid API](http://paddlepaddle.org/documentation/docs/zh/1.2/api_cn/index_cn.html)
。Fluid API是最新的 PaddlePaddle API。它在不牺牲性能的情况下简化了模型配置。
我们建议使用 Fluid API,
它易学易用的特性将帮助您快速完成机器学习任务。
。
下面是
快速的 Fluid API 概述。
下面是
Fluid API 中几个重要概念的概述:
1. `inference_program`:指定如何从数据输入中获得预测的函数
。
1. `inference_program`:指定如何从数据输入中获得预测的函数
,
这是指定网络流的地方。
2. `train_program`:指定如何从 `inference_program` 和`标签值`中获取 `loss` 的函数
。
2. `train_program`:指定如何从 `inference_program` 和`标签值`中获取 `loss` 的函数
,
这是指定损失计算的地方。
3. `optimizer_func`:
“指定优化器配置的函数。优化器负责减少损失并驱动培训。
Paddle 支持多种不同的优化器。
3. `optimizer_func`:
指定优化器配置的函数,优化器负责减少损失并驱动培训,
Paddle 支持多种不同的优化器。
4. `Trainer`:PaddlePaddle Trainer 管理由 `train_program` 和 `optimizer` 指定的训练过程。
通过 `event_handler` 回调函数,用户可以监控培训的进展。
...
...
@@ -193,31 +202,40 @@ PaddlePaddle在API中提供了自动加载[MNIST](http://yann.lecun.com/exdb/mni
5. `Inferencer`:Fluid inferencer 加载 `inference_program` 和由 Trainer 训练的参数。
然后,它可以推断数据和返回预测。
在
这个演示
中,我们将深入了解它们。
在
下面的代码示例
中,我们将深入了解它们。
## 配置说明
加载 PaddlePaddle 的 Fluid API 包。
```python
import os
from PIL import Image
from PIL import Image # 导入图像处理模块
import matplotlib.pyplot as plt
import numpy
import paddle
import paddle
# 导入paddle模块
import paddle.fluid as fluid
from __future__ import print_function
from __future__ import print_function
# 将python3中的print特性导入当前版本
```
### Program Functions 配置
我们需要设置
“推理程序”
函数。我们想用这个程序来演示三个不同的分类器,每个分类器都定义为 Python 函数。
我们需要将图像数据
馈送到分类器
。Paddle 为读取数据提供了一个特殊的层 `layer.data` 层。
我们需要设置
`inference_program`
函数。我们想用这个程序来演示三个不同的分类器,每个分类器都定义为 Python 函数。
我们需要将图像数据
输入到分类器中
。Paddle 为读取数据提供了一个特殊的层 `layer.data` 层。
让我们创建一个数据层来读取图像并将其连接到分类网络。
- Softmax回归:只通过一层简单的以softmax为激活函数的全连接层,就可以得到分类的结果。
```python
def softmax_regression():
"""
定义softmax分类器:
一个以softmax为激活函数的全连接层
Return:
predict_image -- 分类的结果
"""
# 输入的原始图像数据,大小为28*28*1
img = fluid.layers.data(name='img', shape=[1, 28, 28], dtype='float32')
# 以softmax为激活函数的全连接层,输出层的大小必须为数字的个数10
predict = fluid.layers.fc(
input=img, size=10, act='softmax')
return predict
...
...
@@ -227,6 +245,15 @@ def softmax_regression():
```python
def multilayer_perceptron():
"""
定义多层感知机分类器:
含有两个隐藏层(全连接层)的多层感知器
其中前两个隐藏层的激活函数采用 ReLU,输出层的激活函数用 Softmax
Return:
predict_image -- 分类的结果
"""
# 输入的原始图像数据,大小为28*28*1
img = fluid.layers.data(name='img', shape=[1, 28, 28], dtype='float32')
# 第一个全连接层,激活函数为ReLU
hidden = fluid.layers.fc(input=img, size=200, act='relu')
...
...
@@ -241,8 +268,17 @@ def multilayer_perceptron():
```python
def convolutional_neural_network():
"""
定义卷积神经网络分类器:
输入的二维图像,经过两个卷积-池化层,使用以softmax为激活函数的全连接层作为输出层
Return:
predict -- 分类的结果
"""
# 输入的原始图像数据,大小为28*28*1
img = fluid.layers.data(name='img', shape=[1, 28, 28], dtype='float32')
# 第一个卷积-池化层
# 使用20个5*5的滤波器,池化大小为2,池化步长为2,激活函数为Relu
conv_pool_1 = fluid.nets.simple_img_conv_pool(
input=img,
filter_size=5,
...
...
@@ -252,6 +288,7 @@ def convolutional_neural_network():
act="relu")
conv_pool_1 = fluid.layers.batch_norm(conv_pool_1)
# 第二个卷积-池化层
# 使用20个5*5的滤波器,池化大小为2,池化步长为2,激活函数为Relu
conv_pool_2 = fluid.nets.simple_img_conv_pool(
input=conv_pool_1,
filter_size=5,
...
...
@@ -274,13 +311,27 @@ def convolutional_neural_network():
```python
def train_program():
"""
配置train_program
Return:
predict -- 分类的结果
avg_cost -- 平均损失
acc -- 分类的准确率
"""
# 标签层,名称为label,对应输入图片的类别标签
label = fluid.layers.data(name='label', shape=[1], dtype='int64')
# predict = softmax_regression() # uncomment for Softmax回归
# predict = multilayer_perceptron() # uncomment for 多层感知器
predict = convolutional_neural_network() # uncomment for LeNet5卷积神经网络
# predict = softmax_regression() # 取消注释将使用 Softmax回归
# predict = multilayer_perceptron() # 取消注释将使用 多层感知器
predict = convolutional_neural_network() # 取消注释将使用 LeNet5卷积神经网络
# 使用类交叉熵函数计算predict和label之间的损失函数
cost = fluid.layers.cross_entropy(input=predict, label=label)
# 计算平均损失
avg_cost = fluid.layers.mean(cost)
# 计算分类准确率
acc = fluid.layers.accuracy(input=predict, label=label)
return predict, [avg_cost, acc]
...
...
@@ -288,7 +339,7 @@ def train_program():
#### Optimizer Function 配置
在下面的 `Adam optimizer`,`learning_rate` 是
训练的速度,
与网络的训练收敛速度有关系。
在下面的 `Adam optimizer`,`learning_rate` 是
学习率,它的大小
与网络的训练收敛速度有关系。
```python
def optimizer_program():
...
...
@@ -304,14 +355,15 @@ def optimizer_program():
`batch`是一个特殊的decorator,它的输入是一个reader,输出是一个batched reader。在PaddlePaddle里,一个reader每次yield一条训练数据,而一个batched reader每次yield一个minibatch。
```python
# 一个minibatch中有64个数据
BATCH_SIZE = 64
# 每次读取训练集中的500个数据并随机打乱,传入batched reader中,batched reader 每次 yield 64个数据
train_reader = paddle.batch(
paddle.reader.shuffle(
paddle.dataset.mnist.train(), buf_size=500),
batch_size=BATCH_SIZE)
# 读取测试集的数据,每次 yield 64个数据
test_reader = paddle.batch(
paddle.dataset.mnist.test(), batch_size=BATCH_SIZE)
```
...
...
@@ -324,23 +376,24 @@ test_reader = paddle.batch(
#### Event Handler 配置
我们可以在训练期间通过调用一个handler函数来监控培训进度。
我们将在这里演示两个 `event_handler` 程序。请随意修改 Jupyter
笔记本
,看看有什么不同。
我们将在这里演示两个 `event_handler` 程序。请随意修改 Jupyter
Notebook
,看看有什么不同。
`event_handler` 用来在训练过程中输出训练结果
```python
def event_handler(pass_id, batch_id, cost):
# 打印训练的中间结果,训练轮次,batch数,损失函数
print("Pass %d, Batch %d, Cost %f" % (pass_id,batch_id, cost))
```
```python
from paddle.
v2
.plot import Ploter
from paddle.
utils
.plot import Ploter
train_prompt = "Train cost"
test_prompt = "Test cost"
cost_ploter = Ploter(train_prompt, test_prompt)
#
event_handler to plot a figure
#
将训练过程绘图表示
def event_handler_plot(ploter_title, step, cost):
cost_ploter.append(ploter_title, step, cost)
cost_ploter.plot()
...
...
@@ -358,31 +411,49 @@ def event_handler_plot(ploter_title, step, cost):
`feed_order` 用于将数据目录映射到 `train_program`
创建一个反馈训练过程中误差的`train_test`
训练完成后,模型参数存入`save_dirname`中
定义网络结构:
```python
# 该模型运行在单个CPU上
use_cuda = False #
set to True if training with GPU
use_cuda = False #
如想使用GPU,请设置为 True
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
# 调用train_program 获取预测值,损失值,
prediction, [avg_loss, acc] = train_program()
# 输入的原始图像数据,大小为28*28*1
img = fluid.layers.data(name='img', shape=[1, 28, 28], dtype='float32')
# 标签层,名称为label,对应输入图片的类别标签
label = fluid.layers.data(name='label', shape=[1], dtype='int64')
# 告知网络传入的数据分为两部分,第一部分是img值,第二部分是label值
feeder = fluid.DataFeeder(feed_list=[img, label], place=place)
# 选择Adam优化器
optimizer = fluid.optimizer.Adam(learning_rate=0.001)
optimizer.minimize(avg_loss)
```
PASS_NUM = 5
设置训练过程的超参:
```python
PASS_NUM = 5 #训练5轮
epochs = [epoch_id for epoch_id in range(PASS_NUM)]
# 将模型参数存储在名为 save_dirname 的文件中
save_dirname = "recognize_digits.inference.model"
```
```python
def train_test(train_test_program,
train_test_feed, train_test_reader):
# 将分类准确率存储在acc_set中
acc_set = []
# 将平均损失存储在avg_loss_set中
avg_loss_set = []
# 将测试 reader yield 出的每一个数据传入网络中进行训练
for test_data in train_test_reader():
acc_np, avg_loss_np = exe.run(
program=train_test_program,
...
...
@@ -390,17 +461,30 @@ def train_test(train_test_program,
fetch_list=[acc, avg_loss])
acc_set.append(float(acc_np))
avg_loss_set.append(float(avg_loss_np))
#
get test acc and loss
#
获得测试数据上的准确率和损失值
acc_val_mean = numpy.array(acc_set).mean()
avg_loss_val_mean = numpy.array(avg_loss_set).mean()
# 返回平均损失值,平均准确率
return avg_loss_val_mean, acc_val_mean
```
创建执行器:
```python
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
```
设置 main_program 和 test_program :
```python
main_program = fluid.default_main_program()
test_program = fluid.default_main_program().clone(for_test=True)
```
开始训练:
```python
lists = []
step = 0
for epoch_id in epochs:
...
...
@@ -408,12 +492,12 @@ for epoch_id in epochs:
metrics = exe.run(main_program,
feed=feeder.feed(data),
fetch_list=[avg_loss, acc])
if step % 100 == 0:
if step % 100 == 0:
#每训练100次 打印一次log
print("Pass %d, Batch %d, Cost %f" % (step, epoch_id, metrics[0]))
event_handler_plot(train_prompt, step, metrics[0])
step += 1
#
test for epoch
#
测试每个epoch的分类效果
avg_loss_val, acc_val = train_test(train_test_program=test_program,
train_test_reader=test_reader,
train_test_feed=feeder)
...
...
@@ -422,19 +506,25 @@ for epoch_id in epochs:
event_handler_plot(test_prompt, step, metrics[0])
lists.append((epoch_id, avg_loss_val, acc_val))
# 保存训练好的模型参数用于预测
if save_dirname is not None:
fluid.io.save_inference_model(save_dirname,
["img"], [prediction], exe,
model_filename=None,
params_filename=None)
# find the best
pass
# 选择效果最好的
pass
best = sorted(lists, key=lambda list: float(list[1]))[0]
print('Best pass is %s, testing Avgcost is %s' % (best[0], best[1]))
print('The classification accuracy is %.2f%%' % (float(best[2]) * 100))
```
训练过程是完全自动的,event_handler里打印的日志类似如下所示:
训练过程是完全自动的,event_handler里打印的日志类似如下所示。
Pass表示训练轮次,Batch表示训练全量数据的次数,cost表示当前pass的损失值。
每训练完一个Epoch后,计算一次平均损失和分类准确率。
```
Pass 0, Batch 0, Cost 0.125650
...
...
@@ -459,7 +549,7 @@ Test with Epoch 0, avg_cost: 0.053097883707459624, acc: 0.9822850318471338
### 生成预测输入数据
`infer_3.png` 是数字 3 的一个示例图像。把它变成一个 numpy 数组以匹配数据
馈送
格式。
`infer_3.png` 是数字 3 的一个示例图像。把它变成一个 numpy 数组以匹配数据
feed
格式。
```python
def load_image(file):
...
...
@@ -469,7 +559,7 @@ def load_image(file):
im = im / 255.0 * 2.0 - 1.0
return im
cur_dir =
cur_dir =
os.getcwd()
cur_dir = os.getcwd()
tensor_img = load_image(cur_dir + '/image/infer_3.png')
```
...
...
@@ -478,20 +568,23 @@ tensor_img = load_image(cur_dir + '/image/infer_3.png')
```python
inference_scope = fluid.core.Scope()
with fluid.scope_guard(inference_scope):
# Use fluid.io.load_inference_model to obtain the inference program desc,
# the feed_target_names (the names of variables that will be feeded
# data using feed operators), and the fetch_targets (variables that
# we want to obtain data from using fetch operators).
# 使用 fluid.io.load_inference_model 获取 inference program desc,
# feed_target_names 用于指定需要传入网络的变量名
# fetch_targets 指定希望从网络中fetch出的变量名
[inference_program, feed_target_names,
fetch_targets] = fluid.io.load_inference_model(
save_dirname, exe, None, None)
#
Construct feed as a dictionary of
{feed_target_name: feed_target_data}
#
and results will contain a list of data corresponding to fetch_targets.
#
将feed构建成字典
{feed_target_name: feed_target_data}
#
结果将包含一个与fetch_targets对应的数据列表
results = exe.run(inference_program,
feed={feed_target_names[0]: tensor_img},
fetch_list=fetch_targets)
lab = numpy.argsort(results)
# 打印 infer_3.png 这张图片的预测结果
img=Image.open('image/infer_3.png')
plt.imshow(img)
print("Inference result of image/infer_3.png is: %d" % lab[0][0][-1])
```
...
...
@@ -499,11 +592,11 @@ with fluid.scope_guard(inference_scope):
### 预测结果
如果顺利,预测结果输入如下:
`Inference result of image/infer_3.png is: 3`
`Inference result of image/infer_3.png is: 3`
, 说明我们的网络成功的识别出了这张图片!
## 总结
本教程的softmax回归、多层感知
器
和卷积神经网络是最基础的深度学习模型,后续章节中复杂的神经网络都是从它们衍生出来的,因此这几个模型对之后的学习大有裨益。同时,我们也观察到从最简单的softmax回归变换到稍复杂的卷积神经网络的时候,MNIST数据集上的识别准确率有了大幅度的提升,原因是卷积层具有局部连接和共享权重的特性。在之后学习新模型的时候,希望大家也要深入到新模型相比原模型带来效果提升的关键之处。此外,本教程还介绍了PaddlePaddle模型搭建的基本流程,从dataprovider的编写、网络层的构建,到最后的训练和预测。对这个流程熟悉以后,大家就可以用自己的数据,定义自己的网络模型,并完成自己的训练和预测任务了。
本教程的softmax回归、多层感知
机
和卷积神经网络是最基础的深度学习模型,后续章节中复杂的神经网络都是从它们衍生出来的,因此这几个模型对之后的学习大有裨益。同时,我们也观察到从最简单的softmax回归变换到稍复杂的卷积神经网络的时候,MNIST数据集上的识别准确率有了大幅度的提升,原因是卷积层具有局部连接和共享权重的特性。在之后学习新模型的时候,希望大家也要深入到新模型相比原模型带来效果提升的关键之处。此外,本教程还介绍了PaddlePaddle模型搭建的基本流程,从dataprovider的编写、网络层的构建,到最后的训练和预测。对这个流程熟悉以后,大家就可以用自己的数据,定义自己的网络模型,并完成自己的训练和预测任务了。
<a
name=
"参考文献"
></a>
## 参考文献
...
...
@@ -521,6 +614,7 @@ with fluid.scope_guard(inference_scope):
<br/>
<a
rel=
"license"
href=
"http://creativecommons.org/licenses/by-sa/4.0/"
><img
alt=
"知识共享许可协议"
style=
"border-width:0"
src=
"https://i.creativecommons.org/l/by-sa/4.0/88x31.png"
/></a><br
/><span
xmlns:dct=
"http://purl.org/dc/terms/"
href=
"http://purl.org/dc/dcmitype/Text"
property=
"dct:title"
rel=
"dct:type"
>
本教程
</span>
由
<a
xmlns:cc=
"http://creativecommons.org/ns#"
href=
"http://book.paddlepaddle.org"
property=
"cc:attributionName"
rel=
"cc:attributionURL"
>
PaddlePaddle
</a>
创作,采用
<a
rel=
"license"
href=
"http://creativecommons.org/licenses/by-sa/4.0/"
>
知识共享 署名-相同方式共享 4.0 国际 许可协议
</a>
进行许可。
</div>
<!-- You can change the lines below now. -->
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录