Openvino支持PaddlePaddle的算子开发

1、背景

现在用PaddlePaddle框架训练的越来越多,Paddle框架中,算子和openvino的算子有些计算不一样,需要做一个映射,才能在Openvino上跑推理,所以需要在Openvino上支持更多的Paddle的算子。

2、项目开发过程

2.1 Openvino支持Paddle的elementwise_floordiv算子

  • Paddle中的elementwise_floordiv算子,在Openvino中没有,需要根据Paddle这边elementwise_floordev的定义,在Openvino中实现同样的功能。

2.2 任务介绍

  • 在Openvino侧完成elementwise_floordiv的功能,并能跑通单测

2.3 前期准备工作

2.3.1 对Paddle elementwise_floordiv算子的调研
  • 困惑?

    看到这个算子,首先在PaddlePaddle API 文档中 查找python 接口 elementwise_floordiv, 发现对应的版本Paddle版本是1.8的(Paddle已经不支持的版本), 怀疑出题的人是不是搞错了? 先在Paddle官网上查找下1.8和2.x对应的接口,在2.x版本中对应的名字是 floor_divide.

  • 对新旧版本elementwise_floordiv的分析(从文档上分析)

    相同点:

    • 计算功能都是 z = x // y (文档定义,后面有坑)
    • 支持的类型都是int32, int64

    不同点:

    • 参数不同,elementwise_floordiv 多了一个参数axis,axis是指 y shape 在 x shape 上的起点,在floor_divide中少了axis
    • broadcast 方式不一样,在elementwise_floordiv中,broadcast是根据axis 的值来做broadcast, 是paddle的broadcast机制,比如:x_shape = [2, 3, 4, 5], y_shape = [3, 4] ,axis = 1, 在调用该函数,则会 按照 y_shape = [1, 3, 4, 1] 来做broadcast; 然而 floor_divide 不支持上面这种broadcast, 它则是按照Numpy的broadcast的机制;所以其实floor_divide 是 elementwise_floordiv 在axis = -1的这种特殊形式
2.3.2 对Openvino算子的调研
  • 功能拆分

    Openvino算子是基本的数学公式组成,而Paddle一般是大算子,几个小的组合而成。如果Openvino有对应的算子,就直接映射调用,如果没有则需要对Paddle的算子做拆分,先做拆分,再去查找文档,这样比较明确目标。elementwise_floordiv 则可以分成:broadcast, divide, floor 三个小算子。

  • 查找对应算子

    openvino文档中查看上面的拆分的小算子

    • broadcast: Openvino的文档中介绍,支持Numpy的机制,也支持PDPD的机制,满足要求,不需要做shape的转换,可以直接传参数来做PDPD的broadcast
    • divide: 在文档中发现,它有m_pythondiv, auto_broadcast 这两个属性,在调用的时候,设置这两个属性,就满足了Paddle的elementwise_floordiv的功能。于是锁定这个算子。
2.3.3 版本兼容

Paddle 有两个版本,为了兼容性,在设计接口的时候,应该考虑增加属性axis, 如果没有这个属性的时候,默认-1.

2.4 开发过程

2.4.1 环境搭建
  • 选择开发环境,可以直接用物理机,我选择的是docker, 用Paddle提供的镜像(自带安装了paddle2.1版本)registry.baidubce.com/paddlepaddle/paddle:2.1.0, 也可以到Paddle的镜像列表 中去选择其他的镜像。

  • 通过下面的命令创建容器

docker run --net host --name=openvino --user root -it -v `pwd`:/workspace -w /workspace  registry.baidubce.com/paddlepaddle/paddle:2.1.0 /bin/bash
  • 现在github上面,fork openvino 到自己的github上面

  • 在进入容器后,git pull 自己github上面Openvino的代码到本地, 设置upstream, 并创建自己本地的开发分支。通过下面的命令来完成

git remote add upstream https://github.com/openvinotoolkit/openvino
git pull --rebase master:master
git checkout branch dev
git checkout dev
git rebase master
  • 按照openvino的官网文档编译
