神经网络的交换格式(3)

3.形式化描述

本章提供了计算图结构方面的交换格式的正式描述。 这是一个简单的符号,其目标是描述从较低级别构建块构建计算图形片段,最终到达整个网络图形的描述。

Backus-Naur Form(BNF)中的语法用于描述符号的语法。首先,定义语法的词汇元素,并列举与有效计算图相关的约束条件。

3.1.词法元素

描述格式由下列词法元素构成:
<identifier>
一个identifier是ASCII字符序列,其中可能包含下划线.特别的,必须包含下列ASCII字符_, [a-z], [A-Z], [0-9].不能以数字开头.

<numeric-literal>
一个numeric-literal包含一个整数部分,一个可选的小数点(.)和小数部分,还有一个可选的有符号指数.这些部分都必须由十进制数字组成.

<logical-literal>
值为true或false.

<keyword>
下列字符序列在语法中有特殊意义,因此不能用于identifier:
graph, fragment, tensor, extent, scalar, logical, string, shape_of, length_of, range_of, for, in, if, else.

<operator>
下列字符在数学表达式中有特殊意义:+, -, *, /, ^, <, <=, >, >=, ==, !=, &&, ||, !.

Syntactic characters
下列字符有特殊的语法意义:
(, ), [, ], {, }, :, =, ,, ;, ->.

White spaces
White spaces可能插入任意词法元素之间,包括:
space字符,表示horizontal tab, vertical tab,格式补齐和新行的control字符.

Comments
用字符#注释某一行

3.2.语法

计算图中的核心概念是将输入张量映射为输出张量的操作。 需要声明操作,并根据声明的操作构建计算图描述。 因此,我们将描述分为两个关键部分:
- 建立图表的可能操作声明。 操作有一个名称和参数和结果的列表。 形式化的参数是键入的,确定什么样的表达式可以代替它们的位置。
- 实际的图形描述包含一系列针对声明进行验证的操作调用。 这些操作是通过引用它们的名字和提供参数来代替它们的形式参数来调用的。
此外,语法的描述被分成足够的构造,以用于构成扁平的描述和根据需要扩展的成分描述。
以下BNF符号以更正式的方式介绍了描述语法。 由:: = below定义的所有内容都是BNF描述的一部分,并构成有效的语法。 这些BNF规则定义的语法之外的任何东西都被认为是无效的语法。

3.2.1.操作声明
操作声明由fragment关键字引入,具有名称,参数列表和结果列表。 参数是明确键入的,可能有默认值。 结果在 - >符号之后引入。

<operation-declaration> ::= "fragment" <identifier> "(" <parameter-list> ")"
                            "->" "(" <result-list> ")"
<parameter-list> ::= <parameter> ("," <parameter>)*
<parameter> ::= <identifier> ":" <type-spec> ["=" <literal-expr>]
<result-list> ::= <result> ("," <result>)*
<result> ::= <identifier> ":" <type-spec>

类型规范可以表示一个基本类型,数组类型或元组类型。

<type-name> ::= "tensor" | "extent" | "scalar" | "logical" | "string"
<array-type-spec> ::= <type-spec> "[]"
<tuple-type-spec> ::= "(" <type-spec> ("," <type-spec>)+ ")"
<type-spec> ::= <type-name> | <array-type-spec> | <tuple-type-spec>

例如,以下几行展示了一些操作声明:

fragment foo( input: tensor, size: extent[] = [1] ) -> ( output: tensor )

fragment bar( input: tensor, alpha: scalar = 0.5 )
-> ( output: tensor, extra: tensor[] )

3.2.2.图定义
图形定义由一个图形声明及其正文组成。 图声明与操作声明的语法类似,但由graph关键字引入,参数和结果列表不能包含类型规范和默认值。

<graph-definition> ::= <graph-declaration> <body>
<graph-declaration> ::= "graph" <identifier> "(" <identifier-list> ")"
                        "->" "(" <identifier-list> ")"
