开放神经网络交换中间表示 (ONNX IR) 规范¶
目的
本文档包含 ONNX 语义规范。
在 onnx 文件夹 下找到的 .proto
和 .proto3
文件构成了其语法规范,该规范是用 协议缓冲区 定义语言编写的。在 .proto
和 .proto3
文件中找到的注释旨在提高这些文件的可读性,但如果它们与本文档冲突,则不具有规范性。应将此类冲突报告为文档错误。
有关模型验证的说明
一个 工具 可用于根据此规范执行模型的通用验证。它是在 C++ 中实现的,并带有 Python 命令行包装器。
有关此文档和所有相关文档中语言的说明:
本文档中使用 SHOULD、MUST、MAY 等与 RFC 2119 一致。
“列表”的使用应表示项目的排序集合,“集合”应表示唯一的元素的无序集合,而“包”应表示可能不唯一的元素的无序集合。
组件¶
ONNX 是一种开放规范,包含以下组件
可扩展计算图模型的定义。
标准数据类型的定义。
内置算子的定义。
#1 和 #2 共同构成了 ONNX 中间表示或“IR”规范,本文档对此进行了介绍;内置算子在文档末尾列出的文档中进行介绍。具体来说,内置算子被分为一组基本算子和函数。函数是一个算子,其语义是通过使用其他算子(和函数)将其扩展到子图(称为函数体)中正式表达的。在功能方面,与 ONNX 兼容的框架或运行时可以内联函数体以执行它,如果它没有相应的函数实现。
存在两个官方 ONNX 变体;这两个变体之间的主要区别在于默认算子集。ONNX-ML 使用基于神经网络的 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 |
生成工具的版本。 |
域 |
string |
一个反向 DNS 名称,用于指示模型命名空间或域,例如“org.onnx” |
model_version |
int64 |
模型本身的版本,以整数编码。 |
doc_string |
string |
此模型的人类可读文档。允许使用 Markdown。 |
图 |
图 |
评估以执行模型的参数化图。 |
metadata_props |
map<string,string> |
命名元数据值;键应不同。 |
training_info |
TrainingInfoProto[] |
包含训练信息的可选扩展。 |
functions |
FunctionProto[] |
模型本地的可选函数列表。 |
模型 MUST 指定一个域并使用基于相应组织身份的反向域名,与 命名 Java 包 相同的约定。
注意:探索 ONNX 文件
您可以使用协议缓冲区分发版中包含的 protoc
工具来检查 ONNX 文件的内容,您可以像这样执行操作
$ protoc --decode=onnx.ModelProto onnx.proto < yourfile.onnx
其中 onnx.proto 是此存储库的一部分。
或者,您可以使用 Netron 之类的工具来探索 ONNX 文件。
模型语义¶
推理模型的语义是一个无状态函数(除了可能用于随机数生成的 state)。因此,每当使用推理模型(没有随机生成器操作)对相同的输入执行推理时,它都应该产生相同的输出。
训练模型的语义是有状态对象,其状态由训练权重的当前值(以及学习算法所需的任何其他辅助状态,例如动量)组成。具体来说,它的语义通过三种方法来捕获:初始化方法(用于初始化或重置状态变量的值)、训练步骤方法(使用一批输入-输出对进行训练)和推理方法(使用学习权重的当前值执行推理)。前两种方法更新对象的 state,而第三种方法是无副作用的。
可选元数据¶
模型中的“metadata_props”字段可用于任何类型的可选元数据,工具或模型开发人员可以选择将其放置在此处。以下是模型定义的“标准”可选元数据属性。
姓名 |
类型 |
格式 |
描述 |
---|---|---|---|
model_author |
string |
逗号分隔的姓名列表。 |
模型作者的姓名,以及他们所属的机构。 |
model_license |
string |
名称或 URL。 |
模型可用的许可证的知名名称或 URL。 |
运算符集标识符¶
每个运算符集都由一个 (domain, version) 对唯一标识。
姓名 |
类型 |
描述 |
---|---|---|
域 |
string |
要标识的运算符集的域。 |
版本 |
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 |
此版本运算符集的构建元数据。 |
域 |
string |
运算符集的域。在所有集中必须是唯一的。 |
opset_version |
int64 |
运算符集的版本。 |
doc_string |
string |
此运算符集的人类可读文档。允许使用 Markdown。 |
运算符 |
运算符[] |
此运算符集中包含的运算符。 |
运算符集版本是一个简单的整数,当发布运算符集的新版本时,它会单调递增。
除默认运算符集以外的运算符集 MUST 指定一个域,并且 SHOULD 使用基于负责组织身份的反向域名,与用于 命名 Java 包 的约定相同。
运算符¶
图中使用的每个运算符 MUST 由模型导入的运算符集中显式声明。
运算符定义的属性为
姓名 |
类型 |
描述 |
---|---|---|
op_type |
string |
运算符的名称(区分大小写),如图形节点中所用。在运算符集的域中 MUST 是唯一的。 |
since_version |
int64 |
引入此运算符的运算符集的版本。 |
状态 |
运算符状态 |
“EXPERIMENTAL”或“STABLE”之一。 |
doc_string |
string |
此运算符的人类可读文档字符串。允许使用 Markdown。 |
版本值 MUST 与运算符首次发布时的运算符集版本相同。运算符集的后续版本 MUST NOT 更改已发布为 STABLE 的运算符的签名或语义。
“status”属性指示运算符的语法、语义或存在是否处于实验阶段或稳定阶段。一旦运算符被发布为 STABLE,其语法和语义 MUST NOT 在运算符集的后续版本中更改。
有两种截然不同的方法可以将信息传递给运算符 - 输入和属性。输入表示图输入或在图中其他地方计算的值,而属性用于图中为常量的值。这种区别对于某些实现而言可能非常重要,而对于其他实现而言则完全无关紧要。
函数¶
函数可以被认为是一个运算符,它与运算符的实现相结合,该运算符使用其他更基本的运算符,称为函数体。函数体包含一个拓扑排序的节点列表,这些节点形成一个图。因此,函数结合了运算符和图(如下所述)的方面。
模型中包含的每个函数(也称为模型本地函数)都充当对应运算符的默认或回退实现。但是,运行时可以选择使用运算符的替代实现(通常作为优化的内核)。因此,函数的唯一名称非常重要,因为它隐式地与语义规范相关联。
序列化函数(FunctionProto)具有以下属性
姓名 |
类型 |
描述 |
---|---|---|
名称 |
string |
函数的名称 |
域 |
string |
此函数所属的域 |
重载 |
string |
函数唯一 ID 的一部分(在 IR 版本 10 中添加) |
doc_string |
string |
此函数的人类可读文档。允许使用 Markdown。 |
属性 |
string[] |
函数的属性参数 |
attribute_proto |
属性[] |
(IR 版本 9+)具有默认值的函数的属性参数。函数属性应表示为字符串属性或属性,而不是两者都表示。 |
输入 |
string[] |
函数的输入参数 |
输出 |
string[] |
函数的输出参数。 |
节点 |
节点[] |
节点列表,形成一个部分排序的计算图。它必须按拓扑顺序排列。 |
opset_import |
OperatorSetId |
函数实现使用的运算符集标识符集合。 |
value_info |
值信息[] |
(IR 版本 >= 10)用于存储函数中使用的值的类型和形状信息。 |
metadata_props |
map<string,string> |
(IR 版本 >= 10)命名元数据值;键应是不同的。 |
名称和域在 IR 版本 9 之前用于唯一标识运算符。IR 版本 10 添加了 overload 字段,三元组 (name, domain, overload) 在存储在模型中的函数之间充当唯一 ID。这是为了支持在模型中需要为函数的不同调用提供不同的函数体的情况。运算符集版本在 FunctionProto 中没有明确标识,而是由模型中包含的域的运算符集版本隐式确定。
输入、输出、属性和 attribute_proto(在 IR 版本 9 中添加)构成了运算符的签名部分。签名中没有明确包含类型信息。attribute_proto 字段描述了函数的属性参数及其默认值(当没有在调用站点节点中指定时),而 attribute 字段列出了没有默认值的属性参数。这两个列表中的名称必须不同。当函数的属性参数在函数内的节点中使用时,它会被调用站点节点(函数)中为属性指定的实际参数值替换(当指定了这样的属性时),如果属性有指定默认值,它会被默认值替换,否则会被省略。
opset_import 和 node 字段描述了函数的实现。
value_info 字段(在 IR 版本 10 中添加)允许模型存储有关函数中使用的值的类型和形状信息,包括其输入和输出。请注意,这是可选的,ONNX 允许函数是多态的。
图形¶
图用于描述无副作用计算(函数)。序列化图包含一组元数据字段、模型参数列表和计算节点列表。
每个计算数据流图都结构化为一个拓扑排序的节点列表,这些节点形成一个图,该图 MUST 不包含循环。每个节点都表示对运算符或模型本地函数的调用。每个节点都具有零个或多个输入和一个或多个输出。
图形具有以下属性
姓名 |
类型 |
描述 |
---|---|---|
名称 |
string |
模型图的名称。 |
节点 |
节点[] |
节点列表,形成一个基于输入/输出数据依赖关系的部分排序计算图。它是按拓扑顺序排列的。 |
初始化程序 |
张量[] |
命名张量值列表。当初始化程序的名称与图输入的名称相同时,它指定该输入的默认值。当初始化程序的名称与所有图输入不同时,它指定一个常量值。列表的顺序未指定。 |
doc_string |
string |
此模型的人类可读文档。允许使用 Markdown。 |
输入 |
值信息[] |
图的输入参数,可能由“initializer”中找到的默认值初始化。 |
输出 |
值信息[] |
图的输出参数。一旦所有输出参数都被图形执行写入,执行就完成了。 |
value_info |
值信息[] |
用于存储不是输入或输出的值的类型和形状信息。 |
metadata_props |
map<string,string> |
(IR 版本 >= 10)命名元数据值;键应是不同的。 |
值信息具有以下属性
姓名 |
类型 |
描述 |
---|---|---|
名称 |
string |
值/参数的名称。 |
类型 |
类型 |
值的类型,**包括形状信息**。 |
doc_string |
string |
此值的人类可读文档。允许使用 Markdown。 |
每个主(顶层)图 MUST 定义其输入和输出的名称、类型和形状,这些信息作为“值信息”结构指定。主图输入和输出需要具有形状,指示等级,即使不需要指定确切的维度。
嵌套子图(指定为属性值)**必须**定义其输入和输出的名称,**可以**定义其输入和输出的类型。
每个图**必须**指定一个名称。
该图**必须**遵守所有节点输出的单静态赋值 (SSA);这意味着所有节点输出名称**必须**在图中是唯一的。
图**应该**填充文档字符串,**可以**使用 GitHub 风格的 Markdown 语法进行解释。HTML 和其他文本标记语言**不能**在文档字符串中使用。
图内的名称¶
所有名称**必须**遵守 C90 标识符语法规则。
节点、输入、输出、初始化器和属性的名称被组织到几个命名空间中。在一个命名空间内,每个名称**必须**对于每个给定的图都是唯一的。请参见下文,了解当图包含嵌套子图(作为属性值)时进一步说明的情况。
命名空间是
命名空间 |
描述 |
---|---|
属性 |
运算符属性的名称。对于每个运算符都是唯一的。 |
值 |
值的名称 - 节点输入和输出、张量值(如果已命名)、图输入、输出。 |
节点 |
图节点的名称。 |
图 |
域内图的名称,在模型域内是唯一的。 |
运算符 |
域内运算符的名称。 |
Shape |
张量形状变量的名称 - 作用域到图的值信息记录,形状变量在该记录中出现。 |
节点¶
计算节点由名称、它调用的运算符的名称、命名输入列表、命名输出列表和属性列表组成。
输入和输出按位置与运算符输入和输出相关联。属性通过名称与运算符属性相关联。
它们具有以下属性
姓名 |
类型 |
描述 |
---|---|---|
名称 |
string |
节点的可选名称,仅用于诊断目的。 |
输入 |
string[] |
节点用于将输入值传播到节点运算符的值的名称。它必须引用图输入、图初始化器或节点输出。 |
输出 |
string[] |
节点用于从节点调用的运算符捕获数据的输出的名称。它要么在图中引入一个值,要么引用图输出。 |
op_type |
string |
要调用的运算符的符号标识符。 |
域 |
string |
包含由 op_type 命名运算符的运算符集的域。 |
属性 |
属性[] |
命名属性,另一种运算符参数化形式,用于常量值而不是传播值。 |
doc_string |
string |
此值的人类可读文档。允许使用 Markdown。 |
重载 |
string |
函数唯一 ID 的一部分(在 IR 版本 10 中添加) |
metadata_props |
map<string,string> |
(IR 版本 >= 10)命名元数据值;键应是不同的。 |
属于 Value 命名空间的名称可能出现在多个地方,即作为图输入、图初始化器、图输出、节点输入或节点输出。名称作为图输入、图初始化器或作为节点输出的出现被称为定义,名称作为节点输入或作为图输出的出现被称为使用。
图中使用的值名称必须具有唯一的定义,但同一个名称**可以**同时出现在图输入列表和图初始化器列表中。(在嵌套子图存在的情况下,进一步的例外情况适用,如后文所述。)
当一个名称同时出现在初始化器列表和图输入列表中时,运行时**可以**允许调用者为这个(输入)名称指定一个值,覆盖初始化器中指定的值,运行时**可以**允许用户省略为这个(输入)名称指定一个值,选择初始化器中指定的值。不希望被调用者覆盖的常量名称应该只出现在初始化器列表中,而不是出现在图输入列表中。在 IR 版本 >= 4 的模型中,在用作属性值的嵌套子图中,用户**不能**使用同一个名称作为子图初始化器和子图输入,除非对应的操作规范明确允许这样做。在 IR 版本 <= 3 的模型中,用户**可以**使用同一个名称作为子图初始化器和子图输入,但这仅限于通过不打算对应于从节点传递到子图的任何实际输入的初始化器支持常量。特别是,控制流运算符语义决定了提供给子图执行的输入集,这些输入名称**不能**作为子图初始化器出现。子图初始化器名称必须出现在图输入列表中,位于实际输入之后。这允许实际输入和形式输入按位置匹配。
计算图中的边是通过一个节点的输出在后续节点的输入中按名称被引用而建立的。
给定节点的输出将新名称引入图。节点输出的值由节点的运算符计算。节点输入**可以**引用节点输出、图输入和图初始化器。当节点输出的名称与图输出的名称一致时,图输出的值是由该节点计算的相应输出值。嵌套子图中的节点输入**可以**引用在外部图中引入的名称(作为节点输出、图输入或图初始化器)。
该图**必须**对所有节点输出使用单静态赋值,这意味着所有节点输出名称**必须**在图中是唯一的。在嵌套子图的情况下,节点输出名称**必须**与在嵌套子图中可见的外部作用域中的名称不同。
节点依赖项**不能**在计算图中创建循环。
节点中输入和输出的数量、它们的类型、节点中指定的属性集及其类型**必须**满足节点运算符签名所施加的约束。
定义顶层计算图的节点列表**必须**按拓扑顺序排列;也就是说,如果节点 K 位于图中节点 N 之后,则 N 的任何数据输入都不能引用 K 的输出。
节点属性用于将字面量(静态)值传递给运算符。
输入和输出值¶
该表示区分两种类型的值:属性值(静态已知)和输入/输出值。两种情况下允许的值类型不同。
输入和输出值作为图输入、输出和初始化器,以及作为节点输入和输出而被找到。它们的值在运行时确定,要么由启动模型执行的代码确定,要么由计算输出值的运算符确定。
属性¶
属性值仅在节点中找到,通过名称关联传递给运算符。属性值是运行时常量,因为它们的值在模型图构建时确定,因此在运行时不计算。属性的常见用法是表示在模型训练过程中建立的系数。
属性具有以下属性
姓名 |
类型 |
描述 |
---|---|---|
名称 |
string |
属性的名称。在任何给定的运算符和节点的属性、输入和输出中必须是唯一的。 |
doc_string |
string |
此值的人类可读文档。允许使用 Markdown。 |
类型 |
AttributeType |
属性的类型,确定使用哪些剩余字段来保存属性的值。 |
f |
float |
一个 32 位浮点值。 |
i |
int64 |
一个 64 位整数值。 |
s |
byte[] |
UTF-8 字符串。 |
t |
Tensor |
一个张量值。 |
g |
图 |
一个图。 |
floats |
float[] |
一个 32 位浮点值列表。 |
ints |
int64[] |
一个 64 位整数值列表。 |
strings |
byte[][] |
一个 UTF-8 字符串列表。 |
tensors |
张量[] |
一个张量值列表。 |
graphs |
Graph[] |
一个图列表。 |
ref_attr_name |
string |
父函数属性的名称。 |
属性 'name' 和 'type' 对所有属性都是必需的,'doc_string' **应该**对所有属性使用。一个属性**必须**只有一个值承载属性。
如果设置了 'ref_attr_name',则该属性不包含数据,而是对给定名称的父函数属性的引用。只能在函数体内使用。
可变输入和输出¶
运算符的最后一个输入或输出**可以**标记为可变的。例如,运算符 'Max()' 可用于计算可变数量的输入值的最大值。可变运算符具有最小元数,它指定必须指定的最小操作数数量。
对于每个可变运算符输入,必须指定 N 个或更多节点输入,其中 N 是运算符的最小元数。对于每个可变运算符输出,必须指定 N 个或更多节点输出,其中 N 是运算符的最小元数。
可选输入和输出¶
静态可选¶
一些运算符的输入被标记为可选的,这意味着引用的节点**可以**省略为这些输入提供值。
一些运算符的输出是可选的。当运算符的实际输出参数未指定时,运算符实现**可以**省略为这些输出计算值。
有两种方法可以省略可选输入或输出:第一种,仅适用于尾部输入和输出,是简单地不提供该输入或输出;第二种方法是在输入或输出名称的位置使用空字符串。
每个引用具有可选输出的运算符的节点**必须**为计算的每个输出提供一个名称,并且**不能**为未计算的输出提供名称。
上述类型的可选输入和输出被称为 *静态可选*。
动态可选(自 IR-8 起)¶
**IR-8 版本**引入了新的类型构造函数来表示 *动态可选* 输入和输出,除了上面描述的早期静态可选版本。动态可选 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, S) 和 (V’, S’) 相等当且仅当 V = V’ 且 S = 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 Formats for Deep Learning、8-bit Numerical Formats for Deep Neural Networks 和 Open Compute Project 中定义的值 |
有符号整数类型 |
int4, int8, int16, int32, int64 |
支持 4-64 位宽的有符号整数。 |
无符号整数类型 |
uint4, uint8, uint16, uint32, uint64 |
支持 4-64 位宽的无符号整数。 |
复数类型 |
complex64, complex128 |
具有 32 位或 64 位实部和虚部的复数。 |
其他 |
string |
字符串表示文本数据。所有字符串都使用 UTF-8 编码。 |
其他 |
bool |
布尔值表示只有两个值的數據,通常是真和假。 |
输入/输出数据类型¶
以下类型用于定义图和节点输入和输出的类型。
变体 |
类型 |
描述 |
---|---|---|
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;
}
维度大小的空列表 [] 是一个有效的张量形状,表示零维(标量)值。零维张量不同于未知维度的张量,后者用张量消息中不存在的“shape”属性表示。当值类型(包括节点输入)的 shape 属性不存在时,表示相应的运行时值可以具有任何形状。本节介绍如何解释缺少形状或形状中缺少维度等情况。但是,特定的使用环境可能会对类型和形状施加更多限制。例如,模型(顶层图)的输入和输出需要具有形状,表示输入和输出的秩,即使不需要指定确切的维度。
列表中的每个大小都可以表示为一个整数值或一个“维度变量”,即表示该维度的实际大小未静态约束为特定数字的字符串。这对于声明关心维度数但不在乎每个维度的确切大小的接口很有用。维度可以没有 dim_value 或 dim_param 设置。这样的维度表示与其他未知维度无关的未知维度。
例如,一个 NxM 矩阵将具有形状列表 [N,M]。
每个维度变量的名称必须符合C90 标识符语法规则。
目前,维度变量没有作用域。维度变量“N”在模型中的整个图中表示相同的值。例如,如果图有两个形状为 [“N”] 的输入 X 和 Y,那么在运行时传递给 X 和 Y 的值必须是秩为 1 且维度相同的张量。嵌套子图当前与主图共享相同的维度变量作用域。这允许模型将子图中的张量维度与外部图中的张量维度关联起来。
ONNX 支持诸如张量序列之类的类型。维度变量的全局作用域意味着类型为“Sequence<Tensor<float, [M,N]>”的变量表示一个张量序列,所有张量都具有相同的形状。如果该维度在序列中所有张量中没有固定大小,则必须从上述类型中省略维度变量 M 或 N。如果序列中不同的张量可能具有不同的秩,则必须从类型中省略整个形状。
例如,执行矩阵交叉乘积的图可以定义为接受两个形状为 [K,M] 和 [M,N] 的输入,并产生一个形状为 [K,N] 的输出。
形状可以使用整数和变量的组合来定义。
历史说明:以下扩展在早期被考虑过,但从未实现或支持。
使用空字符串(作为维度变量)来表示与任何其他维度无关的未知维度。这被放弃了,转而使用既没有 dim_value 也没有 dim_param 设置的维度。
使用字符串“*”(作为维度变量)来表示零个或多个未知基数的维度序列。这不受支持。在当前实现中,形状中的维度数必须表示张量的秩。未知秩的张量表示为具有无形状的 TypeProto::Tensor 对象,这是合法的。
允许在子图(例如循环体)中使用本地维度变量的作用域机制可能很有用,但目前不支持。
ONNX 支持诸如张量序列之类的类型。维度变量的类型本地作用域机制可能有助于区分以下两种类型:不同大小的方形矩阵序列与相同大小的方形矩阵序列。目前不支持。
属性类型¶
用于属性的类型系统与用于输入和输出的类型系统相关,但略有不同。属性值可以是密集张量、稀疏张量、标量数值、字符串、图或上述类型之一的重复值。
其他元数据¶
ModelProto 结构,以及在 IR 版本 >= 10 中的各种其他结构(GraphProto、FunctionProto、NodeProto)包含一个 metadata_props 字段,允许用户以键值对的形式存储其他元数据。建议用户使用以反向 DNS 名称作为前缀(例如“ai.onnxruntime.key1”)限定的键名,以避免不同用途之间的冲突。将来,ONNX 标准可能会使用非限定名称。
其他规范文档¶
ONNX 规范由本文件、定义 IR 语义和标准数据类型的文件以及定义标准运算符语义和 IR 语法的以下文件组成。后者指定为 Protobuf v2 和 v3 架构文件。
有关更多详细信息,请参阅元数据类别文档。