Skip to content

Commit 010917b

Browse files
authored
New python op doc (#885)
* new python op doc,test=develop * fix typo, test=develop
1 parent 289420a commit 010917b

File tree

4 files changed

+121
-4
lines changed

4 files changed

+121
-4
lines changed

doc/fluid/advanced_usage/development/new_op/index_cn.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44

55
本部分将指导您如何新增Operator,也包括一些必要的注意事项
66

7-
- `如何写新的op <./new_op.html>`_
7+
- `如何写新的C++ op <./new_op.html>`_
88

9-
- `op相关注意事项 <./op_notes.html>`_
9+
- `C++ op相关注意事项 <./op_notes.html>`_
10+
11+
- `如何写新的Python op <./new_python_op.html>`_
1012

1113
.. toctree::
1214
:hidden:
1315

1416
new_op.md
1517
op_notes.md
18+
new_python_op.md
1619

doc/fluid/advanced_usage/development/new_op/new_op.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 如何写新的OP
1+
# 如何写新的C++ OP
22

33
## 概念简介
44

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# 如何写新的Python OP
2+
3+
PaddlePaddle Fluid通过 `py_func` 接口支持在Python端编写op。
4+
5+
6+
## py_func接口概述
7+
8+
`py_func` 具体接口为:
9+
10+
```Python
11+
def py_func(func, x, out, backward_func=None, skip_vars_in_backward_input=None):
12+
pass
13+
```
14+
15+
其中,
16+
17+
- `x` 是Python Op的输入变量,可以是单个 `Variable` 或者 `List[Variable]`
18+
- `out` 是Python Op的输出变量,可以是单个 `Variable` 或者 `List[Variable]`
19+
- `func` 是Python Op的前向函数。在运行网络前向时,框架会调用 `out = func(*x)` ,根据前向输入 `x` 和前向函数 `func` 计算前向输出 `out`
20+
- `backward_func` 是Python Op的反向函数。若 `backward_func``None` ,则该Python Op没有反向计算逻辑;
21+
`backward_func` 不为 `None`,则框架会在运行网路反向时调用 `backward_func` 计算前向输入 `x` 的梯度。
22+
- `skip_vars_in_backward_input` 为反向函数 `backward_func` 中不需要的输入,可以是单个 `Variable` 或者 `List[Variable]`
23+
24+
25+
## 如何使用py_func编写Python Op
26+
27+
以下以tanh为例,介绍如何利用 `py_func` 编写Python Op。
28+
29+
- 第一步:定义前向函数和反向函数
30+
31+
前向函数和反向函数均由Python编写。
32+
33+
若前向函数的输入为 `x_1`, `x_2`, ..., `x_n` ,输出为`y_1`, `y_2`, ..., `y_m`,则前向函数的定义格式为:
34+
```Python
35+
def foward_func(x_1, x_2, ..., x_n):
36+
...
37+
return y_1, y_2, ..., y_m
38+
```
39+
40+
默认情况下,反向函数的输入参数顺序为:所有前向输入变量 + 所有前向输出变量 + 所有前向输出变量的梯度,因此对应的反向函数的定义格式为:
41+
```Python
42+
def backward_func(x_1, x_2, ..., x_n, y_1, y_2, ..., y_m, dy_1, dy_2, ..., dy_m):
43+
...
44+
return dx_1, dx_2, ..., dx_n
45+
```
46+
47+
若反向函数不需要某些前向输入变量或前向输出变量,可设置 `skip_vars_in_backward_input` 进行排除(步骤三中会叙述具体的排除方法)。
48+
49+
此处我们利用numpy库完成tanh的前向函数和反向函数编写。
50+
51+
```Python
52+
import numpy as np
53+
54+
def my_tanh(x):
55+
return np.tanh(x)
56+
57+
def my_tanh_grad(x, y, dy):
58+
return np.array(dy) * (1 - np.square(np.array(y)))
59+
```
60+
61+
注意,前向函数和反向函数的输入均是 `LoDTensor` 类型,输出可以是Numpy Array或 `LoDTensor`
62+
由于 `LoDTensor` 实现了Python的buffer protocol协议,因此我们既可通过 `numpy.array` 直接将 `LoDTensor` 转换为Numpy Array,也可直接将 `LoDTensor` 作为Numpy函数的输入参数。
63+
64+
tanh的反向函数不需要前向输入x,因此我们可定义一个不需要前向输入x的反向函数,并在后续通过 `skip_vars_in_backward_input` 进行排除 :
65+
66+
```Python
67+
def my_tanh_grad_without_x(y, dy):
68+
return np.array(dy) * (1 - np.square(np.array(y)))
69+
```
70+
71+
- 第二步:创建前向输出变量
72+
73+
我们需调用 `Program.current_block().create_var` 创建前向输出变量。在创建前向输出变量时,必须指明变量的名称name、数据类型dtype和维度shape。
74+
75+
```Python
76+
import paddle.fluid as fluid
77+
78+
def create_tmp_var(program, name, dtype, shape):
79+
return program.current_block().create_var(name=name, dtype=dtype, shape=shape)
80+
81+
in_var = fluid.layers.data(name='input', dtype='float32', shape=[-1, 28, 28])
82+
83+
# 手动创建前向输出变量
84+
out_var = create_tmp_var(fluid.default_main_program(), name='output', dtype='float32', shape=[-1, 28, 28])
85+
```
86+
87+
- 第三步:调用 `py_func` 组建网络
88+
89+
`py_func` 的调用方式为:
90+
91+
```Python
92+
fluid.layers.py_func(func=my_tanh, x=in_var, out=out_var, backward_func=my_tanh_grad)
93+
```
94+
95+
若我们不希望在反向函数输入参数中出现前向输入,则可使用 `skip_vars_in_backward_input` 进行排查,简化反向函数的参数列表。
96+
97+
```Python
98+
fluid.layers.py_func(func=my_tanh, x=in_var, out=out_var, backward_func=my_tanh_grad_without_x,
99+
skip_vars_in_backward_input=in_var)
100+
```
101+
102+
至此,使用 `py_func` 编写Python Op的步骤结束。我们可以与使用其他Op一样进行网路训练/预测。
103+
104+
105+
## 注意事项
106+
107+
- `py_func` 的前向函数和反向函数内部不应调用 `fluid.layers.xxx` ,因为前向函数和反向函数是在网络运行时调用的,且输入参数均为C++端的 `LoDTensor`
108+
`fluid.layers.xxx` 是在组建网络的阶段调用的,且输入参数为Python端的 `Variable`
109+
110+
- `skip_vars_in_backward_input` 只能跳过前向输入变量和前向输出变量,不能跳过前向输出的梯度。
111+
112+
- 若某个前向输出变量没有梯度,则 `backward_func` 将接收到 `None` 的输入。若某个前向输入变量没有梯度,则我们应在 `backward_func` 中主动返回
113+
`None`
114+

doc/fluid/advanced_usage/development/new_op/op_notes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# OP相关注意事项
1+
# C++ OP相关注意事项
22

33
## Fluid中Op的构建逻辑
44
### 1.Fluid中Op的构建逻辑

0 commit comments

Comments
 (0)