<identifier-list> ::= <identifier> ("," <identifier>)*

图定义本身由一个赋值列表组成,其中赋值的左侧是一个标识符表达式(单个标识符,元组或数组),右侧是一个操作调用。

<body> ::= "{" <assignment>+ "}"
<assignment> ::= <lvalue-expr> "=" <invocation> [";"]

一个调用由一个标识符和一个参数列表组成:

<invocation> ::= <identifier> "(" <argument-list> ")"
<argument-list> ::= <argument> ("," <argument>)*
<argument> ::= <rvalue-expr> | <identifier> "=" <rvalue-expr>

表达式可以是文字,标识符,数组和元组。 有必要区分文字表达式,左值表达式和右值表达式。
在操作声明中允许使用文字表达式作为默认值:

<literal> ::= <numeric-literal> | <string-literal> | <logical-literal>
<array-literal-expr> ::= "[" [<literal-expr> ("," <literal-expr>)* ] "]"
<tuple-literal-expr> ::= ["("] <literal-expr> ("," <literal-expr>)+ [")"]
<literal-expr> ::= <literal> | <array-literal-expr> | <tuple-literal-expr>

在赋值的左边允许左值表达式:

<array-lvalue-expr> ::= "[" [<lvalue-expr> ("," <lvalue-expr>)* ] "]"
<tuple-lvalue-expr> ::= ["("] <lvalue-expr> ("," <lvalue-expr>)+ [")"]
<lvalue-expr> ::= <identifier> | <array-lvalue-expr> | <tuple-lvalue-expr>

在赋值的右侧允许右值表达式(作为参数值):

<array-rvalue-expr> ::= "[" [<rvalue-expr> ("," <rvalue-expr>)* ] "]"
<tuple-rvalue-expr> ::= ["("] <rvalue-expr> ("," <rvalue-expr>)+ [")"]
<rvalue-expr> ::= <identifier> | <literal> | <array-rvalue-expr> | <tuple-rvalue-expr>

用可能有多个结果(如果操作定义了多个结果)。在这种情况下,返回的表达式是一个元组,左手大小的表达式也必须是一个元组。
作为一个例子,使用上面的声明,我们可以将图的一部分定义为:

graph barfoo( input ) -> ( output )
{
    input = external(shape = [1,10])
    intermediate, extra = bar(input, alpha = 2)
    output = foo(intermediate, size = [3,5])
}

在上面的例子中,外部是一个操作,用于引入从外部源接收数据的张量(见张量引入操作)。

3.2.3.组成元素的描述
组合语法允许赋值列表定义一个新的操作,类似于整个图的定义方式,但是使用fragment关键字。

<operation-definition> ::= <operation-declaration> <body>

而且,更复杂的表达式可以用作调用中的参数(右值表达式)。这些表达式允许编译时算术和参数组合。
可以使用各种算术,比较和逻辑运算符来构建二进制表达式:

<comparison-operator> ::= "<" | "<=" | ">" | ">=" | "==" | "!="
<binary-arithmetic-operator> ::= "+" | "-" | "*" | "/" | "^"
<binary-logical-operator> ::= "&&" | "||"
<binary-operator> ::= <comparison-operator>
                    | <binary-arithmetic-operator>
                    | <binary-logical-operator>

一些一元运算符也可用于算术和逻辑表达式:

<unary-arithmetic-operator> ::= "+" | "-"
<unary-logical-operator> ::= "!"
<unary-operator> ::= <unary-arithmetic-operator>
                   | <unary-logical-operator>

运算符表达式可以使用一元和二元运算符和括号来构建:

<unary-expr> ::= <unary-operator> <rvalue-expr>
<binary-expr> ::= <rvalue-expr> <binary-operator> <rvalue-expr>
<paren-expr> ::= "(" <rvalue-expr> ")"

选择表达式实现分支和数组理解表达式实现循环。数组理解表达式通过迭代另一个数组来生成数组,可以过滤结果项目。

