目的
本文档包含ONNX语义的规范性规范。
“onnx”文件夹下的.proto和.proto3文件构成了用协议缓冲区定义语言编写的语法规范。.proto和.proto3文件中的注释目的是提高这些文件的可读性,但如果它们与本文档冲突,则不具有规范性。此类冲突应报告为文档错误。
模型验证说明
有一个工具可以根据此规范执行模型的一般验证。它在C++中用Python命令行package实现。
本文件及所有相关文件中的语言说明:
在本文件中使用SHOULD、MUST、MAY等与RFC 2119一致。
“list”的使用应表示项目的有序集合,“set”应表示唯一元素的无序集合,“bag”表示可能非唯一元素的无序集合。
Components
ONNX is an open specification that consists of the following components:
- A definition of an extensible computation graph model.
- Definitions of standard data types.
- Definitions of built-in operators.
其中#1和#2包含在本文中;内置操作器在本文档末尾列出的文档中单独介绍。具体来说,内置算子operator被划分为一组原始算子operator和函数。函数是一种算子operator,其语义通过使用其他算子operator(和函数)扩展到子图(称为函数体)中来正式表示。就功能而言,与ONNX兼容的框架或运行时可以内联函数来执行它,如果它没有相应的函数实现。
有两个官方的ONNX变体;两者之间的主要区别在于支持的类型和默认的算子operator集。只有神经网络的ONNX变体只识别张量作为输入和输出类型,而经典的机器学习扩展ONNX-ML也识别序列和映射。ONNX-ML用非神经网络的ML算法扩展了ONNX算子集。
直到IR版本6,ONNX规范和模型格式只处理推理(也称为评分)。从IR版本7开始,ONNX规范和模型格式已扩展到支持训练。ONNX训练模型本身是推理模型的一个扩展,允许只进行推理的runtime忽略与训练相关的扩展并运行推理。然而,在典型的使用场景中,与训练模型相比,仅推理模型可以实现更优化的模型表示(用于推理目的)。
runtime不可知
ONNX并不预先假设或暗示任何特定的运行时实现方法。
例如,一个实现可以由一个解释模型的富运行时组成;
可以是一个代码生成器,它将整个模型转换为某些目标编程语言的可执行代码;
可以是硬件实现;
可以是其中两个或三个的组合。
本规范中的任何内容都不应被解释为主张一种实现方法胜过任何其他方法;
对具体实现的内部工作原理的任何评论都应解释为示例。
ONNX版本控制
ONNX中有几个地方有版本控制功能——IR(中间表示)规范本身、模型版本和算子operator集版本。此外,每一个单独的算子operator都指明它是在哪个版本的包含算子operator集中引入或稳定的。
版本号可以用作简单的数字,也可以用于对语义版本进行编码。如果使用semver,惯例是使用两个最高有效字节作为主要编号,下两个字节用于次要编号,最低有效的四个字节用于构建/错误修复build/bugfix编号。使用semver版本控制时,至少有一个主/辅编号必须为非零。
IR规范对其版本使用简单的单调递增数。有效的IR版本由枚举定义,该枚举当前具有以下值:
// Version 1, published on Oct 10, 2017.
IR_VERSION_2017_10_10 = 0x0000000000000001;
// Version 2, published on Oct 30, 2017
IR_VERSION_2017_10_30 = 0x0000000000000002;
// Version 3 published on Nov 3, 2017
IR_VERSION = 0x0000000000000003;
算子operator集使用简单的版本号。每个算子operator集版本表示算子operator集及其在特定时间点的语义的快照。
本规范没有提供关于模型生产者应该使用什么版本控制方案的指导。
有关IR、算子operator集和模型版本控制的约定和最佳实践的更多详细信息,请参阅版本控制。
可扩展计算图模型
ONNX指定计算图的可移植、序列化格式。它不一定是框架选择的在内部使用和操作计算的形式。例如,如果在优化过程中操作更有效,则实现可能在内存中以不同的方式表示模型。
一个实现可以通过添加表示语义的算子operator来扩展ONNX,这些算子operator超出了所有实现必须支持的标准算子operator集。其机制是将算子operator集添加到依赖于扩展算子operator的模型中的opset_import属性。
模型Models
顶层ONNX构造是一个“Model”,在协议缓冲区中表示为类型onnx.ModelProto
模型结构的主要目的是将元数据与包含所有可执行元素的图形相关联。元数据是在第一次读取模型文件时使用的,它为实现提供了所需的信息,以确定它是否能够执行模型、生成日志消息、错误报告等。此外,元数据对工具(如IDE和模型库)很有用,它需要它来告诉人类一个给定模型的目的和特性。
每个模型都有以下组件:
模型必须指定一个域,并根据负责组织的标识使用反向域名,这与传统上用于命名Java包的约定相同。
注意:检测ONNX文件
可以使用协议缓冲区分发中的Protocol工具检查ONNX文件的内容,方法如下:
$ protoc --decode=onnx.ModelProto onnx.proto < yourfile.onnx
Where onnx.proto is the file that is part of this repository.
Alternatively, you can use a tool like Netron to explore the ONNX file.
模型语义
推理模型的语义是一个无状态函数(除了用于随机数生成的状态)。因此,每当一个推理模型(没有随机的生成器操作)被用于对同一输入执行推理时,它都会产生相同的输出。
训练模型的语义是有状态对象的语义,状态由训练权重的当前值组成(以及学习算法所需的任何其他辅助状态,例如动量)。具体地说,它的语义是通过三种方法获取的:初始化方法(用于初始化或重置状态变量的值)、训练步骤方法(使用一批输入输出对进行训练)和一种推理方法,该方法利用学习到的权重的当前值进行推理。前两个方法更新对象的状态,而第三个方法没有副作用。
可选元数据
模型中的“metadata_props”字段可用于工具或模型开发人员选择放置在其中的任何类型的可选元数据。以下是定义的模型的“标准”可选元数据属性。
算子operator
每个模型都必须显式地命名其功能所依赖的算子operator。算子operator定义可用算子operator及其版本。每个模型按其域定义导入的算子operator。所有模型都隐式导入默认的ONNX算子operator。
每个算子operator应在单独的文档中定义,并使用protobuf作为序列化格式。如何在运行时找到算子operator文档取决于实现。
注:截至本文档的发布,还没有任何ONNX实现用于处理操作文档。
算子operator的属性包括:
算子operator版本是一个简单的整数值,随着新版本的算子operator的发布,该值单调增加。
默认算子operator以外的算子operator必须指定其域,并应根据负责组织的标识使用反向域名,这与用于命名Java包的约定相同。
算子operator
图中使用的每个算子operator必须由模型导入的算子operator之一显式声明。
算子operator定义的属性包括:
版本值必须与首次发布算子operator时的算子operator版本值相同。算子operator的后续版本一旦发布为稳定版本,则不得更改算子operator的签名或语义。
“status”属性指示算子operator的语法、语义或存在是否处于实验阶段或稳定阶段。一旦一个算子operator被发布为稳定的,它的语法和语义在算子operator集的后续版本中就不能改变。
有两种不同的方法将信息传递给算子operator–输入和属性。后者用于表示图中常量的值,而前者表示图形输入或在图中其他地方计算的值。这种区别可能与某些实现的良好性能密切相关,而与其他实现完全无关。 图
图用于描述无副作用的计算(函数)。序列化图由一组元数据字段、一组模型参数和一组计算节点组成。
每一个计算数据流图都被构造成一个拓扑排序的节点列表,这些节点必须没有循环。每个节点表示对算子operator的调用。每个节点有零个或多个输入和一个或多个输出。
图形具有以下属性:
ValueInfo has the following properties:
版本值必须与首次发布算子operator时的算子operator版本值相同。
算子operator的后续版本一旦发布为稳定版本,则不得更改算子operator的签名或语义。
“status”属性指示算子operator的语法、语义或存在是否处于实验阶段或稳定阶段。一旦一个算子operator被发布为稳定的,它的语法和语义在算子operator的后续版本中就不能改变。
有两种不同的方法将信息传递给算子operator–输入和属性。后者用于表示图中常量的值,而前者表示图形输入或在图中其他地方计算的值。这种区别可能与某些实现的良好性能密切相关,而与其他实现完全无关。
图
图用于描述无副作用的计算(函数)。序列化图由一组元数据字段、一组模型参数和一组计算节点组成。
每一个计算数据流图都被构造成一个拓扑排序的节点列表,这些节点必须没有循环。每个节点表示对算子operator的调用。每个节点有零个或多个输入和一个或多个输出。
图形具有以下属性: