社区贡献者分享 | OpenVINO™ 代码贡献助力我的开源之路

原创
06/08 14:00
阅读数 14

点击蓝字

关注我们,让开发变得更有趣

作者 | 占俊坚

排版 | 李擎


摘要


在 OpenVINO™ 2024.1 release 版本中,我为 OpenVINO™ 添加了 TensorFlow 中的 Rint operation 以及 PyTorch 中的 aten::bucketize operation 的支持,在此分享我的实现过程,给有兴趣参与 OpenVINO™ 开源项目的同学参考,希望大家都能积极参与到社区建设当中来!


OpenVINO™ 代码贡献持续开放中,感兴趣的同学可以点击“化身英特尔‘Issues 猎手’,共创百万用户开源生态!”了解详情。


OpenVINO™


介绍


OpenVINO™ 中用来支持 TensorFlow 模型的前端组件称为 TensorFlow Frontend (简称“TF FE” )。TF FE 将一个用 TensorFlow opset 表示的模型转成用 OpenVINO™ opset 表示的模型。为了支持模型中含有 Rint 操作的模型的推理,TF FE 需要支持这个操作。同理 PyTorch 前端需要支持 aten::bucketize 操作。

TensorFlow opset :

https://www.tensorflow.org/api_docs/python/tf/raw_ops

OpenVINO™ opset:

https://docs.openvino.ai/archive/2023.2/openvino_docs_ops_opset13.html

Rint:

https://www.tensorflow.org/api_docs/python/tf/raw_ops/Rint


OpenVINO™


TensorFlow operation 开发过程



1.在 TF FE 的 op 目录为 Rint 实现对应的 loader


一个 loader 负责对一种 TensorFlow 操作进行转换。loader 的职责就是解析 operation 的 attributes, 读取输入,并通过 OpenVINO™ 已有的 operations 去表达。以我的实现为例,我实现了 src/frontends/tensorflow_common/src/op/rint.cpp 文件,内容如下:

