Skip to content

Commit

Permalink
Update new api (PaddlePaddle#5300)
Browse files Browse the repository at this point in the history
* update new api

* update

* modify according to review comment
  • Loading branch information
zyfncg authored Sep 28, 2022
1 parent fc71922 commit 88750a1
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 6 deletions.
17 changes: 15 additions & 2 deletions docs/dev_guides/api_contributing_guides/new_cpp_op_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@
| Python API | [python/paddle](https://github.com/PaddlePaddle/Paddle/tree/develop/python/paddle) 目录下的相应子目录中的 .py 文件,遵循相似功能的 API 放在同一文件夹的原则 |
| 单元测试 | [python/paddle/fluid/tests/unittests](https://github.com/PaddlePaddle/Paddle/tree/develop/python/paddle/fluid/tests/unittests) 目录下的相应文件中:<br/>test_xxx_op.py |

<center><img src="https://github.com/PaddlePaddle/docs/blob/develop/docs/dev_guides/api_contributing_guides/images/api_op_kernel.png?raw=true" width="800" ></center>

用户使用飞桨开发神经网络模型时使用的 Python 接口(如 paddle.add(), paddle.relu()等) 我们一般称都为飞桨的 Python API,每个运算类的 Python API 在框架内部都会对应到一个或者多个 C++ 端算子,每个算子在不同硬件设备上(CPU, GPU 等)实现的运算逻辑代码又被称为 Kernel, 这里主要是由于不同硬件设备提供的编程接口不同,所以虽然同一个算子的不同硬件设备 Kernel 都实现了相同的数学运算逻辑,但在代码实现上却有所差异。算子 InferMeta 函数是在算子 kernel 执行前先将输出结果的维度、数据类型等信息进行处理,由于计算量较小所以可以直接在 CPU 上计算,因此每个算子只需要实现一个 InferMeta 函数,而不必像 Kernel 一样在不同硬件上实现多个。

Python API、算子 Yaml 配置、算子 InferMeta 函数 和算子 Kernel 之间的关系如上图所示,最上层为用户使用的飞桨 Python API 接口,Python API 执行时会进入到 C++ 端由框架进行调度并执行相应的算子逻辑,算子的执行主要包括两个过程:

(1)执行算子 InferMeta 函数完成输出结果的维度、数据类型等静态信息的推导。

(2)根据输入变量的设备信息选择对应的硬件设备来执行算子 Kernel,完成输出结果的数值计算。

Python API 到算子 InferMeta 函数和 Kernel 调用之间的框架调度部分的逻辑代码主要通过算子 Yaml 配置中的信息自动生成,也可以理解为算子 Yaml 配置的作用是通过自动代码生成将上层 Python API 与底层算子的 Kernel 建立连接。


接下来以 trace 算子操作,计算输入 Tensor 在指定平面上的对角线元素之和,并输出相应的计算结果,即以 [paddle.trace](../../api/paddle/trace_cn.html#trace) 为例来介绍如何新增算子。

## 三、新增算子描述及定义
Expand Down Expand Up @@ -968,12 +981,12 @@ PADDLE_ENFORCE_EQ(比较对象 A, 比较对象 B, 错误提示信息)
```
#### 7.3.2 减少反向算子中的无关变量
通常反向算子会依赖于前向算子的某些输入、输出 Tensor,以供反向算子计算使用。但有些情况下,反向算子不需要前向算子的所有输入和输出;有些情况下,反向算子只需要前向算子的部分输入和输出;有些情况下,反向算子只需要使用前向算子中输入和输出变量的 Shape 和 LoD 信息。若开发者在注册反向算子时,将不必要的前向算子输入和输出作为反向算子的输入,会导致这部分显存无法被框架现有的显存优化策略优化,从而导致模型显存占用过高。
通常反向算子会依赖于前向算子的某些输入、输出 Tensor,以供反向算子计算使用。但有些情况下,反向算子不需要前向算子的所有输入和输出;有些情况下,反向算子只需要前向算子的部分输入和输出;有些情况下,反向算子只需要使用前向算子中输入和输出变量的 Shape 和 [LoD](new_cpp_op_cn.html#lod) 信息。若开发者在注册反向算子时,将不必要的前向算子输入和输出作为反向算子的输入,会导致这部分显存无法被框架现有的显存优化策略优化,从而导致模型显存占用过高。
所以在定义反向算子时需要注意以下几点:
- 如果反向不需要前向的某些输入或输出参数,则无需在 args 中设置。
- 如果有些反向算子需要依赖前向算子的输入或输出变量的的 Shape 或 LoD,但不依赖于变量中 Tensor 的内存 Buffer 数据,且不能根据其他变量推断出该 Shape 和 LoD,则可以通过 `no_need_buffer` 对该变量进行配置,详见[YAML 配置规则](new_cpp_op_cn.html#yaml)。示例:
- 如果有些反向算子需要依赖前向算子的输入或输出变量的的 Shape 或 [LoD](new_cpp_op_cn.html#lod),但不依赖于变量中 Tensor 的内存 Buffer 数据,且不能根据其他变量推断出该 Shape 和 [LoD](new_cpp_op_cn.html#lod),则可以通过 `no_need_buffer` 对该变量进行配置,详见[YAML 配置规则](new_cpp_op_cn.html#yaml)。示例:
```yaml
- backward_op : trace_grad
forward : trace (Tensor x, int offset, int axis1, int axis2) -> Tensor(out)
Expand Down
12 changes: 8 additions & 4 deletions docs/dev_guides/api_contributing_guides/new_python_api_cn.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# 开发 API Python 端

## 新增飞桨 API 的场景与意义
API 作为用户使用飞桨框架的接口,承接着实现用户模型开发需求的重要作用。虽然目前飞桨已经提供了一千多个 API 用于支持各类场景下的模型开发,但在某些前沿邻域模型的探索中仍然可能会遇到框架已提供的 API 不足以支撑开发需求的情况,此时就可以通过在飞桨框架中新增 API 来解决这类问题。
开发飞桨 API 可以加深对深度学习框架底层架构的理解,提升技术视野,同时也是在为深度学习框架开源社区的发展提供助力,让更多的 AI 开发者享受到 AI 基础设施带来的便利。

新增飞桨 API 主要包含两种情况:

1. 不需要开发新的 C++ 算子,可以用其他 Python API 组合得到新的 API,只写 Python 代码即可。
Expand Down Expand Up @@ -79,7 +83,7 @@ def zeros(shape, dtype=None, name=None):

#### 2.2.2 代码示例二(调用 C++ 算子接口)

如果 API 的实现中需要调用 C++ 算子,则需要分别实现动态图分支和静态图分支的代码。
如果 API 的实现中需要调用 C++ 算子,则需要分别实现动态图分支和静态图分支的代码(由于飞桨框架同时支持动态图和静态图两种训练模式,动态图和静态图在执行逻辑上有所差异,需要在 Python 端根据当前的运行模式选择进入到对应的执行分支去处理)

接下来以 [paddle.trace](../../api/paddle/trace_cn.html) API 的实现代码为例(示例代码路径:[python/paddle/tensor/math.py](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/tensor/math.py#L2784)),分别介绍动态图分支和静态图分支的开发要点:

Expand Down Expand Up @@ -153,11 +157,11 @@ def trace(x, offset=0, axis1=0, axis2=1, name=None):

对于静态图,一般分为输入参数检查、创建输出 Tensor、添加 OP 几个步骤。

- **输入参数检查:** 包括必要的类型检查、值检查,以及输入 Tensor 的 shape、dtype 等检查,确保组网能正常运行等。
- 输入参数的检查一般仅在静态图分支中使用。主要原因是静态图下该函数仅被执行一次,发生在组网时,而动态图下该函数会被多次执行,Python 端过多的输入检查会影响执行效率。并且由于动态图即时执行的优势,如果发生错误也可以通过分析 C++ 端的报错信息定位问题。
- **输入参数检查:** 包括必要的类型检查、值检查,以及输入 Tensor 的 shape、dtype 等检查,确保组网能正常运行等,这里的参数检查可以帮助用户尽早的暴露问题并修正,从而降低模型的开发调试成本
- 输入参数的检查一般仅在静态图分支中使用。主要原因是静态图下该函数仅在模型组网时执行一次,运行期不会再执行;而动态图下该函数会被多次执行,Python 端过多的输入检查会影响执行效率。并且由于动态图即时执行的优势,如果发生错误也可以通过分析 C++ 端的报错信息定位问题。
- 示例中输入参数检查的代码逻辑比较复杂但仅用于 `trace` 函数,因此在该函数内定义一个检查输入参数的函数 `__check_input`,代码见下文。
- **创建输出 Tensor ,添加 OP:**
- 先创建 LayerHelper 对象,再使用 LayerHelper 对象创建输出 Tensor(LayerHelper 是一个用于创建 OP 输出变量、向 静态图 Program 中添加 OP 的辅助工具类)。
- 先创建 LayerHelper 对象,再使用 LayerHelper 对象创建输出 Tensor([LayerHelper](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/layer_helper.py) 是一个用于创建 OP 输出变量、向 静态图 Program 中添加 OP 的辅助工具类)。
-`append_op` 添加 `inputs``outputs` 项,其中的 key 值(静态图中变量名)一般与 Python 接口中定义的输入输出 Tensor 变量名的命名相同。(注意:这里 `trace` 中的 `Input` 没有与 Python 接口中 `x` 命名直接对应是由于为了兼容旧算子体系下 `trace` 算子的定义实现而做了额外的映射,新增算子时无需考虑这种情况。)

输入参数检查的 `__check_input` 函数代码如下所示,其中检测 Tensor 的数据类型可以用 [check_variable_and_dtype](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/data_feeder.py#L80)[check_type](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/data_feeder.py#L128) 函数进行检测。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

<center><img src="https://github.com/PaddlePaddle/docs/blob/develop/docs/dev_guides/api_contributing_guides/images/paddle_api.png?raw=true" width="800" ></center>

> 说明:一般来说 C++ 相比 Python 有着明显的性能优势,所以为了做到模型训练和推理的高效,除了常用的 API 之外,其他包括框架的执行器调度逻辑,算子的 Kernel 实现等都在 C++ 端完成。并且由于目前像 GPU,NPU,XPU 这样的硬件设备有着比 CPU 更强的计算能力,所以深度学习框架也会把算子运算逻辑的 Kernel 在这些硬件设备上进行实现来达到更好的训练和推理性能,但要在这些硬件上开发一些复杂算子的 Kernel 实现成本还是比较高的,因此我们也提供了组合算子的机制(组合式算子)通过复用已有算子 Kernel 来降低新算子 Kernel 的开发成本。

## <span id="apiDesignDoc">二、飞桨 API 设计文档提交说明</span>

设计文档,通常也叫 RFC(Request for Comment)文档,可方便开发者与飞桨核心团队、其他社区开发者充分交流设计思路,以便进一步完善设计方案,并确保与飞桨设计理念一致。请参考如下步骤完成 API 设计文档的提交:
Expand Down

0 comments on commit 88750a1

Please sign in to comment.