# mypy: 允许未类型化定义
将模型导出为 ONNX IR 格式的函数。
这些模型可以用 ONNX 库加载,然后
转换为在其他深度学习框架上运行的模型。
""
from 未来
导入
注释
导入 contextlib
导入
复制
导入
检查
导入
正则表达式
导入
打字
导入
警告
from 打字
导入
任何,
可调用,
角色
from typing_extensions 导入
已弃用
导入
火炬
导入 torch._C._onnx
是
C_onnx
导入 torch.jit._trace
导入
torch 序列化
from 火炬
导入 _C
from torch.onnx 导入 _constants,
错误,
符号助手 # noqa: F401
from torch.onnx._全局_
导入
全局变量
from torch.onnx._internal 导入
诊断,
jit 工具,
onnx 协议工具,
注册
如果
输入法.
类型检查:
from collections.abc 导入
收藏,
映射,
序列
全部 = [
"is_in_onnx_export",
"选择导出模型模式",
禁用 Apex O2 状态字典钩子,
设置 ONNX 记录日志,
导出器上下文,
"导出",
"模型签名",
"静态输入变更时警告",
"解包量化张量",
"不可转换的运算符",
"注册自定义算子符号",
"注销自定义算子符号",
]
[文档]def is_in_onnx_export() -> bool:
返回是否处于 ONNX 导出过程中。
return GLOBALS.in_onnx_export
# TODO(justinchuby): 从 constant_fold.cpp 中移除对此全局变量的依赖
无法从 torch._C 导入 IValue
_params_dict = {} # type: ignore[var-annotated]
[文档]@deprecated(
在导出模型之前,请设置训练模式,
分类=
无)
@contextlib.contextmanager
定义
选择导出模型模式(
模型,
模式: _C_onnx.
训练模式):
一个上下文管理器,用于临时设置``model``的训练模式
将模式重置,并在退出 with 块时重置。
已弃用
在导出模型之前,请设置训练模式。
参数:
model:与:func:`export`的`model`参数相同类型和意义。
mode:与:func:`export`的`training`参数相同类型和意义。
"文档"
如果
不 isinstance(
模式, _C_onnx.
训练模式):
raise 类型错误(
f"'mode' 应该是 torch.onnx.TrainingMode 枚举类型,但得到了 '{
类型(
模式)}
'。"'
)
原始训练状态:
布尔值 =
假
如果
有属性(
模型,
"训练"):
原始训练 =
模型.
训练
# ONNX opset 12 对训练兼容模型的支持更好,包括更新后的
# dropout 和 batch_norm 操作符版本
如果
模式 == _C_onnx.
训练模式.
训练
或者 (
模式 == _C_onnx.
训练模式.
保留
和
原始训练
):
全局变量.
导出训练 =
真实
如果
全局变量.
导出 ONNX 算子版本 < 12:
警告.
警告(
"您正在以训练模式导出模型,使用 ONNX 操作集版本 "
f"版本"{
全局变量.
导出 ONNX 操作集版本}
.
"低于 opset 12 的操作集版本将无法导出"
"节点如 Dropout 和 BatchNorm 正确。"
)
else:
全局变量.
导出训练 =
假
全局变量.
训练模式 =
模式
如果
模式 == _C_onnx.
训练模式.
训练:
模型.
训练(
是)
elif 模式 == _C_onnx.
训练模式.
评估:
模型.
训练(
错误)
# else 模式 == _C_onnx.TrainingMode.PRESERVE,则什么都不做
尝试:
产生
最后:
如果
有属性(
模型,
"训练")
和
不
模式 == _C_onnx.
训练模式.
保留:
模型.
训练(
原始训练状态)
@deprecated(
"请勿使用此功能。如果需要在用户代码中使用其逻辑,请复制",
分类=
无,
)
@contextlib.contextmanager
定义
禁用 Apex O2 状态字典钩子(
模型: torch.
神经网络.
模块 | torch.
算子.
脚本函数):
一个临时禁用 Apex O2 钩子的上下文管理器。
已弃用
请删除此函数的使用。
"文档"
将 Apex O2 钩子的 state_dict 导出为返回 fp16 权重的 fp32。
导出器无法将它们识别为相同的张量。
由于此钩子仅由优化器使用,因此在导出时可以安全地删除此钩子。
在导出时可以安全地删除此钩子。
如果
不 isinstance(
模型, torch.
算子.
脚本函数):
模型钩子 = {} # type: ignore[var-annotated]
for 模块
在
模型.
模块():
for 键,
钩子
在
模块.
状态字典钩子.
项目():
如果
类型(hook).__name__ ==
O2 状态字典钩子:
如果
模块
不
在
模型钩子:
模型钩子[
模块] = {}
模型钩子[
模块
]
[键] =
钩子
如果
模块
在
模型钩子:
for key 在
模型钩子[
模块]:
模块.
状态字典钩子.
流行(
键)
尝试:
产生
最后:
添加回钩子
for 模块, m_map
在
模型钩子.
项目():
for 键,
钩子
在 m_map.
项目():
模块.
状态字典钩子[
键] =
钩子
else:
尝试:
产生
最后:
通过
@deprecated(该功能将被移除。请移除此函数的使用)
@contextlib.contextmanager
定义
设置 ONNX 日志记录(
详细模式:
布尔):
一个上下文管理器,用于临时设置 ONNX 日志的详细程度。
已弃用
请删除此函数的使用。
"文档"
是否原本启用 = _C._jit_is_onnx_log_enabled
如果
是否原本启用
或者
详细模式:
忽略[truthy 函数]
_C._jit_set_onnx_log_enabled(是)
尝试:
产生
最后:
如果
不
是否原本启用:
忽略[truthy 函数]
_C._jit_set_onnx_log_enabled(错误)
@deprecated(
功能将被移除。请删除此函数的使用
如有需要,请实现等效逻辑,
分类=
无,
)
@contextlib.contextmanager
定义
导出上下文(
模型,
模式: _C_onnx.
训练模式,
详细模式:
布尔):
一个上下文管理器,用于临时设置``model``的训练模式
设置为 ``模式``,禁用 Apex O2 钩子,并设置 ONNX 日志的详细程度。
已弃用
在导出模型之前,请设置训练模式。
"文档"
与 (
选择导出模型模式(
模型,
模式)
是
模式上下文,
禁用 Apex O2 状态字典钩子(
模型)
是
顶点上下文,
设置 ONNX 日志记录(
详细模式)
是
日志上下文,
诊断.
创建导出诊断上下文()
是
诊断上下文,
):
产生 (
模式上下文,
顶点上下文,
日志上下文,
诊断上下文)
定义 _get_torch_export_args(
参数:
元组[
任何, ...
]
kwargs: 字典[
字符串,
任何] |
无,
)
输入文本:
->
翻译:
-> 元组[
元组[
任何, ...
]
字典[
字符串,
任何] |
无]:
从模型和输入参数中获取 torch.onnx.export 的参数。
如果
不 kwargs
和 args
和 isinstance(
参数[-1
]
字典):
kwargs = 参数[-1]
args = 参数
[-1]
返回
参数, kwargs
定义
导出(
模型: torch.
神经网络.
模块 | torch.
算子.
脚本模块 | torch.
算子.
脚本函数,
参数:
元组[
任何, ...] | torch.
张量,
f: 字符串,
*,
kwargs: 字典[
字符串,
任何] |
无 =
无,
导出参数:
布尔值 =
是,
详细模式:
布尔值 =
错误,
训练: _C_onnx.
训练模式 = _C_onnx.
训练模式.
评估,
输入名称:
序列[
字符串] |
无 =
无,
输出名称:
序列[
字符串] |
无 =
无,
操作符导出类型: _C_onnx.
运算符导出类型 = _C_onnx.
运算符导出类型.ONNX,
opset 版本:
整型 |
无 =
无,
执行常量折叠:
布尔值 =
是,
动态轴:
映射[
字符串,
映射[
整数,
字符串]]
| 映射[
字符串,
序列[
整数]]
| 无 =
无,
将初始化器作为输入保留:
布尔值 |
无 =
无,
自定义算子集:
映射[
字符串,
整数] |
无 =
无,
将模块导出为函数:
布尔值 |
收藏[
类型[torch.
神经网络.
模块]] =
错误,
自动微分内联:
布尔值 =
是,
)
输入文本:
->
翻译:
-> 无:
r导出模型为 ONNX 格式。
如果 ``model`` 不是一个 :class:`torch.jit.ScriptModule` 也不是一个
class:`torch.jit.ScriptFunction`,则执行
将模型一次转换为 TorchScript 图以进行导出
(相当于:func:`torch.jit.trace`) 因此这具有相同的有限支持
用于动态控制流,如::func:`torch.jit.trace`。
参数:
模型:要导出的模型。
参数:
参数可以组织为以下形式:
1. 仅需一个参数元组::
args = (x, y, z)
该元组应包含模型输入,使得 ``model(*args)`` 是有效的
模型的调用。任何非 Tensor 参数将被硬编码
导出的模型中;任何 Tensor 参数将成为导出模型的输入,其顺序与元组中出现的顺序相同。
text: text: 在
2. TENSOR::
args = torch.Tensor([1])
这等价于该 Tensor 的 1 元元组
3. 一组以命名参数字典结束的参数:
args = (x, {"y": input_y, "z": input_z})
元组中除了最后一个元素之外的所有元素都将作为非关键字参数传递,
而命名参数将从最后一个元素设置。如果存在命名参数,
字典中没有,则分配默认值,或如果没有提供默认值则为 None。
默认值未提供。
.. 警告::
此行为将在未来的版本中弃用。请使用 kwargs 参数代替。
请使用 kwargs 参数代替。
.. 注意::
如果字典是 args 元组的最后一个元素,它将被
解释为包含命名参数。为了将字典作为最后一个非关键字参数传递,请将空字典作为 args 元组的最后一个元素提供。例如,而不是:
为了将字典作为最后一个非关键字参数传递,请将空字典作为 args 元组的最后一个元素提供。例如,而不是:
例如,而不是:
torch.onnx.export(
模型,
(
x,
# 错误:将被解释为命名参数
{y: z},
),
"test.onnx.pb"
)
写入::
torch.onnx.export(model, (x, {y: z}, {}), "test.onnx.pb")
f: 输出 ONNX 模型文件的路径。例如:"model.onnx"。
kwargs: 模型的命名参数。
export_params: 如果为 True,则所有参数将被导出。如果想要导出未训练的模型,请将其设置为 False。
be exported. Set this to False if you want to export an untrained model.
In this case, the exported model will first take all of its parameters
as arguments, with the ordering as specified by ``model.state_dict().values()``
verbose: 如果为 True,将打印导出模型的描述
模型被导出到 stdout。此外,最终的 ONNX 图将包括导出模型中提到的
字段 `doc_string`,该字段说明了 `model` 的源代码位置
如果为 True,ONNX 导出器将开启日志记录
训练:
* ``TrainingMode.EVAL``: 以推理模式导出模型。
* ``TrainingMode.PRESERVE``: 如果 model.training 为 False,则导出推理模式下的模型;如果 model.training 为 True,则导出训练模式下的模型。
False 和在训练模式下如果 model.training 为 True。
* ``TrainingMode.TRAINING``: 以训练模式导出模型。禁用可能干扰训练的优化
功能。
input_names (字符串列表,默认为空列表):按顺序分配给图输入节点的名称。
input_nodes_of_the_graph (字符串列表,默认为空列表):图输入节点的名称列表。
输出名称(字符串列表,默认为空列表):为图中的输出节点分配的名称。
输出节点的顺序。
operator_export_type(枚举,默认为 OperatorExportTypes.ONNX):
.. 警告::
此选项将在未来的版本中弃用。未来的导出...
图表始终使用默认的 opset 域。
* ``OperatorExportTypes.ONNX``: 将所有操作导出为常规 ONNX 操作
(在默认 opset 域中)。
* ``OperatorExportTypes.ONNX_FALLTHROUGH``:尝试将所有操作
转换为默认 opset 域中的标准 ONNX 操作。如果无法完成
(例如,因为尚未添加将特定 torch op 转换为 ONNX 的支持),
回退到将操作导出到自定义 opset 域而不进行转换。适用
到 `自定义操作 `_
以及 ATen 操作。为了使导出的模型可用,运行时必须支持
这些非标准操作。
* ``OperatorExportTypes.ONNX_ATEN``: 所有 ATen 操作(在 TorchScript 命名空间 "aten" 中)
都导出为 ATen 操作(在操作集域 "org.pytorch.aten" 中)。
`ATen `_ 是 PyTorch 内置的张量库,因此
这指示运行时使用 PyTorch 对这些操作的实现。
.. 警告::
以这种方式导出的模型可能只能由 Caffe2 运行。
如果操作实现中存在数值差异,这可能是有用的。
导致 PyTorch 和 Caffe2(后者更)之间行为存在较大差异
常见于未经训练的模型)。
* ``OperatorExportTypes.ONNX_ATEN_FALLBACK``:尝试导出每个 ATen 操作
在 TorchScript 命名空间"aten"中作为常规 ONNX 操作。如果我们无法做到
(例如,因为尚未添加将特定 torch op 转换为 ONNX 的支持),
回退到导出 ATen op。请参阅 OperatorExportTypes.ONNX_ATEN 的文档。
上下文。
例如:
图(%0 : 浮点数):
%3 : int = prim::Constant[value=0]()
# 转换不支持
%4 : Float = aten::triu(%0, %3)
# 转换支持
%5 : 浮点数 = aten::mul(%4, %0)
返回 (%5)
假设 ``aten::triu`` 在 ONNX 中不受支持,这将导出为:
图(%0 : 浮点数):
%1 : Long() = onnx::Constant[value={0}]()
# not converted
%2 : Float = aten::ATen[operator="triu"](%0, %1)
# converted
%3 : 浮点数 = onnx::Mul(%2, %0)
返回 (%3)
.. 警告::
以这种方式导出的模型可能只能由 Caffe2 运行。
opset_version (int, 默认 17):版本为
`默认(ai.onnx)opset `_
目标。必须大于等于 7 且小于等于 17。
do_constant_folding:应用常量折叠优化。
常量折叠将替换一些所有输入都是常量的操作。
使用预计算的常量节点。
dynamic_axes:
默认情况下,导出的模型将具有所有输入和输出张量的形状
将设置与在 `args` 中给出的完全匹配。要指定张量的轴
动态(即在运行时才知道),将 `dynamic_axes` 设置为具有以下模式的字典:
* KEY (str): 输入或输出名称。每个名称也必须在 ``input_names`` 或
``输出名称``.
* VALUE (字典或列表):如果是一个字典,键是轴索引,值是轴名称。如果是列表,每个元素是一个轴索引。
例如:
例如:
class SumModule(torch.nn.Module):
def forward(self, x):
return torch.sum(x, dim=1)
torch.onnx.export(
SumModule(),
(torch.ones(2, 2),),
"onnx.pb",
输入名称=["x"],
输出名称=["sum"]
)
生成::
输入 {
名称: "x"
...
shape {
dim {
dim_value: 2 # 轴 0
}
dim {
dim_value: 2 # 轴 1
...
output {
name: "sum"
...
shape {
dim {
dim_value: 2 # 轴 0
...
While::
torch.onnx.export(
SumModule(),
(torch.ones(2, 2),),
"onnx.pb",
输入名称=["x"],
输出名称=["sum"]
dynamic_axes={
# 字典值:手动命名的轴
"x": {0: "my_custom_axis_name"},
# 列表值:自动命名
"sum": [0]
},
)
生成::
输入 {
名称: "x"
...
shape {
dim {
维度参数: "my_custom_axis_name" # 轴 0
}
dim {
dim_value: 2 # 轴 1
...
output {
name: "sum"
...
shape {
dim {
dim_param: "sum_dynamic_axes_1" # 轴 0
...
keep_initializers_as_inputs: 如果为 True,则所有
初始化器(通常对应参数)
导出的图也将作为图的输入。如果为 False,
则初始化器不会作为图的输入,只有
非参数输入作为输入添加。
这可能允许后端/运行时进行更好的优化(例如常量折叠)。
。
如果为真,则不会执行 `deduplicate_initializers` 过程。这意味着
初始化器中具有重复值的将不会被去重
将被视为图中的独立输入。这允许不同的
输入初始化器,在导出后由运行时提供。
如果 ``opset_version < 9``,初始化器必须是图的一部分
输入和此参数将被忽略,行为将与将此参数设置为 True 相同
custom_opsets(dict[str, int],默认为空字典):一个具有以下模式的字典:
custom_opsets (dict[str, int], 默认空字典): A dict with schema:
* KEY (str): opset 域名
* VALUE (int): opset 版本
如果模型中引用了自定义 opset 但未在此字典中提及,
则将 opset 版本设置为 1。仅应指定自定义 opset 的域名和版本。
通过此参数指示。
export_modules_as_functions: 启用导出模块为函数的标志
将所有 `nn.Module` 前向调用导出为 ONNX 中的本地函数。或一个集合,以指示
将特定类型的模块导出为 ONNX 中的本地函数。
此功能需要 `opset_version` >= 15,否则导出将失败。这是因为
`opset_version` < 15 表示 IR 版本 < 8,这意味着没有本地函数支持。
模块变量将被导出为函数属性。函数分为两类
属性。
1. 注解属性:通过类型注解的类变量(
`PEP 526 风格 `_
将作为属性导出。
ONNX 本地函数的子图中不使用标注属性,因为它们不是由 PyTorch JIT 跟踪创建的,
但消费者可能会使用它们来判断是否用特定的融合内核替换函数。
2. 推断属性:模块内操作符使用的变量。属性名称
2. 推断属性:模块内操作符使用的变量。属性名称
将具有前缀 "inferred::"。这是为了区分从
Python 模块注解中检索到的预定义属性。
* ``False``(默认):将 ``nn.Module`` 前向调用导出为细粒度节点。
* ``True``:将所有 ``nn.Module`` 前向调用导出为局部函数节点。
* nn.Module 类型集合:将 ``nn.Module`` 的 forward 调用导出为本地函数节点,
仅当找到 ``nn.Module`` 类型的集合时。
autograd_inlining:用于控制是否内联 autograd 函数的标志。
请参阅 https://github.com/pytorch/pytorch/pull/74765 获取更多详细信息。
抛出异常:
class:`torch.onnx.errors.CheckerError`:如果 ONNX 检查器检测到无效的 ONNX 图。
`torch.onnx.errors.UnsupportedOperatorError`: 如果 ONNX 图无法导出,因为它
使用导出器不支持的操作符。
`:class:`torch.onnx.errors.OnnxExporterError`: 导出过程中可能发生的其他错误。
所有错误都是 :class:`errors.OnnxExporterError` 的子类。
"文档"
如果
操作导出类型 != _C_onnx.
运算符导出类型.ONNX:
警告.
警告(
设置 `operator_export_type` 为非默认值已弃用。
该选项将在未来的版本中移除。,
分类=
弃用警告,
)
如果
训练 == _C_onnx.
训练模式.
训练:
警告.
警告(
将 `training` 设置为非默认值已被弃用。
该选项将在未来的版本中删除。请在导出模型之前设置训练模式
之前。,
分类=
弃用警告,
)
args = (参数,)
如果 isinstance(
参数, torch.
张量)
否则 args
如果 kwargs
是
不
无:
args = args + (kwargs,)
_export(
模型,
参数,
f,
导出参数,
详细模式,
训练,
输入名称,
输出名称,
操作符导出类型=
操作符导出类型,
opset 版本=
opset 版本,
执行常量折叠=
执行常量折叠,
动态轴=
动态轴,
将初始化器作为输入保留=
将初始化器作为输入保留,
自定义算子集=
自定义算子集,
将模块导出为函数=
将模块导出为函数,
自动微分内联=
自动微分内联,
)
返回
无
定义 _is_constant_tensor_list(node):
如果 node.
类型() !=
prim::常量:
返回
假
输出类型 = node.
输出().
类型()
如果
输出类型.
是子类型(_C.ListType.
张量()):
返回
真实
如果
输出类型.
是子类型(_C.ListType(_C.
可选类型.
张量之的())):
返回
真实
ONNX 无法处理在常量属性中生成的列表形式的张量,因此我们将它们重新拆分成 prim::ListConstructs
所以我们将它们重新拆分成 prim::ListConstructs
定义 _split_tensor_list_constants(g,
块):
for 节点
在
块.
节点():
for 子块
在 node.
块():
_split_tensor_list_constants(g, 子块)
如果 _is_constant_tensor_list(node):
输入 =
输入文本为空,请提供需要翻译的文本
for val 在 node.
输出().
转换为 I 值():
输入 = g.
插入常量(val)
输入.node().
移动到前面(node)
输入.node().
复制元数据(node)
输入.append(
输入)
lc = (
g.创建(
prim::ListConstruct,
输入)
.插入前(node)
.输出()
.设置类型(_C.ListType.
张量())
)
小写.node().
复制元数据(node)
node.输出().
将所有使用替换为(
小写)
定义
优化图(
图: _C.
图,
操作符导出类型: _C_onnx.
运算符导出类型,
_禁用 Torch 常量传播:
布尔值 =
错误,
固定批次大小:
布尔值 =
错误,
params_dict=无,
动态轴=
无,
输入名称=
无,
模块=
无,
):
如果 params_dict
是
无:
params_dict = {}
# 内联所有内容
_C._jit_pass_inline(图)
# 删除 fork/wait 节点
_C._jit_pass_inline_fork_wait(图)
_C._jit_pass_lint(图)
如果
全局变量.
自动微分内联:
_C._jit_pass_onnx_autograd_function_process(图)
_C._jit_pass_lower_all_tuples(图)
我们现在记录一些操作,如一和零
将它们记录到一个之前记录常数的轨迹中。
使用常量属性来维持我们当前的 ONNX 支持水平
而不是为它们全部实现符号化
如果
禁用 Torch 常量传播
是
错误:
_C.执行_jit_pass_constant_propagation(
图)
_split_tensor_list_constants(图,
图)
# 运行 dce 以消除图中可能存在的死部分
被诸如 symbolic_override 之类的因素所遗留
_C._jit_pass_dce(图)
_C._jit_pass_lint(图)
当使用禁用缓存的 Autocast 时,CSE 应该提高性能
由于在 tracer 上存在限制,自动转换已被禁用,详情请见 https://github.com/pytorch/pytorch/issues/84092
必须在 _C._jit_pass_erase_number_types 之前运行,以防止类型替换
如果 _C._jit_pass_cse(
图):
_C._jit_pass_onnx_lint(图)
_C._jit_pass_canonicalize_graph_fuser_ops(图)
_C._jit_pass_lint(图)
_C._jit_pass_peephole(图,
是)
_C._jit_pass_fuse_addmm(图)
_C._jit_pass_lint(图)
_C._jit_pass_peephole(图,
是)
_C._jit_pass_lower_all_tuples(图)
在_jit_pass_onnx 中,为每个节点调用符号函数进行转换。
然而,有一些节点无法在没有额外上下文的情况下转换。
例如,分割输出的数量(以及它是否是静态的或动态的)是未知的。
#直到它被 listUnpack 节点解包为止。
#这个步骤进行预处理,并准备节点以便能够接收到足够的上下文
#以便符号函数可以使用。
_C._jit_pass_onnx 移除_onnx 中的就地操作(
图,
模块)
_C._jit_pass_onnx_preprocess(图)
# onnx 不支持元组,因此尝试移除它们
_C._jit_pass_lint(图)
# onnx 仅支持张量,但 1 / 2 = 0.5 且 tensor(1) / tensor(2) = 0
_C._jit_pass_prepare_division_for_onnx(图)
_C._jit_pass_onnx_remove_print(图)
_C._jit_pass_onnx_preprocess_caffe2(图)
符号化助手._quantized_ops.
清晰()
# 解包卷积和线性操作中的量化权重并插入到图中。
_C._jit_pass_onnx_unpack_quantized_weights(图, params_dict)
# onnx 仅支持张量,因此我们将所有数字类型转换为张量
_C._jit_pass_erase_number_types(图)
如果
全局变量.
ONNX 形状推理:
输入名称 =
输入文本为空,请提供需要翻译的文本
如果
输入名称
是
无
否则
输入名称
动态轴 = {}
如果
动态轴
是
无
否则
动态轴
_C._jit_pass_onnx 设置动态输入形状(
图,
动态轴,
输入名称)
_C._jit_pass_onnx_lint(图)
图 = _C._jit_pass_onnx(
图,
操作符导出类型)
_C._jit_pass_onnx_lint(图)
_C._jit_pass_lint(图)
_C._jit_pass_onnx_scalar_type_analysis(
图,
是,
全局变量.
导出 ONNX 算子版本
)
_C._jit_pass_lint(图)
_C._jit_pass_onnx_peephole(
图,
全局变量.
导出 ONNX 操作集版本,
固定批次大小
)
_C._jit_pass_lint(图)
# 图已不再是有效的 JIT 图,因为类型已被替换
(例如与 Tensor 相关的 int),因此现在它包含了一些实际上并不存在的操作符
# 存在的。我们不能运行正常的死代码消除,因为它会尝试查找一个操作符是否有副作用,但我们可以在没有副作用的情况下运行死代码消除
# 要查找一个操作符是否有副作用,但我们可以在没有副作用的情况下运行死代码消除
# 不需要查找操作是否有副作用的可消除变体。
_C._jit_pass_dce 允许删除具有副作用节点的操作(
图)
_C._jit_pass_lint(图)
图 = _C._jit_pass_canonicalize(
图)
_C._jit_pass_lint(图)
如果
全局变量.
ONNX 形状推理:
尝试:
_C._jit_pass_onnx_graph_shape_type_inference 操作(
图, params_dict,
全局变量.
导出 ONNX 算子版本
)
除了
运行时错误:
# 形状类型推断错误不应停止导出过程
# https://github.com/pytorch/pytorch/issues/132205
通过
返回
图
定义
静态输入更改时警告(
输入状态):
警告:对输入字典和字符串的更改在追踪的 ONNX 图中不会生效。
我们接受字典和字符串作为 ONNX 输入,但它们只能用于配置。
我们在此处检测这些输入是否被修改,如果被修改,我们将警告用户更改不会在追踪的 ONNX 图中生效。
警告用户,这些更改不会在追踪的 ONNX 图中生效。
"文档"
for 输入,
跟踪输入
在 zip(
输入状态[0
]
输入状态[1
)]
如果 isinstance(
输入,
字典):
如果
列表(
输入.
键()) !=
列表(
跟踪输入.
键()):
警告 = (
"我们检测到您正在修改一个作为模型输入的字典。"
"请注意,ONNX 允许字典作为输入,但应谨慎处理。"
"字典是允许作为 ONNX 输入的,但应小心处理。"
"请注意,字典是允许作为 ONNX 输入的,但应小心处理。"
字典的使用不推荐,除非“
用于配置。
请注意,键的顺序和值必须保持不变。
)
警告.
警告(
警告)
elif isinstance(输入,
字符串):
如果
输入 !=
跟踪输入:
警告 = (
"该模型似乎有字符串输入/输出。"
"请注意,字符串不会作为 ONNX 图的输入/输出出现。"
)
警告.
警告(
警告)
定义
_根据导出类型解析参数(
参数名称,
参数值,
操作符导出类型):
"""解决当 export_type 不等于 operator_export_type.ONNX 时被忽略的参数。"""
返回
参数值
定义
决定保留初始输入(
将初始化器作为输入保留:
布尔值 |
无,
操作符导出类型: _C_onnx.
运算符导出类型,
opset 版本:
整数,
):
决定图中的初始化器是否应列为 ONNX 图输入。
此方法封装了决定图中的初始化器是否应列为 ONNX 图输入的逻辑
(即是否选择 ONNX IR v3 或 v4)。
如果 keep_initializers_as_inputs 未指定(None),则我们决定是否保留
初始化器作为基于导出类型的图输入(val_keep_init_as_ip)。如果导出类型
如果是 ONNX,则不要将初始化器作为输入保留(val_keep_init_as_ip=False)。
导出类型将初始化器作为输入保留(val_keep_init_as_ip=True)。
如果指定了 keep_initializers_as_inputs,则尊重它。除非操作集版本<=8,
在这种情况下必须忽略,因为对于操作集版本<=8,所有初始化器必须
图形输入的一部分(仅允许 ONNX IR v3),即 val_keep_init_as_ip=True。
对于 opset 版本 8 或更低版本,需要特殊处理,因为无论用户输入的 keep_initializers_as_inputs 如何,
图必须遵循 ONNX IR v3 语义,即所有初始化器都必须作为 ONNX 图输入列出。
即便对于 keep_initializers_as_inputs 的用户输入,图也必须遵循 ONNX IR v3 语义,即所有初始化器都必须作为 ONNX 图输入列出。
"文档"
如果
算子版本 < 9:
如果
保持初始化器作为输入
是
错误:
警告.
警告(
"将 'keep_initializers_as_inputs=False' 设置为 opset 版本"
"8 或更低版本会导致无效的 ONNX 图。因此,"
"'keep_initializers_as_inputs=False' 在导出时会被忽略。"
"导出的模型将具有初始化器作为图输入(符合"
"ONNX IR v3 规范)。"
)
返回
真实
即 True 表示初始化器是图输入的一部分(ONNX IR v3)
保持初始化器作为输入 = (
真实
如果
保持初始化器作为输入
是
无
否则
保持初始化器作为输入
)
如果 (
保持初始化器作为输入
是
无
和
操作导出类型
是 _C_onnx.
运算符导出类型.ONNX
):
保持初始化器作为输入 =
假
返回
保持初始化器作为输入
定义
决定添加节点名称(
添加节点名称,
操作符导出类型):
返回
_根据导出类型解析参数(
"添加节点名称",
添加节点名称,
操作导出类型
)
定义
决定常量折叠(
执行常量折叠,
操作符导出类型,
训练):
做常量折叠 =
_根据导出类型解析参数(
执行常量折叠,
执行常量折叠,
操作导出类型
)
如果
做常量折叠
和 (
训练
是
不
无
和
训练
是
不 _C_onnx.
训练模式.
评估
):
警告.
警告(
"建议在以训练兼容模式导出模型时关闭常数折叠('do_constant_folding=False')"
"即使用 'training=TrainingMode.TRAIN' 模式"
或者 'training=TrainingMode.PRESERVE'(当模型处于训练模式时)。否则,一些
可学习模型参数在导出的 ONNX 模型中可能无法正确翻译
因为常量折叠会修改模型参数。请考虑
关闭常量折叠或设置 training=TrainingMode.EVAL。
)
返回
做常量折叠
定义
签名(
模型)
输入文本:
->
翻译:
-> 检查.
签名:
应该是可调用的 = getattr(
模型,
前进,
模型)
如果
可调用(
应该是可调用的):
返回
检查.
签名(
应该是可调用的)
raise ValueError("模型没有前向方法,不可调用")
定义
_决定输入格式(
模型,
参数):
尝试:
sig = 签名(
模型)
除了 ValueError
是 e:
警告.
警告(f"{e}
跳过 _decide_input_format")
返回 args
尝试:
排序列表键 =
列表(
签名.
参数.
键())
如果
有序列表键[0] ==
self:
排序列表键 =
有序列表键[1
]
参数字典:
字典 = {}
如果 isinstance(
参数,
列表):
参数列表 = args
elif isinstance(参数,
元组):
参数列表 =
列表(
参数)
else:
参数列表 = [
参数]
如果 isinstance(
参数列表[-1
]
字典):
参数字典 =
参数列表[-1]
参数列表 =
参数列表
[-1]
非关键词数量 =
长度(
参数列表)
for 可选参数
在
有序列表键[
非关键词数量
]
如果
可选参数
在
参数字典:
参数列表.append(
参数字典[
可选参数])
# 检查此参数是否有默认值
else:
参数 =
签名.
参数[
可选参数]
如果
参数.
默认 !=
参数.
空的:
参数列表.append(
参数.
默认)
args = 参数列表
如果 isinstance(
参数,
列表)
否则
元组(
参数列表)
模型无输入参数的情况
除了
索引错误:
警告.
警告(
无输入参数,跳过_decide_input_format)
除了
异常
是 e:
警告.
警告(f
跳过_decide_input_format
输入文本翻译为简体中文为:\n {e.
参数[0]}")
返回 args
定义
跟踪(
函数,
参数,
操作符导出类型,
返回输出=
错误):
# 特殊情况,用于传递单个 Tensor 的常见情况
如果 isinstance(
参数, torch.
张量):
args = (参数,)
跟踪图,
火炬输出,
输入状态 = torch.
算子._get_trace_graph(
函数,
参数,
严格的=
错误,
_force_outplace=错误,
_return_inputs_states=是,
)
静态输入更改时警告(
输入状态)
跟踪图 =
优化图(
跟踪图,
操作符导出类型, params_dict={})
如果
返回输出:
返回
跟踪图,
torch 输出
返回
跟踪图
定义
跟踪并从模型获取图(
模型,
参数):
# 基本合理性检查:确保状态字典的键相同
在运行模型前后。快速失败!
原始状态字典键 = torch.
算子.
唯一状态字典(
模型).
键()
禁用自动转换缓存,因为它会替换内核的权重和偏差
# (不期望的)常量
# 当有重用权重时,自 https://github.com/pytorch/pytorch/pull/85665 以来没有性能影响
prev_autocast_cache_enabled = torch.is_autocast_cache_enabled()
torch.set_autocast_cache_enabled(错误)
跟踪图,
火炬输出,
输入状态 = torch.
算子._get_trace_graph(
模型,
参数,
严格的=
错误,
_force_outplace=错误,
_return_inputs_states=是,
)
torch.set_autocast_cache_enabled(前自动转换缓存已启用)
静态输入更改时警告(
输入状态)
如果
原始状态字典键 != torch.
算子.
唯一状态字典(
模型).
键():
raise 运行时错误(
运行 tracer 后状态字典已更改;
您的模型中发生了奇怪的事情!
)
返回
跟踪图,
torch 输出
定义
获取参数数量列表(
方法图,
参数列表):
参数数量列表 =
输入文本为空,请提供需要翻译的文本
for 输入_,
参数列表
在 zip(
方法图.
输入(),
参数列表):
如果
打包参数
在
字符串(
输入_.
类型()):
输入变量, _ = torch.
算子._flatten(
参数列表)
参数数量列表.append(
长度(
输入变量))
else:
参数数量列表.append(
参数列表
是
不
无)
返回
参数数量列表
定义
_检查_flatten_没有删除(
原始,
JIT 平坦化):
"""torch.jit._flatten 移除了 None。检查它是否在这种情况下做了这样的事情。"""
定义
展平(x):
如果 isinstance(x, (
列表,
元组)):
for 内部
在 x:
yield from 展平(
内部)
elif isinstance(x, 字典):
for 内部
在 x.
值():
yield from 展平(
内部)
else:
产生 x
展平后为空 =
列表(
展平(
原始))
num_none = 长度(
展平后为空) -
长度(
JIT 平坦化)
断言 num_none >= 0
如果 num_none:
raise ValueError(
f"参数包含"{num_none}
None 之后是扁平化。
"当导出 ScriptModule 或 ScriptFunction 时,参数不能为空,因为这会破坏类型传播。"
"因为这会破坏类型传播。"
)
定义
创建 JIT 图(
模型: torch.
神经网络.
模块 | torch.
算子.
脚本函数,
参数:
序列[
任何]
)
输入文本:
->
翻译:
-> 元组[_C.
图,
列表[_C.IValue
]
任何 |
无, _C.
脚本模块 |
无]:
如果 isinstance(
模型, (torch.
算子.
脚本函数, torch.
算子.
脚本模块)):
展平的参数 =
元组(torch.
算子._flatten(
元组(
参数))[0])
_检查_flatten_没有删除(
参数,
展平参数)
torch 输出 =
无
如果 isinstance(
模型, torch.
算子.
脚本模块):
尝试:
图 =
模型.
前向.
图
# 类型: 忽略[attr-defined]
除了
属性错误
是 e:
raise 运行时错误(
"‘forward’方法必须是一个脚本方法") from e
_C._jit_pass_onnx 函数替换(
图)
frozen_module = _C.
冻结模块(
角色(_C.
脚本模块,
模型._c),
保留参数=
真实
)
模块, params = _C._jit_onnx_list_model_parameters(freezed_module)
method_graph = 模块.
_获取方法(
前进).
图
args_params = 元组(
参数) +
元组(
参数)
参数数量列表 =
获取参数数量列表(
方法图,
参数列表)
输入变量, _ = torch.
算子._flatten(
参数列表)
图 = _C.
扩散并分配输入形状(
方法图,
元组(
输入变量),
参数数量列表,
错误,
假
)
返回
图,
参数,
火炬输出,
模块
torch.jit.ScriptFunction
params = 输入文本为空,请提供需要翻译的文本
图 =
模型.
图
_C._jit_pass_onnx 函数替换(
图)
参数数量列表 =
获取参数数量列表(
图,
参数)
图 = _C.
扩散并分配输入形状(
图,
展平参数,
参数数量列表,
错误,
假
)
返回
图,
参数,
火炬输出,
无
图,
torch 输出 =
跟踪并从模型获取图(
模型,
参数)
_C._jit_pass_onnx_lint(图)
状态字典 = torch.
算子.
唯一状态字典(
模型)
params = 列表(
状态字典.
值())
图输入 =
列表(
图.
输入())
用户输入编号 =
长度(
图输入) -
长度(
状态字典)
参数名称 =
列表(
状态字典.
键())
for i, 输入
在
列举(
图输入):
如果 i >=
用户输入数量:
输入.
设置调试名称(
参数名称[i -
用户输入数量])
_C._jit_pass_onnx 函数替换(
图)
返回
图,
参数,
火炬输出,
无
定义
获取命名参数字典(
图,
参数):
输入和参数名称 = [val.
调试名称() for val
在
图.
输入()]
参数名称 =
输入和参数名称[
长度(
输入和参数名称) -
长度(
参数)
]
_params_dict = 字典(zip(
参数名称,
参数))
返回 _params_dict
定义
_获取示例输出(
模型,
参数):
输入参数 =
复制.
深拷贝(
参数)
输入参数 = {}
如果
输入参数
和 isinstance(
输入参数[-1
]
字典):
输入参数 =
输入参数[-1]
输入参数 =
输入参数
[-1]
示例输出 =
模型(*
输入参数, **
输入参数)
如果 isinstance(
示例输出,
列表):
示例输出 = [
示例输出]
elif 不 isinstance(
示例输出,
元组):
示例输出 = (
示例输出,)
返回
示例输出
_qtype_vtype 映射表 = {
torch.quint8: torch.uint8,
torch.qint8: torch.int8,
torch.qint32: torch.int32,
torch.quint4x2: torch.int8,
}
定义
解包量化张量(
值, cast_onnx_accepted=
是):
如果 isinstance(
值, torch.
张量)
和
值.dtype
在 _qtype_vtype_map:
q_value_dequantize = 值.
反量化()
q_scale = (
torch.张量(
值.q_scale(),
数据类型=torch.double)
如果 cast_onnx_accepted
否则 torch.
张量(
值.q_scale(),
数据类型=torch.float32)
)
q_zero_point = (
torch.张量(
值.q_zero_point(),
数据类型=torch.int64)
如果 cast_onnx_accepted
否则 torch.
张量(
值.q_zero_point(),
数据类型=_qtype_vtype_map[
值.
数据类型])
)
q_value = q_value_dequantize / q_scale + q_zero_point
q_value = q_value.到(
数据类型=_qtype_vtype_map[
值.
数据类型])
返回 q_value, q_scale, q_zero_point
else:
返回 (
值,)
定义
_预追踪量化模型(
模型,
参数):
r如果模型已量化,则返回 `torch.jit.trace(model, args)`。否则不执行任何操作并返回
原始模型。
这是因为 https://github.com/pytorch/pytorch/issues/75761。
"文档"
如果
任何(
有属性(m, "_packed_params") for m
在 getattr(
模型, "modules",
列表)()
) 或者
任何(getattr(arg, "is_quantized",
错误) for
参数
在
参数):
返回 torch.
算子.
跟踪(
模型,
参数)
返回
模型
定义
模型转图(
模型,
参数,
详细模式=
错误,
输入名称=
无,
输出名称=
无,
操作符导出类型=_C_onnx.
运算符导出类型.ONNX,
执行常量折叠=
是,
_禁用 Torch 常量传播=
错误,
固定批次大小=
错误,
训练=_C_onnx.
训练模式.
评估,
动态轴=
无,
)
输入文本:
->
翻译:
-> 元组[
_C.图,
字典[
字符串, torch.
张量
]
torch.张量
| 元组[torch.
张量, ...]
| 列表[torch.
张量]
| 字典[
字符串, torch.
张量]
| 任何
| 无,
]:
将模型转换为 ONNX 图。
返回:
图:包含 ONNX 节点的 TorchScript IR 图。
params_dict:从输入参数名到参数值的字典。
torch_out:由`model`的跟踪产生的输出张量。
如果 ``model`` 是一个 :class:`torch.jit.ScriptModule` 或 :class:`torch.jit.ScriptFunction`,
这将是 None,因为我们没有进行任何跟踪。
"文档"
# TODO: 我们能否简化为总是返回一个 Tensor 或 None 的元组?
# 特殊情况,用于传递单个 Tensor 的常见情况
如果 isinstance(
参数, (torch.
张量,
整数, float,
布尔)):
args = (参数,)
模型 =
_预追踪量化模型(
模型,
参数)
图,
参数,
火炬输出,
模块 =
创建 JIT 图(
模型,
参数)
params_dict = 获取命名参数字典(
图,
参数)
尝试:
图 =
优化图(
图,
操作符导出类型,
_禁用 Torch 常量传播=
_禁用 Torch 常量传播,
固定批次大小=
固定批次大小,
params_dict=params_dict,
动态轴=
动态轴,
输入名称=
输入名称,
模块=
模块,
)
除了
异常:
_C._jit_onnx_log(火炬 IR 图在异常时:,
图)
raise
是否是脚本 = isinstance(
模型, (torch.
算子.
脚本函数, torch.
算子.
脚本模块))
如果
是否是脚本:
示例输出 =
_获取示例输出(
模型,
参数)
示例输出最终 = ()
for 示例输出
在
示例输出:
示例输出最终 +=
解包量化张量(
示例输出)
输出变量,
描述 = torch.
算子._flatten(
最终输出示例)
_C._jit_pass_onnx 分配输出形状(
图,
输出变量,
desc,
全局变量.
ONNX 形状推理,
是否是脚本,
全局变量.
导出 ONNX 操作集版本,
)
# 注意:ONNX 需要关于输出类型的完整信息,这可能会
# 被某些优化删除,因此我们需要再次显式设置它。
else:
如果
不 isinstance(
火炬输出, (
列表,
元组)):
输出包装 = [
火炬输出]
else:
输出包装 =
torch 输出
# 类型:忽略[赋值]
output_tensors, 输出描述 = torch.
算子._flatten(
元组(
输出包装))
# assign_output_shape 过程与量化输出不兼容。
# 在 ONNX 中,量化输出被展平为 3 个值,而打包为
PyTorch 中的单个值。
如果
不
任何(getattr(
输出, "is_quantized",
错误) for out
在 output_tensors):
_C._jit_pass_onnx 分配输出形状(
图,
output_tensors,
输出描述,
全局变量.
ONNX 形状推理,
是否是脚本,
全局变量.
导出 ONNX 操作集版本,
)
_设置输入和输出名称(
图,
输入名称,
输出名称)
params_dict = 获取命名参数字典(
图,
参数)
如果 (
做常量折叠
和
全局变量.
导出 ONNX 算子版本
>= _constants.ONNX 常量折叠最小操作集
):
如果
训练
是
无
或者
训练 == _C_onnx.
训练模式.
评估:
params_dict = _C._jit_pass_onnx_eval_peephole(图, params_dict)
params_dict = _C._jit_pass_onnx_constant_fold(
图, params_dict,
全局变量.
导出 ONNX 算子版本
)
_C._jit_pass_dce 允许删除具有副作用节点的操作(
图)
如果
全局变量.
ONNX 形状推理:
尝试:
_C._jit_pass_onnx_graph_shape_type_inference 操作(
图, params_dict,
全局变量.
导出 ONNX 算子版本
)
除了
运行时错误:
# 形状类型推断错误不应停止导出过程
# https://github.com/pytorch/pytorch/issues/132205
通过
params_dict = _C._jit_pass_onnx_eliminate_unused_items(图, params_dict)
对于 ONNX 操作集小于 9 的情况,常量只有三种数据类型:float16、float、double。
在这个过程中,将其他数据类型的常量转换为 float/double 并添加类型转换操作符。
如果
全局变量.
导出 ONNX 算子版本 < 9:
_C._jit_pass_onnx_cast_all_constant_to_floating(图)
params_dict = _C._jit_pass_filter_non_tensor_arguments(params_dict)
_C._jit_decay_packed_param_input_types(图)
# 如果输出名称缺少合适的名称,仅通过其唯一标识
为调试目的,给他们一个可读的名称
应用友好的调试名称(
图, params_dict)
返回
图, params_dict,
torch 输出
@deprecated(
"无法转换的操作不是最终的。请删除此函数的使用"
)
定义
无法转换的操作(
模型,
参数,
训练: _C_onnx.
训练模式 = _C_onnx.
训练模式.
评估,
opset 版本:
整型 |
无 =
无,
)
输入文本:
->
翻译:
-> 元组[_C.
图,
列表[
字符串
]]
返回由 :mod:`torch.onnx` 支持的所有操作的近似列表。
已在 2.5 版本中弃用。
无法转换的操作不是最终的。请勿使用此函数。
列表是近似的,因为在转换过程中可能会删除一些操作。
处理过程无需转换。某些其他操作可能对特定输入的转换支持有限
该输入将因特定输入而无法转换。请打开 Github 问题
模型:与 :func:`torch.onnx.export` 中的 `model` 参数相同
参数:
模型:与 :func:`torch.onnx.export` 中的 `model` 参数相同
与 :func:`torch.onnx.export` 中的 `args` 参数相同。
与 :func:`torch.onnx.export` 中的 `training` 参数相同。
与 :func:`torch.onnx.export` 中的 `opset_version` 参数相同。
返回:
JIT 图和无法转换的算子列表,格式为 "domain::op"。
"文档"
算子版本 =
算子版本
或者 _constants.
ONNX 默认 opset
全局变量.
导出 ONNX 算子版本 =
算子版本
尝试:
与
导出上下文(
模型,
训练,
详细模式=
错误):
创建一个主要干净的 JIT 图,其中包含纯 aten
我们可以用符号注册表检查的其他操作。
# 注意:我们实际上不想将任何操作转换为 ONNX 或运行任何
# 符号函数,因为失败的可能性更高,或者不可转换的操作在 ONNX 转换过程中破坏了图。
# 因为有更高的可能性在 ONNX 转换过程中失败或不可转换的操作会破坏图。
这种方式我们可以通过查看图中的操作名称来始终生成列表。
的操作。
args = _决定输入格式(
模型,
参数)
模型 =
_预追踪量化模型(
模型,
参数)
图, _, _,
模块 =
创建 JIT 图(
模型,
参数)
_C._jit_pass_inline(图)
_C._jit_pass_onnx 移除_onnx 中的就地操作(
图,
模块)
_C._jit_pass_erase_number_types(图)
_C._jit_pass_dce 允许删除具有副作用节点的操作(
图)
除了
异常
是 e:
raise 错误.
ONNX 导出器错误(
无法在 JIT 图期间发现无法转换的操作,因为出现错误
"生成过程。"
) from e
不支持的操作 =
输入文本为空,请提供需要翻译的文本
for 节点
在
图.
节点():
领域操作 = node.
类型()
如果
域操作.
以...开头((
onnx::,
prim::)):
# 我们将 onnx 和 prim 算子视为支持的操作,尽管某些"prim"
# 算子尚未实现为符号函数,因为它们可能
转换过程中已删除。用户可能仍然会看到错误,尽管它们没有出现在列表中。
即使它们没有出现在列表中,也可能由初步操作引起。
continue
如果
不
注册.
注册表.
是否已注册(
域操作.
去除字符串右侧空白字符(
“_”),
算子版本
):
# 我们认为所有已注册的操作都受支持,即使其中一些只部分支持,
# 因为还没有一种很好的方法来检查,
如果一个操作完全受支持。
TODO(justinchuby): 创建一种检查操作是否完全受支持的方法。
不支持的操作.append(
域操作)
返回
图,
不支持的操作
定义
设置跟踪模块映射(
模型: torch.
神经网络.
模块 | torch.
算子.
脚本模块,
将模块导出为函数:
布尔值 |
收藏[
类型[torch.
神经网络.
模块]],
)
输入文本:
->
翻译:
-> 设置[
字符串]:
定义
__注册属性钩子():
属性名称 =
onnx 属性
定义
_跟踪模块属性前预钩子(
模块,
输入):
setattr(模块,
属性名称,
_获取模块属性(
模块))
定义
_跟踪模块属性前钩子(
模块,
输入,
输出):
跟踪状态 = _C.
_获取追踪状态()
如果
不
跟踪状态:
返回
图 =
跟踪状态.
图()
onnx 属性 = {}
如果
有属性(
模块,
属性名称):
onnx 属性 = getattr(
模块,
属性名称)
delattr(模块,
属性名称)
_C._jit_pass_onnx_track_scope_attributes(图,
onnx 属性)
for m 在
模型.
模块():
m.注册前向钩子(
_跟踪模块属性前钩子)
m.注册前向钩子(
_跟踪模块属性前预钩子)
定义
_未限定变量名(
合法名称:
字符串)
输入文本:
->
翻译:
-> 字符串:
""
解析合格变量名并返回未合格版本。
纯数字原子被认为是不充分的,因此此函数将跳过它们,
并从第一个非数字原子开始。
示例:
>>> _unqualified_variable_name("__main__.Foo.bar")
'bar'
>>> _unqualified_variable_name("__main__.Foo.bar.0")
'bar.0'
"文档"
name_atoms = 合法名称.
分割(
“。”)
for i, 原子
在
反转(
列表(
列举(
名称原子))):
如果
不
原子.isnumeric():
返回
“。”.
连接(
名称原子[i
])
返回 qualified_name
跟踪模块映射 = {
_m: torch._C._jit_onnx_create_full_scope_name(
torch.类型名(
类型(_m)),
_未限定变量名(_n)
)
for _n, _m 在
模型.
命名模块()
}
torch.算子.
跟踪.
跟踪模块映射 =
跟踪模块映射
如果 isinstance(
将模块导出为函数,
布尔)
和
将模块导出为函数:
模块类型名称 = {torch.
类型名(
类型(
模块)) for
模块
在
跟踪模块映射}
elif isinstance(将模块导出为函数,
设置)
和
将模块导出为函数:
定义 _find_typename(v):
如果 isinstance(v,
类型):
返回 torch.
类型名(v)
else:
raise 运行时错误(
"只有 `nn.Module` 类型的才能"
"传递给 `export_modules_as_functions` 参数的集合中。"
f"获取了 `"{
类型(v).__name__}`."
)
模块类型名称 = {_find_typename(v) for v
在
将模块导出为函数}
else:
模块类型名称 =
设置()
如果
模块类型名称:
__注册属性钩子()
返回
模块类型名称
定义 _reset_trace_module_map():
torch.算子.
跟踪.
跟踪模块映射 =
无
_C._jit_pass_onnx 清除作用域记录()
定义
_获取模块属性(
模块):
注释 =
输入法.
获取类型提示(
类型(
模块))
基础 M 注解 =
输入法.
获取类型提示(torch.
神经网络.
模块)
[注释.
流行(k,
无) for k
在
base_m_标注]
# 检查模块属性是否可以被访问。一些类
# 定义属性但不提供在它们的
# 构造函数中访问它们。
#
例如,torch.nn.Embedding 有 `freeze` 变量,但类中指定的类型在构造函数中并未创建属性。换句话说,构造函数中没有 `self.freeze = `。
# type specified in the class but the attribute is not created in the
# constructor. In other words, there is no `self.freeze = <True | False>`
# in the constructor.
#
# 参考:https://github.com/pytorch/pytorch/blob/92de1d322223fb5584e384971b32c46b93bc2f4b/torch/nn/modules/sparse.py#L120
attrs = {}
for k 在
注释:
尝试:
属性[k] = getattr(
模块, k)
除了
属性错误:
_C._jit_onnx_log(f"跳过模块属性 '{k}
“”)
continue
返回 attrs
定义 _export(
模型,
参数,
f,
导出参数=
是,
详细模式=
错误,
训练=_C_onnx.
训练模式.
评估,
输入名称=
无,
输出名称=
无,
操作符导出类型=_C_onnx.
运算符导出类型.ONNX,
导出类型=
无,
opset 版本=
无,
执行常量折叠=
是,
动态轴=
无,
将初始化器作为输入保留=
无,
固定批次大小=
错误,
自定义算子集=
无,
添加节点名称=
是,
ONNX 形状推理=
是,
将模块导出为函数:
任何 =
错误,
自动微分内联=
是,
):
断言
全局变量.in_onnx_export
是
假
如果 isinstance(
模型, torch.
神经网络.
数据并行):
raise ValueError(
"torch.nn.DataParallel 不支持 ONNX "
"导出器,请使用 'attribute' 模块来 "
"从 torch.nn.DataParallel 中解包模型。尝试"
"torch.onnx.export(model.module, ...)"
)
全局变量.onnx_shape_inference = onnx_shape_inference
如果
算子版本
是
无:
算子版本 = _constants.
ONNX 默认 opset
torch.onnx.export 不支持 opset 版本 >=18
如果
算子版本 > _constants.ONNX_TORCHSCRIPT_EXPORTER_MAX_OPSET:
我们不希望失败,因为我们应该仍然允许用户为 opset>17 创建自定义符号函数
# custom symbolic functions for opset>17
警告.
警告(
f"导出到 ONNX 操作集版本"{
opset 版本}
不支持。
f通过'torch.onnx.export()'.
f支持的最高 opset 版本是{_constants.ONNX_TORCHSCRIPT_EXPORTER_MAX_OPSET}
.
f使用较新的 opset 版本时,请考虑使用'torch.onnx.export(..., dynamo=True)'。,
分类=
错误.
Onnx 导出器警告,
)
如果
将模块导出为函数
和
算子版本 < 15:
raise ValueError(
"不支持将 `opset_version` < 15 的 `export_modules_as_functions`"
"这是因为 `opset_version` < 15 表示 IR 版本 < 8,也就是说"
"不支持本地函数。"
)
如果
不
操作符导出类型:
操作导出类型 = _C_onnx.
运算符导出类型.ONNX
# 默认训练模式为 TrainingMode.EVAL,
# 这很好,因为以训练模式运行模型可能会导致
# 内部缓冲区更新,应用 dropout 等。
如果你真的懂得怎么做,你可以切换
training=TrainingMode.TRAINING 或 training=TrainingMode.PRESERVE,
(以保留原始训练模式。)
全局变量.
导出 ONNX 算子版本 =
算子版本
全局变量.
操作导出类型 =
操作导出类型
尝试:
全局变量.in_onnx_export =
真实
_autograd 内联之前 =
全局变量.
autograd 内联
全局变量.
autograd 内联 =
autograd 内联
将模块类型名导出为函数:
设置[
字符串] =
设置()
如果 isinstance(
模型, (torch.
神经网络.
模块, torch.
算子.
脚本模块)):
将模块类型名导出为函数 =
设置跟踪模块映射(
模型,
将模块导出为函数
)
与
导出上下文(
模型,
训练,
详细模式):
保持初始化器作为输入 =
决定保留初始输入(
将初始化器作为输入保留,
操作符导出类型,
opset 版本,
)
添加节点名称的值 =
决定添加节点名称(
添加节点名称,
操作导出类型
)
常量折叠 =
决定常量折叠(
执行常量折叠,
操作符导出类型,
训练
)
通常 f 可以是一个文件对象,但对于大型模型,外部数据格式需要有效的`model_file_location`。export.cpp 中的代码将强制执行此要求。
有效的`model_file_location`。Code in export.cpp will enforce this.
如果 isinstance(f,
字符串):
模型文件位置 = f
else:
模型文件位置 =
请提供需要翻译的文本
args = _决定输入格式(
模型,
参数)
如果
动态轴
是
无:
动态轴 = {}
_验证动态轴(
动态轴,
模型,
输入名称,
输出名称)
图, params_dict,
torch 输出 =
模型转图(
模型,
参数,
详细模式,
输入名称,
输出名称,
操作符导出类型,
常量折叠,
固定批次大小=
固定批次大小,
训练=
训练,
动态轴=
动态轴,
)
如果
自定义操作集
是
无:
自定义操作集 = {}
_C._jit_pass_dce 允许删除具有副作用节点的操作(
图)
节点属性转名称 = {} # type: ignore[var-annotated]
如果
将模块类型名导出为函数:
# 注意:在此步骤之后不能调用 DCE。DCE 将删除函数定义节点。
节点属性转名称 = _C._jit_pass_onnx_function_extraction(
图,
将模块类型名导出为函数,
列表(params_dict.
键()),
)
如果
保持初始化器作为输入
是
不
是:
params_dict = _C._jit_pass_onnx_deduplicate_initializers( # 类型:忽略[赋值]
图,
params_dict, # type: ignore[arg-type]
getattr(模型,
"训练",
错误), # type: ignore[arg-type]
)
_C._jit_pass_onnx 为节点和值分配作用域名称(
图)
延迟权重导出 =
假
如果
导出参数:
(
proto,
导出映射,
使用外部数据格式,
节点名称,
) = 图.
导出 ONNX(
# 类型: 忽略[attr-defined]
params_dict,
opset 版本,
动态轴,
延迟权重导出,
操作符导出类型,
不
详细模式,
将初始化值保持为 IP,
自定义算子集,
添加节点名称,
模型文件位置,
节点属性转名称,
)
else:
(
proto,
导出映射,
_,
_,
) = 图.
导出 ONNX(
# 类型: 忽略[attr-defined]
{},
opset 版本,
动态轴,
延迟权重导出,
操作符导出类型,
不
详细模式,
将初始化值保持为 IP,
自定义算子集,
添加节点名称,
模型文件位置,
节点属性转名称,
)
将函数原型插入到模型原型中。
协议 =
onnx 协议工具.
_添加 onnxscript 函数(
proto,
自定义算子集,
)
如果
详细模式:
_C._jit_onnx_log(导出图:,
图)
onnx 协议工具.
_导出文件(proto, f,
导出映射)
最后:
断言
全局变量.in_onnx_export
全局变量.in_onnx_export =
假
全局变量.
autograd 内联 =
_autograd 内联之前
_reset_trace_module_map()
返回
torch 输出
定义
应用友好的调试名称(
图,
参数):
for n 在
图.
节点():
for v 在 n.
输入():
旧名称 = v.
调试名称()
如果
旧名称 !=
字符串(v.
独特()):
continue
新名称 = f"{n.
类型()}_{v.
独特()}"
v.设置调试名称(
新名称)
如果
旧名称
在
参数:
参数[
新名称] =
参数.
流行(
旧名称)
定义
_设置输入和输出名称(
图,
输入名称,
输出名称):
定义
设置名称(
节点列表, name_list,
描述符):
如果
名称列表
是
无:
返回
如果
长度(name_list) >
长度(
节点列表):
raise 运行时错误(
f"数量为{
描述符}
提供的名称({
长度(name_list)}
)
f超出数量{
描述符}s ({
长度(
节点列表)})"
)
# 标记如果输出节点 DebugName 已设置。
输出节点设置 =
设置()
for i, (名称, node)
在
列举(zip(name_list,
节点列表)):
# 重复输出节点,插入 onnx::Identity 以避免在 setDebugName()后设置相同的 DebugName。
如果
描述符 ==
输出:
如果
节点
在
输出节点集:
标识节点 =
图.
创建(
onnx::恒等)
身份节点.
插入后(node.node())
身份节点.
添加输入(node)
身份节点.
输出().
设置类型(node.
类型())
图.
返回节点().
替换输入(i,
身份节点.
输出())
节点 =
身份节点.
输出()
输出节点集.
添加(node)
如果 node.
调试名称() !=
名称:
node.设置调试名称(
名称)
设置名称(
列表(
图.
输入()),
输入名称,
输入)
设置名称(
列表(
图.
输出()),
输出名称,
输出)
定义
运行符号方法(g,
操作符名称,
符号函数,
参数):
r""
这个跳床函数会在每次调用符号方法时被调用。
从 C++的调用。
"文档"
尝试:
图上下文 =
jit 工具.
图上下文(
图=g,
块=g.
块(),
操作集=
全局变量.
导出 ONNX 操作集版本,
原始节点=
无, # type: ignore[arg-type]
params_dict=_params_dict,
环境={},
环境中的值=
设置(),
新节点=
[]
)
返回
符号函数(
图上下文, *
参数)
除了
类型错误
是 e:
处理我们没有成功派发的特定情况
将文本翻译为简体中文如下:
# 到 symbolic_fn。否则,回溯将包含线索
您需要。
e.args = (f"{e.参数[0]}
翻译时发生{
操作符名称})",)
raise
定义
_添加块(node: _C.
节点)
输入文本:
->
翻译:
-> _C.区块:
返回 node.
添加块()
定义
添加输入到块(
块: _C.
区块):
返回
块.
向块添加输入()
# 类型: 忽略[attr-defined]
定义
添加输出到块(
块: _C.
区块,
值: _C.
值)
输入文本:
->
翻译:
-> 整数:
返回
块.
注册输出(
值)
定义
应该使用 aten 回退(
名称:
字符串,
opset 版本:
整数,
操作符导出类型: _C_onnx.
运算符导出类型
):
对于所有构建,如果 domain=="aten" 且 operator_export_type==ONNX_ATEN,
无论符号是否存在,都会创建一个 aten::ATen 操作符
是否可导出 aten 操作符 =
注册.
注册表.
是否已注册(
名称,
opset 版本)
is_onnx_aten_export = 操作导出类型 == _C_onnx.
运算符导出类型.ONNX_ATEN
is_aten_fallback_export = (
操作导出类型 == _C_onnx.
运算符导出类型.ONNX_ATEN_FALLBACK
)
如果
不
名称.
以...开头(
aten::):
返回
假
如果 is_onnx_aten_export
或者 (is_aten_fallback_export
和
不
可导出 aten 操作):
返回
真实
返回
假
定义
_获取 aten 操作重载名称(n: _C.
节点)
输入文本:
->
翻译:
-> 字符串:
# 返回非 Caffe2 构建的 ATen 操作的`overload_name`属性
架构 = n.
架构()
如果
不
架构.
以...开头(
aten::):
返回
请提供需要翻译的文本
返回 _C.
解析模式(
架构).
重载名称
定义
_运行符号函数(
图: _C.
图,
块: _C.
区块,
node: _C.节点,
输入:
任何,
环境:
字典[_C.
值, _C.
值
]
环境中的值:
设置[_C.
值
]
新节点:
列表[_C.
节点
]
操作符导出类型=_C_onnx.
运算符导出类型.ONNX,
)
输入文本:
->
翻译:
-> _C.价值 |
序列[_C.
价值 |
无] |
无:
运行符号函数。
该函数用于 C++中将节点导出为 ONNX。
返回:
单个值或值的元组。
当节点直接克隆到新图中时为 None。
"文档"
算子版本 =
全局变量.
导出 ONNX 算子版本
# 请参阅注释[就地导出]
节点种类 = node.
类型()
如果
节点类型.
以...结尾(
“_”):
处理 relu_ -> relu; add_ -> add 等。
网络操作名称 =
节点类型
[-1]
else:
网络操作名称 =
节点种类
命名空间,
操作名称 =
jit 工具.
解析节点类型(
ns 操作名)
图上下文 =
jit 工具.
图上下文(
图=
图,
块=
块,
操作集=
opset 版本,
原始节点=node,
params_dict=_params_dict,
环境=
环境,
环境中的值=
环境中的值,
新节点=
新节点,
)
直接请求 ATen 导出
如果
应该使用 aten 回退(
ns 操作名,
opset 版本,
操作符导出类型):
attrs = {
k + _ + node.
类似(k
)0]:
符号化助手.
节点获取(node, k)
for k 在 node.
属性名称()
}
输出 = node.
输出大小()
属性[
输出] =
输出
返回
图上下文.
aten 操作(
操作符名称,
*输入,
重载名称=
_获取 aten 操作重载名称(node),
**属性,
)
尝试:
域名 =
命名空间
符号函数名 = f"{
域名}::{
操作符名称}"
符号函数组 =
注册.
注册表.
获取函数组(
符号函数名
)
如果
符号函数组
是
不
无:
符号 fn =
符号函数组.
获取(
opset 版本)
如果
符号 fn
是
不
无:
# TODO 包装几乎相同的属性分配或注释差异。
attrs = {
k: 符号化助手.
节点获取(node, k) for k
在 node.
属性名称()
}
返回
符号函数(
图上下文, *
输入, **
属性)
attrs = {
k + _ + node.
类似(k
)0]:
符号化助手.
节点获取(node, k)
for k 在 node.
属性名称()
}
如果
命名空间 ==
onnx:
# 克隆节点以触发 ONNX 形状推理
返回
图上下文.
操作(
操作符名称, *
输入, **
属性,
输出=node.
输出大小()
) # 类型: 忽略[attr-defined]
raise 错误.
不支持的运算符错误(
符号函数名,
opset 版本,
符号函数组.
获取最小支持版本()
如果
符号函数组
否则
无,
)
除了
运行时错误:
如果
操作导出类型 == _C_onnx.
运算符导出类型.ONNX_FALLTHROUGH:
返回
无
elif 操作导出类型 == _C_onnx.
运算符导出类型.ONNX_ATEN_FALLBACK:
当 `operator_export_type==ONNX_ATEN_FALLBACK` 时,为非 Caffe2 构建输出 ATen 操作
attrs = {
k + _ + node.
类似(k
)0]:
符号化助手.
节点获取(node, k)
for k 在 node.
属性名称()
}
返回
图上下文.
aten 操作(
操作符名称,
*输入,
重载名称=
_获取 aten 操作重载名称(node),
**属性,
)
raise
除了
类型错误
是 e:
处理我们没有成功派发的特定情况。
否则,堆栈跟踪将包含你需要的信息线索。
e.args = (f"{e.参数[0]}
输入文本翻译为简体中文为:\n
发生翻译时{
操作符名称}
)。",)
raise
定义
验证自定义操作名称(
符号名称:
字符串):
如果
不
正则表达式.
匹配(r
^[a-zA-Z0-9-_]+::[a-zA-Z-_]+[a-zA-Z0-9-_]*$,
符号名称):
raise 错误.
ONNX 导出器错误(
f"注册操作员失败"{
符号名称}
.
"符号名称必须匹配格式 domain::name"
"并且应该以字母开头,只包含"
"字母数字字符"
)
ns, _ = jit 工具.
解析节点类型(
符号名称)
如果
命名空间 ==
onnx:
raise ValueError(
f"注册操作员失败"{
符号名称}. {ns}
域名无法修改。
)
[文档]def 注册自定义操作符号(
符号名称: str,
符号函数: Callable,
操作集版本: int,
):
注册自定义运算符的符号函数。
当用户为自定义/贡献运算符注册符号时,
建议通过 setType API 为该运算符添加形状推断。
否则导出的图在某些极端情况下可能存在形状推断错误。
例如,在 `test_operators.py` 中 `setType` 的用法是 `test_aten_embedding_2`。
请参阅模块文档中的“自定义算子”部分以获取示例用法。
参数:
符号名称 (str):自定义操作符在 "::" 中的名称
格式。
symbolic_fn (Callable):一个函数,它接受 ONNX 图和
当前操作符的输入参数,并返回新的
图中添加的操作节点。
opset_version (int): 在其中注册的 ONNX opset 版本。
"""
如果 symbolic_name.startswith("::"):
symbolic_name = f"aten{symbolic_name}"
_verify_custom_op_name(symbolic_name)
registration.custom_onnx_symbolic(symbolic_name, opset_version)(symbolic_fn)
[文档]def unregister_custom_op_symbolic(symbolic_name: str, opset_version: int):
取消注册 ``symbolic_name``。
请参阅模块文档中的“自定义运算符”部分以获取示例用法。
参数:
symbolic_name (str): 自定义运算符的名称,格式为 "::"
格式。
opset_version (int): 在其中注销的 ONNX opset 版本。
"""
如果 symbolic_name.startswith("::"):
symbolic_name = f"aten{symbolic_name}"
_verify_custom_op_name(symbolic_name)
registration.registry.unregister(symbolic_name, opset_version)
定义
_验证动态轴(
动态轴,
模型,
输入名称,
输出名称):
确保动态轴参数遵循预期格式。
如果
长度(
动态轴) == 0:
返回
如果
有属性(
模型,
"图"):
提取用于动态轴的有效输入/输出名称集
如果 (
输入名称
是
无)
或者
长度(
输入名称) == 0:
输入名称 = [x.
调试名称() for x
在
模型.
图.
输入()]
如果 (
输出名称
是
无)
或者
长度(
输出名称) == 0:
输出名称 = [y.
调试名称() for y
在
模型.
图.
输出()]
合法名称 =
设置((
输入名称
或者
[] + (
输出名称
或者 []))
如果动态轴以列表形式而不是字典形式提供,则应首先将其转换为预期的格式。如果需要,轴名称
应首先转换为预期的字典格式。如果需要,轴名称
对于动态轴,不提供名称,应自动生成名称
提供动态轴的指定输入/输出
for 键,
值
在
动态轴.
项目():
如果 key
不
在
有效名称:
警告.
警告(
f"提供的键"{
键}
动态轴不是一个有效的输入/输出名称"
)
如果 isinstance(
值,
列表):
警告.
警告(
未找到指定输入动态轴的名称。
f自动生成的名称将应用于每个输入动态轴{
键}"
)
值字典 = {}
for i, x 在
列举(
值):
如果
不 isinstance(x,
整数):
raise ValueError(
"轴索引类型应为一个整数"
)
如果 x
在 value_dict:
警告.
警告(
f"为输入提供了重复的动态轴索引"{x}
已被提供{
键}
。
)
else:
value_dict[x] = 字符串(
键) +
"_动态轴_" +
字符串(i + 1)
动态轴[
键] =
值字典
定义
模型签名(
模型: torch.
神经网络.
模块 |
可调用)
输入文本:
->
翻译:
-> 检查.
签名:
返回
检查.
签名(
模型.
前向传播
如果 isinstance(
模型, torch.
神经网络.
模块)
否则
模型
)