Open Neural Network Exchange 中间表示 (ONNX IR) 规范

目的

本文档包含 ONNX 语义的规范性说明。

位于 onnx 文件夹 下的 .proto.proto3 文件构成了其使用 Protocol Buffers 定义语言编写的语法规范。 .proto.proto3 文件中的注释旨在提高这些文件的可读性,但如果与本文档冲突,则不具有规范性。此类冲突应作为文档错误进行报告。

关于模型验证的说明

提供了一个 工具 来根据本规范对模型进行通用验证。该工具用 C++ 实现,并带有 Python 命令行包装器。

关于本文档及所有相关文档中语言的说明:

  1. 本文档中 SHOULD、MUST、MAY 等词语的使用与 RFC 2119 一致。

  2. “list” 指一个有序的元素集合,“set” 指一个无序的唯一元素集合,“bag” 指一个无序的可能包含非唯一元素的集合。

组件

ONNX 是一个开放的规范,包含以下组件:

  1. 可扩展计算图模型的定义。

  2. 标准数据类型的定义。

  3. 内置运算符的定义。

第 1 项和第 2 项共同构成了 ONNX 中间表示(或称“IR”)规范,本文档涵盖了这一点;内置运算符在末尾列出的文档中进行介绍。具体来说,内置运算符分为一组基本运算符和函数。函数是一种运算符,其语义通过使用其他运算符(和函数)的子图(称为函数体)进行扩展来形式化表达。从功能上看,一个 ONNX 兼容框架或运行时可以内联函数体来执行它,如果它没有相应的功能实现的话。

ONNX 有两个官方变体;两者之间的主要区别在于默认的运算符集。ONNX-MLONNX 运算符集的基础上增加了非神经网络的 ML 算法。

直到 IR 版本 6,ONNX 规范和模型格式仅支持推理(也称为评分)。从 IR 版本 7 开始,ONNX 规范和模型格式也支持训练。ONNX 训练模型是推理模型的扩展。仅推理的运行时可以忽略训练相关扩展来使用训练模型。然而,仅推理的模型可能比训练模型启用更优化的表示形式。

运行时无关

ONNX 不预设或暗示任何特定的运行时实现方法。

例如,一个实现可能包含一个解释模型的丰富运行时;它可能是一个将整个模型翻译成某种目标编程语言的可执行代码的代码生成器;它可能是一个硬件实现;它也可能是以上几种的组合。

本规范中的任何内容都不应被解释为提倡一种实现方法优于另一种;任何关于具体实现内部工作原理的评论都应被视为示例。

ONNX 版本控制

IR 规范、单个模型和运算符集都有版本。此外,每个单独的运算符都指示了其所属运算符集的哪个版本引入或稳定。

版本号可以简单地用数字表示,或者用来编码 语义版本(也称为 SemVer)。如果使用语义版本,惯例是将最高两位字节用于主版本号,接下来的两位字节用于次版本号,最低的四字节用于补丁/构建/错误修复号。使用语义版本时,主版本号/次版本号至少有一个必须是非零的。

IR 规范使用简单的单调递增数字作为其版本。有效的 IR 版本由 onnx.Version 枚举在 onnx.proto 中定义。

运算符集使用简单的版本号。每个运算符集版本代表了运算符集合及其在特定时间点的语义的快照。

本规范不提供关于模型生产者应使用何种版本控制方案的指导。

有关 IR、运算符集和模型版本控制的约定和最佳实践的更多详细信息,请参阅 Versioning

可扩展计算图模型

ONNX 指定了计算图的可移植、序列化格式。它不一定是框架在内部选择使用的形式。例如,如果内存中的模型表示形式在优化过程中更易于操作,实现可能会以不同的方式表示模型。

实现可以扩展 ONNX,通过添加运算符来表达标准运算符集之外的语义,而所有实现都必须支持这些标准运算符集。实现此目的的机制是向模型中的 opset_import 属性添加运算符集,该属性依赖于扩展运算符。

模型

ONNX 的顶级构造是“Model”,在 Protocol Buffers 中表示为 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[]

模型本地函数的可选列表。

configuration

DeviceConfigurationProto[]

(IR 版本 >= 11) 用于分布式执行的多设备配置的可选列表。

模型必须指定一个域,并使用基于负责组织身份的反向域名称,这与 命名 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”相同。