<select-expr> ::= <rvalue-expr> "if" <rvalue-expr> "else" <rvalue-expr>
<comprehension-expr> :: "[" <rvalue-expr> "for" <identifier>
                        "in" <rvalue-expr> ["if" <rvalue-expr>] "]"

当理解表达式包含if条件时,if被解释为理解表达式的一部分,而不是作为in关键字后的理解表达式内部的选择表达式的一部分。

下标表达式可以引用数组中的单个条目或一系列条目,在这种情况下,范围的开始(包含)和结束(排除)用下面的词隔开:。 开始和结束都是可选的,在这种情况下,分别取0或数组的长度。

<subscript-expr> ::= <rvalue-expr> "[" (<rvalue-expr> |
                     <rvalue-expr> ":" <rvalue-expr>) "]"

一些特殊的关键字可以用于内置函数。

<builtin-name> ::= "shape_of" | "length_of" | "range_of"
                 | "extent" | "scalar" | "logical" | "string"
<builtin-expr> ::= <builtin-name> "(" <rvalue-expr> ")"

最后,扩展的右值表达式是上述所有结构(包括基本右值表达式和调用的定义中的结构)的联合。

<rvalue-expr> ::= <indentifier>
                | <literal>
                | <binary-expr>
                | <unary-expr>
                | <paren-expr>
                | <array-rvalue-expr>
                | <tuple-rvalue-expr>
                | <subscript-expr>
                | <select-expr>
                | <comprehension-expr>
                | <builtin-expr>
                | <invocation>

3.2.4.整个结构的描述
NNEF结构描述由版本信息,自定义操作定义的可选列表和顶层图形定义组成。 据说不包含自定义操作定义的NNEF文档是平坦的。 图形定义必须始终存在。

<document> ::= <version> <operation-definition>* <graph-definition>

版本信息是由version关键字引入的,并且由实数数字文字定义为由点分隔的主版本和次版本:

<version> ::= "version" <numeric-literal>

下面是一个简单的例子:

version 1.0

fragment foo( input: tensor, flag: logical ) -> ( output: tensor )
{
    output = ...
}

fragment bar( input: tensor, param: scalar ) -> ( output: tensor )
{
    output = ...
}

graph foobar( input ) -> ( output )
{
    input = external(shape = [4,10])
    hidden = foo(input, flag = true)
    output = bar(hidden, param = 3.14)
}

请注意,可以仅从预定义操作(请参阅操作)构建网络,而不需要在实际文档描述中定义网络。 因此,自定义操作定义通常是不必要的。 此外,图形定义主体总是可以不使用扩展表达式来编写; 它们只是定义复合操作所必需的。 因此,没有自定义操作的网络总是可以使用平面语法来编写。

出于这个原因,图形定义主体仅限于使用平面语法。这样,没有自定义操作的网络总是可以依赖于平面语法来解释。

3.3.语义

下面的小节定义了何时由片段定义构成的语法上有效的文档在语义上也是明确定义的。 语义有效性规则描述了片段的正确定义和调用,包括适当的命名和引用,参数号,参数类型,参数范围,正确使用声明参数和本地标识符。
3.3.1片段定义

声明
每个片段声明必须具有唯一的名称。片段仅基于其名称来识别。
形式参数名称和结果名称在声明中必须是唯一的,也就是说,有效声明不能包含多个形式参数或名称相同的结果。 但是,不同的声明可能使用相同的形式参数名称。
默认值表达式类型必须匹配形式参数的类型。

