ONNX 版本管理

本文档描述了 ONNX 的版本管理规则。MUST、SHOULD 等术语的使用与 RFC2119 一致。

版本管理原则

ONNX 为三类实体定义了版本管理策略和机制:

  • 中间表示 (IR) 规范,这是图和算子的抽象模型以及表示它们的具体格式。这些总是作为整体进行版本管理,并被称为IR 版本

  • 由给定 ONNX 图引用的算子规范。我们称之为算子版本

  • 定义特定图(基于特定算子)的已定义/训练模型。我们称之为模型版本

这三种实体类型的版本管理是独立的,并且在很大程度上互不影响。IR 规范的演进速度(通常较慢)与算子规范不同。模型版本的演进与其他两个版本完全独立。

只有 IR 版本和算子版本强制要求特定的版本管理策略。对于模型版本,它们仅为建议。对于模型版本管理,ONNX 用户和系统可以遵循任何适合的本地约定;但是,为了便于管理 ONNX 模型共享集合,它们应该遵守模型版本管理下描述的策略。

新的 IR 和算子版本作为 ONNX *版本发布*的一部分发布,这些发布有自己的版本管理方案。发布版本管理方案本身不在此标准中描述。它在 ONNX 发布管理文档 中讨论。

语义化版本控制还是简单数字?

ONNX 版本管理系统允许使用简单的单调递增数字或 语义化版本控制 (SemVer)。对于 IR 和算子集,版本管理基于简单数字。对于模型,ONNX 不要求任何方案,但建议一套共享约定。

模型正在使用哪种版本管理方案可以通过检查最重要的四个字节来明确,当使用语义化版本控制时,这四个字节必须是非零的,当使用简单数字时,它们必须是零。换句话说,当使用 SemVer 时,MAJOR 或 MINOR 版本号中至少有一个必须是非零的。

SemVer、文件和消费者

对于模型和发布版本管理,ONNX 基于 SemVer 2.0.0 中定义的原则和语法。在本文档中,我们使用与 SemVer 2.0.0 一致的术语,如重大更改非重大更改补丁

由于 ONNX 模型是序列化的文件(而非 API),因此有必要明确序列化模型与消费该模型的软件之间的关系。粗略地说,序列化模型扮演着 API 的*被调用者*角色,而序列化模型的消费者扮演着 API 的*调用者*角色。

ONNX 版本管理原则基于健壮性原则:“保守你所做的,宽容你从他人那里接受的”。

  1. 给定 ONNX 模型(以及 ONNX 规范本身)的生产者必须严格遵守本规范中定义的重大更改与非重大更改规则。

  2. 给定 ONNX 模型的消费者应该消费更新的 ONNX 文件,前提是新的 ONNX 文件在 IR 版本、引用的算子版本或模型版本(即两个 ONNX 文件之间的 MAJOR 版本号未更改)方面没有重大更改。

  3. 给定 ONNX 模型的消费者可以消费更新的 ONNX 文件,前提是新的 ONNX 文件在 IR 版本、引用的算子版本或模型版本方面有一个或多个重大更改。

在 Protobuf 中序列化 SemVer 版本号

为了效率,ONNX 将 MAJOR、MINOR 和 PATCH 值序列化为打包的 64 位整数;最重要的两个字节是 MAJOR 部分,接下来的两个字节是 MINOR 部分,最不重要的四个字节是 PATCH 部分。

例如,*1.2.345* 表示为*0x0001000200000159*。

预发布版本和构建元数据不存储在模型中。

IR 版本管理

IR 格式使用简单的单调递增数字进行版本管理。对 ONNX 规范的格式或语义的重大更改需要递增版本号。对 IR 格式的非重大更改不需要更改版本号。

注意:重大更改包括那些不改变序列化的二进制格式,但仍然会破坏使用读写库的软件的更改。例如,更改消息属性的拼写将导致访问该属性的代码出错。

IR 格式遵循 proto3 规范中更新消息类型部分定义的版本管理指南。

作为一项通用原则,实现应在面对缺失字段时保持健壮。然而,为了确保基本的互操作性,一部分消息字段将被标记为给定 IR 版本的必需字段,所有生产者都必须正确设置这些字段。必需字段必须始终带有注释。

// This field MUST be present for this version of the IR.

例如,每个模型都必须存在 `ModelProto.ir_version` 属性。ONNX 检查器 (`onnx/checker.py`) 将强制执行这些规则。

由于协议缓冲区消息定义(.proto / .proto3 文件)预计会被多个独立开发者使用,因此对这些定义的更改不应破坏依赖于生成的语言绑定的代码(例如,更改现有字段的类型)。

算子版本管理

IR 可以独立于算子集进行演进。算子代表给定操作的签名和语义。算子是抽象接口,因为它们不暗示特定的实现;相反,它们只是模型作者与模型可能在其上执行的实现之间的约定。

给定算子由一个三元组标识:`(domain, op_type, since_version)`,在文本中写为 `domain.op_type:since_version`(例如,`com.acme.FastConv:3`)。`since_version` 是引入该算子的算子集的版本。重大的算子更改包括:

  • 添加/删除/重命名属性。这甚至包括添加新的可选属性的情况,其中省略该属性将意味着一个默认值,产生与前一个算子版本相同的语义。

  • 添加/删除/重新排序输入或输出。

  • 添加/删除输入和输出支持的类型,以及更改属性使用的类型。

  • 支持新行为,即使现有参数签名在其他方面相同(例如,在 Mean 算子中隐式支持张量广播)。