运算符集版本是一个简单的整数值,随着运算符集新版本的发布而单调递增。

非默认运算符集必须指定一个域,并应使用基于负责组织身份的反向域名称,这与 命名 Java 包 的约定相同。

运算符集

每个模型必须明确命名其功能所依赖的运算符集。运算符集定义了可用的运算符及其版本。每个模型通过其域定义导入的运算符集。所有模型都隐式导入默认的 ONNX 运算符集。

每个运算符集应在一个单独的文档中定义,也使用 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[]

此运算符集中包含的运算符。

运算符集版本是一个简单的整数值,随着运算符集新版本的发布而单调递增。

非默认运算符集必须指定一个域,并应使用基于负责组织身份的反向域名称,这与 命名 Java 包 的约定相同。

运算符

图中使用的每个运算符都必须由模型导入的运算符集之一明确声明。

运算符定义的属性是:

名称

类型

描述

op_type

string

运算符的名称(区分大小写),在图节点中使用。必须在运算符集的域内唯一。

since_version

int64

此运算符引入的运算符集的版本。

status

OperatorStatus

“EXPERIMENTAL”或“STABLE”之一。

doc_string

string

此运算符的人类可读文档字符串。允许使用 Markdown。

版本值必须是运算符首次发布时运算符集版本的值。运算符集的后续版本不得更改已发布为 STABLE 的运算符的签名或语义。

“status”属性指示运算符的语法、语义或存在是处于实验阶段还是稳定阶段。一旦运算符被发布为 STABLE,其语法和语义在运算符集的后续版本中不得更改。

有两种不同的方式向运算符传递信息——输入和属性。输入代表图输入或在图中其他地方计算的值,而属性用于表示图中常量的值。这种区别对于实现某些高性能可能非常重要,而对其他实现则完全无关紧要。

函数

函数可以被认为是运算符与使用其他更原始的运算符实现的组合,这些运算符称为函数体。函数体由构成图的有向无环图(DAG)的节点列表组成。因此,函数结合了运算符和图(如下所述)的方面。

模型中包含的每个函数(也称为模型本地函数)作为相应运算符的默认或备用实现。然而,运行时可以选择使用运算符的替代实现(通常是优化内核)。因此,函数的唯一名称很重要,因为它与语义规范隐式关联。

序列化的函数(FunctionProto)具有以下属性:

名称

类型

描述

name

string

函数的名称。

domain

string

此函数所属的域。

overload

string

(IR 版本 10 中添加) 函数唯一 ID 的一部分。

doc_string

string

此函数的文档字符串,人类可读。允许使用 Markdown。

attribute

string[]

函数的属性参数。

attribute_proto

Attribute[]

(IR 版本 9+) 带有默认值的函数属性参数。函数属性应表示为字符串属性或 Attribute,而非两者兼有。

输入

string[]

函数的输入参数。

输出

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 中明确标识,但它由模型中包含的域的运算符集版本隐式确定。

输入、输出、属性和 attribute_proto(在 IR 版本 9 中添加)构成运算符的签名部分。签名中不包含显式的类型信息。attribute_proto 字段描述了函数的属性参数及其默认值(当未由调用站点节点指定时),而 attribute 字段列出了没有默认值的属性参数。这两个列表中的名称必须不重复。当函数的一个属性参数在函数内的节点中使用时,如果属性在调用站点节点上指定了实际参数值,则用该值替换;如果属性有默认值,则用默认值替换;否则,则省略。

opset_import 和 node 字段描述了函数的实现。

value_info 字段(在 IR 版本 10 中添加)允许模型存储函数(包括其输入和输出)中使用值的数据类型和形状信息。请注意,这是可选的,ONNX 允许函数是多态的。

图用于描述无副作用的计算(函数)。序列化图由一组元数据字段、一个模型参数列表和一个计算节点列表组成。

每个计算数据流图的结构是一个拓扑排序的节点列表,形成一个图,该图必须没有循环。每个节点代表对运算符或模型本地函数的调用。每个节点有零个或多个输入和一个或多个输出。

图具有以下属性:

名称

类型

描述

name

string

模型图的名称。

node

Node[]

节点列表,构成一个基于输入/输出数据依赖的部分排序计算图。按拓扑顺序排列。