cd openvino
git submodule update --init --recursive
chmod +x install_build_dependencies.sh
./install_build_dependencies.sh
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 \
..
make -j30 && make install

  • 在编译执行过程中,遇到下面的问题,然后按照下面的指令解决即可

1. libusb is required, please install it  ( apt-get install libusb-1.0-0-dev)
2. lfs is not found (apt-get install git-lfs 或者 git lfs install --system)
3. shell check的问题 (apt-get install shellcheck)
4. Cython的问题 (pip install Cython --install-option="--no-cython-compile")
5. 找不到python头文件的问题,可以把上面的cmake命令按照下面命令执行即可:
cmake .. \
-DPYTHON_INCLUDE_DIR=$(python -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())")  \
-DPYTHON_LIBRARY=$(python -c "import distutils.sysconfig as sysconfig; print(sysconfig.get_config_var('LIBDIR'))") \
-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 \


  • 编译完成后,跑下openvino里面本来就有的paddle单测
cd bin/intel64/Release
./paddle_tests --gtest_filter=PaddleFuzzyOpTest/FrontEndFuzzyOpTest.testOpFuzzy/*
2.4.2 开发写代码
  • openvino工程中有paddle的目录,paddle的目录结构如下

在这里插入图片描述

  • 在op_table.cpp中注册函数如下,第一个是注册Paddle的OP,第二个是绑定的OP到对应的实现函数名:

在这里插入图片描述在这里插入图片描述

  • elementwise_floordiv 函数功能的实现以及功能

    • 代码实现:在op目录下面的 elementwise_ops.cpp中增加 elementwise_floordiv 的实现,代码以及代码含义如下图
      在这里插入图片描述

    • 参数设置以及出现的问题:按照前期对Openvino中Divide的分析,需要设置m_pythondiv = true 和 auto_broadcast = PDPD, 就可以满足要求; 实际上m_pythondiv = true 是实现了 z = x//y的功能,和Paddle 的文档一致,但是在单测中,输入负数的时候,他们的结果计算是不一致的,比如:x = [-3], y = [2] 的时候,Paddle的z = -1, Openvino 的z = -2, Paddle的行为和文档预期不符合;最后查看Paddle底层的调用,实际调用的static_cast(x/y);要和Paddle实际逻辑一致,需要 m_pythondiv = false.

  • 增加单测代码

    • 代码实现:在目录src/core/tests/frontend/paddle/test_models/gen_scripts下面增加单测代码,原工程中本来就有elementwise相关的单测文件,直接在原来的文件中增加foor_div的单测, 代码以及含义如下图
      在这里插入图片描述

    • 遇到的问题以及考虑:数据测试范围设置,如果不设置负数的输入的话,上面Paddle的问题无法出现

  • 注册单测实例,在src/core/tests/frontend/paddle/op_fuzzy.cpp 中注册在这里插入图片描述

  • 完成后,编译,跑单测:
    在这里插入图片描述

  • 单测完成后,就提交代码,按照下面的命令提交, 然后在github上面写PR信息就可以了,等待反馈,有问题修改

git checkout master
git pull --rebase upstream master:master
git chekcout dev
git rebase master
git push origin -f dev:dev

2.5 总结

Openvino的这次比赛,主要是做Paddle算子到Openvino上的映射,Paddle的算子丰富后,Paddle的模型直接可以在Openvino上面跑。要完成这样的题目,首先要对Paddle和Openvino都要有一定的了解。参加这次比赛,收获颇多,对做这样算子映射的一些思考:

  • 开始写代码前的调研很重要,对算子在Paddle上逻辑实现,参数,版本等都要深入分析,然后再根据Openvino的特点做算子拆分或者合并;如果我前期没有深入调研,在Openvino上的实现,可能就用小算在来组合,这样效率就比较低了。
  • 单测的数据范围和类型要尽量全,对于这个算子,如果没有负数的测试,就不会发现Paddle的文档和实际逻辑不一致的问题。

此文章为搬运
原项目链接

Logo

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

更多推荐