调用
调用必须为每个没有默认值的形式参数分配一个唯一的参数值。具有默认值的形式参数也可以被赋予不同的值。
有两种方法为形式参数赋值:基于位置(位置参数)和基于名称(命名参数)。 位置参数按照其声明的顺序对应于形式参数。 命名的参数可以以任何顺序出现,与形式参数声明的顺序无关。 只有张量参数(基元,数组或元组,请参阅类型系统和类型检查)可能是位置型的,所有其他参数都必须命名。
有效的调用必须满足以下条件:
- 调用中的操作名称必须与已声明的操作相对应。
- 一个调用的声明不能超过声明中正式参数的个数。
- 每个不具有默认值的形式参数都必须分配一个参数值。
- 位置参数必须在命名参数之前。
- 每个命名参数都必须具有与为该操作声明的形式参数相对应的名称。
- 命名参数必须是唯一的,即每个参数名称在调用中只能出现一次。
- 命名参数不得引用也由位置参数分配的形式参数。
在调用中,没有分配任何值但具有默认值的形式参数将取代它们的默认值。

分配
赋值的右边可以是任何类型的表达式,但左边必须是一个标识符或一个数组或元组(或任何这样的组合)的结果被解压缩的标识符。 也就是说,元组中的数组是允许的,但左侧不允许调用和常量表达式。

图表和片段主体中的标识符用法
有效的正式标识符使用规则如下:
- 片段的参数不能出现在片段体内赋值的左边,它们只能被用作赋值右边的表达式的参数。
- 图形的参数(隐式类型张量)必须定义为外部操作的结果。外部操作不能用在片段中。
- 一个片段的结果必须在片段体内恰好分配一次。
- 片段或主图形中的局部标识符必须首先出现在赋值的左侧,然后在后续赋值的右侧用作表达式中的参数。
- 在分配的左侧不能多次使用相同的标识符(在一个片段内或在主图表中)。
- 图体中的所有单个标识符必须是张量类型。 也就是说,张量数组,张量元组和元参数类型的标识符是不允许的。 当张量数组或元组用于赋值的左侧时,它们必须通过数组或元组表达式从单个张量标识符明确构造。
上述规则确保生成的图形是非循环的,并且操作写入主体的顺序导致图形的有效拓扑排序。 但是,并不是唯一有效的排序,只要满足上述约束条件,就可以对操作进行重新排序或并行执行。
不允许在图体中使用张量数组或张量元组的标识符的目的是让每个张量都可以用一个单独的名字来标识。
参数有效性
根据类型检查,每个操作可以进一步限制所允许的参数集超出通常认为有效的范围。 例如,操作可能会限制它们接受和作为结果返回的张量的数据类型,或者可能会限制参数在特定的范围内。 具有多个张量参数的操作可以定义张量形状一致性规则,并且还定义了它们的结果的形状。 这些规则对于每个操作都是特定的,并且针对操作中的每个原语分别进行描述。

3.3.2类型系统和类型检查
在语法中定义的语法中,操作的形式参数是明确键入的。此外,文字常量也有一个隐式定义的类型。表达式的类型是从它们的参数类型派生的。
类型可以是原始的也可以是复合的。化合物类型由基元或其他化合物类型组成。
此外,张量具有指定其内容类型的关联数据类型。

原始类型
以下原始类型被定义:
- tensor(张量): 一个多维数组
- extent: 用来描述张量范围和尺寸的(有符号的)整数值
- scalar(标量): 一个用于描述操作中的超参数的真实值
- logical: 通常用于描述标志和分支条件的逻辑值
- string: 一般字符序列,用来表示枚举和名字
extent, scalar, logical and string构成原始类型,其值在编译时已知。

复合类型
复合类型遵循一些构建模板:
- Array(数组)类型是从单个项目类型构建的。数组中的每个项目都具有相同的类型。例如,extent []是一个extent数组。
- Tuple(元组)类型由多个项目类型构建,并且始终具有固定数量的项目。例如,(extent,tensor,tensor)是一个extent和两个tensor的元组。
复合类型的项目类型也可以是一个复合类型,从而启用数组的数组,数组的数组或包含数组的元组。 例如,scalar[] []是一个二维标量数组(每个子数组的长度可能不同),(extent,scalar)[]是一个元组数组,而(extent [],tensor)是一个 包含数组的元组。