initializer

Tensor[]

命名的张量值列表。当一个 initializer 的名称与图输入相同,它指定该输入的默认值。当一个 initializer 的名称与所有图输入名称都不同时,它指定一个常量值。列表的顺序不确定。

doc_string

string

此模型的文档字符串,人类可读。允许使用 Markdown。

输入

ValueInfo[]

图的输入参数,可能由“initializer”中的默认值初始化。

输出

ValueInfo[]

图的输出参数。一旦所有输出参数都被图执行写入,执行即完成。

value_info

ValueInfo[]

用于存储不是输入或输出的值的类型和形状信息。

metadata_props

map<string,string>

(IR 版本 >= 10) 命名的元数据值;键应不重复。

ValueInfo 具有以下属性:

名称

类型

描述

name

string

值/参数的名称。

type

类型

包括形状信息的类型。

doc_string

string

此值的文档字符串,人类可读。允许使用 Markdown。

每个主(顶层)图必须定义其输入和输出的名称、类型和形状,这些是通过“value info”结构指定的。主图的输入和输出被要求具有形状,表示输入的秩,尽管不必指定精确的维度。

嵌套子图(在属性值中指定)必须定义其输入和输出的名称,并且可以定义其输入和输出的类型。

每个图必须指定一个名称。

图必须对所有节点输出遵循单赋值(SSA);这意味着所有节点输出名称在图中必须是唯一的。

图应填充文档字符串,这些字符串可以被解释为使用 GitHub 风格的 Markdown 语法。不允许在文档字符串中使用 HTML 和其他文本标记语言。

图中的名称

所有名称应遵循 C90 标识符语法规则

节点、输入、输出、initializer 和属性的名称组织在几个命名空间中。在一个命名空间内,给定图的每个名称必须是唯一的。请参见下文,以在图包含嵌套子图(作为属性值)的情况下进行进一步澄清。

命名空间是:

Namespace

描述

Attribute

运算符属性的名称。对每个运算符都唯一。

Value

值的名称——节点输入和输出、张量值(如果命名)、图输入、输出。

Node

图节点名称。

Graph

域内图的名称,在模型域内唯一。

Operator

域内运算符的名称。

Shape

张量形状变量的名称——作用域在图的值信息记录中,这是形状变量出现的地方。

节点

计算节点包含一个名称、一个它调用的运算符的名称、一个命名输入列表、一个命名输出列表以及一个属性列表。

输入和输出在位置上与运算符的输入和输出相关联。属性通过名称与运算符的属性相关联。

它们具有以下属性:

名称

类型

描述

name

string

节点的可选名称,仅用于诊断目的。

输入

string[]

节点使用的值名称,用于将输入值传播到节点运算符。它必须引用图输入、图 initializer 或节点输出。

输出

string[]

节点使用的输出名称,用于从节点调用的运算符捕获数据。它在图中引入一个值,或引用一个图输出。

op_type

string

要调用的运算符的符号标识符。

domain

string

包含由 op_type 命名的运算符的运算符集的域。

attribute

Attribute[]

命名的属性,另一种形式的运算符参数化,用于常量值而非传播值。

doc_string

string

此值的文档字符串,人类可读。允许使用 Markdown。

overload

string

(IR 版本 10 中添加) 函数唯一 ID 的一部分。

metadata_props

map<string,string>

(IR 版本 >= 10) 命名的元数据值;键应不重复。

device_configurations

NodeDeviceConfigurationProto[]

(IR 版本 >= 11) 此节点的多个设备执行配置。

值命名空间中的名称可以在多个地方出现,即作为图输入、图 initializer、图输出、节点输入或节点输出。名称作为图输入、图 initializer 或节点输出出现时,称为定义;名称作为节点输入或图输出出现时,称为使用。

图中使用的值名称必须有一个唯一的定义,除了同一个名称可能同时出现在图输入列表和图 initializer 列表中。(在存在嵌套子图的情况下,适用其他例外情况,如下文所述。)

