首页 > 其他分享 >为 Paddle2ONNX 添加对 Opset 18 的支持

为 Paddle2ONNX 添加对 Opset 18 的支持

时间:2024-11-29 17:12:19浏览次数:12  
标签:info node name 18 Opset reduce auto Paddle2ONNX input

1 简介

随着ONNX标准的不断更新,保持 Paddle2ONNX 与最新版本的兼容性显得尤为重要。本篇文章将详细介绍如何为 Paddle2ONNX 项目升级其依赖的 ONNX Opset 版本。

2 添加对 Opset 18 的支持

2.1 升级 ONNX 依赖库版本

支持 Opset 18 前我们需要修改 ONNX 的 branch 参数到最新的 commit id

[submodule "third_party/onnx"]
	path = third_party/onnx
	url = https://github.com/onnx/onnx.git
	branch = ad834eb73ee0cd9b6fa9ea892caeed5fa17d7dc0

2.2 修改 CMakeLists.txt

Paddle2ONNX 在 CMakeLists.txt 中规定了最大 Opset 的数值,我们需要修改为 18,关于 Opset Version 与 ONNX Version 的对应关系,可以查看 Opset Version 与 ONNX Version 对应表

# if you build from other version of onnx this should be modified
# refer to https://github.com/onnx/onnx/blob/main/docs/Versioning.md#released-versions
add_definitions(-DMAX_ONNX_OPSET_VERSION=18)
add_definitions(-DPADDLE2ONNX_LIB)

2.3 添加 Paddle2ONNX 算子对 Opset 18 的支持

paddle2onnx/mapper/mapper.h 中添加对 Opset 18 的支持

class Mapper {
  public:
    void Run() {
      int32_t opset_version = helper_->GetOpsetVersion();
      Assert(opset_version >= 7 && opset_version <= MAX_ONNX_OPSET_VERSION,
             "[Paddle2ONNX] Only support opset_version in range of [7, " +
             std::to_string(MAX_ONNX_OPSET_VERSION) + "].");
      if (IsExportAsCustomOp()) {
        return ExportAsCustomOp();
      }

      if (opset_version == 18) {
        Opset18();
      } else if (opset_version == 17) {
        Opset17();
      } else if (opset_version == 16) {
        Opset16();
      } else if (opset_version == 15) {
        Opset15();
      } else if (opset_version == 14) {
        Opset14();
      } else if (opset_version == 13) {
        Opset13();
      } else if (opset_version == 12) {
        Opset12();
      } else if (opset_version == 11) {
        Opset11();
      } else if (opset_version == 10) {
        Opset10();
      } else if (opset_version == 9) {
        Opset9();
      } else if (opset_version == 8) {
        Opset8();
      } else {
        Opset7();
      }
    }

    virtual void Opset18() { Opset17(); }
};

3 升级 Paddle2ONNX 算子对 Opset18 的支持

3.1 升级 LayerNormalize 算子

当 Opset 升级到17后,LayerNormalize 算子会出现问题,这里做的针对性的修复,主要是 ScaleBias 两个参数的 Shape 需要与 input_shape[norm_axis:] 一致