// Copyright (C) 2018-2024 Intel Corporation// SPDX-License-Identifier: Apache-2.0//
#include "common_op_table.hpp"#include "openvino/op/round.hpp"
using namespace std;using namespace ov::op;
namespace ov {namespace frontend {namespace tensorflow {namespace op {
OutputVector translate_rint_op(const NodeContext& node) { default_op_checks(node, 1, {"Rint"});
auto input = node.get_input(0); // using default round mode "half_to_even" in openvino, // as TF has only that mode auto round_mode = v5::Round::RoundMode::HALF_TO_EVEN; auto res = make_shared<v5::Round>(input, round_mode); set_node_name(node.get_name(), res); return res->outputs();}} // namespace op} // namespace tensorflow} // namespace frontend}  // namespace ov

loader 输入的 NodeContext 包含 Rint 的所有的 inputs 和 attributes 。首先使用 default_op_checks 函数校验 Rint 操作已被支持,且输入的个数大于1。然后通过 get_input 方法获取输入。根据 Tensorflow 文档中对 Rint 的定义,在 OpenVINO™ 的 Operation Sets 文档中 https://docs.openvino.ai/archive/2023.2/openvino_docs_ops_opset13.html 找到可以表达 Rint 的 v5::Round(注:复杂的 Operation 可能需要组合使用多个 OpenVINO™ 的 Operation)。最后返回包含输出的 vector。



2. 注册 Rint Operation


分别在 src/frontends/tensorflow/src/op_table.cpp 中加上一行 {"Rint", CreatorFunction(translate_rint_op)}, 在 src/frontends/tensorflow_common/include/common_op_table.hpp 加上一行 OP_CONVERTER(translate_rint_op); 来注册该 Operation。



3. Build 项目


编译 build 整个 OpenVINO™ 项目。可能需要解决代码规范问题,以及可能出现的编译错误。注意新手在 build 的时候可能会踩坑,注意仔细查看相应平台的 build 文档如 build_linux.md,一步一步操作。例如这个例子中,需要加上文档中提示的编译 Python API 所需要的 -DENABLE_PYTHON=ON 选项。



4. 实现对应单测并验证


在 tests/layer_tests/tensorflow_tests/test_tf_Rint.py 目录中实现对应的单测,如下所示:

# Copyright (C) 2018-2024 Intel Corporation# SPDX-License-Identifier: Apache-2.0
import numpy as npimport pytestimport tensorflow as tffrom common.tf_layer_test_class import CommonTFLayerTest
class TestRint(CommonTFLayerTest): def _prepare_input(self, inputs_info): assert 'input:0' in inputs_info inputs_shape = inputs_info['input:0'] inputs_data = {} rng = np.random.default_rng() inputs_data['input:0'] = rng.uniform(-5.0, 5.0, inputs_shape).astype(self.input_type) return inputs_data
def create_tf_rint_net(self, input_shape, input_type): self.input_type = input_type tf.compat.v1.reset_default_graph() with tf.compat.v1.Session() as sess: input = tf.compat.v1.placeholder(input_type, input_shape, 'input') tf.raw_ops.Rint(x=input) tf.compat.v1.global_variables_initializer() tf_net = sess.graph_def
ref_net = None
return tf_net, ref_net
@pytest.mark.parametrize("input_shape", [[], [6], [2, 5], [5, 4, 1]]) @pytest.mark.parametrize("input_type", [np.float32, np.float64]) @pytest.mark.precommit @pytest.mark.nightly def test_rint_basic(self, input_shape, input_type, ie_device, precision, ir_version, temp_dir, use_legacy_frontend): self._test(*self.create_tf_rint_net(input_shape, input_type), ie_device, precision, ir_version, temp_dir=temp_dir,                use_legacy_frontend=use_legacy_frontend)

通过 @pytest.mark.parametrize 装饰器配置单测的输入的 shape 和 type,注意要配置输入 shape 为空的 corner case。在 create_tf_rint_net 方法中定义包含 Rint 操作的网络。在 _prepare_input 方法中根据输入的 shape 和 type,随机生成输入的 tensor。


实现之后,执行以下命令开始测试:

export TEST_DEVICE=CPUcd openvino/tests/layer_tests/tensorflow_testspytest test_tf_Shape.py


小 Tips:

  1. pytest 加上 -s 选项,否则      pytest 不打印 print 的结果到标准输出。

  2. pytest 加上 —maxfail=1 可以在第一组测试用例 fail 的时候停止,防止 console 出现过多的错误用例信息。或者也可以在 pytest.mark.parametrize 装饰器可以先配置一组参数,调通后再添加多组参数。

  3. 单测的实现可以多参考同目录的其他 operation 单测,基本涵盖了各种情况的测试。














整个 PR 的链接为 https://github.com/openvinotoolkit/openvino/pull/24059/files。


OpenVINO™


PyTorch operation 开发过程



实现步骤及思路同上面的 TensorFlow operation 一致。以我实现的 aten::bucketize operation 为例。


在 PyTorch 的文档中查阅 torch.bucketize 的签名如下torch.bucketize(input, boundaries, ***, out_int32=False, right=False, out=None) → Tensor

https://pytorch.org/docs/stable/tensors.html#torch.Tensor


然后在 OpenVINO™ 的 opset 文档查阅到可用的 v3::Bucketize 进行相应的转换。

// Copyright (C) 2018-2024 Intel Corporation// SPDX-License-Identifier: Apache-2.0//
#include "openvino/op/bucketize.hpp"
#include "openvino/frontend/pytorch/node_context.hpp"#include "openvino/op/add.hpp"#include "openvino/op/concat.hpp"#include "openvino/op/convert_like.hpp"#include "openvino/op/logical_or.hpp"#include "openvino/op/multiply.hpp"#include "utils.hpp"
namespace ov {namespace frontend {namespace pytorch {namespace op {
using namespace ov::op;
OutputVector translate_bucketize(const NodeContext& context) { num_inputs_check(context, 2, 5); auto input = context.get_input(0); auto boundaries = context.get_input(1);
element::Type output_type = ov::element::i64; if (!context.input_is_none(2) && context.const_input<bool>(2)) { output_type = ov::element::i32; }
bool with_right_bound = true; if (!context.input_is_none(3)) { with_right_bound = !context.const_input<bool>(3); }
auto bucketize = context.mark_node(std::make_shared<v3::Bucketize>(input, boundaries, output_type, with_right_bound));
if (!context.input_is_none(4)) { context.mutate_input(4, bucketize); }
return {bucketize};};
} // namespace op} // namespace pytorch} // namespace frontend}  // namespace ov

具体地,首先进行参数个数的校验,然后获取2个输入,随后,通过读取第3个输入决定输出的类型是否为 int32 (默认 int64)。接下来,读取第3个输入,判断 with_right_bound是否为 true。紧接着,就可以创建一个 v3::Bucketize node。接着读取第3个输入,判断输出是否返回。最后返回输出。


后续的注册 operation, build OpenVINO™ 项目以及实现单测的步骤和上面的 Rint 操作思路基本一致。具体可以参考 PR (链接:https://github.com/openvinotoolkit/openvino/pull/23527/files


OpenVINO™


总结


OpenVINO™ 的社区非常活跃,maintainer 会耐心回答大家的问题,并仔细 review 我们提交的代码。通过这2个 PR,我熟悉了给开源项目贡献代码的流程,也对 OpenVINO™ 有了更深入的了解。看到自己的代码可以被合入到一个拥有百万用户级别的开源项目,我感到非常有成就感!在这里,我鼓励大家可以积极参与,为开源项目做贡献的同时,提升自身技能,共创开源之路!


OpenVINO™ 代码贡献持续开放中,感兴趣的同学可以点击“化身英特尔‘Issues 猎手’,共创百万用户开源生态!”了解详情


OpenVINO™

--END--


      
      
      

点击下方图片,让我们一起成为“Issues 猎手”,共创百万用户开源生态!


           
           
           
你也许想了解(点击蓝字查看)⬇️
➡️  OpenVINO™ 助力 Qwen 2 —— 开启大语言模型新时代
➡️  揭秘XPU架构下AIGC的推理加速艺术--AI PC 新纪元:将 AI 引入 NPU,实现快速低功耗推理
➡️  隆重介绍 OpenVINO™ 2024.0: 为开发者提供更强性能和扩展支持
➡️  隆重推出 OpenVINO 2023.3 ™ 最新长期支持版本
➡️   OpenVINO™ 2023.2 发布:让生成式 AI 在实际场景中更易用
➡️ 开发者实战 | 介绍OpenVINO™ 2023.1:在边缘端赋能生成式AI
➡️ 5周年更新 | OpenVINO™  2023.0,让AI部署和加速更容易
➡️ OpenVINO™5周年重头戏!2023.0版本持续升级AI部署和加速性能
➡️ 开发者实战系列资源包来啦!


          
          
          

扫描下方二维码立即体验 

OpenVINO™ 工具套件 2024.1


点击 阅读原文  获取工具套件 ,评论区已开放,欢迎大家留言评论!

文章这么精彩,你有没有“在看”?

本文分享自微信公众号 - OpenVINO 中文社区(openvinodev)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部