当一个名称同时出现在 initializer 列表和图输入列表时,运行时可以允许调用者指定一个值来覆盖此(输入)名称指定的值,并且运行时可以允许用户省略此(输入)名称的值,而选择 initializer 指定的值。不打算被调用者覆盖的常量的名称应仅出现在 initializer 列表中,而不是出现在图输入列表中。在 IR 版本 >= 4 的模型中,在用作属性值的嵌套子图中,用户不得将同一名称同时用作子图 initializer 和子图输入,除非相应的 op 的规范明确允许。在 IR 版本 <= 3 的模型中,用户可以使用同一名称同时作为子图 initializer 和子图输入,但这仅限于支持通过 initializer 传递常量,而这些常量不打算对应于从节点传递到子图的任何实际输入。特别是,控制流运算符语义决定了提供给子图执行的输入的集合,并且这些输入名称不得出现在子图 initializer 中。子图 initializer 名称必须出现在图输入列表之后。这允许实际输入和形式输入按位置匹配。

计算图中的边是通过一个节点的输出被后续节点的输入按名称引用而建立的。

给定节点的输出将新名称引入图中。节点输出的值由节点的运算符计算。节点输入可以引用节点输出、图输入和图 initializer。当节点输出的名称与图输出的名称相同时,图输出的值就是该节点计算的相应输出值。嵌套子图中的节点输入可以引用在外部图(作为节点输出、图输入或图 initializer)中引入的名称。

图必须对所有节点输出使用单赋值,这意味着所有节点输出名称在图中必须是唯一的。对于嵌套子图,节点输出名称以及子图的输入和 initializer 名称必须与在嵌套子图中可见的外部作用域的名称不同。也就是说,不允许变量遮蔽。

节点依赖不得在计算图中创建循环。

节点中的输入和输出数量、它们的类型、节点中指定的属性集及其类型必须满足节点运算符签名施加的约束。

定义顶层计算图的节点列表必须按拓扑顺序排列;也就是说,如果节点 K 在图中跟随节点 N,则 N 的任何数据输入都不能引用 K 的输出。

节点属性用于将字面量(静态)值传递给运算符。

输入和输出值

表示区分两种值:属性值(静态已知)和输入/输出值。两种情况下允许的值类型不同。

输入和输出值作为图输入、输出和 initializer,以及节点输入和输出。它们的值在运行时确定,由启动模型执行的代码确定,或由计算输出值的运算符确定。

属性

属性值仅在节点中找到,通过名称关联传递给运算符。属性值是运行时常量,因为它们的值在模型图构建时确定,因此不在运行时计算。属性的常见用途是表示模型训练期间建立的系数。

属性具有以下属性:

名称

类型

描述

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”应应用于所有属性。属性必须只有一个值携带字段。

如果设置了“ref_attr_name”,则此属性不包含数据,而是对给定名称的父函数属性的引用。只能在函数体内部使用。

可变输入和输出

运算符的最后一个输入或输出可以标记为可变。例如,“Max()”运算符可用于计算可变数量输入值的最大值。可变运算符具有最小元数,指定了必须提供的最少操作数。

对于每个可变运算符输入,必须提供 N 个或更多节点输入,其中 N 是运算符的最小元数。对于每个可变运算符输出,必须提供 N 个或更多节点输出,其中 N 是运算符的最小元数。

可选输入和输出

静态可选

一些运算符有标记为可选的输入,这意味着引用节点可以省略为这些输入提供值。

一些运算符有可选的输出。当运算符的实际输出参数未指定时,运算符实现可以省略为这些输出计算值。

有两种方式可以使可选输入或输出未指定:第一种方法(仅适用于尾部输入和输出)是根本不提供该输入或输出;第二种方法是用空字符串代替输入或输出名称。

每个引用具有可选输出的运算符的节点必须为计算出的每个输出提供一个名称,并且不得为未计算出的输出提供名称。

上述类型的可选输入和输出称为静态可选

动态可选(自 IR-8 起)

IR-8 版本引入了一种新的类型构造器来表示动态可选输入和输出,这与上面描述的早期静态可选版本相对应。动态可选的 INT64 张量是一种与 INT64 张量类型不同的类型。相比之下,静态可选的 INT64 张量没有不同的类型,它与 INT64 张量具有相同的类型。运算符 OptionalOptionalGetElement 必须显式用于在动态可选类型和底层非可选类型之间进行转换。动态可选比静态可选具有更强的表达能力。

外部张量数据