张量数据类型
一般来说,张量持有标量数据。但是张量有两种特殊的数据类型,不能与标量混合:
- logical(逻辑): 用于存储比较操作的结果
- coordinate(坐标): 将位置信息存储在另一个张量中
一般来说,操作接受标量张量。 一些操作导致或接受逻辑张量,而另一些操作则接受和返回坐标张量。 标量张量不能代替逻辑或坐标张量,反之亦然。 每个原始操作分别详细描述了具体的要求。

文字常量的类型
文字常量的类型如下所示:
- <numeric-literal>的类型是extentscalar,取决于它是分别表示整数还是实数。
- <string-literal>的类型是字符串。
- 常量true和false的类型是是logical

类型转换
以下隐式类型转换是允许的:
- 如果tensor的期望数据类型是scalar,则可以使用scalar值代替tensor参数。
- 如果tensor的期望数据类型是logical,则可以使用logical值代替tensor参数。
- 如果一个数组元素的类型可以被转换,那么它可以转换为另一个数组类型。
- 如果元组的类型数量相同,则可以将元组类型转换为另一个元组类型,并将相应的项类型转换为另一个元组类型的元素类型。
当代替张量参数代替元参数时,它们表现为单一形状的恒定张量。
当一个表达式在一个操作的调用中代替一个形式参数或者被分配给一个片段的结果时,表达式类型必须是相等的或者可以转换成形式参数或结果类型; 否则调用无效。
此外,必要时可以使用内置函数强制显式类型转换。

3.3.3构建扩展表达式
这里描述的表达式主要使用组合语法(除了简单的数组和元组构造)。算术,比较和逻辑表达式的目的是双重的:
- 一个是作为某些操作的简写符号。例如,对于张量a和b,a + b相当于add(a,b)
- 另一个是构建或计算元操作的元参数的值。例如,一个操作可能需要一个范围数组,可以使用表达式来构建,例如[1,2,3]
因此,表达式既可以是经常使用的算术运算符,比较运算符或逻辑运算符,也可以用于操作数据结构(如数组或元组)作为操作参数。 在操作数据结构时,所需的一组典型操作是从其组件或部件构建数据结构,并将数据结构解剖为其组件或部件。
以下小节描述了内置操作符和数据结构操作符符号。

内置操作
算术,比较和逻辑表达式可以由标识符和常量构建(请参阅语法)。 一个表达式可以是一个常量表达式,它只能由元参数类型构建,并且可以是非常量表达式,在这种情况下,它可以映射到基本片段(请参阅操作)。 下表总结了允许的参数类型和每个操作符的结果类型。 所有的操作符也适用于张量,因此这里没有明确列出。 在这种情况下,结果也是一个张量(适当的数据类型)。 如果至少有一个参数是张量,则该操作将映射到适当的片段。 在这种情况下,参数类型必须以与显式调用片段相同的方式进行检查。
适用于标量的算术运算符也适用于范围参数(但不是混合的),在这种情况下结果也是一个范围。 适用于任何类型的比较运算符(为了方便起见用下面的字标记)必须具有相同类型的参数。
表1.内置运算符列表参数和结果类型,以及映射到片段
使用上述以外的参数类型的运算符会导致无效的表达式。
算术运算符+, - ,*,/,比较运算符<,<=,>,> =,==,!=和逻辑运算符&&,||,! 的语义,沿用他们的通常定义。