以下情况不属于重大更改:

  • 澄清规范歧义以匹配普遍的实现实践。

算子或函数的语义更改必须在一个新的算子中引入,该新算子必须在一个新的算子集中引入。

在实践中,这意味着 ONNX 存储库中的 BC(向后兼容)重大更改需要贡献者遵循以下步骤:

  1. 递增 `DomainToVersionRange` 中的最大版本号。

  2. 将旧的算子 schema 复制到一个 `old.cc` 文件。

  3. 将 `SinceVersion` 标识符更新为步骤 (1) 中的新最大版本。

  4. 在相应的 `operator_sets` 头文件中注册新算子。

  5. 向 `convert.h` 添加一个版本适配器,以便版本转换器可以将旧版本的算子升级到新版本。如果遵循旧 schema 的算子在新 schema 下仍然有效(通常是这种情况),则可以是一个 `CompatibleAdapter`。

  6. 也可以向 `convert.h` 添加一个版本适配器,用于将新算子降级到旧版本,但这并非强制。

节点如何绑定到算子声明被严格定义,并且旨在增强 ONNX 实现之间的模型兼容性,遵循健壮性原则的保守条款的精神。

ONNX 实现如何将算子声明绑定到特定实现超出了本规范的范围。ONNX 的实现可以根据健壮性原则的宽容条款的精神,选择引入更复杂的算子声明/实现绑定模式。

算子集

ONNX 使用算子集来分组不可变的算子规范。算子集代表特定版本的域,由一对(域,版本)标识。这代表了属于指定域和指定版本的所有算子的集合(称为 `opset_version`)。当给定算子集的清单发生变化(通过添加、删除或包含算子的语义更改)时,其版本必须增加。

模型将其所需的算子集声明为 `ModelProto.opset_import` 中的 `(domain, opset_version)` 对列表。空字符串("")域表示作为 ONNX 规范一部分定义的算子;其他域对应于其他供应商的算子集(这意味着它们可用于提供特定于供应商的 ONNX 扩展)。给定模型指定的算子集并集必须为模型图中每个节点提供兼容的算子声明。

示例

本节仅供参考,不具规范性。

给定以下算子集:

算子集

算子

备注

1

{A}

A 引入

2

{A, B}

B 引入

3

{A’, B, C}

A 更新(为 A’),C 引入

4

{B, C’}

A 删除,C 更新(为 C’)

给定算子集的算子将具有以下 `since_version` 值:

Operator

算子集 1

算子集 2

算子集 3

算子集 4

A

1

1

3

-

B

-

2

2

2

C

-

-

3

4

备注

  • 与先前算子集版本相比新增或更新的值以**粗体**显示。

模型版本管理

本规范部分不具规范性。它仅概述了一套推荐的做法。

模型作者和应用程序/系统可以自行选择忽略模型版本管理机制和策略规则。对于将在开发者、团队或组织之间共享的模型,模型作者和应用程序/系统应遵守以下版本策略:

签名更改

  1. `ModelProto.graph.GraphProto.input` 或 `.output` 的重大更改必须递增 `ModelProto.model_version` 的 MAJOR 版本。重大更改包括:

    • 输入或输出的语义发生重大更改(例如,将输入张量的必需内容从彩色图像更改为黑白图像)。

    • 将输入或输出的声明类型更改为不兼容的类型(例如,`tensor(int)->tensor(string)`)。

    • 添加一个没有有意义或指定默认值的新输入。请记住,输入的默认值在初始化列表中指定。

    • 删除一个没有有意义或指定默认值的现有输出。

  2. `ModelProto.graph.GraphProto.input` 或 `.output` 的非重大更改必须递增 `ModelProto.model_version` 的 MINOR 版本。非重大更改包括:

    • 将输入或输出的声明类型更改为兼容/扩展类型(例如,`tensor(int32)->tensor(int64)`,`tensor(float16)->tensor(float32)`)。

    • 添加一个有意义或指定默认值的新输入。

    • 添加仅在存在先前版本图中不可能存在的输入时触发的新行为(通常是通过存在新输入或允许先前无效的输入值)。

准确性或性能更改

对准确性或性能有重大影响但未更改模型输入或输出的更改,应递增 `ModelProto.model_version` 的 PATCH 版本。

已发布的版本

ONNX 版本

IR 版本

ai.onnx 算子集版本

ai.onnx.ml 算子集版本 ai.onnx.ml

ai.onnx.training 算子集版本

1.0

3

1

1

-

1.1

3

5

1

-

1.1.2

3

6

1

-

1.2

3

7

1

-

1.3

3

8

1

-

1.4.1

4

9

1

-

1.5.0

5

10

1

-

1.6.0

6

11

2

-

1.7.0

7

12

2

1

1.8.0

7

13

2

1

1.8.1

7

13

2

1

1.9.0

7

14

2

1

1.10.0

8

15

2

1

1.10.1

8

15

2

1

1.10.2

8

15

2

1

1.11.0

8

16

3

1

1.12.0

8

17

3

1

1.13.0

8

18

3

1

1.13.1

8

18

3

1

1.14.0

9

19

3

1

1.14.1

9

19

3

1

1.15.0

9

20

4

1

1.16.0

10

21

5

1

1.17.0

10

22

5

1

1.18.0

11

23

5

1

1.19.0

12

24

5

1

该表的程序化可访问版本可在此处找到。有限的版本号信息也维护在 version.hschema.h 中。请在每次发布新版本的 ONNX 时更新所有内容。