大型常量张量(如 initializer)的原始数据可以序列化到单独的文件中。在这种情况下,张量必须提供相对于模型文件的文件名,并且不得使用 value 字段。它可以提供该文件中的字节偏移量和长度。它也可以指定文件的 SHA1 摘要。一个文件可以包含多个张量的数据。

有关详细信息,请参阅 External Data

标准数据类型

ONNX 有两个官方变体;两者之间的主要区别在于支持的类型和运算符。

在支持的类型方面,ONNXONNX-ML 定义都将张量、稀疏张量、序列、映射和可选类型识别为输入和输出类型。序列和映射从 IR 版本 6(ONNX 1.6.0 发布)开始支持。可选类型从 IR 版本 8(ONNX 1.10.0 发布)开始支持。

ONNX 支持以下数据类型作为图和节点的输入输出以及图的 initializer:

原始数值、字符串和布尔类型必须用作张量元素。

张量定义

张量是向量和矩阵的推广;向量有一个维度,矩阵有两个维度,而张量可以有任意数量的维度,包括零。零维张量在逻辑上等同于标量值。

数学上,张量可以定义为一对序列/列表 (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, [R]),其中 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 Learning8-bit Numerical Formats for Deep Neural NetworksOpen Compute Project 中定义。

有符号整数类型

int4, int8, int16, int32, int64

支持 4-64 位宽的有符号整数。

无符号整数类型

uint4, uint8, uint16, uint32, uint64

支持 4-64 位宽的无符号整数。

复数类型

complex64, complex128

具有 32 位或 64 位实部和虚部的复数。

其他

string

字符串表示文本数据。所有字符串都使用 UTF-8 编码。

其他

bool

布尔值表示只有两个值的数据,通常是 true 和 false。

输入/输出数据类型

以下类型用于定义图和节点的输入输出类型。

Variant

类型

描述

ONNX

dense tensors

表示一个张量。参见上面的定义。

ONNX

sequence

序列是密集、有序的元素集合,其元素类型是同质的。

ONNX

map

映射是关联表,由键类型和值类型定义。

ONNX

optional

可选类型是包装器,可能包含张量、序列或映射类型的元素,或者为空(不包含任何内容)。Details

静态张量形状

除了元素类型,张量类型还有一个静态形状。张量变量的静态形状与张量值的运行时(动态)形状相关,但不同。静态张量形状是一个记录列表,指示张量是向量、矩阵还是高维值。例如,一个 100x100 的矩阵的形状是 [100,100]。

静态形状由“TensorShapeProto”定义。

message TensorShapeProto {
  message Dimension {
    oneof value {
      int64 dim_value = 1;
      string dim_param = 2;
    };
  };
  repeated Dimension dim = 1;
}

由 Tensor 类型消息引用。

  message Tensor {
    optional TensorProto.DataType elem_type = 1;
    optional TensorShapeProto shape = 2;
  }

空维度大小列表 [] 是一个有效的张量形状,表示零维(标量)值。零维张量与未知维度的张量不同,后者由 Tensor 消息中缺失的“shape”属性表示。当值(包括节点输入)的类型中缺失 shape 属性时,表示相应的运行时值可以具有任何形状。本小节描述了如何解释缺失的形状或带有缺失维度的形状等。但是,特定的使用上下文可能对类型和形状施加进一步的约束。例如,模型(顶层图)的输入和输出被要求具有形状,表示输入的秩,尽管不必指定确切的维度。

列表中的每个大小可以表示为整数值或“维度变量”,一个字符串表示该维度的实际大小在静态上不受限于特定数字。这对于声明关心维度数量但不关心每个维度确切大小的接口很有用。维度可以既没有 set dim_value 也没有 set 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 支持诸如张量序列之类的类型。一种仅限于类型范围的维度变量的作用域机制可能有助于区分以下两种类型:一个由(不同大小的)方阵组成的序列 vs. 一个由(相同大小的)方阵组成的序列。这目前不支持。

属性类型

用于属性的类型系统与用于输入和输出的类型系统相关,但略有不同。属性值可以是密集张量、稀疏张量、标量数值、字符串、图,或者上述类型之一的重复值。

其他元数据

ModelProto 结构,以及 IR 版本 >= 10 中,各种其他结构(GraphProto, FunctionProto, NodeProto)包含一个 metadata_props 字段,允许用户以键值对的形式存储其他元数据。建议用户使用以反向 DNS 名称作为前缀限定的键名(例如,“ai.onnxruntime.key1”)以避免不同用法之间的冲突。ONNX 标准将来可能会使用未限定的名称。