数组
在构建数组时,项目必须是相同的类型,或者必须可以将所有项目转换为通用类型。
构建数组最简单的方法是枚举它的项目,如[a,b,c]。 或者,可以通过使用+运算符连接两个数组来构建数组,例如[a,b] + [c,d],从而得到[a,b,c,d]或x + y,其中x和y是数组。 作为连接的泛化,可以使用运算符多次复制数组。 例如,[a,b] 2的结果是[a,b,a,b]。
要访问数组中的项目或项目范围,可以使用下标运算符[]。 数组索引从0开始,直到数组的长度减1.有两种类型的下标表达式,这取决于[]中的表达式:
- 如果下标表达式是extent类型的单个表达式,则结果是该数组的单个项目,下标表达式的类型是该数组元素的类型。例如,如果a = [1,2,3],那么a [1]等于2。
- 如果下标表达式是一个范围(由:定界的extent类型的两个表达式),则结果是数组的子序列,下标表达式的类型与数组类型相同。 范围的开始是包容性的; 最后是排他性的。 范围可以在两端开放(通过省略:之前或之后的表达式)。 如果它在开始处是打开的,则它被定义为从0开始。如果在结尾处打开,则结束被定义为数组的长度。 如果开始小于或等于结束,结果是空数组。 例如,假设a = [1,2,3],则a [0:2]等于a [:2]并且等于[1,2],或者[1:3]等于a [1: ]和等于[2,3]。 而且,[2:2]等于[]。

元组
元组可以通过枚举由运算符分隔的项来构造。例如,a,b是一个元组,或者可选地,为了更好的易读性,可以像(a,b)中那样将元组加括号。
如果表达式是元组类型的,则可以通过将其分配给包含元组项的标识符的元组来解压缩它。 例如,如果t是三项的元组,则a,b,c = t将元组的项目解包到标识符a,b和c。 左边的元组必须和右边的元素数量相同。 具有多个结果的片段基本上返回一个元组。
下标也可以通过下标进行访问:下标必须是整数文字,即范围和索引变量或表达式是被禁止的。 a,b,c = t相当于a = t [0]; b = t [1]; c = t [2]。

字符串
虽然字符串是一种基本类型,但是字符串可以被认为是字符数组,并且一些操作可以像数组一样被定义。字符串不能作为数组表达式来构建,然而:
- 字符串可以使用+运算符连接,并与*运算符重复使用。
- 可以使用范围索引创建子字符串。当下标表达式是单个索引时,它也会导致一个单个字符的字符串。

内置函数
一些内置函数用于查询来自张量,数组和字符串的信息。 为了描述这些函数的接口,为了方便起见,使用相同的符号。 但是,这些函数必须用一个位置参数来调用。 任何类型的说明符代表任何类型都可以代替。
- shape_of(x:tensor) - >(shape:extent [])返回张量x的形状。 如果用非张量值代替张量x,则返回单体形状。 得到的阵列的长度是张量的等级,或者至少2,取其大者
- length_of(x:any []) - >(length:extent)返回数组x的长度
- length_of(x:string) - >(length:extent)返回字符串x的长度
- range_of(x:any []) - >(range:extent [])返回范围从0(包括)到数组x的长度(不包括)
- range_of(x:string) - >(range:extent [])返回范围从0(包括)到字符串x的长度(不包括)

此外,可以使用元参数类型的类型名称作为一元函数(scalar,extent,logical,string)强制显式类型转换:

  • 如果传递值为真,则scalar(x:logical) - >(y:scalar)将返回1.0,否则返回0.0
  • 如果scalar(x:string) - >(y:scalar)描述一个有效的标量字面值(根据语法),则返回字符串的标量表示,否则调用无效
  • scalar(x:extent) - >(y:scalar)返回与传入相同的值,只更改类型
  • 如果传递的值为真,extent(x:logical) - >(y:extent)将返回1,否则返回0
  • extent(x:string) - >(y:extent)返回字符串的范围表示,如果它描述了有效的范围字面值(根据语法),否则调用无效
  • extent(x:scalar) - >(y:extent)将传递的值截断为最接近的较小整数值
  • 如果传递值为0,logical(x:extent) - >(y:logical)将返回false,否则返回true
  • 如果传递的值是0.0,logical(x:scalar) - >(y:logical)将返回false,否则返回true
  • 如果传入的字符串是空字符串(''),则logical(x:string) - >(y:logical)将返回false,否则返回true
  • string(x:any) - >(y:string)根据语法中的文字表示形式返回任何基本类型值的字符串表示形式

