# mypy: 允许未类型化定义
ONNX 验证模块提供了一套验证 ONNX 模型正确性的工具。
from 未来
导入
注释
全部 = [
OnnxBackend,
VerificationOptions,
"验证",
"检查导出模型差异",
"验证信息",
"验证 ONNX 程序",
"图信息",
"OnnxTestCaseRepro 美化打印器",
"Onnx 测试用例重现",
"查找不匹配",
verify_aten_graph,
]
导入 contextlib
导入
复制
导入 dataclasses
导入 datetime
导入 difflib
导入
枚举
导入 functools
导入
输入/输出
导入 itertools
导入
操作系统
导入 tempfile
导入 typing_extensions
导入
警告
from collections.abc 导入
收藏,
映射,
序列
from 打字
导入
任何,
可调用,
联合
导入 numpy
是 np
导入 numpy.typing
是 npt
导入
火炬
导入 torch._C._onnx
是
C_onnx
from 火炬
导入 _C
from torch.onnx 导入 _constants,
_实验性,
工具
from torch.onnx._全局_
导入
全局变量
from torch.onnx._internal 导入 onnx_proto_utils
from torch.onnx._internal.exporter._verification 导入 (
验证信息,
verify_onnx_program,
)
from torch.types 导入
数
# TODO: 更新弃用信息,推荐使用新类
验证信息.__module__ =
torch.onnx.verification
verify_onnx_program.__module__ = torch.onnx.verification
# 以下内容已弃用 ####################################################################
orts_提供者 = (
CPU 执行提供者,)
_数值类型 =
并集[
数字,
火炬.
张量,
numpy.
纳维数组]
_模型类型 =
并集[
火炬.
神经网络.
模块,
火炬.
算子.
脚本模块]
输入参数类型 =
并集[
火炬.
张量,
元组[
任何, ...]]
输入关键字参数类型 =
映射[
字符串,
任何]
输出类型 =
并集[
序列[
数值类型
]
序列]
[docs]类 OnnxBackend(enum.Enum):
"""用于导出验证的 ONNX 后端使用的枚举类。"""
.. 已弃用:: 2.7
考虑使用 `torch.onnx.export(..., dynamo=True)` 并使用返回的
`ONNXProgram` 来测试 ONNX 模型。
"""
REFERENCE = "ONNX 参考评估器"
ONNX 运行时 CPU = "CPU 执行提供者"
ONNX 运行时 CUDA = "CUDA 执行提供者"
[文档]@dataclasses.dataclass
类 VerificationOptions:
ONNX 导出验证的选项。
.. deprecated:: 2.7
考虑使用 ``torch.onnx.export(..., dynamo=True)`` 并使用返回的
ONNXProgram 用于测试 ONNX 模型。
属性:
flatten: 如果为 True,将嵌套的列表/元组/字典输入展开成一个扁平化的列表
Tensors for ONNX。如果需要保留嵌套结构,请设置为 False
对于 ONNX,这在导出 ScriptModules 时通常是默认情况。默认为 True。
忽略 None:是否在 torch 输出中忽略 None 类型,这通常是
案例带有跟踪。如果 torch 输出应保持 None 类型,请将其设置为 False。
通常导出 ScriptModules 时是这样的情况。默认为 True。
检查形状:是否检查 PyTorch 和 ONNX Runtime 输出的形状
它们完全相同。将其设置为 False 以允许输出形状广播。
默认为 True。
检查 dtype:是否检查 PyTorch 和 ONNX Runtime 输出的数据类型
默认为 True,保持一致。
后端:ONNX 验证后端。默认为 OnnxBackend.ONNX_RUNTIME_CPU。
rtol:ONNX 与 PyTorch 输出比较中的相对容差。
atol:ONNX 与 PyTorch 输出比较中的绝对容差。
remained_onnx_input_idx: 如果提供,则仅传递指定的输入到 ONNX 模型。当模型中有未使用的输入时,请提供列表。
to the ONNX model. Supply a list when there are unused inputs in the model.
由于未使用的输入将在导出的 ONNX 模型中删除,提供所有输入将导致意外输入时出错。此参数告诉
all inputs will cause an error on unexpected inputs. This parameter tells
输入验证器,该验证器将传递到 ONNX 模型中。
可接受错误百分比:比较中元素不匹配的可接受百分比。
应该是一个介于 0.0 和 1.0 之间的浮点数。
"""
flatten: 布尔类型 = True
ignore_none: 布尔类型 = True
check_shape: 布尔类型 = True
check_dtype: 布尔类型 = True
backend: OnnxBackend = OnnxBackend.ONNX_RUNTIME_CPU
rtol: float = 1e-3
atol: float = 1e-7
remained_onnx_input_idx: Sequence[int] | None = None
acceptable_error_percentage: 浮点数 | None = None
定义
_展平元组(
元素):
扁平化 =
输入文本为空,请提供需要翻译的文本
为 t
在
元素:
如果 isinstance(t,
元组):
扁平化.
扩展(
_展平元组(t))
else:
扁平化.append(t)
返回
扁平化
# TODO(justinchuby): 在输入为 None 时添加类型检查,通过缩小返回类型
定义 _to_numpy(
元素)
翻译
列表 | npt.NDArray:
如果 isinstance(
元素,
火炬.
张量):
如果
元素.
需要梯度:
返回
元素.detach().cpu().numpy()
else:
返回
元素.cpu().numpy()
如果...否则 isinstance(
元素, (
列表,
元组)):
返回 [_to_numpy(
输入)
为
输入
在
元素]
如果...否则 isinstance(
元素, (
布尔, int, float)):
返回
numpy.
数组(
元素)
如果...否则 isinstance(
元素,
字典):
扁平化 =
输入文本为空,请提供需要翻译的文本
为 k
在
元素:
扁平化.
扩展
[_to_numpy(k), _to_numpy(
元素[k
))])
返回
扁平化
返回
元素
def _inline_flatten_list(输入,
资源列表)
翻译
列表:
为 i
在
输入:
资源列表.append(i)
如果
不 isinstance(
i, (列表,
元组)
) 否则 _inline_flatten_list(i,
资源列表)
返回
资源列表
def _解包为 numpy(
值, cast_onnx_accepted=
是)
翻译
列表:
解包的值 =
输入文本为空,请提供需要翻译的文本
为
值
在
值:
解包值.
扩展(
工具.
解包量化张量(
值, cast_onnx_accepted=cast_onnx_accepted)
)
返回 [_to_numpy(v)
为 v
在
解包值]
def 运行 onnx(
ONNX 会话,
输入)
翻译
输出类型:
关键词输入 = {}
如果
输入
和 isinstance(
输入[-1
]
字典):
关键词输入 =
输入[-1]
输入 =
输入
[-1]
输入 =
_解包为 numpy(
_展平元组(
输入))
ort 输入 = {}
为
输入名称,
输入
在
kw 输入.
项目():
ort 输入[
输入名称] = _to_numpy(
输入)
输入 = _to_numpy(
输入)
如果
有属性(
ONNX 会话,
获取输入):
# onnxruntime.InferenceSession
输入名称 = [i.
名称
为 i
在
ONNX 会话.
获取输入()]
如果...否则
有属性(
ONNX 会话,
输入名称):
# onnx 参考参考评估器
输入名称 =
ONNX 会话.
输入名称
else:
提升 ValueError(f
"未知 ONNX 后端类型:"{
类型(
ONNX 会话)}.")
为 i,
输入
在
列举(
输入):
如果 i ==
长度(
输入名称)
或者
输入名称[i]
在
ort 输入:
提升 ValueError(
f"位置输入太多。输入:"{
输入}
. 关键字输入:{
kw 输入}
.
f输入名称:{
输入名称}
。
)
ort 输入[
输入名称[i]] =
输入
onnx_outs = ONNX 会话.run(
无,
ort 输入)
返回 onnx_outs
def _ort 会话(
模型:
字符串 |
输入/输出.BytesIO, ort_providers:
序列[
字符串] =
orts_提供者
):
尝试:
导入 onnxruntime # type: ignore[import]
除了
导入错误
是 e:
提升
导入错误(
"onnxruntime 是导出验证所必需的。") from e
如果 ort_providers is
无:
ort_providers = orts_提供者
会话选项 = onnxruntime.SessionOptions()
抑制 ort 警告。
0:详细,1:信息,2:警告。3:错误,4:致命。默认为 2。
会话选项.
日志严重级别 = 3
ort_session = onnxruntime.推理会话(
模型
如果 isinstance(
模型,
字符串)
否则
模型.
获取值(),
会话选项,
提供者=ort_providers,
)
返回 ort_session
def _onnx 参考评估会话(
模型:
字符串 |
输入/输出.BytesIO):
尝试:
导入 onnx
from onnx 导入 reference
是
onnx 参考
# 类型: 忽略[attr-defined]
除了
导入错误
是
异常:
提升
导入错误(
"onnx >= 1.13 是参考评估器所必需的。") from exc
协议 = (
onnx.加载(
模型)
# 类型: 忽略[attr-defined]
如果 isinstance(
模型,
字符串)
否则 onnx.
从字符串加载模型(
模型.
获取值())
# 类型: 忽略[attr-defined]
)
onnx 会话 =
onnx 参考.
参考评估器(proto)
返回
onnx 会话
def _onnx 后端会话(
模型:
字符串 |
输入/输出.BytesIO,
后端:
Onnx 后端):
如果
后端 ==
Onnx 后端.
参考:
onnx 会话 =
_onnx 参考评估会话(
模型)
如果...否则
后端
在 {
Onnx 后端.
ONNX 运行时 CPU,
Onnx 后端.
ONNX 运行时 CUDA}:
onnx 会话 =
_ort 会话(
模型, (
后端.
值,))
else:
提升 ValueError(f
不支持的后端:{
后端}")
返回
onnx 会话
def 在 NumPy 中比较 ONNX 和 PyTorch 的输出(
ONNX 输出:
输出类型,
pt_outs: 输出类型,
选项:
验证选项,
):
断言
长度(
ONNX 输出) ==
长度(pt_outs), (
fONNX 运行时输出数量不同:({
长度(
ONNX 输出)}
)PyTorch:({
长度(pt_outs)})"
)
可接受错误百分比 =
选项.
可接受错误百分比
如果
可接受错误百分比
和 (
可接受错误百分比 > 1.0
或者
可接受错误百分比 < 0.0
):
提升 ValueError(
如果设置,可接受错误百分比应在 0.0 和 1.0 之间
)
为 ort_out, pt_out
在 zip(
ONNX 输出, pt_outs):
尝试:
# TODO: 一旦解决所有形状不一致的问题后,请移除 `check_shape` 选项。
如果
不
选项.
检查形状:
# 允许不同的但可广播的输出形状。
ort_out, pt_out = numpy.
广播数组(ort_out, pt_out)
火炬.
测试.
断言关闭(
ort_out,
pt_out,
相对误差=
选项.
相对误差,
精度=
选项.
精度,
检查数据类型=
选项.
检查数据类型,
等于 NaN=
是,
)
除了
断言错误
是 e:
如果
可接受错误百分比:
错误百分比 = 1 -
numpy.
总和(
numpy.isclose(ort_out, pt_out,
相对误差=
选项.
相对误差,
精度=
选项.
精度)
) / numpy.
生产(ort_out.
形状)
如果
错误百分比 <=
可接受错误百分比:
警告.
警告(
f"抑制的断言错误:"
输入文本翻译为简体中文为:\n{e}.
输入文本翻译为简体中文为:\n"
f"错误百分比"{error_percentage} "
f"在可接受范围内"{
可接受错误百分比}
。
)
continue
如果 ort_out.dtype ==
numpy.uint8
或者 ort_out.dtype ==
numpy.int8:
警告.
警告(
ONNX 输出已量化)
如果 pt_out.dtype ==
numpy.uint8
或者 pt_out.dtype ==
numpy.int8:
警告.
警告(
"PyTorch 输出已量化")
提升
def _比较 onnx 和 pytorch 输出(
ONNX 输出:
输出类型,
pt_outs: 任何,
选项:
验证选项,
):
""
比较 ONNX 和 PyTorch 的输出。
参数:
onnx_outs: ONNX 后端输出。
pt_outs: PyTorch 输出。
选项:验证选项。
抛出异常:
AssertionError:如果 ONNX 模型和 PyTorch 模型的输出在指定精度上不相等。
ValueError:如果提供的参数无效。
ValueError:如果提供的参数无效。
"文档"
如果
选项.
忽略 None。:
# torch.jit._flatten 过滤 None 类型。
pt_outs, _ = 火炬.
算子._flatten(pt_outs)
else:
pt_outs = _inline_flatten_list[pt_outs
]
[]
pt_outs_np = _解包为 numpy(pt_outs, cast_onnx_accepted=
错误)
onnx_outs = _inline_flatten_list(ONNX 输出,
[]
在 NumPy 中比较 ONNX 和 PyTorch 的输出(onnx_outs,
PyTorch 输出(NumPy 格式),
选项)
def 准备输入数据供 PyTorch 使用(
参数, kwargs):
准备输入以执行 PyTorch 模型。
在将输入发送到 PyTorch 模型之前,对输入的任何未来更改/格式化都应在该函数中完成。
该函数应处理发送到 PyTorch 模型之前的输入的任何更改/格式化。
参数:
args:PyTorch 模型前向方法的定位参数。
kwargs:PyTorch 模型前向方法的键值参数。
返回:
args:PyTorch 模型前向方法的定位参数。
kwargs:PyTorch 模型前向方法的键值参数。
"文档"
如果 isinstance(
参数, (
火炬.
张量,
字典)):
args = (参数,)
# 在位操作将更新输入张量数据。
# 因此,在每次前向调用之前都会复制输入。
args = 复制.
深拷贝(
参数)
如果 kwargs:
kwargs = 复制.
深拷贝(kwargs)
else:
kwargs = {}
返回
参数, kwargs
def _prepare_input_for_export(参数, kwargs):
"""准备输入以导出 ONNX 模型。
在将输入发送到处理之前,对输入进行任何未来的更改/格式化。
应在此函数中实现 :func:`torch.onnx.export` api。
参数:
args:PyTorch 模型前向方法的定位参数。
kwargs:PyTorch 模型前向方法的键值参数。
返回:
onnx_inputs:ONNX 模型导出的位置参数,类似于 `args`。
func:`torch.onnx.export`。
"文档"
参数, kwargs =
准备输入数据供 PyTorch 使用(
参数, kwargs)
如果
不 kwargs
和
长度(
参数) > 0
和 isinstance(
参数[-1
]
字典):
onnx_inputs = args + ({},)
如果...否则 kwargs:
onnx_inputs = args + (kwargs,)
else:
onnx_inputs = args
返回 onnx_inputs
def 准备 ONNX 输入(
参数, kwargs,
剩余的 onnx 输入索引:
序列[int] |
无,
展平:
布尔值
):
在 ONNX 后端执行 ONNX 模型之前,准备输入。
在将输入发送到 ONNX 后端之前,对输入进行任何未来的更改/格式化。
此函数中应执行运行。
参数:
args:PyTorch 模型前向方法的定位参数。
kwargs:PyTorch 模型前向方法的键值参数。
remained_onnx_input_idx:用于 ONNX 模型执行的输入索引。
展平:在派发到 ONNX 模型执行之前是否展平输入。
返回:
onnx_inputs:在 ONNX 后端中 ONNX 模型执行的定位参数。
"文档"
onnx_inputs = _prepare_input_for_export(参数, kwargs)
如果
展平:
onnx 输入, _ =
火炬.
算子._flatten(
onnx 输入)
如果...否则 onnx_inputs
和
onnx 输入[-1] == {}:
处理空 kwargs(通常在 flatten 过程中被移除)
onnx_inputs = onnx 输入
[-1]
如果
剩余的 onnx 输入索引 is
不
无:
返回 [
onnx 输入[i]
为 i
在
剩余的 onnx 输入索引]
else:
返回 onnx_inputs
定义 _try_clone_model(
模型):
用于在模型状态前向突变的情况下保留原始模型。
尝试:
返回
复制.
深拷贝(
模型)
除了
异常:
警告.
警告(
复制模型失败。模型状态可能在验证期间被突变。
)
返回
模型
定义 _compare_onnx_pytorch_model(
pt_model: _模型类型,
onnx 模型文件:
字符串 |
输入/输出.BytesIO,
输入参数:
输入参数类型,
输入参数:
输入关键字参数类型 |
无,
额外测试输入:
序列[
输入参数类型] |
无,
选项:
验证选项,
):
将 ONNX 模型运行输出与 PyTorch 模型运行输出进行比较。
参数:
pt_model: PyTorch 模型。
ONNX 模型文件路径或文件对象。
PyTorch 模型前向方法的定位参数。
PyTorch 模型前向方法的键值参数。
PyTorch 模型额外的定位参数。
前进方法。
选项:验证选项。
抛出异常:
AssertionError:如果 ONNX 模型和 PyTorch 模型的输出在指定精度上不相等。
ValueError:如果提供的参数无效。
"文档"
onnx 会话 =
_onnx 后端会话(
onnx 模型文件,
选项.
后端)
定义
比较 onnx 与 PyTorch 模型及输入(
输入参数,
输入参数):
pt 参数,
pt 参数字典 =
准备输入数据供 PyTorch 使用(
输入参数,
输入参数)
# TODO: 移除此行并单独处理模型修改。参见 #77679
pt_model_copy = _try_clone_model(pt_model)
pt_outs = pt_model_copy(*pt 参数, **pt_kwargs)
onnx_inputs = 准备 ONNX 输入(
输入参数,
输入参数,
选项.
剩余的 onnx 输入索引,
选项.
展平
)
onnx_outs = 运行 onnx(
ONNX 会话,
onnx 输入)
_比较 onnx 和 pytorch 输出(
ONNX 输出=
ONNX 输出,
pt_outs=pt_outs,
选项=
选项,
)
比较 onnx 与 PyTorch 模型及输入(
输入参数,
输入参数)
如果
额外测试输入:
为
测试输入参数
在
额外测试输入:
比较 onnx 与 PyTorch 模型及输入(
测试输入参数, {})
类
图形差异:
表示两个图之间差异的类。
定义 __init__(
自身,
图_a: _C.
图,
图_b: _C.
图):
构建一个_GraphDiff 对象。
参数:
graph_a (_C.Graph):第一个要比较的图。
图_b (_C.Graph):第二个用于比较的图。
"文档"
自身.
图_a =
图_a
自身.
图_b =
图_b
定义 __str__(
自身):
“见函数::func:`diff_report`。”
返回
自身.
差异报告()
定义
_缩进(
自身,
行:
字符串)
翻译
字符串:
返回 "
输入文本翻译为简体中文为:\n".
连接
["
制表符" +
行
为
行
在
行.
按行分割()])
定义
差异报告(
自身)
翻译
字符串:
返回图差异的字符串表示。
报告显示了第一个发生分歧的节点对。它还显示了节点对的位置。
节点对的位置。
返回:
图差表示(字符串):图差异的字符串表示。
"文档"
图_a =
自身.
图_a
图_b =
自身.
图_b
图_a_str =
字符串(
图_a)
图_b_str =
字符串(
图_b)
如果
图_a_str ==
图_b_str:
返回
请提供需要翻译的文本
graph_diff = 差分库.ndiff(
图_a_str.
按行分割(
是),
图_b_str.
按行分割(
是)
)
图形差异报告 = [
图差异:,
自身.
_缩进(
输入文本翻译为简体中文为:"".
连接(
图差分))]
为
节点_a,
节点_b
在 itertools.zip_longest(
图_a.
节点(),
图_b.
节点()):
如果
字符串(
节点_a) !=
字符串(
节点_b):
图形差异报告.append(
"第一个分叉操作符:")
节点差异 =
差分库.ndiff(
字符串(
节点_a).
按行分割(
是),
字符串(
节点_b).
按行分割(
是)
)
源打印输出 = [
"节点差异:",
自身.
_缩进(
输入文本翻译为简体中文为:"".
连接(
节点差异))]
栈_a =
节点_a.
源范围()
如果 node_a
否则
无
如果 stack_a:
source_printout.扩展(
["前源位置:",
自身.
_缩进(
字符串(stack_a))]
)
stack_b = 节点_b.
源范围()
如果
节点_b
否则
无
如果 stack_b:
source_printout.扩展(
["后者源位置:",
自身.
_缩进(
字符串(stack_b))]
)
图形差异报告.
扩展(source_printout)
断开
返回 "
输入文本翻译为简体中文为:\n".
连接(
图形差异报告)
定义
_检查图像差异(
模型:
火炬.
神经网络.
模块 |
火炬.
算子.
脚本模块,
test_input_groups: 序列[
元组[
元组[
任何, ...
]
映射[
字符串,
任何
]],
导出选项:
_实验性.
导出选项,
模型转图函数:
可调用[
[
火炬.
神经网络.
模块,
元组[
任何, ...
]
映射[
字符串,
任何
]
_实验性.
导出选项,
]
_C.图,
]
) 翻译
字符串:
检查由 `model_to_graph_func` 产生的图在 `test_input_groups` 中是否相同。
参数:
模型:参见 :func:`check_export_model_diff`。
test_input_groups:参见 :func:`check_export_model_diff`。
导出选项:参见 :func:`check_export_model_diff`。
model_to_graph_func: 将 PyTorch 模型转换为 JIT IR 图的函数。
返回:
图差表示(字符串):图差异的字符串表示。
"文档"
如果
长度(test_input_groups) < 2:
提升 ValueError(
"至少需要两组测试输入进行比较。")
引用 JIT 图 =
无
为
参数, kwargs
在 test_input_groups:
JIT 图 =
模型转图函数(
模型,
参数, kwargs,
导出选项)
如果
引用 JIT 图 is
无:
引用 JIT 图 =
JIT 图
continue
图形差异报告 =
图形差异(ref_jit_graph,
JIT 图).
差异报告()
如果
图形差异报告:
返回
图形差异报告
返回
请提供需要翻译的文本
定义
从模型中追踪的图形(
模型:
火炬.
神经网络.
模块 |
火炬.
算子.
脚本模块,
参数:
元组[
任何, ...
]
kwargs: 映射[
字符串,
任何
]
导出选项:
_实验性.
导出选项,
) 翻译 _C.
图:
作为 ONNX 导出步骤的一部分,从 PyTorch 模型创建一个追踪的 JIT 图。
参数:
模型:参见 :func:`check_export_model_diff`。
参数:参见 :func:`check_export_model_diff`。
关键字参数:参见 :func:`check_export_model_diff`。
导出选项:参见 :func:`check_export_model_diff`。
返回:
jit_graph (_C.Graph): 跟踪的 JIT 图。
"文档"
训练 =
导出选项.
训练
详细 =
导出选项.
详细
与
工具.
导出上下文(
模型,
训练,
详细模式):
导出输入 = _prepare_input_for_export(
参数, kwargs)
模型 =
工具.
_预追踪量化模型(
模型,
导出输入)
JIT 图, _, _, _ =
工具.
创建 JIT 图(
模型,
导出输入)
返回
JIT 图
定义
从模型生成 ONNX 图(
模型:
火炬.
神经网络.
模块 |
火炬.
算子.
脚本模块,
参数:
元组[
任何, ...
]
kwargs: 映射[
字符串,
任何
]
导出选项:
_实验性.
导出选项,
) 翻译 _C.
图:
作为 ONNX 导出步骤的一部分,从 PyTorch 模型导出 ONNX JIT 图。
参数:
模型:参见 :func:`check_export_model_diff`。
参数:参见 :func:`check_export_model_diff`。
关键字参数:参见 :func:`check_export_model_diff`。
导出选项:参见 :func:`check_export_model_diff`。
返回:
onnx_graph (_C.Graph):一个 ONNX 即时编译图。
"文档"
# TODO:重构 utils.py 以删除上下文设置的重复代码。参见#78834
算子版本 =
导出选项.
算子版本
操作导出类型 =
导出选项.
操作导出类型
将模块导出为函数 =
导出选项.
将模块导出为函数
训练 =
导出选项.
训练
详细 =
导出选项.
详细
动态轴 =
导出选项.
动态轴
输入名称 =
导出选项.
输入名称
输出名称 =
导出选项.
输出名称
如果
算子版本 is
无:
算子版本 = _constants.
ONNX 默认 opset
工具.
设置跟踪模块映射(
模型,
将模块导出为函数)
如果
不
操作符导出类型:
操作导出类型 = _C_onnx.
运算符导出类型.ONNX
全局变量.
导出 ONNX 算子版本 =
算子版本
全局变量.
操作导出类型 =
操作导出类型
与
工具.
导出上下文(
模型,
训练,
详细模式):
做常量折叠 =
工具.
决定常量折叠(
导出选项.
执行常量折叠,
操作符导出类型,
训练
)
如果
动态轴 is
无:
动态轴 = {}
工具.
_验证动态轴(
动态轴,
模型,
输入名称,
输出名称)
导出输入 = _prepare_input_for_export(
参数, kwargs)
导出输入 =
工具.
_决定输入格式(
模型,
导出输入)
onnx 图, _, _ =
工具.
模型转图(
模型,
导出输入,
详细模式,
输入名称,
输出名称,
操作符导出类型,
执行常量折叠,
训练=
训练,
动态轴=
动态轴,
)
返回 onnx_graph
定义 _onnx_graph_from_aten_graph(
graph: 火炬.
图,
导出选项:
_实验性.
导出选项,
params_dict: 字典[
字符串,
任何] |
无 =
无,
) 翻译
元组[
火炬.
图,
字典[
字符串,
任何
]]
如果 params_dict is
无:
params_dict = {}
操作导出类型 =
导出选项.
操作导出类型
动态轴 =
导出选项.
动态轴
或者 {}
输入名称 =
导出选项.
输入名称
训练 =
导出选项.
训练
做常量折叠 =
导出选项.
做常量折叠
算子版本 =
导出选项.
算子版本
或者 _constants.
ONNX 默认 opset
全局变量.
导出 ONNX 算子版本 =
算子版本
全局变量.
操作导出类型 =
操作导出类型
做常量折叠 =
工具.
决定常量折叠(
执行常量折叠,
操作符导出类型,
训练
)
# TODO: 下面正在进行 aten 图到 onnx 的转换。应该将其抽象为
# torch/onnx/utils.py 中的函数。
图 = graph.
复制()
图 =
工具.
优化图(
graph,
操作符导出类型,
params_dict=params_dict,
动态轴=
动态轴,
输入名称=
输入名称,
)
如果
训练 is
无
或者
训练 == _C_onnx.
训练模式.
评估:
params_dict = 火炬._C._jit_pass_onnx_eval_peephole(graph, params_dict)
如果 (
做常量折叠
和
算子版本 >= _constants.
ONNX 常量折叠最小操作集
):
params_dict = _C._jit_pass_onnx_constant_fold(graph, params_dict, opset 版本)
_C._jit_pass_dce 允许删除具有副作用节点的操作(graph)
如果
全局变量.
ONNX 形状推理:
_C._jit_pass_onnx_graph_shape_type_inference 操作(graph, params_dict,
opset 版本)
params_dict = _C._jit_pass_onnx_eliminate_unused_items(graph, params_dict)
对于 ONNX 操作集小于 9 的情况,常量只有三种数据类型:float16、float、double。
在这个过程中,将其他数据类型的常量转换为 float/double 并添加类型转换操作符。
如果
算子版本 < 9:
_C._jit_pass_onnx_cast_all_constant_to_floating(graph)
params_dict = _C._jit_pass_filter_non_tensor_arguments(params_dict)
_C._jit_decay_packed_param_input_types(graph)
_C._jit_pass_dce 允许删除具有副作用节点的操作(graph)
如果
导出选项.
详细模式:
打印(
ONNX 图:, graph)
返回 graph, params_dict
定义 _onnx_proto_from_onnx_graph(
onnx 图:
火炬.
图,
导出选项:
_实验性.
导出选项,
params_dict: 字典[
字符串,
任何
]
) 翻译
元组[
字节,
映射[
字符串,
字节
]]
算子版本 =
导出选项.
算子版本
或者 _constants.
ONNX 默认 opset
动态轴 =
导出选项.
动态轴
或者 {}
操作导出类型 =
导出选项.
操作导出类型
保持初始化器作为输入 =
工具.
决定保留初始输入(
导出选项.
将初始化器作为输入保留,
操作符导出类型,
opset 版本,
)
添加节点名称的值 =
工具.
决定添加节点名称(
是,
操作符导出类型)
自定义操作集 =
导出选项.
自定义操作集
或者 {}
proto, 导出映射, _, _ =
onnx 图.
导出 ONNX(
# 类型: 忽略[attr-defined]
params_dict,
opset 版本,
动态轴,
错误,
操作符导出类型,
不
导出选项.
详细模式,
将初始化值保持为 IP,
自定义算子集,
添加节点名称,
输入文本翻译为简体中文为:"",
{},
)
返回 proto, export_map
[文档]def check_export_model_diff(
模型: torch.nn.Module | torch.jit.ScriptModule,
test_input_groups: Sequence[tuple[tuple[Any, ...], Mapping[str, Any]]],
export_options: _experimental.ExportOptions | None = None,
) -> 字符串
验证导出模型在不同输入组之间的差异。
每个输入组导出一张图。然后比较导出的图
彼此之间,并报告第一对节点的差异。此函数
首先检查 jit 图。如果没有发现差异,然后检查 onnx
图
除非另有说明,jit/ONNX 图应保持相同
输入用于导出的输入之一。差异意味着导出的图
在运行在其他输入组时通常不准确,这通常会导致
运行时错误或输出不匹配。
Args:
模型 (torch.nn.Module 或 torch.jit.ScriptModule):要导出的模型。
测试输入组(Sequence[Tuple[Tuple[Any, ...], Mapping[str, Any]]]):一个序列
输入组用于导出模型。每个输入组是一对
(args, kwargs)
export_options (_实验性的.ExportOptions, 可选): 一个 _实验性的.ExportOptions
对象,用于控制导出行为。
返回:
str: 包含导出模型差异的字符串。
```python
# 假设输入文本为:
input_text = '"""'
# 翻译函数(此处仅为示例,实际翻译功能需要调用真实的翻译 API)
def translate_to_simplified_chinese(text):
# 这里应该调用真实的翻译 API 进行翻译
# 由于示例中不使用真实的 API,以下为模拟翻译结果
return text
# 输出翻译结果
translated_text = translate_to_simplified_chinese(input_text)
print(translated_text)
```
export_options = (
_experimental.ExportOptions() if export_options is None else export_options
)
jit_diff_report = _check_graph_diff(
模型,测试输入组,导出选项,从模型中追踪的图
)
如果启用 jit_diff_report:
返回 jit_diff_report
return _check_graph_diff(
模型, 测试输入组, 导出选项, _onnx_graph_from_model
)
[文档]@typing_extensions.
已弃用(
"torch.onnx.verification.* 已弃用。请考虑使用 torch.onnx.export(..., dynamo=True) "
使用 ONNXProgram 测试 ONNX 模型,
分类=
无,
)
定义
核实(
模型:
_模型类型,
输入参数:
输入参数类型,
输入参数:
输入关键字参数类型 |
无 =
无,
执行常量折叠:
布尔值 =
是,
动态轴:
映射[
字符串,
映射[int,
字符串] |
映射[
字符串,
序列[int]]]
| 无 =
无,
输入名称:
序列[
字符串] |
无 =
无,
输出名称:
序列[
字符串] |
无 =
无,
训练: _C_onnx.
训练模式 = _C_onnx.
训练模式.
评估,
opset 版本:
整型 |
无 =
无,
将初始化器作为输入保留:
布尔值 =
是,
详细模式:
布尔值 =
错误,
固定批次大小:
布尔值 =
错误,
使用外部数据:
布尔值 =
错误,
额外测试输入:
序列[
输入参数类型] |
无 =
无,
选项:
验证选项 |
无 =
无,
):
验证模型导出为 ONNX 与原始 PyTorch 模型是否一致。
已弃用
考虑使用 `torch.onnx.export(..., dynamo=True)` 并使用返回的
测试 ONNX 模型的 ``ONNXProgram``。
参数:
模型:参见 :func:`torch.onnx.export`。
输入参数:参见 :func:`torch.onnx.export`。
输入关键字参数:参见 :func:`torch.onnx.export`。
做常数折叠:参见 :func:`torch.onnx.export`。
dynamic_axes: 请参阅 :func:`torch.onnx.export`。
input_names: 请参阅 :func:`torch.onnx.export`。
output_names: 请参阅 :func:`torch.onnx.export`。
training: 请参阅 :func:`torch.onnx.export`。
opset_version: 请参阅 :func:`torch.onnx.export`。
keep_initializers_as_inputs: 请参阅 :func:`torch.onnx.export`。
verbose: 请参阅 :func:`torch.onnx.export`。
fixed_batch_size: 已废弃的参数,仅由 rnn 测试用例使用。
使用外部数据:明确指定是否导出包含外部数据的模型。
附加测试输入:元组列表。每个元组是一组
输入参数以进行测试。目前仅支持 `*args`。
选项:控制验证行为的 VerificationOptions 对象。
抛出异常:
AssertionError:如果 ONNX 模型和 PyTorch 模型的输出在指定精度上不相等。
ValueError:如果提供的参数无效。
ValueError:如果提供的参数无效。
"文档"
如果
选项 is
无:
选项 =
验证选项()
如果
训练 ==
火炬.onnx.
训练模式.
训练:
模型.
训练()
如果...否则
训练 ==
火炬.onnx.
训练模式.
评估:
模型.
评估()
与
火炬.
不梯度(), contextlib.
退出栈()
是
栈:
模型_f:
字符串 |
输入/输出.BytesIO =
输入/输出.BytesIO()
如果
使用外部数据:
临时目录路径 =
栈.
进入上下文(
临时文件.
临时目录())
模型_f = os.
路径.
连接(
临时目录路径,
"模型.onnx")
导出输入 = _prepare_input_for_export(
输入参数,
输入参数)
# TODO(#77679): 移除此条并单独处理模型修改。
模型复制 = _try_clone_model(
模型)
工具._export(
模型,
导出所需输入,
模型_f,
opset 版本=
opset 版本,
执行常量折叠=
执行常量折叠,
将初始化器作为输入保留=
将初始化器作为输入保留,
动态轴=
动态轴,
输入名称=
输入名称,
输出名称=
输出名称,
固定批次大小=
固定批次大小,
训练=
训练,
详细模式=
详细模式,
)
_compare_onnx_pytorch_model(
pt_model=模型副本,
onnx 模型文件=
模型_f,
输入参数=
输入参数,
输入参数=
输入参数,
额外测试输入=
额外测试输入,
选项=
选项,
)
[文档]@typing_extensions.
已弃用(
"torch.onnx.verification.* 已弃用。请考虑使用 torch.onnx.export(..., dynamo=True) "
并使用 ONNXProgram 测试 ONNX 模型
)
定义
验证 aten 图(
graph: 火炬.
图,
输入参数:
元组[
任何, ...
]
导出选项:
_实验性.
导出选项,
params_dict: 字典[
字符串,
任何] |
无 =
无,
验证选项:
验证选项 |
无 =
无,
) 翻译
元组[
断言错误 |
无,
火炬.
图,
输出类型,
输出类型]:
验证将 aten 图导出为 ONNX 与原始 PyTorch 模型的一致性。
已弃用
考虑使用 `torch.onnx.export(..., dynamo=True)` 并使用返回的
测试 ONNX 模型的 ``ONNXProgram``。
"文档"
如果
验证选项 is
无:
验证选项 =
验证选项()
如果 params_dict is
无:
params_dict = {}
原始的 JIT 图 =
图
图 = graph.
复制()
执行 aten 图并获取参考 torch jit 输出。
图输入 =
列表(graph.
输入())
JIT 输入 =
元组
[
参数
为
参数
在
输入参数
如果
参数 is
不
无])
权重 = [params_dict[v.
调试名称()]
为 v
在
图输入[
长度(
JIT 输入)
]]
断言
所有(w is
不
无
为 w
在
权重)
# TODO: 仅在检测到 Graph 中的突变时复制参数。
JIT 输入 =
复制.
深拷贝(
JIT 输入)
JIT 输入与参数 =
JIT 输入 +
元组(
权重)
jit 输出 =
火炬._C.
_jit 解释图(graph,
jit 输入和参数)
# 类型: 忽略[attr-defined]
如果
不 isinstance(jit_outs, (
列表,
元组)):
jit 输出 = [jit_outs]
将 aten 图转换为 onnx 图。
graph, onnx_params_dict = _onnx_graph_from_aten_graph(
graph, 导出选项, params_dict
)
proto, export_map = _onnx_proto_from_onnx_graph(
graph, 导出选项, onnx_params_dict
)
模型_f:
字符串 |
输入/输出.BytesIO =
输入/输出.BytesIO()
onnx 协议工具.
_导出文件(proto,
模型_f,
导出映射)
# 备注:验证不稳定。尝试捕获以输出调试信息。
尝试:
# 备注:输入可能被 dce'ed,因此我们需要从输入参数中移除这些。
新输入名称 = {v.
调试名称()
为 v
在 graph.
输入()}
新输入参数 =
输入文本为空,请提供需要翻译的文本
为 v,
参数
在 zip(
原始 jit 图.
输入(),
输入参数):
如果 v.
调试名称()
在
新输入名称:
新输入参数.append(arg)
输入参数 =
元组(
新输入参数)
onnx_inputs = 准备 ONNX 输入(
输入参数,
{},
验证选项.
剩余的 onnx 输入索引,
验证选项.
展平,
)
onnx 会话 =
_onnx 后端会话(
模型_f,
验证选项.
后端)
onnx_outs = 运行 onnx(
ONNX 会话,
onnx 输入)
删除
onnx 会话
释放设备内存
尝试:
_比较 onnx 和 pytorch 输出(
onnx_outs=onnx_outs,
pt_outs=jit_outs,
选项=
验证选项,
)
除了
断言错误
是 e:
返回 e, graph, jit_outs, onnx_outs
返回
无, graph, jit_outs, onnx_outs
除了
异常
是 e:
打印(
"验证过程中出现意外错误。")
打印(
"jit 图:",
原始 jit 图)
打印(
onnx 图:, graph)
提升 e
[文档]
类
图信息美化打印器:
图信息:
图信息 |
无
上方打印机:
图形信息美化打印机 |
无
下方打印机:
图形信息美化打印机 |
无
图结构_lambdas:
映射[int,
字符串]
连接器_str_lambdas:
映射[int,
字符串]
子节点_str_lambdas:
映射[int,
字符串]
定义 __init__(
自身,
图信息:
图信息 |
无):
自身.
图信息 =
图信息
如果 (
图信息 is
不
无
和
图信息.
上层图信息 is
不
无
和
图信息.
下层图信息 is
不
无
):
自身.
上方打印机 =
图信息美化打印器(
图信息.
上层图信息)
自身.
下位打印机 =
图信息美化打印器(
图信息.
下位图信息)
else:
自身.
上方打印机 =
无
自身.
下位打印机 =
无
定义
总行数(
自身)
翻译 int:
如果
自身.
图信息 is
无:
返回 1
如果
自身.
上方打印机
和
自身.
下方打印机:
返回 (
自身.
上方打印机.
总行数() +
自身.
下方打印机.
总行数() + 1
)
返回 2
# 两行:节点数 + ID。
定义
节点数量段字符串(
自身)
翻译
字符串:
如果
自身.
图信息 is
无:
返回 "..."
节点数量 =
自身.
图信息.
必要节点数()
存在差异 =
自身.
图信息.
存在差异()
错误节点类型 = (
f“(”{
自身.
图信息.
必要节点类型().
流行()})"
如果
节点数量 == 1
和
存在差异
否则
请提供需要翻译的文本
)
返回 f"{
节点数量} {'X'
如果
存在差异
否则 chr(
10003)} {
错误节点类型}"
定义 _graph_id_segment_str(
自身)
翻译
字符串:
如果
自身.
图信息 is
无:
返回
请提供需要翻译的文本
返回 f
"id: "{
自身.
图信息.id}"
定义
最大分段列数(
自身)
翻译 int:
返回
最大值(
地图(
长度, (
自身.
节点数量段字符串(),
自身._graph_id_segment_str()))
)
定义 _graph_segment_str_at_line(
自身,
行: int)
翻译
字符串:
获取给定行处的图段的字符串表示
如果
行 == 0:
结果字符串 =
自身.
节点数量段字符串()
结果字符串 +=
输入文本为空,请提供需要翻译的文本 * (
自身.
最大分段列数() -
长度(
结果字符串))
返回
结果字符串
如果
行 == 1:
结果字符串 =
自身._graph_id_segment_str()
结果字符串 +=
输入文本为空,请提供需要翻译的文本 * (
自身.
最大分段列数() -
长度(
结果字符串))
返回
结果字符串
如果 0 <=
行 <
自身.
总行数():
返回
输入文本为空,请提供需要翻译的文本 *
自身.
最大分段列数()
返回
请提供需要翻译的文本
定义
行连接分段字符串(
自身,
行: int)
翻译
字符串:
获取给定行的连接器段字符串。
如果
自身.
上方打印机 is
无
和
自身.
下位打印机 is
无:
返回
请提供需要翻译的文本
总行数 =
自身.
上方打印机.
总行数()
如果
自身.
上方打印机
否则 1
下方总行数 =
自身.
下方打印机.
总行数()
如果
自身.
下位打印机
否则 1
如果
行 == 0:
返回 " __"
如果...否则
行 <
总行数 + 1:
返回 " | "
如果...否则
行 ==
总行数 + 1:
返回 " |__"
如果...否则
行 <
总行数 +
下方总行数 + 1:
返回 " "
返回
请提供需要翻译的文本
定义 _children_str_at_line(
自身,
行: int)
翻译
字符串:
获取给定行下子元素的字符串表示。
递归调用 `_str_at_line` 在子节点上。
"文档"
如果
自身.
上方打印机 is
无
和
自身.
下位打印机 is
无:
返回
请提供需要翻译的文本
总行数 =
自身.
上方打印机.
总行数()
如果
自身.
上方打印机
否则 1
下方总行数 =
自身.
下方打印机.
总行数()
如果
自身.
下位打印机
否则 1
如果 0 <=
行 <
上方总行数:
返回 (
自身.
上方打印机._str_at_line(
行)
如果
自身.
上方打印机
否则 "..."
)
如果...否则
总行数 <
行 <
总行数 +
下方总行数 + 1:
返回 (
自身.
下方打印机._str_at_line(
行 -
总行数 - 1)
如果
自身.
下位打印机
否则 "..."
)
返回
请提供需要翻译的文本
定义 _str_at_line(
自身,
行: int)
翻译
字符串:
获取给定行处的图字符串表示。
返回 (
自身._graph_segment_str_at_line(
行)
+ 自身.
行连接分段字符串(
行)
+ 自身._children_str_at_line(
行)
)
定义
美化打印(
自身):
如果
自身.
图信息 is
无:
打印(
无)
返回
# 打印树。
打印(
树:.
中心(80,
等于))
总行数 =
自身.
总行数()
为
行
在
范围(
总行数):
打印(
自身._str_at_line(
行).
去除字符串右侧空白字符())
如果
自身.
图信息.
存在差异():
# 概括存在差异的叶子子图。
打印(
"差异叶子子图:".
中心(80,
等于))
打印(
[
图信息.
标识符
为
图信息
在
自身.
图信息.
所有误匹配叶图信息()
]
)
节点类型不匹配的总结
不匹配的节点类型:
字典[
字符串, int] = {}
为
图信息
在
自身.
图信息.
所有误匹配叶图信息():
节点类型 =
图信息.
必要节点类型()
如果
长度(
节点类型) == 1:
节点种类 =
节点类型.
流行()
不匹配的节点类型[
节点类型] = (
不匹配的节点类型.
获取(
节点类型, 0) + 1
)
打印(
"不匹配节点类型:".
中心(80,
等于))
打印(
不匹配的节点类型)
else:
打印(
"未找到不匹配。".
中心(80,
等于))
[文档]
类
Onnx 测试用例复现:
定义 __init__(
自身,
复现目录):
自身.
复现目录 =
复现目录
自身.proto,
自身.
输入,
自身.
输出 =
onnx 协议工具.
加载测试用例(
复现目录
)
@classmethod
定义
创建测试用例复现(
类, proto:
字节,
输入,
输出,
目录:
字符串,
名称:
字符串 |
无 =
无
):
在"{dir}/test_{name}"下创建一个 ONNX 测试用例的副本。
测试用例包含模型以及输入/输出数据。目录结构如下:
结构如下:
dir
└── test_
└┴┬┬ model.onnx
└─┴┴ test_data_set_0
└─┬┬┬ input_0.pb
┌
└── input_1.pb
┌
└── output_0.pb
┌
└── output_1.pb
参数:
proto: ONNX 模型 proto.
模型输入
模型输出
保存复现结果的目录
测试用例名称。如未指定,则根据当前时间生成名称
将被生成。
返回:
重复的路径。
"文档"
如果
名称 is
无:
名称 =
日期时间.
日期时间.
现在().strftime(
"%Y_%m_"%d_%H_%M_%S_
格式化文件")
返回
onnx 协议工具.
导出为测试用例(
proto,
_to_numpy(输入),
_to_numpy(输出),
名称,
目录,
)
定义
验证(
自身,
选项:
验证选项):
运行带有 options.backend 的 ONNX 测试用例,并与预期输出进行比较。
参数:
options:验证的选项。
提升:
AssertionError:如果 options.backend 的输出和预期输出不匹配
ValueError:如果提供的参数无效。
"文档"
onnx 会话 =
_onnx 后端会话(
输入/输出.BytesIO(
自身.proto),
选项.
后端)
运行输出 =
ONNX 会话.run(
无,
自身.
输入)
如果
有属性(
ONNX 会话,
获取输出):
输出名称 = [o.
名称
为 o
在
ONNX 会话.
获取输出()]
如果...否则
有属性(
ONNX 会话,
输出名称):
输出名称 =
ONNX 会话.
输出名称
else:
提升 ValueError(f
"未知 ONNX 会话类型:"{
类型(
ONNX 会话)}")
预期输出 = [
自身.
输出[
名称]
为
名称
在
输出名称]
在 NumPy 中比较 ONNX 和 PyTorch 的输出(
运行输出,
预期输出,
选项)
[文档]@typing_extensions.
已弃用(
"torch.onnx.verification.* 已弃用。请考虑使用 torch.onnx.export(..., dynamo=True) "
并使用 ONNXProgram 测试 ONNX 模型
)
@dataclasses.数据类
类
图信息:
GraphInfo 包含 TorchScript 图及其转换为 ONNX 图的验证信息。
已弃用
考虑使用 `torch.onnx.export(..., dynamo=True)` 并使用返回的
测试 ONNX 模型的 ``ONNXProgram``。
"文档"
graph: 火炬.
图
输入参数:
元组[
任何, ...]
params_dict: 字典[
字符串,
任何]
导出选项:
_实验性.
导出选项 = dataclasses.
字段(
默认工厂=
_实验性.
导出选项
)
_错误匹配:
断言错误 |
无 = dataclasses.
字段(
默认=
无,
初始化=
错误)
pt_outs: 序列[
数值类型] |
无 = dataclasses.
字段(
默认=
无,
初始化=
错误)
上层图信息:
图信息 |
无 = dataclasses.
字段(
默认=
无,
初始化=
错误)
下位图信息:
图信息 |
无 = dataclasses.
字段(
默认=
无,
初始化=
错误)
id: 字符串 = dataclasses.
字段(
默认=
输入文本翻译为简体中文为:"")
_onnx 图:
火炬.
图 |
无 = dataclasses.
字段(
初始化=
错误,
默认=
无)
_排除节点类型:
冻结集合[
字符串] =
冻结集合(
{prim::常量,
prim::ListConstruct,
aten::ScalarImplicit}
)
定义
清晰(
自身):
清除之前验证的状态和结果。
自身.
匹配错误 =
无
自身.pt_outs =
无
自身.
onnx 图 =
无
自身.
上层图信息 =
无
自身.
下层图信息 =
无
定义
美化打印树(
自身):
美化打印 `GraphInfo` 树。
每个节点代表一个子图,显示子图中的节点数,
以及子图在 torch 和 ONNX 之间是否存在输出不匹配的勾选标记。
子图的 ID 显示在节点下方。对于任何
通过调用 `graph_info.find_partition(id)` 可以检索子图。
示例::
==================================== 树:=====================================
5 X __2 X __1 √
id: | id: 0 | id: 00
| |
| |__1 X (激活函数)
| id: 01
|
|__3 X __1 √
id: 1 | id: 10
|
|__2 X __1 X (aten::relu)
id: 11 | id: 110
|
|__1 √
id: 111
=========================== 不匹配的叶子子图:========================
['01', '110']
============================= 不匹配的节点类型:========================
{'aten::relu': 2}
"文档"
图信息美化打印器(
自身).
美化打印()
定义
美化打印不匹配(
自身, graph:
布尔值 =
错误):
美化显示 torch 与 ONNX 之间的不匹配细节。
参数:
graph: 如果为 True,则打印 ATen JIT 图和 ONNX 图。
"文档"
打印(f
图分区不匹配信息{
自身.id}
输入文本翻译为简体中文为:": ".
中心(80,
等于))
如果 graph:
打印(
"ATen JIT 图".
中心(80,
等于))
# TODO: 一个更紧凑的图形打印器。
# * 删除步长、梯度、设备信息。
# * 在单独的一行上显示源位置。
打印(
自身.graph)
如果
自身.
onnx 图 is
不
无:
打印(
ONNX 图.
中心(80,
等于))
打印(
自身.
_onnx 图)
如果
自身.
存在差异():
打印(
"不匹配错误".
中心(80,
等于))
打印(
自身.
_错误匹配)
else:
打印(
"无不匹配".
中心(80,
等于))
定义
存在差异(
自身)
翻译
布尔:
"如果子图在 torch 和 ONNX 之间存在输出不匹配,则返回 True。"
返回
自身.
匹配错误 is
不
无
定义
必要节点数(
自身)
翻译 int:
返回子图中节点数量,不包括在 `_EXCLUDED_NODE_KINDS` 中的节点。
返回
总和(
1 为 n
在
自身.
图.
节点()
如果 n.
类型()
不
在
自身.
_排除的节点类型
)
定义
必要节点类型(
自身)
翻译
设置[
字符串]:
返回子图中节点类型的集合,排除那些在`_EXCLUDED_NODE_KINDS`中的节点类型。
返回 {
n.类型()
为 n
在
自身.
图.
节点()
如果 n.
类型()
不
在
自身.
_排除的节点类型
}
定义
所有误匹配叶图信息(
自身)
翻译
列表[
图信息]:
返回所有不匹配的`图信息`对象的列表。
如果
不
自身.
存在差异():
返回
输入文本为空,请提供需要翻译的文本
无冲突子项 = (
自身.
上层图信息 is
无
或者
不
自身.
上层图信息.
存在差异()
) 和 (
自身.
下层图信息 is
无
或者
不
自身.
下位图信息.
存在差异()
)
如果
无差异子节点:
返回 [
自身]
结果 =
输入文本为空,请提供需要翻译的文本
如果
自身.
上层图信息 is
不
无:
结果 +=
自身.
上层图信息.
所有误匹配叶图信息()
如果
自身.
下层图信息 is
不
无:
结果 +=
自身.
下位图信息.
所有误匹配叶图信息()
返回
结果
定义
查找分区(
自身, id:
字符串)
翻译
图信息 |
无:
查找具有给定 ID 的`GraphInfo`对象。
如果
标识符 ==
自身.id:
返回 self
当前长度 =
长度(
自身.id)
如果
长度(id) >
当前长度:
如果 id[
当前长度] ==
"零"
和
自身.
上层图信息 is
不
无:
返回
自身.
上层图信息.
查找分区(id)
如果...否则 id[
当前长度] ==
1
和
自身.
下层图信息 is
不
无:
返回
自身.
下位图信息.
查找分区(id)
返回
无
定义
导出重试(
自身,
复现目录:
字符串 |
无 =
无,
名称:
字符串 |
无 =
无
) 翻译
字符串:
将子图及其输入/输出数据导出为 ONNX 格式,以便重现。
重现目录将包含以下文件:
dir
└── test_
└┴┬┬ model.onnx
└─┴┴ test_data_set_0
└─┬┬┬ input_0.pb
┌
└── input_1.pb
┌
└── output_0.pb
┌
└── output_1.pb
参数:
repro_dir: 导出重放文件的目录。默认为当前工作目录,如果为空则使用。
working directory if None.
测试案例文件夹的可选名称:“test_{name}”。
返回:
导出重放目录的路径。
"文档"
如果
复现目录 is
无:
复现目录 = os.
获取当前工作目录()
复现目录 = os.
路径.
连接(
复现目录,
onnx 调试)
onnx 图, onnx_params_dict = _onnx_graph_from_aten_graph(
自身.
图,
自身.
导出选项,
自身.params_dict
)
proto, _ = _onnx_proto_from_onnx_graph(
onnx 图,
自身.
导出选项, onnx_params_dict
)
返回
Onnx 测试用例复现.
创建测试用例复现(
proto, 自身.
输入参数,
自身.pt_outs,
复现目录,
名称
)
定义
_图分区轴心(
自身)
翻译 int:
找到分区图的中心点索引。
中心点是分割图的节点。每个部分应该
除去非必要操作后,具有相同数量的节点
`_EXCLUDED_NODE_KINDS`,例如`prim::Constant`。
如果图中的节点数为奇数,则上半部分将多一个节点。
如果图中没有可以划分的节点,则返回-1。
返回:
轴点索引。
"文档"
包含的节点索引。 = [
i
为 i, n
在
列举(
自身.
图.
节点())
如果 n.
类型()
不
在
自身.
_排除的节点类型
]
半索引 =
长度(
包含节点索引) // 2 - 1
如果
半索引 >= 0
和
长度(
包含节点索引) >
半索引:
返回
包含节点索引[
半索引] + 1
返回 -1
定义
分区上层图(
自身)
翻译
火炬.
图:
轴心 =
自身.
_图分区轴心()
如果
轴心 == -1:
返回
火炬.
图()
图 =
自身.
图.
复制()
# 复制以不修改父图。
原始输出 =
列表(
图.
输出())
定义
处理桥接值为大写(
新输出:
列表[
火炬.
值
]
桥接值:
火炬.
价值
) 翻译
火炬.
值:
将桥接值添加为上层图输出。
新输出.append(
桥接值)
返回
桥接值
新输出:
列表[
火炬.
值] =
输入文本为空,请提供需要翻译的文本
处理桥接值上限 = functools.
偏函数(
处理桥接值为大写,
新输出
)
_, 删除节点,
完整上节点集, _ =
自身.
分区节点(
图,
轴心,
处理桥接值上限
)
为 _
在
列举(
原始输出):
图.
删除输出(0)
为
输出
在
新输出:
图.
注册输出(
输出)
为
节点
在
反转(
删除节点):
节点.
摧毁()
为 i,
输入
在
反转(
列表(
列举(
列表(
图.
输入())))):
如果 (
不
节点使用(
输入,
完整上节点集)
和
输入
不
在
新输出
):
尝试:
图.
删除输入(i)
除了
运行时错误
是 e:
打印(
输入,
图)
提升 e
返回
图
定义
_分区低阶图(
自身)
翻译
火炬.
图:
轴心 =
自身.
_图分区轴心()
如果
轴心 == -1:
返回
火炬.
图()
图 =
自身.
图.
复制()
# 复制以不修改父图。
原始输出 =
列表(
图.
输出())
原始输入 =
列表(
图.
输入())
定义
__处理桥接值(下)(
图:
火炬.
图,
桥接值:
火炬.
价值
) 翻译
火炬.
值:
将桥接值添加为下桥图输入。
新输入 =
图.
添加输入()
桥接值.
将所有使用替换为(
新输入)
新输入.
复制元数据(
桥接值)
返回
新输入
处理下级桥接值 = functools.
偏函数(
__处理桥接值(下),
图
)
上层节点,
下级节点, _,
完整下级节点集 =
自身.
分区节点(
图,
轴心,
处理下级桥接值
)
新输出 = [
输出
为
输出
在
原始输出
如果
由...制作(
输出,
下级节点)
]
为 _
在
列举(
原始输出):
图.
删除输出(0)
为
输出
在
新输出:
图.
注册输出(
输出)
为
输入
在
原始输入:
如果
节点使用(
输入,
完整下节点集):
新输入 =
图.
添加输入()
输入.
将所有使用替换为(
新输入)
新输入.
复制元数据(
输入)
为
节点
在
反转(
上层节点):
如果
节点
不
在
完整下节点集:
尝试:
节点.
摧毁()
除了
运行时错误
是 e:
打印(
节点,
图)
提升 e
为 _
在
原始输入:
图.
删除输入(0)
返回
图
定义
分区节点(
自身,
节点:
火炬.
节点,
完整上节点集:
设置[
火炬.
节点
]
完整下节点集:
设置[
火炬.
节点
]
原始图输出:
设置[
火炬.
值
]
覆盖桥值:
设置[
火炬.
值
]
处理桥梁值:
可调用[[
火炬.
值
]
火炬.
值
]
):
如果
节点
在
完整下节点集:
返回
如果 (
_节点有使用者_(
节点,
完整下节点集)
和
节点.
类型()
在
自身.
_排除的节点类型
):
完整下节点集.
更新(
_所有节点
[
节点]))
为
输入
在
节点.
输入():
如果
输入
在
覆盖桥值:
continue
自身.
分区节点(
输入.
节点(),
完整上节点集,
完整下节点集,
原始图输出,
覆盖桥值,
处理桥梁值,
)
else:
为
输出
在
节点.
输出():
如果
输出
在
覆盖桥值:
continue
如果 (
节点使用(
输出,
完整下节点集)
或者
输出
在
原始图输出
):
覆盖桥值.
添加(
处理桥梁值(
输出))
定义
分区节点(
自身,
图:
火炬.
图,
轴心: int,
处理桥梁值:
可调用[[
火炬.
值
]
火炬.
值
]
)
输入文本:
->
翻译:
-> 元组[
列表[
火炬.
节点
]
列表[
火炬.
节点
]
设置[
火炬.
节点
]
设置[
火炬.
节点
]]
节点 =
列表(
图.
节点())
上层节点 =
节点
[
轴心]
下层节点 =
节点[
轴心
]
`上层节点` 和 `完整的上层节点集` 的区别在于后者
# 递归包含 `upper_nodes` 的子块中的节点。
# 同样适用于 `lower_nodes` 和 `complete_lower_nodes_set`。
# 此外,`complete_lower_nodes_set` 将包括从 `upper_nodes` 确定要复制到 `lower_nodes` 的节点。
# # 确定要复制到 `lower_nodes` 的节点。
完整的节点集 =
_所有节点(
上层节点)
完整下级节点集 =
_所有节点(
下级节点)
原始图输出 =
设置(
图.
输出())
桥值是从上层图中产生的值,并被消耗
通过下级图。这些值需要成为上级图输出
# 和低级图输入,以建立交互。
所有图输入均标记为已覆盖。如果任何图输入
需要由下级图使用,则保留在下级图输入中。
覆盖桥值 =
设置(
图.
输入())
为
节点
在
上层节点:
自身.
分区节点(
节点,
完整上节点集,
完整下节点集,
原始图输出,
覆盖桥值,
处理桥梁值,
)
返回 (
上层节点,
下级节点,
完整上节点集,
完整下节点集,
)
定义 _bridge_kwargs(
自身):
pt_outs = 自身.pt_outs
图输出 =
列表(
自身.
图.
输出())
断言 pt_outs is
不
无
断言
长度(
图输出) ==
长度(pt_outs), (
f"{长度(
图输出)}
与{
长度(pt_outs)}
输入文本翻译为简体中文为:\n
图:{
自身.
图}"
)
返回 {v.
调试名称(): o for v, o
在 zip(
图输出, pt_outs)}
定义
分区图的参数和参数(
自身,
图:
火炬.
图,
桥接参数:
映射[
字符串,
_数值类型 |
序列[
数值类型]],
完整参数:
映射[
字符串,
火炬.
张量
]
完整参数:
映射[
字符串,
火炬.
张量
]
):
输入名称 = [
输入.
调试名称()
为
输入
在
图.
输入()]
args = 元组(
桥接参数[k]
为 k
在
输入名称
如果 k
在
桥接参数)
args += 元组(
完整参数[k]
为 k
在
输入名称
如果 k
在
完整参数)
params = {k: 完整参数[k]
为 k
在
输入名称
如果 k
在
完整参数}
断言
长度(
参数) +
长度(
参数) ==
长度(
输入名称), (
f"{长度(
参数)} + {
长度(
参数)}
与{
长度(
输入名称)}: {
输入名称}"
)
返回
参数, params
定义
验证导出(
自身,
选项:
验证选项
) 翻译
元组[
断言错误 |
无,
火炬.
图,
输出类型,
输出类型]:
""
验证从 TorchScript IR 图导出到 ONNX 的导出。
将 TorchScript IR 图导出到 ONNX,包括输入、参数和导出。
记录在此对象中的选项。然后根据提供的验证选项验证导出的 ONNX 图。
与提供的验证选项下的原始 TorchScript IR 图进行验证。
参数:
验证选项:验证选项。
返回:
错误:验证过程中抛出的 AssertionError。如果没有错误,则返回 None。
错误被抛出。
onnx_graph:导出的 ONNX 图以 TorchScript IR 格式。
onnx_outs:在`options`中运行导出的 ONNX 模型后的输出。
backend:在`options`中的后端。
运行 TorchScript IR 图输出的结果。
"文档"
返回
验证 aten 图(
自身.
图,
输入参数=
自身.
输入参数,
params_dict=自身.params_dict,
导出选项=
自身.
导出选项,
验证选项=
选项,
)
定义
查找不匹配(
自身,
选项:
验证选项 |
无 =
无,
):
""
找到 TorchScript IR 图和导出的 ONNX 模型之间的所有不匹配。
二分搜索模型图以找到表现出不匹配的最小子图。
为每个子图创建一个`GraphInfo`对象,记录测试输入和导出选项,以及验证结果。
记录测试输入和导出选项,以及验证结果。
参数:
验证选项:验证选项。
"文档"
自身.
清晰()
如果
选项 is
无:
选项 =
验证选项()
如果
自身.
导出选项.
详细模式:
打印(
自身.
图)
如果
长度(
列表(
自身.
图.
输出())) == 0:
返回
断言
长度(
自身.
输入参数) +
长度(
自身.params_dict) ==
长度(
列表(
自身.
图.
输入())
), (
f"图输入数量("{
长度(
列表(
自身.
图.
输入()))}
)与
f"提供的张量参数("{
长度(
自身.
输入参数)} + {
长度(
自身.params_dict)}
).
)
自身.
_错误匹配,
自身.
_onnx 图,
自身.pt_outs, _ =
自身.
验证导出(
选项
)
如果
自身.
匹配错误 is
无:
图中未找到不匹配项。
返回
如果
自身.
必要节点数() <= 1:
达到叶子节点,不再进行分区。
返回
全部参数 = {
k.调试名称(): v
为 k, v
在 zip(
自身.
图.
输入(),
自身.
输入参数)
}
完整参数集 =
自身.params_dict
上层图 =
自身.
分区上层图()
上层参数,
上层参数集 =
自身.
分区图的参数和参数(
上层图, {},
完整参数,
完整参数集
)
自身.
上层图信息 =
图信息(
上层图,
上层参数,
上层参数,
自身.
导出选项,
id=自身.
标识符 + "0",
)
自身.
上层图信息.
查找不匹配(
选项)
桥接关键字参数 =
自身.
上层图信息._bridge_kwargs()
低阶图 =
自身.
_分区低阶图()
低阶参数,
低阶参数集 =
自身.
分区图的参数和参数(
低阶图,
桥接参数,
完整参数,
完整参数集
)
自身.
下层图信息 =
图信息(
低阶图,
低阶参数,
低阶参数值,
自身.
导出选项,
id=自身.
标识符 + "1",
)
自身.
下位图信息.
查找不匹配(
选项)
定义
_所有节点(
节点:
收藏[
火炬.
节点])
翻译
设置[
火炬.
节点]:
所有节点 =
设置(
节点)
为 n
在
节点:
为 b
在 n.
块():
所有节点.
更新(
_所有节点(
列表(b.
节点())))
返回
所有节点
定义
节点使用(
值:
火炬.
值,
节点:
收藏[
火炬.
节点])
翻译
布尔:
返回
任何(
使用.
用户
在
节点
为
使用
在
值.
使用者())
定义
_节点有使用者_(
节点:
火炬.
节点,
节点:
收藏[
火炬.
节点])
翻译
布尔:
为
输出
在
节点.
输出():
如果
节点使用(
输出,
节点):
返回
真实
返回
假
定义
由...制作(
值:
火炬.
值,
节点:
收藏[
火炬.
节点])
翻译
布尔:
返回
值.
节点()
在
节点
[文档]@typing_extensions.
已弃用(
"torch.onnx.verification.* 已弃用。请考虑使用 torch.onnx.export(..., dynamo=True) "
并使用 ONNXProgram 测试 ONNX 模型
)
定义
查找不匹配(
模型:
火炬.
神经网络.
模块 |
火炬.
算子.
脚本模块,
输入参数:
元组[
任何, ...
]
执行常量折叠:
布尔值 =
是,
训练: _C_onnx.
训练模式 = _C_onnx.
训练模式.
评估,
opset 版本:
整型 |
无 =
无,
将初始化器作为输入保留:
布尔值 =
是,
详细模式:
布尔值 =
错误,
选项:
验证选项 |
无 =
无,
) 翻译
图信息:
r找出原始模型和导出模型之间的所有不匹配。
已弃用
考虑使用 `torch.onnx.export(..., dynamo=True)` 并使用返回的
测试 ONNX 模型的 ``ONNXProgram``。
实验。该 API 可能会更改。
这个工具帮助调试原始 PyTorch 模型与导出模型之间的不匹配
ONNX 模型。它通过二分搜索模型图来找到最小的子图
展示出不匹配。
参数:
模型:要导出的模型。
输入参数:模型输入参数。
与 :func:`torch.onnx.export` 中的 `do_constant_folding` 相同。
与 :func:`torch.onnx.export` 中的 `training` 相同。
与 :func:`torch.onnx.export` 中的 `opset_version` 相同。
与 :func:`torch.onnx.export` 中的 `keep_initializers_as_inputs` 相同。
verbose:与 :func:`torch.onnx.export` 中的 `verbose` 相同。
选项:不匹配验证的选项。
返回:
包含不匹配信息的 GraphInfo 对象。
示例::
>>> 导入 torch
>>> 导入 torch.onnx.verification
>>> torch.manual_seed(0)
>>> 设置 opset_version 为 15
>>> # 定义 aten::relu 的自定义符号函数。
>>> # 自定义符号函数错误,这将导致不匹配。
>>> def incorrect_relu_symbolic_function(g, self):
... return self
>>> torch.onnx.register_custom_op_symbolic(
aten::relu
incorrect_relu_symbolic_function
... opset_version=opset_version,
... )
>>> class Model(torch.nn.Module):
... def __init__(self) -> None:
... super().__init__()
... self.layers = torch.nn.Sequential(
... torch.nn.Linear(3, 4),
... torch.nn.ReLU(),
... torch.nn.Linear(4, 5),
... torch.nn.ReLU(),
... torch.nn.Linear(5, 6),
... )
... def forward(self, x):
... 返回 self.layers(x)
>>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_ONNX)
>>> graph_info = torch.onnx.verification.find_mismatch(
... 模型(),
... (torch.randn(2, 3),),
... opset_version=opset_version,
... )
===================== 图划分不匹配信息:=====================
================================ 不匹配错误 ================================
张量相似度不接近!
不匹配的元素:12 / 12 (100.0%)
最大的绝对差异:0.2328854203224182 在索引 (1, 2) 处(允许达到 1e-07)
最大相对差异:0.699536174352349 在索引(1,3)处(允许误差高达 0.001)
==================================== 树:=====================================
5 X __2 X __1 √
id: | id: 0 | id: 00
| |
| |__1 X (激活函数)
| id: 01
|
|__3 X __1 √
id: 1 | id: 10
|
|__2 X __1 X (aten::relu)
id: 11 | id: 110
|
|__1 √
id: 111
=========================== 不匹配的叶子子图:========================
['01', '110']
============================= 不匹配的节点类型:========================
{'aten::relu': 2}
"文档"
如果
选项 is
无:
选项 =
验证选项()
如果
算子版本 is
无:
算子版本 = _constants.
ONNX 默认 opset
从 aten 图开始,在图分区上执行二分搜索以找到操作导出差异。
# TODO: 从 utils.py 的`export`复制到`_optimize_graph`。
如果
训练 ==
火炬.onnx.
训练模式.
训练:
模型.
训练()
如果...否则
训练 ==
火炬.onnx.
训练模式.
评估:
模型.
评估()
与
火炬.
不梯度():
导出输入 = _prepare_input_for_export(
输入参数, {})
args = 工具.
_决定输入格式(
模型,
导出所需输入)
模型 =
工具.
_预追踪量化模型(
模型,
参数)
图,
参数,
_torch 输出,
_模块 =
工具.
创建 JIT 图(
模型,
参数)
params_dict = 工具.
获取命名参数字典(
图,
参数)
工具.
应用友好的调试名称(
图, params_dict)
图信息 =
图信息(
图,
输入参数,
params_dict,
_实验性.
导出选项(
执行常量折叠=
执行常量折叠,
训练=
训练,
opset 版本=
opset 版本,
将初始化器作为输入保留=
将初始化器作为输入保留,
详细模式=
详细模式,
),
)
图信息.
查找不匹配(
选项)
图信息.
美化打印不匹配()
图信息.
美化打印树()
返回
图信息