多设备配置(IR 版本 >= 11)

ONNX 通过设备配置规范支持多设备执行,这些规范支持分布式推理和训练。这包括对张量并行(在多个设备上分片张量)和流水线并行(将不同子图分发到不同设备)的支持。

设备配置

模型可以使用模型中包含的DeviceConfigurationProto指定一个或多个多设备配置。每个配置描述了可用于模型执行的特定设备安排。

设备配置的属性是:

名称

类型

描述

name

string

配置的名称。此字段对于当前 IR 版本是必需的。

num_devices

int32

此配置中的设备数量。此字段对于当前 IR 版本是必需的。

device

string[]

设备的可选名称。如果提供,长度必须等于 num_devices。

节点设备配置

单个节点可以通过NodeDeviceConfigurationProto指定特定于设备的执行信息。这允许对计算如何在设备之间进行细粒度分配进行控制。

节点设备配置的属性是:

名称

类型

描述

configuration_id

string

配置的 ID。必须匹配 DeviceConfigurationProto 的名称。此字段对于当前 IR 版本是必需的。

sharding_spec

ShardingSpecProto[]

节点输入和输出的分片规范。

pipeline_stage

int32

此节点的可选流水线阶段标识符。

分片规范

分片描述了张量如何在多个设备之间进行分区或复制。ShardingSpecProto 为节点的特定输入或输出张量定义了分片行为。

分片规范的属性是:

名称

类型

描述

tensor_name

string

标识要分片的输入或输出张量。必须匹配节点输入或输出列表中的名称。此字段对于当前 IR 版本是必需的。

device

int64[]

张量分片或复制的设备列表。

index_to_device_group_map

IntIntListEntryProto[]

当设备 ID 代表多个物理设备时,指示设备组的可选映射。

sharded_dim

ShardedDimProto[]

张量每个轴的分片规范。

分片维度

ShardedDimProto 描述了张量的一个轴如何跨设备分片。

分片维度的属性是:

名称

类型

描述

int64

要分片的张量轴。必须在 [-r, r-1] 范围内,其中 r 是张量秩。此字段对于当前 IR 版本是必需的。

simple_sharding

SimpleShardedDimProto[]

描述轴如何分成多个分片。

简单分片维度

SimpleShardedDimProto 指定 N 个块被分成 M 个分片,其中 N 可能是符号化的,但 M 必须是常量。

简单分片维度的属性是:

名称

类型

描述

dim_value

int64

要分片的维度值(dim_param 的替代选项)。

dim_param

string

要分片的符号维度参数(dim_value 的替代选项)。

num_shards

int64

将维度分割成的分片数量。此字段对于当前 IR 版本是必需的。

多设备执行语义

多设备注释是对执行后端的提示,不影响模型的计算语义。后端可以忽略这些注释,如果指定的配置不受支持或不可用。所有用于多设备执行的通信操作(例如设备间的数据传输)都是隐式的,由运行时处理。

对于张量并行,张量可以:

  • Split(拆分)到不同设备,将数据的不同部分分发到不同设备

  • Replicated(复制)到不同设备,其中相同的张量数据在多个设备上重复

流水线并行通过可选的流水线阶段标识符来指示,这些标识符建议如何将子图分发到不同设备以进行流水线执行。

有关多设备执行模式和示例的更详细信息,请参阅 Multi-Device Proposal

其他规范文档

ONNX 规范由本文档组成,本文档定义了 IR 和标准数据类型的语义,以及定义标准运算符语义和 IR 语法的以下文档。后者以 Protobuf v2 和 v3 模式文件形式指定。

有关更多详细信息,请参阅 metadata category documentation

运算符

神经网络运算符

经典机器学习运算符

语法

ONNX 模型和图 - protobuf v2

ONNX 模型和图 - protobuf v3

ONNX-ML 模型和图 - protobuf v2

ONNX-ML 模型和图 - protobuf v3

ONNX 运算符集 - protobuf v2

ONNX 运算符集 - protobuf v3

ONNX-ML 运算符集 - protobuf v2

ONNX-ML 运算符集 - protobuf v3

版本控制约定和最佳实践

版本控制