Skip to content

Commit 08297b7

Browse files
authored
Polish dy2stat doc (PaddlePaddle#4145)
* Polish dy2stat doc * fix code style
1 parent 3073f78 commit 08297b7

File tree

2 files changed

+18
-40
lines changed

2 files changed

+18
-40
lines changed

docs/guides/04_dygraph_to_static/basic_usage_cn.md

+9-36
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
动静转换(@to_static)通过解析 Python 代码(抽象语法树,下简称:AST) 实现一行代码即可将动态图转为静态图的功能,只需在待转化的函数前添加一个装饰器 ``@paddle.jit.to_static``
77

8-
如下是使用 @to_static 进行动静转换的两种方式
8+
使用 @to_static 即支持 **可训练可部署** ,也支持**只部署**(详见[模型导出](#2)) ,常见使用方式如下
99

1010
- 方式一:使用 @to_static 装饰器装饰 ``SimpleNet`` (继承了 ``nn.Layer``) 的 ``forward`` 函数:
1111

@@ -28,8 +28,8 @@
2828
net.eval()
2929
x = paddle.rand([2, 10])
3030
y = paddle.rand([2, 3])
31-
out = net(x, y)
32-
paddle.jit.save(net, './net')
31+
out = net(x, y) # 动转静训练
32+
paddle.jit.save(net, './net') # 导出预测模型
3333
```
3434

3535
- 方式二:调用 ``paddle.jit.to_static()`` 函数,仅做预测模型导出时推荐此种用法。
@@ -53,17 +53,20 @@
5353
net = paddle.jit.to_static(net) # 动静转换
5454
x = paddle.rand([2, 10])
5555
y = paddle.rand([2, 3])
56-
out = net(x, y)
57-
paddle.jit.save(net, './net')
56+
out = net(x, y) # 动转静训练
57+
paddle.jit.save(net, './net') # 导出预测模型
5858
```
5959

60-
方式一和方式二的主要区别是,使用 @to_static 除了支持预测模型导出外,在模型训练时,还会转为静态图子图训练,而方式二仅支持预测模型导出。@to_static 的基本执行流程如下图:
60+
方式一和方式二的主要区别是,前者直接在 `forward()` 函数定义处装饰,后者显式调用了 `jit.to_static()`方法,默认会对 `net.forward`进行动静转换。
61+
62+
两种方式均支持动转静训练,如下是@to_static 的基本执行流程:
6163

6264
<img src="https://raw.githubusercontent.com/PaddlePaddle/docs/develop/docs/guides/04_dygraph_to_static/images/to_static_train.png" style="zoom:50%" />
6365

6466

6567

6668
## 二、动转静模型导出
69+
<span id='2'></span>
6770

6871
动转静模块**是架在动态图与静态图的一个桥梁**,旨在打破动态图模型训练与静态部署的鸿沟,消除部署时对模型代码的依赖,打通与预测端的交互逻辑。下图展示了**动态图模型训练——>动转静模型导出——>静态预测部署**的流程。
6972

@@ -350,22 +353,6 @@ paddle.save(layer_state_dict, "net.pdiparams") # 导出模型
350353

351354
上图展示了动态图下**模型训练——>参数导出——>预测部署**的流程。如图中所示,动态图预测部署时,除了已经序列化的参数文件,还须提供**最初的模型组网代码**
352355

353-
在动态图下,模型代码是 **逐行被解释执行** 的。如:
354-
355-
```python
356-
import paddle
357-
358-
zeros = paddle.zeros(shape=[1,2], dtype='float32')
359-
print(zeros)
360-
361-
#Tensor(shape=[1, 2], dtype=float32, place=CPUPlace, stop_gradient=True,
362-
# [[0., 0.]])
363-
```
364-
365-
366-
**从框架层面上,上述的调用链是:**
367-
368-
> 前端 zeros 接口 &rarr; core.ops.fill_constant (Pybind11) &rarr; 后端 Kernel &rarr; 前端 Tensor 输出
369356

370357
如下是一个简单的 Model 示例:
371358

@@ -430,20 +417,6 @@ paddle.save(main_program.state_dict(), para_path) # 导出为 .pdiparams
430417

431418
在静态图编译期,变量 ``Variable`` 只是**一个符号化表示**,并不像动态图 ``Tensor`` 那样持有实际数据。
432419

433-
```python
434-
import paddle
435-
# 开启静态图模式
436-
paddle.enable_static()
437-
438-
zeros = paddle.zeros(shape=[1,2], dtype='float32')
439-
print(zeros)
440-
# var fill_constant_1.tmp_0 : LOD_TENSOR.shape(1, 2).dtype(float32).stop_gradient(True)
441-
```
442-
443-
**从框架层面上,静态图的调用链:**
444-
445-
> layer 组网(前端) &rarr; InferShape 检查(编译期) &rarr; Executor(执行期) &rarr; 逐个执行 OP
446-
447420

448421
如下是 ``SimpleNet`` 的静态图模式下的组网代码:
449422

docs/guides/04_dygraph_to_static/principle_cn.md

+9-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
## 一、 设置 Placeholder 信息
66

77

8-
静态图下,模型起始的 Placeholder 信息是通过 ``paddle.static.data`` 来指定的,并以此作为编译期的 ``InferShape`` 推导起点。
8+
静态图下,模型起始的 Placeholder 信息是通过 ``paddle.static.data`` 来指定的,并以此作为编译期的 ``InferShape`` 推导起点,即用于推导输出 Tensor 的 shape
99

1010
```python
1111
import paddle
@@ -41,7 +41,7 @@ class SimpleNet(paddle.nn.Layer):
4141
net = SimpleNet()
4242

4343
# 通过 InputSpec 设置 Placeholder 信息
44-
x_spec = InputSpec(shape=[None, 10], name='x')
44+
x_spec = InputSpec(shape=[None, 10], name='x')
4545
y_spec = InputSpec(shape=[3], name='y')
4646

4747
net = paddle.jit.to_static(net, input_spec=[x_spec, y_spec]) # 动静转换
@@ -138,14 +138,14 @@ def add_two(x, y):
138138
+ **只有**控制流的判断条件 **依赖了``Tensor``**(如 ``shape````value`` ),才会转写为对应 Op
139139

140140

141-
142-
143141
### 3.1 IfElse
144142

145143
无论是否会转写为 ``cond_op`` ,动转静都会首先对代码进行处理,**转写为 ``cond`` 接口可以接受的写法**
146144

147145
**示例一:不依赖 Tensor 的控制流**
148146

147+
如下代码样例中的 `if label is not None`, 此判断只依赖于 `label` 是否为 `None`(存在性),并不依赖 `label` 的Tensor值(数值性),因此属于**不依赖 Tensor 的控制流**
148+
149149
```python
150150
def not_depend_tensor_if(x, label=None):
151151
out = x + 1
@@ -176,6 +176,8 @@ def not_depend_tensor_if(x, label=None):
176176

177177
**示例二:依赖 Tensor 的控制流**
178178

179+
如下代码样例中的 `if paddle.mean(x) > 5`, 此判断直接依赖 `paddle.mean(x)` 返回的Tensor值(数值性),因此属于**依赖 Tensor 的控制流**
180+
179181
```python
180182
def depend_tensor_if(x):
181183
if paddle.mean(x) > 5.: # <---- Bool Tensor 类型
@@ -235,6 +237,7 @@ def convert_ifelse(pred, true_fn, false_fn, true_args, false_args, return_vars):
235237
``For/While`` 也会先进行代码层面的规范化,在逐行执行用户代码时,才会决定是否转为 ``while_op``
236238

237239
**示例一:不依赖 Tensor 的控制流**
240+
如下代码样例中的 `while a < 10`, 此循环条件中的 `a` 是一个 `int` 类型,并不是 Tensor 类型,因此属于**不依赖 Tensor 的控制流**
238241

239242
```python
240243
def not_depend_tensor_while(x):
@@ -269,6 +272,8 @@ def not_depend_tensor_while(x):
269272

270273
**示例二:依赖 Tensor 的控制流**
271274

275+
如下代码样例中的 `for i in range(bs)`, 此循环条件中的 `bs` 是一个 `paddle.shape` 返回的 Tensor 类型,且将其 Tensor 值作为了循环的终止条件,因此属于**依赖 Tensor 的控制流**
276+
272277
```python
273278
def depend_tensor_while(x):
274279
bs = paddle.shape(x)[0]

0 commit comments

Comments
 (0)