ONNX 版本控制¶
本文档描述了 ONNX 版本控制的规则。MUST, SHOULD 等词汇与 RFC2119 中的用法一致。
版本控制原则¶
ONNX 定义了三种实体类型的版本控制策略和机制:
用于图和算子的抽象模型及其具体表示格式的中间表示 (IR) 规范。它们始终以原子方式进行版本控制,并称为 IR 版本。
给定 ONNX 图可能引用的算子规范。我们称此为 算子版本。
使用特定算子定义特定图的已定义/已训练模型。我们称此为 模型版本。
这三种实体类型的版本控制是独立且很大程度上彼此无关的。IR 规范的演进速度与算子规范不同(通常更慢)。模型版本完全独立于其他两个版本。
版本管理方面的具体策略仅对 IR 版本和算子版本强制执行。对于模型版本控制,它们只是建议。对于模型版本控制,ONNX 用户和系统可以(MAY)遵循任何合理的本地习惯;但是,为了方便管理共享的 ONNX 模型集合,它们应该(SHOULD)遵守模型版本控制下描述的策略。
新的 IR 和算子版本作为 ONNX 版本发布的一部分发布,ONNX 版本发布有自己的版本控制方案。版本发布版本控制方案不作为标准本身的一部分进行描述。它在ONNX 版本发布管理文档中讨论。
语义版本控制还是简单数字?¶
ONNX 版本控制系统允许使用简单的单调递增数字或语义版本控制 (SemVer)。对于 IR 和算子集,版本控制基于简单数字。对于模型,ONNX 不要求任何方案,但推荐一套共享约定。
通过检查最高有效四字节可以清楚模型使用的版本控制方案,使用语义版本控制时此四字节必须(MUST)非零,使用简单数字时必须(MUST)为零。换句话说,使用 SemVer 时,MAJOR 或 MINOR 数字中至少有一个必须非零。
SemVer、文件和消费者¶
对于模型和版本发布版本控制,ONNX 基于 SemVer 2.0.0 定义的原则和语法。在本文档中,我们使用与 SemVer 2.0.0 一致的术语 破坏性更改、非破坏性更改 和 补丁。
因为 ONNX 模型是序列化文件(而非 API),所以有必要澄清序列化模型与消费该模型的软件之间的关系。大致来说,序列化模型扮演 API 的 被调用者 角色,而序列化模型的消费者扮演 API 的 调用者 角色。
ONNX 版本控制原则基于健壮性原则:“严格要求自己,宽松接受他人”。
给定 ONNX 模型(以及 ONNX 规范本身)的生产者必须(MUST)严格遵守本规范中定义的破坏性更改与非破坏性更改的规则。
给定 ONNX 模型的消费者应该(SHOULD)消费更新的 ONNX 文件,前提是新 ONNX 文件的 IR 版本、引用的算子版本或模型版本没有破坏性更改(意味着两个 ONNX 文件之间的 MAJOR 版本号没有改变)。
给定 ONNX 模型的消费者可以(MAY)消费更新的 ONNX 文件,前提是新 ONNX 文件的 IR 版本、引用的算子版本或模型版本存在一项或多项破坏性更改。
在 protobuf 中序列化 SemVer 版本号¶
为了提高效率,ONNX 将 MAJOR、MINOR 和 PATCH 值序列化为一个位打包的 64 位整数;最高有效两位字节是 MAJOR 部分,接下来的最高有效两位字节是 MINOR 部分,最低有效四字节是 PATCH 部分。
例如,1.2.345 表示为 0x0001000200000159。
预发布和构建元数据不存储在模型中。
IR 版本控制¶
IR 格式使用简单数字进行版本控制,这些数字必须(MUST)单调递增。对 ONNX 规范的格式或语义的破坏性更改需要增加版本号。对 IR 格式的非破坏性更改不需要更改版本号。
注意:破坏性更改包括那些不改变序列化二进制格式,但仍会导致使用读写该格式的库的软件崩溃的更改。例如,更改消息属性的拼写会导致访问该属性的代码崩溃。
IR 格式遵守 proto3 规范的更新消息类型章节中定义的版本控制指南。
作为一般原则,实现在面对缺失字段时应该(SHOULD)是健壮的。但是,为了确保基本互操作性,给定 IR 版本的一些消息字段将被标记为必需字段,并且所有生产者必须(MUST)正确设置这些字段。必需字段必须(MUST)始终使用以下注释标记:
// This field MUST be present for this version of the IR.
例如,ModelProto.ir_version
属性必须(MUST)存在于每个模型中。ONNX 检查器 (onnx/checker.py
) 将强制执行这些规则。
由于协议缓冲区消息定义(.proto / .proto3 文件)预计会被多个独立的开发者消费,因此对这些定义的更改不应该(SHOULD NOT)破坏依赖于生成的语言绑定(例如,更改现有字段的类型)的代码。
算子版本控制¶
IR 可以独立于算子集进行演进。算子表示给定操作的签名和语义。算子是抽象接口,它们不意味着特定的实现;相反,它们仅仅是模型作者与该模型可能在其上执行的实现之间的契约。
给定算子由三元组 (领域, 算子类型, 起始版本)
标识,在文本中写作 领域.算子类型:起始版本
(例如,com.acme.FastConv:3
)。起始版本
是引入该算子的算子集版本。破坏性算子更改包括:
添加/删除/重命名属性。这甚至包括添加新的可选属性的情况,其中省略该属性将意味着一个默认值,其语义与之前的算子版本相同。
添加/删除/重新排序输入或输出。
添加/删除输入和输出支持的类型,以及更改属性使用的类型。
即使现有参数签名相同,也支持新行为(例如,在 Mean 算子中隐式支持张量广播)。
以下情况不属于破坏性更改:
澄清规范中的模糊之处,使其与主流实现实践相匹配。
算子或函数语义的更改必须(MUST)在新算子中引入,而新算子必须(MUST)在新的算子集中引入。
实际上,这意味着 ONNX 仓库中的 BC(向后兼容性)破坏性更改要求贡献者遵循以下步骤:
增加
DomainToVersionRange
中的最大版本。将旧的算子模式复制到
old.cc
文件。将
SinceVersion
标识符更新为步骤 (1) 中的新最大版本。在相应的
operator_sets
头文件中注册新算子。向
convert.h
添加一个版本适配器,以便版本转换器可以将旧版本的算子升级到新版本。如果遵循旧模式的算子在新模式下仍然有效(通常是这样),这可以是一个CompatibleAdapter
。可以将新算子降级到旧版本的版本适配器也可以添加到
convert.h
中,但不是强制性的。
节点如何绑定到算子声明是严格定义的,其设计旨在提高 ONNX 实现之间的模型兼容性,这符合健壮性原则的保守条款的精神。
ONNX 实现如何将算子声明绑定到特定实现超出了本规范的范围。ONNX 的实现可以选择(MAY)引入更复杂的算子声明/实现绑定模式,这符合健壮性原则的宽松条款的精神。
算子集¶
ONNX 使用算子集将不可变的算子规范分组在一起。一个算子集代表一个特定版本的领域,由一对(领域,版本)表示。这代表属于指定领域的指定版本(称为 opset_version
)的所有算子集合。当给定算子集的清单因包含的算子的添加、删除或语义更改而发生变化时,其版本必须(MUST)增加。
模型在 ModelProto.opset_import
中声明它们需要的算子集,形式为 (领域, opset_version)
对的列表。空字符串 (“”) 领域表示作为 ONNX 规范一部分定义的算子;其他领域对应于其他供应商的算子集(意味着它们可用于提供供应商特定的 ONNX 扩展)。给定模型指定的算子集联合必须(MUST)为模型的图中的每个节点提供兼容的算子声明。
示例¶
本节不具有规范性,仅供参考。
给定以下算子集
算子集版本 |
算子 |
注释 |
---|---|---|
1 |
{A} |
引入 A |
2 |
{A, B} |
引入 B |
3 |
{A’, B, C} |
A 更新(到 A’),引入 C |
4 |
{B, C’} |
A 移除,C 更新(到 C’) |
给定算子集的算子将具有以下 起始版本
值
算子 |
算子集版本 1 |
算子集版本 2 |
算子集版本 3 |
算子集版本 4 |
---|---|---|---|---|
A |
1 |
1 |
3 |
- |
B |
- |
2 |
2 |
2 |
C |
- |
- |
3 |
4 |
注意
与前一个算子集版本相比,新增或更新的值以粗体显示。
模型版本控制¶
规范的本节不具有规范性。它仅概述了一系列推荐实践。
模型作者和应用程序/系统可以(MAY)选择忽略模型版本控制机制和策略规则。对于将在开发者、团队或组织之间共享的模型,模型作者和应用程序/系统应该(SHOULD)遵守以下版本策略:
签名更改¶
对 ModelProto.graph.GraphProto.input 或 .output 的破坏性更改必须(MUST)增加
ModelProto.model_version
的 MAJOR 版本。破坏性更改包括:输入或输出语义的破坏性更改(例如,将输入张量所需的内容从彩色图像更改为黑白图像)。
将输入或输出的声明类型更改为不兼容的类型(例如,
tensor(int)->tensor(string)
)。添加没有有意义或指定默认值的新输入。请注意,输入的默认值在初始化器列表中指定。
移除没有有意义或指定默认值的现有输出。
对 ModelProto.graph.GraphProto.input 或 .output 的非破坏性更改必须(MUST)增加
ModelProto.model_version
的 MINOR 版本。非破坏性更改包括:将输入或输出的声明类型更改为兼容/拓宽的类型(例如,
tensor(int32)->tensor(int64)
,tensor(float16)->tensor(float32)
)。添加具有有意义或指定默认值的新输入。
添加仅在图的先前版本中不可能存在的输入(通常通过存在新输入或允许先前无效的输入值)存在时才触发的新行为。
精度或性能更改¶
显著影响精度或性能但未更改模型输入或输出的更改,应该(SHOULD)增加 ModelProto.model_version
的 PATCH 版本。
已发布的版本¶
ONNX 版本 |
IR 版本 |
算子集版本 ai.onnx |
算子集版本 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 |
上述表格的程序化可访问版本可从此处获得。有限的版本号信息也保存在 version.h 和 schema.h 中。每当发布新版本的 ONNX 时,请更新所有这些文件。