Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
FluidDoc
提交
e3582790
F
FluidDoc
项目概览
PaddlePaddle
/
FluidDoc
通知
5
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
23
列表
看板
标记
里程碑
合并请求
111
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
F
FluidDoc
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
23
Issue
23
列表
看板
标记
里程碑
合并请求
111
合并请求
111
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
未验证
提交
e3582790
编写于
10月 23, 2019
作者:
Z
Zeng Jinle
提交者:
GitHub
10月 23, 2019
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
refine_design_idea_doc, test=develop (#1533)
上级
787cd28b
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
40 addition
and
42 deletion
+40
-42
doc/fluid/advanced_usage/design_idea/fluid_design_idea.md
doc/fluid/advanced_usage/design_idea/fluid_design_idea.md
+40
-42
doc/fluid/advanced_usage/design_idea/image/executor_design.png
...luid/advanced_usage/design_idea/image/executor_design.png
+0
-0
未找到文件。
doc/fluid/advanced_usage/design_idea/fluid_design_idea.md
浏览文件 @
e3582790
...
...
@@ -2,53 +2,51 @@
## 简介
本篇文档主要介绍
Fluid
底层的设计思想,帮助用户更好的理解框架运作过程。
本篇文档主要介绍
飞桨(PaddlePaddle,以下简称Paddle)
底层的设计思想,帮助用户更好的理解框架运作过程。
阅读本文档,您将了解:
-
Fluid
内部的执行流程
-
Paddle
内部的执行流程
-
Program 如何描述模型
-
Executor 如何执行运算
## 1.
Fluid
内部执行流程
## 1.
Paddle
内部执行流程
Fluid
使用一种编译器式的执行流程,分为编译时和运行时两个部分,具体包括:编译器定义 Program ,创建Executor 运行 Program 。
Paddle
使用一种编译器式的执行流程,分为编译时和运行时两个部分,具体包括:编译器定义 Program ,创建Executor 运行 Program 。
本地训练任务执行流程图如下所示:
<p
align=
"center"
>
<img
src=
"https://raw.githubusercontent.com/PaddlePaddle/FluidDoc/develop/doc/fluid/advanced_usage/design_idea/image/fluid_process.png"
width=
800
>
</p>
1.
编译时,用户编写一段python程序,通过调用 Fluid 提供的算子,向一段 Program 中添加变量(Tensor)以及对变量的操作(Operators 或者 Layers)。用户只需要描述核心的前向计算,不需要关心反向计算、分布式下以及异构设备下如何计算。
2.
原始的 Program 在平台内部转换为中间描述语言:
`ProgramDesc`
。
3.
编译期最重要的一个功能模块是
`Transpiler`
。
`Transpiler`
接受一段
`ProgramDesc`
,输出一段变化后的
`ProgramDesc`
,作为后端
`Executor`
最终需要执行的 Fluid Program
4.
后端 Executor 接受 Transpiler 输出的这段 Program ,依次执行其中的 Operator(可以类比为程序语言中的指令),在执行过程中会为 Operator 创建所需的输入输出并进行管理。
1.
编译时,用户编写一段python程序,通过调用 Paddle 提供的算子,向一段 Program 中添加变量(Tensor)以及对变量的操作(Operators 或者 Layers)。用户只需要描述核心的前向计算,不需要关心反向计算、分布式下以及异构设备下如何计算。
2.
原始的 Program 在框架内部转换为中间描述语言:
`ProgramDesc`
。
3.
`Transpiler`
接受一段
`ProgramDesc`
,输出一段变化后的
`ProgramDesc`
,作为后端
`Executor`
最终需要执行的 Program 。
`Transpiler`
并非必需步骤。
4.
执行
`ProgramDesc`
中定义的 Operator(可以类比为程序语言中的指令),在执行过程中会为 Operator 创建所需的输入输出并进行管理。
## 2. Program设计思想
用户完成网络定义后,一段
Fluid 程序中通常存在 2 段
Program:
用户完成网络定义后,一段
Paddle 程序中通常存在 2 个
Program:
1.
fluid.default_startup_program:定义了
创建模型参数,输入输出,以及模型中可学习参数的初始化等各种操作
1.
fluid.default_startup_program:定义了
模型参数初始化、优化器参数初始化、reader初始化等各种操作。
default_startup_program 可以由框架自动生成,使用时无需显
示
地创建
default_startup_program 可以由框架自动生成,使用时无需显
式
地创建
如果调用修改了参数的默认初始化方式,框架会自动的将相关的修改加入default_startup_program
2.
fluid.default_main_program :定义了神经网络模型,前向反向计算,以及优化算法对网络中可学习参数的更新
使用Fluid的核心就是构建起 default_main_program
2.
fluid.default_main_program :定义了神经网络模型,前向反向计算,以及模型参数更新、优化器参数更新等各种操作。
使用Paddle的核心就是构建起 default_main_program
<a
name=
"ProgramsAndBlocks"
></a>
### Programs and Blocks
Fluid
的 Program 的基本结构是一些嵌套 blocks,形式上类似一段 C++ 或 Java 程序。
Paddle
的 Program 的基本结构是一些嵌套 blocks,形式上类似一段 C++ 或 Java 程序。
blocks中包含:
...
...
@@ -68,7 +66,7 @@ int main(){ //block 0
}
```
类似的,在下列
Fluid
的 Program 包含3段block:
类似的,在下列
Paddle
的 Program 包含3段block:
```
python
import
paddle.fluid
as
fluid
# block 0
...
...
@@ -95,7 +93,7 @@ prob = ie()
```
### BlockDesc and ProgramDesc
用户描述的block与program信息在
Fluid中以
[
protobuf
](
https://en.wikipedia.org/wiki/Protocol_Buffers
)
格式保存,所有的
`protobuf`
信息被定义在
`framework.proto`
中,在Fluid
中被称为BlockDesc和ProgramDesc。ProgramDesc和BlockDesc的概念类似于一个
[
抽象语法树
](
https://en.wikipedia.org/wiki/Abstract_syntax_tree
)
。
用户描述的block与program信息在
Paddle中以
[
protobuf
](
https://en.wikipedia.org/wiki/Protocol_Buffers
)
格式保存,所有的
`protobuf`
信息被定义在
`framework.proto`
中,在Paddle
中被称为BlockDesc和ProgramDesc。ProgramDesc和BlockDesc的概念类似于一个
[
抽象语法树
](
https://en.wikipedia.org/wiki/Abstract_syntax_tree
)
。
`BlockDesc`
中包含本地变量的定义
[
vars
](
../../api_guides/low_level/program.html#variable
)
,和一系列的operator
`ops`
:
...
...
@@ -108,7 +106,7 @@ message BlockDesc {
}
```
parent
ID
表示父块,因此block中的操作符可以引用本地定义的变量,也可以引用祖先块中定义的变量。
parent
_idx
表示父块,因此block中的操作符可以引用本地定义的变量,也可以引用祖先块中定义的变量。
Program 中的每层 block 都被压平并存储在数组中。blocks ID是这个数组中块的索引。
...
...
@@ -120,7 +118,7 @@ message ProgramDesc {
### 使用Blocks的Operator
[
Programs and Blocks
](
#Programs
and
Blocks)的例子中,IfElseOp这个Operator包含了两个block——true分支和false分支。
[
Programs and Blocks
](
#Programs
And
Blocks
)
的例子中,IfElseOp这个Operator包含了两个block——true分支和false分支。
下述OpDesc的定义过程描述了一个operator可以包含哪些属性:
...
...
@@ -152,7 +150,7 @@ message AttrDesc {
Executor 在运行时将接受一个
`ProgramDesc`
、一个
`block_id`
和一个
`Scope`
。
`ProgramDesc`
是
`block`
的列表,每一项包含
`block`
中所有参数和
`operator`
的
`protobuf`
定义;
`block_id`
指定入口块;
`Scope`
是所有变量实例的容器。
其中
`Scope`
包含了
`name`
与
`Variable`
的映射,所有变量都被定义在
`Scope`
里。大部分API会默认使用
`global_scope`
,例如 :code:
`Executor.run`
,
您也可以指定网络运行在某个特定的
`Scope`
中,一个网络可以在不同的
`Scope`
内运行,并在该
`Scope`
内更新不同的
`Variable`
。
其中
`Scope`
包含了
`name`
与
`Variable`
的映射,所有变量都被定义在
`Scope`
里。大部分API会默认使用
`global_scope`
,例如
`Executor.run`
,
您也可以指定网络运行在某个特定的
`Scope`
中,一个网络可以在不同的
`Scope`
内运行,并在该
`Scope`
内更新不同的
`Variable`
。
完成的编译执行的具体过程如下图所示:
...
...
@@ -160,9 +158,9 @@ Executor 在运行时将接受一个`ProgramDesc`、一个`block_id`和一个`Sc
<img
src=
"https://raw.githubusercontent.com/PaddlePaddle/FluidDoc/develop/doc/fluid/advanced_usage/design_idea/image/executor_design.png"
width=
600
>
</p>
1.
Executor 为每一个block创建一个Scope,Block是可嵌套的,因此Scope也是可嵌套的
2.
创建所有Scope中的变量
3.
按顺序创建并执行所有operator
1.
Executor 为每一个block创建一个Scope,Block是可嵌套的,因此Scope也是可嵌套的
。
2.
创建所有Scope中的变量
。
3.
创建并执行所有operator。
...
...
@@ -182,7 +180,7 @@ class Executor{
scope
->
Var
(
Var
->
Name
());
}
//创建OP并
按顺序
执行
//创建OP并执行
for
(
auto
&
op_desc
:
block
.
AllOps
()){
auto
op
=
CreateOp
(
*
op_desc
);
op
->
Run
(
*
local_scope
,
place_
);
...
...
@@ -192,7 +190,7 @@ class Executor{
**创建Executor**
Fluid
中使用fluid.Executor(place)创建Executor,place属性由用户定义,代表程序将在哪里执行。
Paddle
中使用fluid.Executor(place)创建Executor,place属性由用户定义,代表程序将在哪里执行。
下例代码表示创建一个Executor,其运行场所在CPU内:
...
...
@@ -203,7 +201,7 @@ exe = fluid.Executor(cpu)
**运行Executor**
Fluid
使用Executor.run来运行程序。定义中通过Feed映射获取数据,通过fetch
\_
list获取结果:
Paddle
使用Executor.run来运行程序。定义中通过Feed映射获取数据,通过fetch
\_
list获取结果:
```
python
...
...
...
@@ -215,11 +213,11 @@ outs = exe.run(
## 代码实例
本节通过
[
Fluid
编程指南
](
../../beginners_guide/programming_guide/programming_guide.html
)
中简单的线性回归例子,为您介绍上述内容如何在代码中实现。
本节通过
[
编程指南
](
../../beginners_guide/programming_guide/programming_guide.html
)
中简单的线性回归例子,为您介绍上述内容如何在代码中实现。
**定义Program**
您可以随意定义自己的数据和网络结构,定义的结果都将作为一段 Program 被
Fluid
接收,Program 的基本结构是一些 blocks,本节的 Program 仅包含一个 block 0:
您可以随意定义自己的数据和网络结构,定义的结果都将作为一段 Program 被
Paddle
接收,Program 的基本结构是一些 blocks,本节的 Program 仅包含一个 block 0:
```
python
#加载函数库
...
...
@@ -230,8 +228,8 @@ import numpy
train_data
=
numpy
.
array
([[
1.0
],[
2.0
],[
3.0
],[
4.0
]]).
astype
(
'float32'
)
y_true
=
numpy
.
array
([[
2.0
],[
4.0
],[
6.0
],[
8.0
]]).
astype
(
'float32'
)
#定义网络
x
=
fluid
.
layers
.
data
(
name
=
"x"
,
shape
=
[
1
],
dtype
=
'float32'
)
y
=
fluid
.
layers
.
data
(
name
=
"y"
,
shape
=
[
1
],
dtype
=
'float32'
)
x
=
fluid
.
data
(
name
=
"x"
,
shape
=
[
None
,
1
],
dtype
=
'float32'
)
y
=
fluid
.
data
(
name
=
"y"
,
shape
=
[
None
,
1
],
dtype
=
'float32'
)
y_predict
=
fluid
.
layers
.
fc
(
input
=
x
,
size
=
1
,
act
=
None
)
#定义损失函数
cost
=
fluid
.
layers
.
square_error_cost
(
input
=
y_predict
,
label
=
y
)
...
...
@@ -298,9 +296,9 @@ blocks {
```
从输出结果中可以看到,整个定义过程在框架内部转化为了一段ProgramDesc,以block idx为索引。本次线性回归模型中仅有1个block,ProgramDesc中也仅有block 0一段BlockDesc。
BlockDesc中包含定义的 vars 和一系列的 ops,以输入x为例,python代码中定义 x 是一个数据类型为"float
32"的1维数据:
BlockDesc中包含定义的 vars 和一系列的 ops,以输入x为例,python代码中定义 x 是一个数据类型为"float32"的1维数据:
```
python
x
=
fluid
.
layers
.
data
(
name
=
"x"
,
shape
=
[
1
],
dtype
=
'float32'
)
x
=
fluid
.
data
(
name
=
"x"
,
shape
=
[
None
,
1
],
dtype
=
'float32'
)
```
在BlockDesc中,变量x被描述为:
```
...
...
@@ -319,15 +317,15 @@ vars {
}
persistable: false
```
在
Fluid
中所有的数据类型都为LoD-Tensor,对于不存在序列信息的数据(如此处的变量X),其lod_level=0。
在
Paddle
中所有的数据类型都为LoD-Tensor,对于不存在序列信息的数据(如此处的变量X),其lod_level=0。
dims表示数据的维度,这里表示 x 的维度为[-1,1],其中-1是batch的维度,无法确定具体数值时,
Fluid
自动用 -1 占位。
dims表示数据的维度,这里表示 x 的维度为[-1,1],其中-1是batch的维度,无法确定具体数值时,
Paddle
自动用 -1 占位。
参数
`persistable`
表示该变量在整个训练过程中是否为持久化变量。
**创建Executor**
Fluid
使用Executor来执行网络训练,Executor运行细节请参考
[
Executor设计思想
](
#Executor设计思想
)
的介绍。作为使用者,实际并不需要了解内部机制。
Paddle
使用Executor来执行网络训练,Executor运行细节请参考
[
Executor设计思想
](
#Executor设计思想
)
的介绍。作为使用者,实际并不需要了解内部机制。
创建Executor只需调用 fluid.Executor(place) 即可,在此之前请您依据训练场所定义place变量:
```
python
...
...
@@ -338,14 +336,14 @@ Fluid使用Executor来执行网络训练,Executor运行细节请参考[Executo
```
**运行Executor**
Fluid
使用Executor.run来运行一段Program。
Paddle
使用Executor.run来运行一段Program。
正式进行网络训练前,需先执行参数初始化。其中 defalut_startup_program 中定义了
创建模型参数,输入输出,以及模型中可学习参数的
初始化等各种操作。
正式进行网络训练前,需先执行参数初始化。其中 defalut_startup_program 中定义了
模型参数初始化、优化器参数初始化、reader
初始化等各种操作。
```
python
#参数初始化
exe
.
run
(
fluid
.
default_startup_program
())
```
由于传入数据与传出数据存在多列,因此
fluid
通过 feed 映射定义数据的传输数据,通过 fetch_list 取出期望结果:
由于传入数据与传出数据存在多列,因此
Paddle
通过 feed 映射定义数据的传输数据,通过 fetch_list 取出期望结果:
```
python
#开始训练
outs
=
exe
.
run
(
...
...
@@ -362,5 +360,5 @@ Fluid使用Executor.run来运行一段Program。
[6.099215 ]], dtype=float32), array([1.6935859], dtype=float32)]
```
至此您已经了解了
Fluid
内部的执行流程的核心概念,更多框架使用细节请参考
[
使用指南
](
../../user_guides/index_cn.html
)
相关内容,
[
模型库
](
../../user_guides/models/index_cn.html
至此您已经了解了
Paddle
内部的执行流程的核心概念,更多框架使用细节请参考
[
使用指南
](
../../user_guides/index_cn.html
)
相关内容,
[
模型库
](
../../user_guides/models/index_cn.html
)
中也为您提供了丰富的模型示例以供参考。
doc/fluid/advanced_usage/design_idea/image/executor_design.png
查看替换文件 @
787cd28b
浏览文件 @
e3582790
69.5 KB
|
W:
|
H:
111.5 KB
|
W:
|
H:
2-up
Swipe
Onion skin
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录