void LayerNormMapper::Opset17() {
  auto input_info = GetInput("X");
  auto output_info = GetOutput("Y");

  constexpr std::array<P2ODataType, 3> T = {P2ODataType::FP16, P2ODataType::FP32, P2ODataType::FP64};

  auto input_name = input_info[0].name;
  auto input_type = input_info[0].dtype;
  auto input_shape = input_info[0].shape;
  if (std::find(T.begin(), T.end(), input_type) == T.end()) {
    input_name = helper_->AutoCast(input_name, input_info[0].dtype, P2ODataType::FP32);
    input_type = P2ODataType::FP32;
  }

  bool has_input_Bias = HasInput("Bias");
  bool has_input_Scale = HasInput("Scale");
  if (has_input_Bias && has_input_Scale) {
    auto scale_info = GetInput("Scale");
    auto scale_name = scale_info[0].name;
    auto scale_type = scale_info[0].dtype;
    if (std::find(T.begin(), T.end(), scale_type) == T.end()) {
      scale_name = helper_->AutoCast(scale_name, scale_type, P2ODataType::FP32);
      scale_type = P2ODataType::FP32;
    }

    auto bias_info = GetInput("Bias");
    auto bias_name = bias_info[0].name;
    auto bias_type = bias_info[0].dtype;
    if (std::find(T.begin(), T.end(), bias_type) == T.end()) {
      bias_name = helper_->AutoCast(bias_name, bias_type, P2ODataType::FP32);
      bias_type = P2ODataType::FP32;
    }

    auto layer_norm_node = helper_->MakeNode(
      "LayerNormalization",
      {input_name, scale_name, bias_name},
      {output_info[0].name});
    AddAttribute(layer_norm_node, "axis", begin_norm_axis_);
    AddAttribute(layer_norm_node, "epsilon", epsilon_);
    return;
  }

  if (has_input_Scale) {
    auto scale_info = GetInput("Scale");
    auto scale_name = scale_info[0].name;
    auto scale_type = scale_info[0].dtype;
    if (std::find(T.begin(), T.end(), scale_type) == T.end()) {
      scale_name = helper_->AutoCast(scale_name, scale_type, P2ODataType::FP32);
      scale_type = P2ODataType::FP32;
    }

    auto layer_norm_node = helper_->MakeNode(
      "LayerNormalization",
      {input_name, scale_name},
      {output_info[0].name});
    AddAttribute(layer_norm_node, "axis", begin_norm_axis_);
    AddAttribute(layer_norm_node, "epsilon", epsilon_);
    return;
  }

  std::vector<int64_t> normalized_shape;
  for (int64_t i = begin_norm_axis_;i < input_shape.size();i++) {
    normalized_shape.emplace_back(input_shape[i]);
  }

  if (has_input_Bias) {
    auto bias_info = GetInput("Bias");
    auto bias_name = bias_info[0].name;
    auto bias_type = bias_info[0].dtype;
    if (std::find(T.begin(), T.end(), bias_type) == T.end()) {
      bias_name = helper_->AutoCast(bias_name, bias_type, P2ODataType::FP32);
      bias_type = P2ODataType::FP32;
    }

    std::string scale_name = helper_->Constant(normalized_shape, GetOnnxDtype(P2ODataType::FP32), static_cast<float>(1.0));
    auto layer_norm_node = helper_->MakeNode(
      "LayerNormalization",
      {input_name, scale_name, bias_name},
      {output_info[0].name});
    AddAttribute(layer_norm_node, "axis", begin_norm_axis_);
    AddAttribute(layer_norm_node, "epsilon", epsilon_);
    return;
  }

  if (!has_input_Bias && !has_input_Scale) {
    std::string scale_name = helper_->Constant(normalized_shape, GetOnnxDtype(P2ODataType::FP32), static_cast<float>(1.0));
    auto layer_norm_node = helper_->MakeNode(
      "LayerNormalization",
      {input_name, scale_name},
      {output_info[0].name});
    AddAttribute(layer_norm_node, "axis", begin_norm_axis_);
    AddAttribute(layer_norm_node, "epsilon", epsilon_);
  }
}

3.2 升级并拆分 Reduce 系列算子

在 Opset Version 升级到 18 后,Reduce 算子的输入发生了变化,因此需要对 Reduce 系列的算子进行全面的升级,为了不将所有的代码耦合在一起,我将他们做了拆分,具体代码如下:

#include "paddle2onnx/mapper/tensor/reduce_logsumexp.h"

