为 OpenVINO 新增 Paddle 算子转换支持

介绍

在这篇教程中,你将会一步一步学习如何为 OpenVINO 新增 Paddle 算子转换支持。PaddlePaddle 是最受欢迎的国产深度学习框架之一,虽然 OpenVINO 已经支持了Paddle离线模型的载入以及转换为IR格式,但是随着框架的迭代更新,会不断的构建新的算子,甚至修改部分算子的API接口,所以需要开发者添加新的算子映射支持,从而使 Paddle 以及 OpenVINO 更加易用。

第一步:Fork

首先至OpenVino官方仓库,然后点击fork按钮,生成自己目录下的仓库,比如https://github.com/USERNAME/openvino

第二步:克隆

将远程仓库 clone 到本地:

$ git clone https://https://github.com/USERNAME/openvino
$ cd openvino

第三步:创建本地分支

不建议直接在master分支上直接进行开发,一般从 master 分支上创建新分支。

$ git checkout -b BRANCHNAME

BRANCHNAME建议起一个有意义的分支名字

第四步:开始正式开发

在正式开发前,我们先尝试从源码编译OpenVINO

4.1 尝试从源码编译

4.1.1 更新子模块仓库源码
$ git submodule update --init --recursive

如果位于中国地区拉取仓库比较慢,可以尝试使用一下脚本进行拉取

$ cd openvino
$ chmod +x scripts/submodule_update_with_gitee.sh
$ ./scripts/submodule_update_with_gitee.sh
4.1.2 下载、build依赖
$ chmod +x install_build_dependencies.sh
$ ./install_build_dependencies.sh
4.1.3 构建项目
export OPENVINO_BASEDIR=`pwd`
$ mkdir build
$ cd build
$ cmake \
-DCMAKE_BUILD_TYPE= Release -DCMAKE_INSTALL_PREFIX="${OPENVINO_BASEDIR}/openvino_dist" \
-DPYTHON_EXECUTABLE=$(which python3) \
-DENABLE_MYRIAD=OFF \
-DENABLE_VPU=OFF \
-DENABLE_PYTHON=ON \
-DNGRAPH_PYTHON_BUILD_ENABLE=ON \
-DENABLE_DEBUG_CAPS=ON \
-DENABLE_CPU_DEBUG_CAPS=ON  \
-DENABLE_TESTS=ON \
..

关于编译的选择项可以查看此处

4.1.4 编译、安装
$ make -j$(nproc); make install

上述便完成了openvino的编译安装过程。

若完成了以上步骤,便可以继续下面的正式开发了。

注意:每次修改代码后需要再次编译安装

4.2 新增算子转换(以gather_nd算子为例)

新增一个算子的转换我们需要在下列文件中添加对应文件或代码

本教程也会以上面顺序进行介绍

4.2.1 算子映射的实现

首先在 Paddle Docs 查看该算子的用法,再根据算子旁边的 源代码 链接跳转至该算子的 Python 前端代码,如下图:

下面是截取的部分代码,并简要说明其作用

    helper = LayerHelper('gather_nd', **locals())
    dtype = helper.input_dtype()
    output = helper.create_variable_for_type_inference(dtype)
    helper.append_op(
        type="gather_nd",
        inputs={"X": x, "Index": index},
        outputs={"Out": output},
    )
    return output

LayerHelper 是一个用于创建 OP 输出变量、向静态图 program 中添加 OP 的辅助工具类。在这里我们实例了一个 gather_nd 算子,并将输入 Tensor,输出 Tensor, 以两个字典的形式,作为参数添加 OP

所以通过这部分代码我们可以知道在 Paddle 这边 gather_nd 通过输入inputs字典,字典中有 “X” ,“Index” 字段。

而其他的算子的参数并非也是Tensor输入的,而是作为 attributes 字典输入的,例如shard_index 则是以attr字典输入的

同时查阅openvino的算子表可以知道openvino有GatherND算子,于是我们可以尝试写出如下转换代码

...

 NamedOutputs gather_nd(const NodeContext& node) {
     const auto data_node = node.get_input("X");
     const auto index_node = node.get_input("Index");
     return node.default_single_output_mapping({std::make_shared<default_opset::GatherND>(data_node, index_node)},
                                               {"Out"});
...

关于NodeContext的说明可以查看openvino官方文档关于前端拓展的部分

4.2.2 注册算子

src/frontends/paddle/src/op_table.cpp文件中注册该算子。

...
OP_CONVERTER(gather_nd);
...
std::map<std::string, CreatorFunction> get_supported_ops() {
    return {
 {"gather_nd", op::gather_nd},
 };

加入自己新增的算子即可

4.2.3 新增单测

为了测试自己新加入的单测是否完成转换或者精度要求,需要添加单元测试

具体在src/core/tests/frontend/paddle/test_models/gen_scripts文件夹中新建一个单测文件

例如:generate_gather_nd.py

在编写单元测试过程中请尽量全面,如输入类型的单测,输入与输入之间关系的单测(如果存在的话),输入值为特殊值的情况(空数组,负值,零值)等

4.2.4 注册单测实例

在完成单测的编写后,需要在op_fuzzy.cpp中注册单测src/core/tests/frontend/paddle/op_fuzzy.cpp

例如:

std::string("gather_nd_float32"),
std::string("gather_nd_int64"),
std::string("gather_nd_int32"),
std::string("gather_nd_empty"),
std::string("gather_nd_low_index"),
std::string("gather_nd_high_rank1"),
std::string("gather_nd_high_rank2"),
4.2.5 重新编译以及运行单测
$ make -j$(nproc); make install
$ cd bin/intel64/Release
$ ./paddle_tests --gtest_filter=PaddleFuzzyOpTest/FrontEndFuzzyOpTest.testOpFuzzy/*

此处的*是通配符,若只想测试添加的算子可以例如:*gather_nd* 的写法进行测试

在测试完毕后有一个单测发生了报错,根据提示说明openvino是不支持 rank=0 的数组输入的,所以我们需要对用户的输入数据进行检查。

4.2.6 映射实现的优化

根据单侧我们需要不断迭代自己的算子映射的实现,就本例子为例,我们可以对于输入张量进行检查:

...
auto shape = index_node.get_partial_shape();
if (shape.is_static() && shape.rank().get_length() == 0)
    PADDLE_OP_CHECK(node, false, "zero 'indices' input rank is not allowed for gather_nd");
...

这里我们对输入的index进行形状检查,并进行报错提示。PADDLE_OP_CHECK是一个宏定义,当第二个参数是false时,将第三个输入作为报错信息。

4.2.7 PR的提交

在完成上述过程后我们便可以尝试提交一个PR啦。

在提交之前先将代码提交并同步至远端

$ git status
$ git add changfile
$ git commit -m "add a paddle op gather_op"

commit 信息尽量有意义且能说明每次的修改内容

推送至远端

$ git fetch upstream
$ git pull upstream BRANCHNAME

开启一个PR 并填写 title ,为了方便review,请附上自己单侧的通过情况的截图以及Paddle算子对应算子的实现

至此便完成了openvino gather_nd 算子对于Paddle的转换支持 🎉

总结

本文章简单介绍了如何向 OpenVINO 新增一个Paddle算子转换的流程,学习到了如何在 OpenVINO 中添加算子转换实现以及添加对应的单测,本任务难度适中,建议尝试。但是在实现映射的过程中,需要注意在不同尺度,不同组合方式的输入精度对齐的问题,可以查看OpenVINO 算子实现,以及Paddle算子的实现。
此文章为搬运
原项目链接

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