onnx-mlir

Logo

MLIR 编译器基础设施中 ONNX 模型的表示和参考降低

在 GitHub 上查看项目 onnx/onnx-mlir

操作指南

使用 Python 进行推理
使用 C/C++ 进行推理
使用 Java 进行推理

参考

ONNX 方言
OMTensor C99 运行时 API
OMTensorList C99 运行时 API
OMTensor Java 运行时 API
OMTensorList Java 运行时 API
生成 ONNX 方言
关于文档

开发

添加操作
测试指南
错误处理
命令行选项
检测
常量传播
添加加速器

工具

工具

RunONNXModel.py
DocCheck

此项目由 onnx 维护

托管在 GitHub Pages 上 — 主题由 orderedlist 提供

导入 ONNX 定义并支持操作

目录

  1. 概述
  2. 添加操作
  3. 自定义操作
  4. 构建
  5. 关于版本的详细信息

    概述

    ONNX-MLIR 定义了一个 ONNX 方言来表示 ONNX 指定的操作。ONNX 方言是使用 MLIR 表格生成工具创建的。每个操作的定义都通过 Python 脚本 utils/gen_onnx_mlir.py 从 ONNX 自动转换。此脚本从 ONNX 包中检索操作定义,以生成用于方言表格生成的 ONNXOps.td.inc 和用于 ONNX-MLIR 中 ONNX 模型导入器的 OpBuilderTable.inc。以下部分将描述如何使用 gen_onnx_mlir.py 将操作添加到 ONNX-MLIR 中的 ONNX 方言中,以及如何细化操作的定义。

添加操作

要为 ONNX 方言生成操作,请将此操作添加到 gen_onnx_mlir.py 中的字典“version_dict”中。此目录的键是操作名称,值是此操作的操作集列表。通常只支持此操作的最高版本操作集(在 onnx-mlir/third_party/onnx 中)。有关版本控制的详细信息,请参阅版本部分。有了此条目,脚本将为 ONNX 方言生成操作定义。

自定义

添加接口和特性

默认情况下,所有操作都具有形状推断接口和Pure特性。如果操作具有ResultTypeInferenceOpInterface,则使用字典OpsWithResultTypeInference。此接口推断结果张量的类型,而不是形状。如果操作具有子图,则它将具有接口HasOnnxSubgraphOpInterface

添加规范化接口

如果应在传递过程中对操作本地应用转换,则可以使用规范化接口进行此转换。要为操作启用规范化,请将此操作的名称添加到OpsWithCanonicalizer的此列表中,然后操作在其定义中将具有hasCanonicalizer = 1;

自定义构建器

操作的默认构建器需要结果的类型作为参数。但是,可以推断结果的类型。自定义构建器可能有助于简化代码。根据推理类型,有两种构建器,无等级类型和广播类型。要为操作启用特殊构建器,您可以分别将其名称添加到custom_builder_unranked_ops_listcustom_builder_broadcast_ops_list中。

请注意,可以使用returnType避免在重写规则中使用特殊构建器的需要。请参阅MLIR 文档ONNX-MLIR 中的示例。更好的解决方案可能是将此类类型推断代码移到 ONNXOpHelper.cpp 中并摆脱自定义构建器。

请注意,可以使用returnType避免在重写规则中使用特殊构建器的需要。更好的解决方案可能是将此类类型推断代码移到 ONNXOpHelper.cpp 中并摆脱自定义构建器。

自定义验证器

操作的操作描述列出了每个输入/输出和属性的允许类型。表生成将生成一个默认验证器来检查 IR 的允许类型。如果操作具有额外的约束,则应定义自定义验证器以增强错误检测。例如,操作的两个输入可能需要相同的元素类型或相同的秩。此类信息可以在 ONNX 操作定义中找到,但不能用方言定义来表达。测试这些约束的最佳方法是在验证器中。要将自定义验证器的接口添加到操作,请在gen_onnx_mlir.py中找到下面的数组并添加您的操作。

OpsWithVerifier = ['AveragePool', 'Conv', 'InstanceNormalization', 'Mod']

然后,您将在 ONNXOps.td.inc 中的操作定义中找到以下行

let verifier = [{ return ::verify(*this); }];

当新操作声明为使用自定义验证器时,您需要在src/Dialect/ONNX/ONNXOps.cpp中添加实现代码。最好查看其他操作以获取通用模式,例如搜索static LogicalResult verify(ONNXInstanceNormalizationOp op)。请注意,每次创建此类操作时,验证器都会执行。因此,您需要确保它可以与张量和 MemRef 以及可能未排序的张量一起使用。因此,请将每个测试保护到适当的环境中。例如,一旦张量被排序,您就可以验证秩是否在批准的范围内(如果有此类约束);在它被排序之前,请不要执行此测试。