namespace paddle2onnx {
REGISTER_MAPPER(logsumexp, ReduceLogSumExpMapper)

int32_t ReduceLogSumExpMapper::GetMinOpset(bool verbose) {
  constexpr int op_version = 11;
  Logger(verbose, op_version) << RequireOpset(op_version) << std::endl;
  return op_version;
}

void ReduceLogSumExpMapper::Opset18() {
  GetAttr("keepdim", &keep_dim_);
  GetAttr("reduce_all", &reduce_all_);
  GetAttr("axis", &dim_);

  auto x_info = GetInput("X");
  std::string dims;
  if (!reduce_all_) {
    dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, dim_);
  } else {
    dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, Arange(0, x_info[0].Rank()));
  }

  std::string input_name = x_info[0].name;
  auto input_tpye = x_info[0].dtype;
  if (x_info[0].dtype == P2ODataType::BOOL) {
    input_name = helper_->AutoCast(input_name, input_tpye, P2ODataType::INT32);
    input_tpye = P2ODataType::INT32;
  }
  auto reduce_node = helper_->MakeNode("ReduceLogSumExp", {input_name, dims});

  // Add attribute
  AddAttribute(reduce_node, "keepdims", static_cast<int64_t>(keep_dim_));
  auto out_node_name = reduce_node->output(0);

  bool reduce_all_axes = dim_.size() == x_info[0].Rank();
  if (reduce_all_) {
    reduce_all_axes = true;
  }
  if (!keep_dim_ && reduce_all_axes) {
    out_node_name = helper_->Reshape(out_node_name, {-1});
  }
  auto out_info = GetOutput("Out");
  helper_->AutoCast(out_node_name, out_info[0].name, input_tpye, out_info[0].dtype);
}
}  // namespace paddle2onnx
#include "paddle2onnx/mapper/tensor/reduce_max.h"

namespace paddle2onnx {
REGISTER_MAPPER(reduce_max, ReduceMaxMapper)
REGISTER_MAPPER(reduce_any, ReduceMaxMapper)

int32_t ReduceMaxMapper::GetMinOpset(bool verbose) {
  int op_version = 11;

  auto x_info = GetInput("X");
  if (x_info[0].dtype == P2ODataType::FP64) {
    op_version = 12;
  }

  Logger(verbose, op_version) << RequireOpset(op_version) << std::endl;
  return op_version;
}

void ReduceMaxMapper::Opset18() {
  GetAttr("keep_dim", &keep_dim_);
  GetAttr("reduce_all", &reduce_all_);
  GetAttr("in_dtype", &in_dtype_);
  GetAttr("out_dtype", &out_dtype_);
  GetAttr("dim", &dim_);

  auto x_info = GetInput("X");
  std::string dims;
  if (!reduce_all_) {
    dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, dim_);
  } else {
    dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, Arange(0, x_info[0].Rank()));
  }

  std::string input_name = x_info[0].name;
  auto input_tpye = x_info[0].dtype;
  if (x_info[0].dtype == P2ODataType::BOOL) {
    input_name = helper_->AutoCast(input_name, input_tpye, P2ODataType::INT32);
    input_tpye = P2ODataType::INT32;
  }
  auto reduce_node = helper_->MakeNode("ReduceMax", {input_name, dims});

  // Add attribute
  AddAttribute(reduce_node, "keepdims", static_cast<int64_t>(keep_dim_));
  auto out_node_name = reduce_node->output(0);

  bool reduce_all_axes = dim_.size() == x_info[0].Rank();
  if (reduce_all_) {
    reduce_all_axes = true;
  }
  if (!keep_dim_ && reduce_all_axes) {
    out_node_name = helper_->Reshape(out_node_name, {-1});
  }
  auto out_info = GetOutput("Out");
  helper_->AutoCast(out_node_name, out_info[0].name, input_tpye, out_info[0].dtype);
}

void ReduceMaxMapper::Opset12() {
  // The implementation logic of Opset12 is the same as that of Opset11, with the difference being that Opset12 supports input data types as double.
  Opset11();
}
} // namespace paddle2onnx
#include "paddle2onnx/mapper/tensor/reduce_min.h"

