Skip to content

Commit 7811fc0

Browse files
committed
[CustomOP Optional] Add custom operator optional chapter
1 parent a85842a commit 7811fc0

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

docs/guides/custom_op/new_cpp_op_cn.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,112 @@ PD_BUILD_GRAD_OP(custom_add)
13191319
```
13201320
13211321
1322+
#### optional 机制
1323+
1324+
自定义算子的 optional 机制主要用于传入 Tensor 可能为 None 的场景,C++ 算子通过判断输入的 optional Tensor 是否为 None,可以执行不同的操作。
1325+
1326+
下面结合具体的使用示例进行介绍,自定义一个输入为 `Tensor x` 和 `optional<Tensor> y`,输出为 `Tensor out` 的加法算子:
1327+
1328+
$$
1329+
out =
1330+
\begin{cases}
1331+
x + y &(if\ \ y\ \ is\ \ valid) \\
1332+
x + x &(if\ \ y\ \ is\ \ None) \\
1333+
\end{cases}
1334+
$$
1335+
1336+
函数实现如下:
1337+
```c++
1338+
#include <vector>
1339+
1340+
#include "paddle/extension.h"
1341+
1342+
/*
1343+
if (y) {
1344+
out = x + y;
1345+
} else {
1346+
out = x + x;
1347+
}
1348+
*/
1349+
std::vector<paddle::Tensor> AddForward(
1350+
const paddle::Tensor& x,
1351+
const paddle::optional<paddle::Tensor>& y) { // NOLINT
1352+
PD_CHECK(x.place() == paddle::PlaceType::kCPU, "x must be a CPU Tensor.");
1353+
paddle::Tensor out = paddle::empty(x.shape(), x.dtype(), x.place());
1354+
1355+
if (y) {
1356+
out = x + y.get();
1357+
} else {
1358+
out = x + x;
1359+
}
1360+
1361+
return {out};
1362+
}
1363+
1364+
std::vector<paddle::DataType> AddInferDtype(
1365+
const paddle::DataType& x_dtype,
1366+
const paddle::optional<paddle::DataType>& y_dtype) {
1367+
if (y_dtype) {
1368+
return {*y_dtype};
1369+
}
1370+
return {x_dtype};
1371+
}
1372+
1373+
std::vector<std::vector<int64_t>> AddInferShape(
1374+
const std::vector<int64_t>& x_shape,
1375+
const paddle::optional<std::vector<int64_t>>& y_shape) {
1376+
if (y_shape) {
1377+
return {*y_shape};
1378+
}
1379+
return {x_shape};
1380+
}
1381+
1382+
/*
1383+
if (y) {
1384+
x_grad = out_grad;
1385+
} else {
1386+
x_grad = out_grad + out_grad;
1387+
}
1388+
*/
1389+
std::vector<paddle::Tensor> AddBackward(
1390+
const paddle::Tensor& x,
1391+
const paddle::optional<paddle::Tensor>& y,
1392+
const paddle::Tensor& out_grad) { // NOLINT
1393+
PD_CHECK(x.place() == paddle::PlaceType::kCPU, "x must be a CPU Tensor.");
1394+
paddle::Tensor x_grad = paddle::zeros(x.shape(), x.dtype(), x.place());
1395+
1396+
if (y) {
1397+
x_grad = out_grad;
1398+
} else {
1399+
x_grad = out_grad + out_grad;
1400+
}
1401+
1402+
return {x_grad};
1403+
}
1404+
1405+
PD_BUILD_OP(custom_add)
1406+
.Inputs({"X", paddle::Optional("Y")})
1407+
.Outputs({"Out"})
1408+
.SetKernelFn(PD_KERNEL(AddForward))
1409+
.SetInferShapeFn(PD_INFER_SHAPE(AddInferShape))
1410+
.SetInferDtypeFn(PD_INFER_DTYPE(AddInferDtype));
1411+
1412+
PD_BUILD_GRAD_OP(custom_add)
1413+
.Inputs({"X", paddle::Optional("Y"), paddle::Grad("Out")})
1414+
.Outputs({paddle::Grad("X")})
1415+
.SetKernelFn(PD_KERNEL(AddBackward));
1416+
```
1417+
1418+
相比于算子的常规实现,使用 optional 机制需要注意以下几点:
1419+
1420+
1. 输入的 optional Tensor 类型,应该修改为 `const paddle::optional<paddle::Tensor>&` 而非 `const paddle::Tensor&`;相应的 `InferShapeFn``InferDtypeFn` 输入类型分别修改为 `const paddle::optional<std::vector<int64_t>>&``const paddle::optional<paddle::DataType>&`
1421+
1422+
2. 定义算子时,需要使用 `paddle::Optional` 标注 optional 类型的 Tensor;
1423+
1424+
3. 暂不支持 optional\<Tensor\> 类型的输出,因此反向算子做计算时,无法输出前向算子 optional Tensor 类型输入的梯度。
1425+
1426+
4. optional 的定义可以参考源码文件 `paddle/utils/optional.h`,用法与 boost optional 基本一致。
1427+
13221428
## 自定义算子编译与使用
13231429

13241430
本机制提供了两种编译自定义算子的方式,分别为 **使用 `setuptools` 编译****即时编译** ,下面依次通过示例介绍。

0 commit comments

Comments
 (0)