编译时分支
编译时分支是通过语法z = x if condition else y来实现的。 条件必须是逻辑类型的表达式(因此它的值在编译时已知)。 此外,x或y的评估应该是懒惰的,也就是说,在评估条件之后,只应该评估x和y中适当的一个,这样也可以使得未评估也是无效的(例如将一个数组索引 ),这在表达某些结构(如递归)时是必要的,如下所示:

fragment sum_of( items: scalar[] ) -> ( sum: scalar )
{
    sum = items[0] + sum_of(items[1:]) if length_of(items) > 0 else 0.0
}

在上面递归求和数组中的项的例子中,当length_of(items)== 0时,表达式items [0]和items [1:]都将是无效的,但是由于懒惰评估而不被考虑。

编译时循环
循环的一个显而易见的方法是使用递归,如上例所示。但是,编写和难以理解通常是非常麻烦的。

实现循环的一个更简单的方法是数组理解,它通过迭代另一个数组并转换它的项来生成一个数组。 一般来说,表达式b = [f(i)for i in a)迭代数组a并通过将f应用于每个项目i来生成项目。 在许多情况下,我不是一个项目本身,而是一个索引运行通过一个范围和下标多个数组,如在c = [a [i] * b [i]为我在range_of(a)],乘以相应的项目 数组a和b。 结果数组的长度等于迭代数组的长度。 可选地,可以提供条件来过滤所得到的项目:仅当条件c(i)的计算结果为真时,b = [f(i)for i in a c(i)]才输出项目。 在这种情况下,输出的长度等于满足条件的项目的数量。

调用链
操作调用可以像编程语言中的函数调用一样链接,在这种情况下,操作的结果是另一个操作的输入。 作为一个例子,链z = g(f(x),y)等价于下面的调用序列

t = f(x)
z = g(t,y)

如果一个操作返回多个结果,被调用片段的相应参数必须是一个元组,以使链接有效。 如果目标是忽略某些结果,则必须首先明确地解压缩结果,并且只有传递给第二个调用的相关参数或元组的相应项必须通过下标来选择。

请注意,包含多个运算符的表达式也是隐式链接的。例如a = b + c * d相当于a = add(b,mul(c,d))

3.3.4.张量形状传播

基本操作根据输入的形状和元参数来定义输出的形状。 从显式提供形状的输入,变量和常量开始,形状通过图传播,并且可以计算所有张量形状。
但是,可以将某些尺寸的输入张量的形状信息保留为未指定的尺寸(请参见外部数据源),从而推迟依赖于输入的操作的形状传播。 在这种情况下,当输入的形状信息变得可用时,形状传播和确认必须按照与输入张量完全定义相同的方式执行。
但请注意,某些操作参数可能取决于张量形状,即使某些输入形状未指定,这些参数也必须具有有效值。 例如,变量张量必须有明确定义的形状(没有未指定的维度),如果变量的形状取决于未指定形状的输入,则认为无效(例如,匹配线性运算参数的形状时)。 因此,变量形状所依赖的输入的这些维度不能保持不确定。

3.3.5导出标识符

某些张量需要从主要描述之外引用,以便能够将更多的信息附加到图上(如参数数据或量化信息)。 有两种张量可供参考:变量和激活张量。

变量声明为一个明确的字符串标签(见变量),这个标签必须是全局唯一的(共享权重除外),因此它可以用来引用一个变量。 此标签用于将张量数据附加到序列化的变量。

图表体中的激活张量也具有全局唯一标识符(分配的左侧必须具有先前未使用的名称),因此这些也可以用于参考激活张量,例如将量化信息附加到激活。
请注意,变量也可以是主要图形描述的一部分,因此可以通过两种机制(变量定义的字符串标签和分配中使用的标识符名称)引用它们。

variable42 = variable(label = 'block1/conv1/weights', ...)

在上面的例子中,尽管出于不同的目的,名称’variable42’和’block1 / conv1 / weights’指的是相同的张量。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值