开放神经网络交换中间表示 (ONNX IR) 规范¶
目的
本文档包含 ONNX 语义的规范性说明。
在 onnx 文件夹下找到的 .proto
和 .proto3
文件构成了使用 Protocol Buffers 定义语言编写的其语法的规范性说明。在 .proto
和 .proto3
文件中找到的注释旨在提高这些文件的可读性,但如果与本文档冲突,则不具有规范性。应将此类冲突报告为文档错误。
模型验证注意事项
提供了一个工具,用于对照本规范执行模型的一般验证。它使用 C++ 实现,带有 Python 命令行包装器。
本文档及所有相关文档的语言注意事项:
本文档中使用 SHOULD、MUST、MAY 等词语与 RFC 2119 一致。
‘list’ 表示有序的项目集合,‘set’ 表示无序的唯一元素集合,‘bag’ 表示无序的可能包含非唯一元素的集合。
组件¶
ONNX 是一个开放规范,由以下组件组成
可扩展计算图模型的定义。
标准数据类型的定义。
内置运算符的定义。
#1 和 #2 共同构成了本文所涵盖的 ONNX 中间表示(或称 ‘IR’)规范;内置运算符在末尾列出的文档中有所介绍。具体来说,内置运算符分为一组基本运算符和函数。函数是一种运算符,其语义通过使用其他运算符(和函数)扩展到子图(称为函数体)中来正式表达。从功能上看,如果 ONNX 兼容的框架或运行时没有对应的函数实现,它可以内联函数体来执行它。
有两个官方的 ONNX 变体;两者之间的主要区别在于默认运算符集。ONNX-ML 扩展了 ONNX 运算符集,增加了不基于神经网络的机器学习算法。
在 IR 版本 6 之前,ONNX 规范和模型格式仅涉及推理(也称为评分)。从 IR 版本 7 开始,ONNX 规范和模型格式也支持训练。ONNX 训练模型是推理模型的扩展。仅用于推理的运行时可以忽略与训练相关的扩展来使用训练模型。然而,仅用于推理的模型可能支持一种比训练模型更适用于推理目的的表示形式。
运行时无关¶
ONNX 不预设或暗示任何特定的运行时实现方法。
例如,实现可以是一个解释模型的丰富运行时;它可以是一个将整个模型翻译成某个目标编程语言的可执行代码的代码生成器;它可以是一个硬件实现;它也可以是其中两者或三者的组合。
本规范中的任何内容都不应被理解为提倡某种实现方法优于其他方法;对具体实现内部工作原理的任何评论都应被视为示例。
ONNX 版本控制¶
IR 规范、独立模型和运算符集都有版本。此外,每个独立运算符都指示它在其所属的运算符集中是在哪个版本中引入或稳定的。
版本号可以用作简单数字,或用于编码语义版本(又称 SemVer)。如果使用语义版本,惯例是将最重要的两个字节用于主要版本号,接下来的两个字节用于次要版本号,最不重要的四个字节用于补丁/构建/错误修复版本号。使用语义版本控制时,主要/次要版本号中至少有一个 MUST 是非零的。
IR 规范使用简单的单调递增数字作为其版本。有效的 IR 版本由 onnx.proto 中的 onnx.Version
枚举定义。
运算符集使用简单的版本号。每个运算符集版本代表了特定时间点运算符集及其语义的快照。
本规范不提供关于模型生产者应使用何种版本控制方案的指导。
有关 IR、运算符集和模型版本控制的约定和最佳实践的更多详细信息,请参见版本控制。
可扩展计算图模型¶
ONNX 指定了计算图的可移植、序列化格式。这不一定是一个框架选择在内部使用的形式。例如,如果在优化过程中操作更有效率,实现可以在内存中以不同的方式表示模型。
实现 MAY 通过添加表达超出所有实现 MUST 支持的标准运算符集之外语义的运算符来扩展 ONNX。其机制是在依赖于扩展运算符的模型中将运算符集添加到 opset_import
属性中。
模型¶
顶层 ONNX 结构是‘模型’,在协议缓冲区中表示为类型 onnx.ModelProto
模型结构的主要目的是将元数据与包含所有可执行元素的图关联起来。元数据在首次读取模型文件时使用,为实现提供了确定是否能够执行模型、生成日志消息、错误报告等所需的信息。此外,元数据对诸如 IDE 和模型库之类的工具很有用,它们需要元数据来向人们介绍给定模型的目的和特征。
每个模型包含以下组件
名称 |
类型 |
描述 |
---|---|---|
ir_version |
int64 |
模型假定的 ONNX 版本。 |
opset_import |
OperatorSetId |
提供给模型的运算符集标识符集合。实现必须支持集合中的所有运算符,否则拒绝该模型。 |
producer_name |
string |
用于生成模型的工具名称。 |
producer_version |
string |
生成工具的版本。 |
domain |
string |
一个反向 DNS 名称,用于指示模型命名空间或域,例如 ‘org.onnx’ |
model_version |
int64 |
模型本身的版本,编码为一个整数。 |
doc_string |
string |
此模型的人类可读文档。允许使用 Markdown。 |
graph |
Graph |
用于执行模型的参数化图。 |
metadata_props |
map<string,string> |
命名的元数据值;键应唯一。 |
training_info |
TrainingInfoProto[] |
包含训练信息的可选扩展。 |
functions |
FunctionProto[] |
模型本地函数的可选列表。 |
模型 MUST 指定一个域,并使用基于负责组织的身份的反向域名,这与命名 Java 包的惯例相同。
注意:探索 ONNX 文件
您可以使用作为 Protocol Buffers 分发一部分的 protoc
工具来检查 ONNX 文件的内容,方法如下
$ protoc --decode=onnx.ModelProto onnx.proto < yourfile.onnx
其中 onnx.proto 是本仓库的一部分文件。
或者,您可以使用像 Netron 这样的工具来探索 ONNX 文件。
模型语义¶
推理模型的语义是一个无状态函数(可能除了用于生成随机数的状体)。因此,每当使用推理模型(不包含随机数生成操作)对相同的输入执行推理时,预期会产生相同的输出。
训练模型的语义是一个有状态对象,其状态包括已训练权重的当前值(以及学习算法所需的任何其他辅助状态,例如动量)。具体来说,其语义通过三种方法捕获:初始化方法(用于初始化或重置状态变量的值)、训练步骤方法(使用一批输入-输出对进行训练)以及使用当前学习权重值执行推理的推理方法。前两种方法更新对象的状态,而第三种方法没有副作用。
可选元数据¶
模型中的 ‘metadata_props’ 字段可用于工具或模型开发者选择放置的任何可选元数据。以下是定义的模型“标准”可选元数据属性。
名称 |
类型 |
格式 |
描述 |
---|---|---|---|
model_author |
string |
逗号分隔的名称列表。 |
模型的作者(或作者们)的个人姓名,和/或他们的组织。 |
model_license |
string |
名称或 URL。 |
模型可用的许可的常用名称或 URL。 |
运算符集标识符¶
每个运算符集由(域,版本)对唯一标识。
名称 |
类型 |
描述 |
---|---|---|
domain |
string |
被标识的运算符集的域。 |
version |
int64 |
被标识的运算符集的版本。与运算符集中的 ‘opset_version’ 相同。 |
运算符集版本是一个简单的整数值,随着新版本运算符集的发布而单调递增。
除默认运算符集之外的运算符集 MUST 指定一个域,并且 SHOULD 使用基于负责组织的身份的反向域名,这与命名 Java 包的惯例相同。
运算符集¶
每个模型 MUST 明确命名其功能所依赖的运算符集。运算符集定义了可用的运算符及其版本。每个模型通过其域定义导入的运算符集。所有模型隐式导入默认的 ONNX 运算符集。
每个运算符集 SHALL 在单独的文档中定义,也使用 protobuf 作为序列化格式。运行时如何找到运算符集文档取决于实现。
注意:截至本文档发布之时,已知没有任何 ONNX 实现会处理运算符集文档。
运算符集的属性为
名称 |
类型 |
描述 |
---|---|---|
magic |
string |
值 ‘ONNXOPSET’ |
ir_version |
int32 |
与运算符对应的 ONNX 版本。 |
ir_version_prerelease |
string |
IR 的 SemVer 的预发布组件。 |
ir_build_metadata |
string |
此版本运算符集的构建元数据。 |
domain |
string |
运算符集的域。在所有集合中必须唯一。 |
opset_version |
int64 |
运算符集的版本。 |
doc_string |
string |
此运算符集的人类可读文档。允许使用 Markdown。 |
operator |
Operator[] |
此运算符集中包含的运算符。 |
运算符集版本是一个简单的整数值,随着新版本运算符集的发布而单调递增。
除默认运算符集之外的运算符集 MUST 指定一个域,并且 SHOULD 使用基于负责组织的身份的反向域名,这与命名 Java 包的惯例相同。
运算符¶
图中使用的每个运算符 MUST 由模型导入的运算符集之一明确声明。
运算符定义的属性为
名称 |
类型 |
描述 |
---|---|---|
op_type |
string |
运算符的名称(区分大小写),在图节点中使用。在运算符集的域中 MUST 唯一。 |
since_version |
int64 |
此运算符引入时所在的运算符集版本。 |
status |
OperatorStatus |
‘EXPERIMENTAL’ 或 ‘STABLE’ 之一。 |
doc_string |
string |
此运算符的人类可读文档字符串。允许使用 Markdown。 |
版本值 MUST 与运算符首次发布时所在的运算符集版本值相同。一旦运算符发布为 STABLE,后续的运算符集版本 MUST NOT 更改其签名或语义。
‘status’ 属性指示运算符的语法、语义或存在状态是实验性还是稳定。一旦运算符发布为 STABLE,其语法和语义在后续运算符集版本中 MUST NOT 更改。
有两种不同的方式将信息传递给运算符——输入和属性。输入代表图输入或图内其他地方计算的值,而属性用于图中是常量的值。这种区别对于某些实现实现良好的性能可能高度相关,而对于其他实现则完全无关。
函数¶
函数可以被认为是运算符与使用其他更原始的 op 实现运算符的组合,这被称为函数体。函数体由构成图的拓扑排序节点列表组成。因此,函数结合了运算符和图(如下所述)的各个方面。
模型中包含的每个函数(也称为模型本地函数)作为对应运算符的默认或备用实现。然而,运行时可以选择使用运算符的替代实现(通常是优化内核)。因此,函数的唯一名称很重要,因为它隐式关联着语义规范。
序列化的函数(一个 FunctionProto)具有以下属性
名称 |
类型 |
描述 |
---|---|---|
name |
string |
函数的名称 |
domain |
string |
此函数所属的域 |
overload |
string |
函数唯一 ID 的一部分(在 IR 版本 10 中添加) |
doc_string |
string |
此函数的人类可读文档。允许使用 Markdown。 |
attribute |
string[] |
函数的属性参数 |
attribute_proto |
Attribute[] |
(IR 版本 9+)带有函数默认值的属性参数。函数属性应表示为字符串属性或 Attribute,不能同时表示为两者。 |
input |
string[] |
函数的输入参数 |
output |
string[] |
函数的输出参数。 |
node |
Node[] |
节点列表,形成部分有序的计算图。它必须是拓扑排序的。 |
opset_import |
OperatorSetId |
函数实现使用的运算符集标识符集合。 |
value_info |
ValueInfo[] |
(IR 版本 >= 10)用于存储函数中使用的值(包括其输入和输出)的类型和形状信息。 |
metadata_props |
map<string,string> |
(IR 版本 >= 10)命名的元数据值;键应唯一。 |
在 IR 版本 9 之前,名称和域用于唯一标识运算符。IR 版本 10 添加了 overload 字段,并且三元组(name, domain, overload)作为存储在模型中的函数之间的唯一 ID。这旨在支持模型中对函数进行不同调用时需要不同函数体的情况。FunctionProto 中未明确标识 opset 版本,但它由模型中包含的域的 opset 版本隐式确定。
input、output、attribute 和 attribute_proto(在 IR 版本 9 中添加)构成了运算符的签名部分。签名中没有明确包含类型信息。attribute_proto 字段描述了函数的属性参数及其默认值(当调用站点节点未指定时),而 attribute 字段列出了没有默认值的属性参数。这两个列表中的名称必须不同。当函数的属性参数在函数内的节点中使用时,如果调用站点节点(函数)指定了该属性,则将其替换为指定给该属性的实际参数值;如果属性指定了默认值,则将其替换为默认值;否则省略该属性。
opset_import 和 node 字段描述了函数的实现。
value_info 字段(在 IR 版本 10 中添加)允许模型存储函数中使用的值(包括其输入和输出)的类型和形状信息。请注意,这是可选的,ONNX 允许函数是多态的。
图¶
图用于描述无副作用的计算(函数)。序列化的图由一组元数据字段、一个模型参数列表和一个计算节点列表组成。
每个计算数据流图的结构是一个拓扑排序的节点列表,这些节点组成一个图,该图 MUST 没有循环。每个节点代表对运算符或模型本地函数的调用。每个节点有零个或多个输入和一个或多个输出。
图具有以下属性
名称 |
类型 |
描述 |
---|---|---|
name |
string |
模型图的名称。 |
node |
Node[] |
节点列表,基于输入/输出数据依赖关系形成部分有序的计算图。它是拓扑排序的。 |
initializer |
Tensor[] |
命名张量值的列表。当初始化器与图输入具有相同的名称时,它指定该输入的默认值。当初始化器与所有图输入名称不同时,它指定一个常量值。列表的顺序是未指定的。 |
doc_string |
string |
此模型的人类可读文档。允许使用 Markdown。 |
input |
ValueInfo[] |
图的输入参数,可能由 ‘initializer’ 中找到的默认值初始化。 |
output |
ValueInfo[] |
图的输出参数。一旦图执行已写入所有输出参数,则执行完成。 |
value_info |
ValueInfo[] |
用于存储非输入或输出值的类型和形状信息。 |
metadata_props |
map<string,string> |
(IR 版本 >= 10)命名的元数据值;键应唯一。 |
ValueInfo 具有以下属性
名称 |
类型 |
描述 |
---|---|---|
name |
string |
值/参数的名称。 |
type |
类型 |
值的类型,包括形状信息。 |
doc_string |
string |
此值的人类可读文档。允许使用 Markdown。 |
每个主(顶级)图 MUST 定义其输入和输出的名称、类型和形状,这些都在‘值信息’结构中指定。主图的输入和输出需要有形状,指示秩,即使不需要指定确切的维度。
嵌套子图(指定为属性值)MUST 定义其输入和输出的名称,并且 MAY 定义其输入和输出的类型。
每个图 MUST 指定一个名称。
图 MUST 遵循所有节点输出的单一静态赋值(SSA);这意味着所有节点输出名称在图内 MUST 唯一。
Graphs SHOULD 填充文档字符串,这些字符串 MAY 使用 GitHub 风格的 markdown 语法解释。HTML 和其他文本标记语言 MAY NOT 用于文档字符串。
图内的名称¶
所有名称 SHOULD 遵循 C90 标识符语法规则。
节点、输入、输出、初始化器和属性的名称组织到多个命名空间中。在一个命名空间内,对于给定的每个图,每个名称 MUST 唯一。在图包含嵌套子图(作为属性值)的情况下,请参阅下文以获得进一步的澄清。
命名空间包括
命名空间 |
描述 |
---|---|
属性 |
运算符的属性名称。对于每个运算符都是唯一的。 |
值 |
值的名称——节点输入 & 输出,张量值(如果已命名),图输入,图输出。 |
节点 |
图节点的名称。 |
Graph |
域内图的名称,在模型域内唯一。 |
运算符 |
域内运算符的名称。 |
Shape |
张量形状变量的名称——限定在图的值信息记录范围内,这是形状变量出现的地方。 |
节点¶
计算节点由名称、调用运算符的名称、命名输入列表、命名输出列表和属性列表组成。
输入和输出按位置与运算符输入和输出相关联。属性按名称与运算符属性相关联。
它们具有以下属性
名称 |
类型 |
描述 |
---|---|---|
name |
string |
节点的可选名称,仅用于诊断目的。 |
input |
string[] |
节点用于将输入值传播到节点运算符的值的名称。它必须引用图输入、图初始化器或节点输出。 |
output |
string[] |
节点用于捕获由节点调用的运算符的数据的输出名称。它要么在图中引入一个值,要么引用一个图输出。 |
op_type |
string |
要调用的运算符的符号标识符。 |
domain |
string |
包含由 op_type 命名的运算符的运算符集的域。 |
attribute |
Attribute[] |
命名属性,另一种形式的运算符参数化,用于常量值而不是传播值。 |
doc_string |
string |
此值的人类可读文档。允许使用 Markdown。 |
overload |
string |
函数唯一 ID 的一部分(在 IR 版本 10 中添加) |
metadata_props |
map<string,string> |
(IR 版本 >= 10)命名的元数据值;键应唯一。 |
属于 Value 命名空间的名称可能出现在多个地方,即作为图输入、图初始化器、图输出、节点输入或节点输出。名称作为图输入、图初始化器或节点输出的出现被称为定义,名称作为节点输入或图输出的出现被称为使用。
图中使用的一个值名称必须具有唯一的定义,例外情况是相同的名称 MAY 同时出现在图输入列表和图初始化器列表中。(存在嵌套子图时,如后所述,还有其他例外。)
当名称同时出现在初始化器列表和图输入列表中时,运行时 MAY 允许调用者为此(输入)名称指定一个值,从而覆盖初始化器中指定的值,并且运行时 MAY 允许用户省略为此(输入)名称指定值,选择初始化器中指定的值。不应被调用者覆盖的常量名称应仅出现在初始化器列表中,而不应出现在图输入列表中。在 IR 版本 >= 4 的模型中,在用作属性值的嵌套子图中,除非相应的 op 规范明确允许,否则用户 MUST NOT 将相同的名称同时用作子图初始化器和子图输入。在 IR 版本 <= 3 的模型中,用户 MAY 将相同的名称同时用作子图初始化器和子图输入,但这仅限于通过初始化器支持不旨在对应从节点传递到子图的任何实际输入的常量。特别是,控制流运算符的语义确定了提供给子图执行的输入集,并且这些输入名称 MUST NOT 作为子图初始化器出现。子图初始化器名称必须出现在实际输入之后的图输入列表中。这使得实际输入和形式输入能够按位置匹配。
计算图中的边是通过一个节点的输出在后续节点的输入中被名称引用来建立的。
给定节点的输出在图中引入新的名称。节点输出的值由节点的运算符计算。节点输入 MAY 引用节点输出、图输入和图初始化器。当节点输出的名称与图输出的名称一致时,图输出的值就是该节点计算出的相应输出值。嵌套子图中的节点输入 MAY 引用外部图中引入的名称(作为节点输出、图输入或图初始化器)。
图 MUST 对所有节点输出使用单一静态赋值,这意味着所有节点输出名称在图内 MUST 唯一。在嵌套子图的情况下,节点输出名称 MUST 与在嵌套子图中可见的外部范围中的名称不同。
节点依赖关系 MUST NOT 在计算图中创建循环。
节点中输入和输出的数量、它们的类型、节点中指定的属性集及其类型 MUST 满足节点运算符签名强加的约束。
定义顶级计算图的节点列表 MUST 按照拓扑顺序排列;也就是说,如果在图中节点 K 在节点 N 之后,则 N 的任何数据输入都不能引用 K 的输出。
节点属性用于将文字(静态)值传递给运算符。
输入和输出值¶
表示区分两种类型的值:属性值(静态已知)和输入/输出值。在这两种情况下允许的值类型不同。
输入和输出值以图输入、输出和初始化器以及节点输入和输出的形式存在。它们的值在运行时确定,或者由启动模型执行的代码确定,或者由计算输出值的运算符确定。
属性¶
属性值仅存在于节点中,通过名称关联传递给运算符。属性值是运行时常量,因为它们的值在构建模型图时确定,因此不在运行时计算。属性的一个常见用途是表示在模型训练期间建立的系数。
属性具有以下属性
名称 |
类型 |
描述 |
---|---|---|
name |
string |
属性的名称。对于任何给定的运算符和节点,在属性、输入和输出中必须唯一。 |
doc_string |
string |
此值的人类可读文档。允许使用 Markdown。 |
type |
AttributeType |
属性的类型,决定使用剩余哪个字段来存储属性值。 |
f |
float |
一个 32 位浮点值。 |
i |
int64 |
一个 64 位整数值。 |
s |
byte[] |
UTF-8 字符串。 |
t |
Tensor |
一个张量值。 |
g |
Graph |
一个图。 |
floats |
float[] |
一个 32 位浮点值列表。 |
ints |
int64[] |
一个 64 位整数值列表。 |
strings |
byte[][] |
一个 UTF-8 字符串列表。 |
tensors |
Tensor[] |
一个张量值列表。 |
graphs |
Graph[] |
一个图列表。 |
ref_attr_name |
string |
父函数的属性名称。 |
属性 ‘name’ 和 ‘type’ 是所有属性必需的,‘doc_string’ SHOULD 用于所有属性。属性 MUST 只有一个携带值的属性。
如果设置了“ref_attr_name”,则此属性不包含数据,而是对父函数具有给定名称的属性的引用。只能在函数体内部使用。
可变参数输入和输出¶
算子的最后一个输入或输出可以标记为可变参数。例如,算子“Max()”可用于计算可变数量输入值的最大值。可变参数算子具有最小元数,它指定必须指定的最小操作数数量。
对于每个可变参数算子输入,必须指定 N 个或更多节点输入,其中 N 是算子的最小元数。对于每个可变参数算子输出,必须指定 N 个或更多节点输出,其中 N 是算子的最小元数。
可选输入和输出¶
静态可选¶
某些算子的输入被标记为可选,这意味着引用节点可以放弃为此类输入提供值。
某些算子的输出是可选的。当未指定算子的实际输出参数时,算子实现可以放弃为此类输出计算值。
有两种方法可以使可选输入或输出未指定:第一种,仅适用于尾随输入和输出,即简单地不提供该输入或输出;第二种方法是使用空字符串代替输入或输出名称。
引用具有可选输出的算子的每个节点必须为每个已计算的输出提供名称,并且不得为未计算的输出提供名称。
上述类型的可选输入和输出被称为 静态可选 (static-optional)。
动态可选 (自 IR-8 起)¶
IR-8 版本引入了一种新的类型构造函数来表示 动态可选 (dynamic-optional) 输入和输出,作为对前面描述的静态可选版本的补充。动态可选的 INT64 张量与 INT64 张量类型是不同的类型。相比之下,静态可选的 INT64 张量没有单独的类型,它与 INT64 张量具有相同的类型。必须显式使用算子 Optional
和 OptionalGetElement
来在动态可选类型和底层非可选类型之间进行转换。动态可选比静态可选具有更强的表达能力。
外部张量数据¶
大型常量张量(如初始化器)的原始数据可以序列化到单独的文件中。在这种情况下,张量必须提供相对于模型文件的文件名,并且不得使用值字段。它可以提供在该文件内的字节偏移量和长度。它还可以指定文件的 SHA1 摘要。一个文件可以包含多个张量的数据。
更多详细信息请参见 外部数据。
标准数据类型¶
ONNX 有两种官方变体;两者之间的主要区别在于支持的类型和支持的算子。
关于支持的类型,ONNX 和 ONNX-ML 定义都将张量、稀疏张量、序列、映射和可选类型识别为输入和输出类型。序列和映射从 IR 版本 6 (ONNX 1.6.0 发布) 开始支持。可选类型从 IR 版本 8 (ONNX 1.10.0 发布) 开始支持。
ONNX 支持以下数据类型用于图和节点的输入和输出以及图的初始化器。
基本数值、字符串和布尔类型必须用作张量的元素。
张量定义¶
张量是向量和矩阵的推广;向量有一维,矩阵有两维,而张量可以有任意维数,包括零维。零维张量在逻辑上等同于标量值。
在数学上,张量可以定义为一个序列/列表对 (V, S),其中 S 是张量的形状(非负整数列表),V 是一个值列表,其长度等于 S 中维度的乘积。当且仅当 V = V’ 且 S = S’ 时,两个张量 (V, S) 和 (V’, S’) 才相等。S 的长度被称为秩。
如果 S 的长度为 0,则 V 的长度必须为 1,因为空乘积定义为 1。在这种情况下,张量表示一个标量。
S 可以包含值为 0 的维度。如果任何维度为 0,则 V 的长度必须为 0。
如果 S 的长度为 1,则 V 的长度等于 S 中的单个维度。在这种情况下,张量表示一个向量。
表示长度为 1 的向量的张量形状为 [1],而表示标量的张量形状为 []。它们都只有一个元素,但标量不是长度为 1 的向量。
张量的形状 S 是一个列表,但可以表示为一个张量,其值为 S 且形状为 [R],其中 R 是张量的秩。
对于张量 (V, S),表示其形状的张量是 (S, [R])。
标量的形状是 []。表示为张量时,[] 的形状是 [0]。
表示形式¶
通常将张量表示为嵌套列表。这通常很好用,但当涉及零维度时就会出现问题。形状为 (5, 0) 的张量可以表示为 [[], [], [], [], []],但 (0, 5) 表示为 [],这会丢失第二维为 5 的信息。
嵌套列表不能完整表示具有零维度值的张量。
张量元素类型¶
分组 |
类型 |
描述 |
---|---|---|
浮点类型 |
float16, float32, float64, bfloat16, float8e4m3fn, float8e5m2, float8e4m3fnuz, float8e5m2fnuz, float4e2m1 |
遵循 IEEE 754-2008 标准浮点数据表示或定义在 深度学习的 FP8 格式、深度神经网络的 8 位数值格式 以及 开放计算项目 论文中的值。 |
有符号整数类型 |
int4, int8, int16, int32, int64 |
支持 4-64 位宽度的有符号整数。 |
无符号整数类型 |
uint4, uint8, uint16, uint32, uint64 |
支持 4-64 位宽度的无符号整数。 |
复数类型 |
complex64, complex128 |
实部和虚部为 32 位或 64 位的复数。 |
其他 |
string |
字符串表示文本数据。所有字符串都使用 UTF-8 编码。 |
其他 |
bool |
布尔值表示只有两个值的数据,通常是 true 和 false。 |
输入/输出数据类型¶
以下类型用于定义图和节点输入和输出的类型。
变体 |
类型 |
描述 |
---|---|---|
ONNX |
稠密张量 |
表示张量。参见上面的定义。 |
ONNX |
序列 |
序列是同质类型的稠密、有序的元素集合。 |
ONNX |
映射 |
映射是关联表,由键类型和值类型定义。 |
ONNX |
可选 |
可选类型是包装器,可能包含张量、序列或映射类型的元素,也可能为空(不包含任何元素)。详情 |
静态张量形状¶
除了元素类型外,张量类型还具有静态形状。张量变量的静态形状与张量值的运行时(动态)形状相关,但不同。静态张量形状是一个记录列表,指示张量是向量、矩阵还是更高维的值。例如,一个 100x100 的矩阵形状为 [100,100]。
静态形状由“TensorShapeProto”定义。
message TensorShapeProto {
message Dimension {
oneof value {
int64 dim_value = 1;
string dim_param = 2;
};
};
repeated Dimension dim = 1;
}
由张量类型消息引用。
message Tensor {
optional TensorProto.DataType elem_type = 1;
optional TensorShapeProto shape = 2;
}
维度大小的空列表 [] 是有效的张量形状,表示零维(标量)值。零维张量与未知维度的张量不同,未知维度的张量在 Tensor 消息中通过缺少“shape”属性来指示。当值的类型(包括节点输入)中缺少 shape 属性时,表示相应的运行时值可以具有任何形状。本小节描述如何解释缺少 shape 或缺少维度的 shape 等情况。然而,特定的使用上下文可能会对类型和形状施加进一步的约束。例如,模型的输入和输出(顶层图)要求具有形状,指示输入和输出的秩,即使不必指定确切的维度。
列表中的每个大小可以表示为整数值或“维度变量”,即一个字符串,表示该维度的实际大小未静态限制为特定数字。这对于声明关心维度数量但不关心每个维度的确切大小的接口很有用。维度可以既不设置 dim_value 也不设置 dim_param。这样的维度表示与其他未知维度无关的未知维度。
例如,一个 NxM 矩阵的形状列表为 [N,M]。
每个维度变量的名称应遵循 C90 标识符语法规则。
目前,维度变量没有作用域。模型中整个图中,维度变量“N”表示相同的值。例如,如果图有两个输入 X 和 Y,形状都为 [“N”],那么在运行时为 X 和 Y 传入的值必须是秩为 1 且具有相同维度的张量。嵌套子图目前与主图共享相同的维度变量作用域。这允许模型关联子图内部张量的维度与外部图中张量的维度。
ONNX 支持张量序列等类型。维度变量的全局作用域意味着类型为“Sequence<Tensor<float, [M,N]>”的变量表示一个张量序列,其中所有张量都具有相同的形状。如果该维度在序列中的所有张量中没有固定大小,则必须从上述类型中省略维度变量 M 或 N。如果序列中不同的张量可能具有不同的秩,则必须从类型中省略整个形状。
例如,执行矩阵叉积的图可以定义为接受形状为 [K,M] 和 [M,N] 的两个输入,并产生形状为 [K,N] 的输出。
形状可以使用整数和变量的组合来定义。
历史说明:以下扩展在早期曾被考虑,但从未实现或支持。
使用空字符串(作为维度变量)表示与其他任何维度无关的未知维度。这种做法已被弃用,取而代之的是使用既未设置 dim_value 也未设置 dim_param 的 Dimension。
使用字符串“*”(作为维度变量)表示数量未知的零个或多个维度序列。这不被支持。在当前实现中,形状中的维度数量必须表示张量的秩。未知秩的张量使用没有形状的 TypeProto::Tensor 对象表示,这是合法的。
允许维度变量作用域限定在子图(例如循环体)内的机制可能很有用,但目前不支持。
ONNX 支持张量序列等类型。针对类型局部维度变量的作用域机制可能有助于区分以下两种类型:不同大小的方阵序列 与 相同大小的方阵序列。这目前不支持。
属性类型¶
用于属性的类型系统与用于输入和输出的类型系统相关联但略有不同。属性值可以是稠密张量、稀疏张量、标量数值、字符串、图,或上述类型之一的重复值。
其他元数据¶
ModelProto 结构,以及 IR 版本 >= 10 中的各种其他结构 (GraphProto, FunctionProto, NodeProto),包含一个 metadata_props 字段,允许用户以键值对的形式存储其他元数据。建议用户使用带有反向 DNS 名称作为前缀的键名(例如“ai.onnxruntime.key1”)以避免不同用途之间的冲突。未限定名称将来可能被 ONNX 标准使用。
其他规范文档¶
ONNX 规范由本文档(定义 IR 的语义和标准数据类型)以及定义标准算子语义和 IR 语法的以下文档组成。后者以 Protobuf v2 和 v3 模式文件指定。
更多详细信息请参见 元数据类别文档。