namespace paddle2onnx {
REGISTER_MAPPER(reduce_min, ReduceMinMapper)
REGISTER_MAPPER(reduce_all, ReduceMinMapper)

int32_t ReduceMinMapper::GetMinOpset(bool verbose) {
  int op_version = 11;

  auto x_info = GetInput("X");
  if (x_info[0].dtype == P2ODataType::FP64) {
    op_version = 12;
  }

  Logger(verbose, op_version) << RequireOpset(op_version) << std::endl;
  return op_version;
}

void ReduceMinMapper::Opset18() {
  GetAttr("keep_dim", &keep_dim_);
  GetAttr("reduce_all", &reduce_all_);
  GetAttr("in_dtype", &in_dtype_);
  GetAttr("out_dtype", &out_dtype_);
  GetAttr("dim", &dim_);

  auto x_info = GetInput("X");
  std::string dims;
  if (!reduce_all_) {
    dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, dim_);
  } else {
    dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, Arange(0, x_info[0].Rank()));
  }

  auto input_node_name = x_info[0].name;
  auto input_tpye = x_info[0].dtype;
  if (x_info[0].dtype == P2ODataType::BOOL) {
    input_node_name = helper_->AutoCast(x_info[0].name, x_info[0].dtype, P2ODataType::INT32);
    input_tpye = P2ODataType::INT32;
  }

  // Add attribute
  auto reduce_node = helper_->MakeNode("ReduceMin", {input_node_name, dims});
  AddAttribute(reduce_node, "keepdims", static_cast<int64_t>(keep_dim_));

  auto out_node_name = reduce_node->output(0);
  bool reduce_all_axes = dim_.size() == x_info[0].Rank();
  if (reduce_all_) {
    reduce_all_axes = true;
  }
  if (!keep_dim_ && reduce_all_axes) {
    out_node_name = helper_->Reshape(out_node_name, {-1});
  }
  auto out_info = GetOutput("Out");
  helper_->AutoCast(out_node_name, out_info[0].name, input_tpye, out_info[0].dtype);
}

void ReduceMinMapper::Opset12() {
  // The implementation logic of Opset12 is the same as that of Opset11, with the difference being that Opset12 supports input data types as double.
  Opset11();
}
}  // namespace paddle2onnx
#include "paddle2onnx/mapper/tensor/reduce_mean.h"

namespace paddle2onnx {
REGISTER_MAPPER(reduce_mean, ReduceMeanMapper)

int32_t ReduceMeanMapper::GetMinOpset(bool verbose) {
  constexpr int op_version = 11;
  Logger(verbose, op_version) << RequireOpset(op_version) << std::endl;
  return op_version;
}

void ReduceMeanMapper::Opset18() {
  auto axis_name_ = "dim";
  GetAttr("keep_dim", &keep_dim_);
  GetAttr("reduce_all", &reduce_all_);
  GetAttr("in_dtype", &in_dtype_);
  GetAttr("out_dtype", &out_dtype_);
  if (IsAttrVar(axis_name_)) {
    auto info = GetAttrVar(axis_name_);
    TryGetValue(info[0], &dim_);
  } else {
    GetAttr(axis_name_, &dim_);
  }

  auto x_info = GetInput("X");
  std::string dims;
  if (IsAttrVar(axis_name_)) {
    auto info = GetAttrVar(axis_name_);
    dims = helper_->AutoCast(info[0].name, info[0].dtype, P2ODataType::INT64);
  } else {
    if (!reduce_all_) {
      dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, dim_);
    } else {
      dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, Arange(0, x_info[0].Rank()));
    }
  }

  // Add attribute
  auto reduce_node = helper_->MakeNode("ReduceMean", {x_info[0].name, dims});
  AddAttribute(reduce_node, "keepdims", static_cast<int64_t>(keep_dim_));
  auto out_node_name = reduce_node->output(0);

  bool reduce_all_axes = dim_.size() == x_info[0].Rank();
  if (reduce_all_) {
    reduce_all_axes = true;
  }
  if (!keep_dim_ && reduce_all_axes) {
    out_node_name = helper_->Reshape(out_node_name, {-1});
  }
  auto out_info = GetOutput("Out");
  helper_->AutoCast(out_node_name, out_info[0].name, x_info[0].dtype, out_info[0].dtype);
}
}  // namespace paddle2onnx
#include "paddle2onnx/mapper/tensor/reduce_sum.h"

