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操作符中隐式支持张量广播)。

以下内容不会中断:

  • 澄清规范中的歧义以符合主流的实现实践。

对操作符或函数语义的更改必须在一个新的操作符中引入,该操作符必须在一个新的operator set中引入。

在实践中,这意味着ONNX仓库中的破坏性更改要求贡献者遵循以下步骤:

  1. 增加DomainToVersionRange中的最大版本。

  2. 将旧的操作符模式复制到一个old.cc文件中。

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

  4. 在相应的operator_sets头文件中注册新操作符。

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

  6. 一个版本适配器,用于将新操作符降级到旧版本,也可以添加到convert.h中,但不是强制性的。

节点如何绑定到操作符声明是严格定义的,并且旨在增加跨ONNX实现的模型兼容性,遵循稳健性原则的保守条款。

ONNX 实现如何将操作符声明绑定到特定实现不在本规范的范围内。ONNX 的实现可以选择引入更复杂的操作符声明/实现绑定模式,遵循鲁棒性原则的自由条款。

操作符集合

ONNX 使用操作符集来将不可变的操作符规范分组在一起。一个操作符集代表一个特定版本的域,由一对(域,版本)表示。这表示属于指定域并具有指定版本的所有操作符的集合(称为 opset_version)。当给定操作符集的清单通过添加、删除或包含操作符的语义发生变化时,其版本必须增加。

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

示例

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

给定以下操作符集合:

操作集

Operators

评论

1

{A}

介绍

2

{A, B}

B 介绍

3

{A’, B, C}

A 更新为 A’,C 引入

4

{B, C’}

A 已删除,C 已更新(至 C’)

给定操作符集的操作符将具有以下since_version值:

操作符

操作集 1

操作集 2

操作集 3

操作集 4

A

1

1

3

-

B

-

2

2

2

C

-

-

3

4

备注:

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

模型版本控制

本规范的部分内容并非规范性要求。它只是概述了一系列推荐的做法。

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

签名更改

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

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

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

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

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

  2. 对ModelProto.graph.GraphProto.input或.output的非破坏性更改必须增加ModelProto.model_version的次要版本。非破坏性更改包括:

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

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

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

准确性或性能变化

显著影响准确性或性能但不改变模型输入或输出的更改应增加ModelProto.model_version的PATCH版本。

已发布版本

ONNX 版本

IR 版本

Opset 版本 ai.onnx

Opset 版本 ai.onnx.ml

Opset 版本 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

上述表格的程序化可访问版本可在这里找到。有限的版本号信息也保存在version.hschema.h中。每当发布新版本的ONNX时,请更新所有这些内容。