提示

自定义导入器

special_op_handler:在 frontend_dialect_transformer.cpp 中创建特殊导入函数。目前,特殊处理程序用于具有操作参数的操作

任意额外定义

如果操作的定义需要除上述内容之外的其他代码,则可以将代码放在字典custom_definition_misc中。键是操作名称,值是代码。

自定义导入器

special_op_handler:在 frontend_dialect_transformer.cpp 中创建特殊导入函数。目前,特殊处理程序用于具有操作参数的操作

任意额外定义

如果操作的定义需要除上述内容之外的其他代码,则可以将代码放在字典custom_definition_misc中。键是操作名称,值是代码。

构建

为了运行 gen_onnx_mlir.py,必须安装 ONNX。请参阅自述文件。在您的构建目录中,执行以下命令。

 make OMONNXOpsIncTranslation

此命令将生成这两个文件(src/Dialect/ONNX/ONNXOps.td.inc 和 OpBuilderTable.inc),并将它们复制到 src 目录中的正确位置。如果您修改了 gen_onnx_mlir.py,则也需要检入两个生成的文件。它们在 ONNX-MLIR 构建中被视为源文件,以便 ONNX-MLIR 的用户无需安装特定版本的 ONNX。请勿直接修改这些文件。您也可以使用 utils 目录中生成的文件直接运行脚本。python ../utils/gen_onnx_mlir.py

更新文档

在添加新的操作版本或更改 ONNX 版本时,我们希望在支持的操作的 ONNX 文档中反映这些更改。虽然最新的ONNX 规范始终可用,但我们支持的规范通常会滞后一些,此外,我们还在版本化名称下支持旧版本,如上一节所述。

有一个方便的命令可以更新 ONNX 和 Krnl 方言,如下所示。

make onnx-mlir-docs

上述命令在通常的build目录中运行,它会将新的方言 md 文件直接安装到docs/Dialects目录中。

添加操作/对 Krnl 方言进行更改时应使用相同的命令。

操作版本

ONNX-MLIR 项目在 ONNX 版本为 1.7.0 时启动,并且不打算向后兼容。我们依赖 onnx/converter 将模型转换为 ONNX-MLIR 支持的版本。随着 ONNX 版本的演变,ONNX-MLIR 试图跟随,但可能落后于最新版本。

操作版本

如前所述,我们尝试支持最新版本的 ONNX 操作。当前支持的每个操作的版本记录在utils/gen_onnx_mlir.py中。此机制在版本中提供了一些稳定性。要检查版本中的更改,请使用标志“–check-version”运行 gen_onnx_mlir.py,并将报告更改。要移动到较新的版本,请手动更新脚本中的版本字典。

支持多个版本

要支持操作的多个版本,应将选定的版本添加到utils/gen_onnx_mlir.py中的版本字典中。例如,ReduceSum 有两个受支持的版本(操作集),11 和 13。version_dic 中的相应条目为'ReduceSum': [13, 11]

在 ONNX 方言中,最高版本的运算符名称中没有版本号,而其他版本的名称后面则跟着“V”和版本号。例如,opset 13 的 ReduceSum 将是 ONNXReduceSumOp,而 opset 11 的 ReduceSum 则是 'ONNXReduceSumV11Op'。由于大多数 ONNX 运算符在升级到更高版本时都是兼容的,因此我们可以在方言中保留运算符的名称,只需更新 gen_onnx_mlir.py 中的 version_dict,而无需修改 ONNX-MLIR 中的代码。

导入模型时,会使用不高于下一个可用版本的最高版本。以 ReduceSum 为例,如果 opset 为 12,则会选择 ONNXReduceSumV11Op。

迁移

要迁移新版本的 ONNX,首先需要升级 third_part/onnx 和您的 ONNX 安装。然后,您可以使用标志 --check_operation_version 运行 gen_onnx_mlir.py。所有操作的最高版本将输出为新的 version_dict。如果运算符的接口保持不变(根据 ONNX 的变更文档),则可以直接使用新版本。如果接口发生了变化,则可以将新版本插入版本列表的开头。对于现有代码,所有相应的代码都需要更改。例如,当 ReduceSum 从版本 11 迁移到 13 时,首先将 ONNXReduceSumOp 替换为 ONNXReduceSumOpV11。然后,版本 13 的代码将使用 ONNXReduceSumOp。这种设计的理由是,大多数 ONNX 更改都不会改变接口。我们不希望开发人员承担记住使用哪个版本的运算符的负担,除非绝对必要。并不总是需要保留旧版本的代码,这些代码可以重写为新的运算符。因此,我们只需要方言定义,而不需要推理或降低的代码。