ONNX 版本控制

本文档描述了 ONNX 版本控制规则。MUST、SHOULD 等的使用与 RFC2119 保持一致。

版本控制原则

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

  • the 中间表示 (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. 将旧的算子模式复制到一个名为 old.cc 的文件。

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

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

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

  6. 也可以向 convert.h 添加一个版本适配器来将新算子降级到旧版本,但这不是强制性的。

节点如何绑定到算子声明是严格定义的,并且旨在提高 ONNX 实现之间的模型兼容性,符合鲁棒性原则的保守条款的精神。

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

算子集

ONNX 使用算子集将不可变算子规范组合在一起。一个算子集表示一个特定版本的域,由一对 (域,版本) 表示。这表示属于指定域的具有指定版本的所有算子(称为 opset_version)。当给定算子集的库存因添加、删除或所含算子的语义变化而发生变化时,其版本必须增加。

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

示例

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

给定以下算子集

OpSet

算子

注释

1

{A}

A 引入

2

{A, B}

B 引入

3

{A’, B, C}

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

4

{B, C’}

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

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

算子

OpSet 1

OpSet 2

OpSet 3

OpSet 4

A

1

1

3

-

1

-

2

2

2

1

-

-

3

4

**3**

  • **4**

B

**2**

2

2

  1. 2

    • C

    • **3**

    • **3**

    • 3

  2. **4**

    • 注释

    • 从先前 OpSet 版本中新增或更新的值以 **粗体** 显示。

    • 模型版本控制

本节规范不具有规范性。它只是概述了一组推荐做法。

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

签名更改

对 ModelProto.graph.GraphProto.input 或 .output 的重大更改必须增加 ModelProto.model_version 的主版本。重大更改包括

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

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

添加一个没有有意义或指定默认值的新的输入。回想一下,输入的默认值在初始化器列表中指定。

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

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

对 ModelProto.graph.GraphProto.input 或 .output 的非重大更改必须增加 ModelProto.model_version 的次版本。非重大更改包括