namespace paddle2onnx {
REGISTER_MAPPER(reduce_sum, ReduceMapperSum)

int32_t ReduceMapperSum::GetMinOpset(bool verbose) {
  constexpr int op_version = 13;
  Logger(verbose, op_version) << RequireOpset(op_version) << std::endl;
  return op_version;
}

void ReduceMapperSum::Opset13() {
  auto axis_name_ = "dim";
  GetAttr("keep_dim", &keep_dim_);
  GetAttr("reduce_all", &reduce_all_);
  GetAttr("in_dtype", &in_dtype_);
  GetAttr("out_dtype", &out_dtype_);
  if (IsAttrVar(axis_name_)) {
    auto info = GetAttrVar(axis_name_);
    TryGetValue(info[0], &dim_);
  } else {
    GetAttr(axis_name_, &dim_);
  }

  auto x_info = GetInput("X");
  std::string dims;
  if (IsAttrVar(axis_name_)) {
    auto info = GetAttrVar(axis_name_);
    dims = helper_->AutoCast(info[0].name, info[0].dtype, P2ODataType::INT64);
  } else {
    if (!reduce_all_) {
      dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, dim_);
    } else {
      dims = helper_->Constant(ONNX_NAMESPACE::TensorProto::INT64, Arange(0, x_info[0].Rank()));
    }
  }

  // Add attribute
  auto reduce_node = helper_->MakeNode("ReduceSum", {x_info[0].name, dims});
  AddAttribute(reduce_node, "keepdims", static_cast<int64_t>(keep_dim_));
  auto out_node_name = reduce_node->output(0);

  bool reduce_all_axes = dim_.size() == x_info[0].Rank();
  if (reduce_all_) {
    reduce_all_axes = true;
  }
  if (!keep_dim_ && reduce_all_axes) {
    out_node_name = helper_->Reshape(out_node_name, {-1});
  }
  auto out_info = GetOutput("Out");
  helper_->AutoCast(out_node_name, out_info[0].name, x_info[0].dtype, out_info[0].dtype);
}
}  // namespace paddle2onnx

由于篇幅的限制,这里仅放出了部分核心代码,更多代码请到 Paddle2ONNX PR 1250 内查看。

4 升级 CI 机制

原先的 CI 机制对所有的 Opset Version 都进行验证,这在高 Opset Version 上是不合适的,所以对原先的 C I机制进行了升级,仅对指定的 Opset Version 进行验证

4.1 升级 onnxbase

class APIOnnx(object):
    def run(self):
        """
        1. use dygraph layer to make exp
        2. dygraph layer to onnx
        3. use onnx to make res
        4. compare diff
        """
        self._mkdir()
        self.set_input_spec()
        for place in self.places:
            paddle.set_device(place)
            exp = self._mk_dygraph_exp(self._func)
            res_fict = {}

            assert len(self.ops) <= 1, "Need to make sure the number of ops in config is 1."

            # Save Paddle Inference model
            if os.path.exists(self.name):
                shutil.rmtree(self.name)
            paddle.jit.save(self._func, os.path.join(self.name, "model"), self.input_spec)

            # Get PaddleInference model path
            pdmodel_path = os.path.join(self.name, "model.pdmodel")
            pdiparams_path = os.path.join(self.name, "model.pdiparams")
            if len(self.ops) > 0:
                self.dev_check_ops(self.ops[0], pdmodel_path)

            original_model_file = pdmodel_path
            params_file = pdiparams_path
            if not os.path.exists(params_file):
                params_file = ""

            # clip extra
            model_file = os.path.join(self.name, "cliped_model.pdmodel")
            self.clip_extra_program_only(original_model_file, model_file)

            for v in self._version:
                onnx_model_str = c_p2o.export(
                    model_file, params_file, v, False, True, True, True,
                    True, {}, "onnxruntime", "", "", False)
                with open(os.path.join(self.name, self.name + '_' + str(v) + ".onnx"), "wb") as f:
                    f.write(onnx_model_str)
                res_fict[str(v)] = self._mk_onnx_res(ver=v)

            for v in self._version:
                compare(res_fict[str(v)], exp, delta=self.delta, rtol=self.rtol)

4.2 升级单测代码

由于篇幅的限制,更多代码请到 Paddle2ONNX PR 1250 内查看。

5 参考链接

标签:info,node,name,18,Opset,reduce,auto,Paddle2ONNX,input
From: https://www.cnblogs.com/Zheng-Bicheng/p/18577065

