@@ -1319,6 +1319,112 @@ PD_BUILD_GRAD_OP(custom_add)
1319
1319
```
1320
1320
1321
1321
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
+
1322
1428
## 自定义算子编译与使用
1323
1429
1324
1430
本机制提供了两种编译自定义算子的方式,分别为 ** 使用 ` setuptools ` 编译** 与 ** 即时编译** ,下面依次通过示例介绍。
0 commit comments