相关文章

  • 【Paddle2ONNX】为 Paddle2ONNX 适配自适应 ONNX IR Version 功能
    1简介最近在浏览Paddle2ONNX的Issues时,我发现有用户需要让Paddle2ONNX支持导出的ONNX模型根据OpsetVersion自适应IRVersion的功能。这个功能对于老的Runtime来说还是很重要的,于是我动手添加了这个功能,这里写一篇博客和大家分享下。能否指定IRrepresentation......
  • hhdb数据库介绍(10-18)
    监控智能逻辑拓扑管理平台通过可视化方式将集群中前端应用、逻辑库、计算节点、数据节点、存储节点等物理与逻辑上的组件完整展示。并通过前端应用连接池与后端数据库连接池信息动态生成组件上的QPS与连接数信息。通过智能逻辑拓扑用户可以快速了解整个集群的运行状态,帮助用户实......
  • ubuntu1804点击桌面无任何反应假死,但可以输入用户名密码
    前言全局说明今天在搞ubuntu1804上远程登录功能时,安装了xrdp后,重启进入桌面就无法用鼠标点击VM虚拟机里的桌面了,网上找了一圈,都没有回答的。无意中结束桌面后,发现登录处有个齿轮里有个ubuntuonayland选择后,登录后桌面就可以使用了。虽然不是完全解决问题,但起码能登录......
  • pip 下载包失败(特定版本eg: torch==2.2.1+cu118)
    背景介绍:有时候要复现其项目的实验时,环境需要尽量与原作者实验的环境靠近,这样结果浮现的也更加准确。这里当需要安装torch==2.2.1+cu118等特定版本的时,出现了报错:解决办法:根据PyTorch官方推荐,可以使用以下命令来安装torch2.2.1+cu118版本pipinstalltorch==2.2.1t......
  • 国标GB28181软件LiteGBS国标GB28181网页直播平台录像机如何绑定宇视云存储
    在信息技术迅猛发展和安全需求日益增加的今天,视频监控系统已成为我们生活中的一个重要组成部分。它在公共安全、城市治理、企业安全防护以及各类建筑项目中扮演着至关重要的角色。正是在这样的大环境下,遵循国家标准GB28181协议的LiteGBS国标GB28181软件视频云服务应需而生,它以全面......
  • 国标GB28181公网直播LiteGBS国标GB28181-2022平台:摄像头显示网络不发达是怎么回事?
    LiteGBS国标GB28181软件国标流媒体解决方案是一款专门用于接入国标设备的视频解决方案,不但从设备接入层面上,完整地接入了内网或者公网的国标设备,而且在输出层面上,完全采用了最新的互联网思维方式,国标IPC/NVR能通过平台同步输出RTMP/HLS/HTTP-FLV等多种视频流格式,非常好地解决了传......
  • 洛谷P1807 最长路
    洛谷P1807最长路#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintinf=-1e6;constintmaxx=2550005;intn,m,head,tail,g[1505][1505],q[maxx];intdp[1505];boolflag[1505];//flag记录点是否在队内signedmain(){ cin>>n>>m; f......
  • ctfshow -web -118-124
    118.((Bash内置变量))这题实话不会,看了别人的wp。此题过滤了许多,白名单只有大写字母和符号。一般在Linux下环境变量PATH一般是/bin,题目路径PWD是/var/www/html可以利用切片来得到我们需要的字母。但是题目过滤了数字,无法使用切片。换一种方式获取字符。linux可以利用~获得变......
  • hhdb数据库介绍(9-18)
    SQL语法支持计算节点语法特殊功能默认分片规则建表在使用关系集群数据库时,需要先将表的分片规则信息配置好之后才能创建表。实际使用过程中,用户可能对关系集群数据库及分片规则不了解,这就需要一种能直接过渡到HHDBServer的方案,该方案能根据逻辑库关联的分片节点数量自动对表生......
  • 软件设计:实验18:迭代器模式
    实验18:迭代器模式本次实验属于模仿型实验,通过本次实验学生将掌握以下内容: 1、理解迭代器模式的动机,掌握该模式的结构;2、能够利用迭代器模式解决实际问题。 [实验任务一]:JAVA和C++常见数据结构迭代器的使用信1305班共44名同学,每名同学都有姓名,学号和年龄等属性,分别使用JAVA......