来自
未来
导入
注释
__all__ = [
"验证信息",
"验证 ONNX 程序",
]
导入 dataclasses
导入
记录日志
导入
数学
来自
打字
导入
任意,
类型检查
导入
火炬
来自 torch.utils
导入 _pytree
如果
类型检查:
来自 onnxscript
导入 ir
来自 torch.onnx._internal.exporter
导入
_onnx 程序
日志记录器 =
记录日志.
获取日志记录器(__name__)
@dataclasses.数据类
类
验证信息:
验证 ONNX 程序中一个值的验证信息。
这个类包含最大绝对差异、最大相对差异、
预期值与实际值之间的绝对和相对差异直方图
值。它还包括预期和实际的数据类型。
历史图表示为张量的元组,其中第一个张量是历史图计数,第二个张量是边界。
名称:值的名称(输出或中间值)。
属性:
名称:值的名称(输出或中间值)。
最大绝对差:预期值与实际值之间的最大绝对差。
最大相对差:预期值与实际值之间的最大相对差。
绝对差直方图:表示绝对差直方图的张量元组。
第一个张量是直方图计数,第二个张量是箱边。
rel_diff_hist:表示相对差异直方图的张量元组。
第一个张量是直方图计数,第二个张量是箱边。
expected_dtype:预期值的类型。
actual_dtype:实际值的类型。
```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)
```
名称:
字符串
最大绝对差:
浮点数
最大相对差:
浮点数
绝对差分历史:
元组[
火炬.
张量,
火炬.
张量]
相对差分历史:
元组[
火炬.
张量,
火炬.
张量]
预期数据类型:
火炬.dtype
实际数据类型:
火炬.dtype
# NOTE: 我们不需要包括形状,因为预期的形状已经已知
# 并且由运行时检查
[文档] @类方法
def from_tensors(
cls,
name: str,
expected: torch.Tensor | float | int | bool,
actual: torch.Tensor | float | int | bool,
) -> VerificationInfo:
"""从两个张量创建一个 VerificationInfo 对象。
Args:
名称:值的名称。
预期:预期的张量。
实际:实际的张量。
返回值:
验证信息对象:VerificationInfo 对象。
"""
如果 expected 不是 torch.Tensor 类型:
expected = torch.tensor(expected)
如果实际不是 torch.Tensor 类型:
实际 = torch.tensor(actual)
max_abs_diff, max_rel_diff, abs_diff, rel_diff = _compare_tensors(
预期,实际
)
bins = torch.tensor(
[0.0, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1.0, 10, 1000000],
dtype=torch.float,
)
abs_diff_hist = torch.histogram(abs_diff.float(), bins=bins)
rel_diff_hist = torch.histogram(rel_diff.float(), bins=bins)
return cls(
name=name,
max_abs_diff=max_abs_diff,
max_rel_diff=max_rel_diff,
abs_diff_hist=abs_diff_hist,
rel_diff_hist=rel_diff_hist,
expected_dtype=expected.dtype,
actual_dtype=actual.dtype,
)
def 比较张量(
预期:
火炬.
张量,
实际:
火炬.
张量,
) -> 元组[float, float,
火炬.
张量,
火炬.
张量
]:
将张量移动到同一设备
预期 =
预期.detach().cpu()
实际的 =
实际.detach().cpu()
如果
预期.
元素数量() == 0
或
实际.
元素数量() == 0:
返回
数学.
无穷,
数学.
无穷,
火炬.
张量(
数学.
无穷),
火炬.
张量(
数学.
无穷)
如果
预期.dtype ==
火炬.
布尔:
预期 =
预期.
到(
火炬.float32)
实际的 =
实际.
到(
火炬.float32)
如果
火炬.
是复杂的(
预期):
预期 =
火炬.
真实查看(
预期)
绝对差 =
火炬.
绝对值(
预期 -
实际)
eps = 1e-7
标准化器 =
火炬.
绝对值(
预期) + eps
相对差 =
绝对差 /
标准化器
最大绝对差异 =
绝对差.
最大值().
项目()
最大相对差异 =
相对差异.
最大值().
项目()
返回
最大绝对差值,
最大相对差值,
绝对差,
相对差
def verify_onnx_program(
ONNX 程序: _onnx_program.
ONNX 程序,
参数:
元组[
任意, ...] | None =
无,
kwargs: 字典[
字符串,
任意] | None =
无,
比较中间变量:
布尔类型 =
错误,
) -> 列表[
验证信息
]:
验证 ONNX 模型,通过比较与从 ExportedProgram 导出的预期值。
参数:
onnx_program: 要验证的 ONNX 程序。
args: 模型的输入参数。
kwargs: 模型的关键字参数。
比较中间值:是否验证中间值。这是否
因此需要更长时间,所以默认情况下已禁用。
返回:
包含每个值验证信息的 VerificationInfo 对象。
```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)
```
导出程序 =
onnx 程序.
导出程序
如果
导出程序
是
无:
抛出异常 ValueError(
"ONNX 程序不包含导出的程序。"
"请提供一个导出的程序以验证 ONNX 程序。"
)
如果 args
是 None
和 kwargs
是
无:
# 用户未提供示例输入,使用默认示例输入
如果
导出的程序.
示例输入
是
无:
抛出异常 ValueError(
"未提供示例输入,且 exported_program 不包含示例输入。"
"请提供参数以验证 ONNX 程序。"
)
参数, kwargs =
导出的程序.
示例输入
如果 args
是
无:
args = ()
如果 kwargs
是
无:
kwargs = {}
# 将 ONNX 程序和 VerificationInterpreter 的参数扁平化
平铺参数, _ =
导出的程序._get_flat_args_with_check(
参数, kwargs)
如果 not
比较中间结果:
# Compare the output values
火炬输出, _ = _pytree.
树扁平化(
导出的程序.
模块()(*
参数, **kwargs)
)
ONNX 输出 =
ONNX 程序(*
平铺参数)
结果 = []
为
火炬输出,
ONNX 输出,
输出值
在
压缩(
火炬输出,
ONNX 输出,
onnx 程序.
模型.
图.
输出
):
结果.
追加(
验证信息.
从张量中(
名称=
字符串(
输出值.
名称),
预期=
torch 输出,
实际=
onnx 输出,
)
)
返回
结果
使用 _VerificationInterpreter 获取中间值
按设计输出值也包含在内
解释器 = _VerificationInterpreter(onnx_program)
解释器.run(*
平铺参数)
返回
解释器.
验证信息
def 创建值映射(
图: ir.
图) ->
字典[
字符串, ir.
值
]:
返回一个字典,将名称映射到图中的值。
该映射不包括子图中的值。
参数:
图:提取映射的图。
返回:
一个将名称映射到值的字典。
```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)
```
values = {}
值.
更新(
图.
初始化器)
值的名称可以是 None 或 "",我们需要排除这些。
为
输入
在
图.
输入:
如果 not
输入.
名称:
继续
值[
输入.
名称] =
输入
为
节点
在
图:
为 value
在
节点.
输出:
如果 not
值.
名称:
继续
值[
值.
名称] = value
返回 values
类 _VerificationInterpreter(
火炬.fx.
解释器):
"用于验证转换后的 ONNX 模型准确性的解释器,通过比较中间值。"
要比较模型,首先使用 ONNX 程序初始化解释器。
然后,使用输入参数调用 :meth:`run` 方法来执行模型。
The :meth:`run` 方法将执行模型并填充每个值的验证信息。
attr:`verification_infos` 属性包含每个值的验证信息。
::
onnx_program = torch.onnx.export(model, args, dynamo=True)
interpreter = _VerificationInterpreter(onnx_program)
interpreter.run(*args)
verification_infos = interpreter.verification_infos
for info in verification_infos:
print("value name:", info.name, info)
验证信息包括最大绝对差,最大相对差
差异以及预期值之间绝对和相对差异的直方图
实际值。请参阅::class:`VerificationInfo` 以获取更多详细信息。
属性:
verification_infos:每个值的验证信息列表。
当调用 `run` 方法时,它会被填充。
```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)
```
def 初始化(
自身, onnx_program:
火炬.onnx.
ONNX 程序) ->
无:
"""使用 ONNX 程序初始化 _VerificationInterpreter。"""
参数:
onnx_program: 要验证的 ONNX 程序。
```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)
```
如果
onnx 程序.
导出程序
是
无:
抛出异常 ValueError(
"ONNX 程序不包含 exported_program。"
"请提供一个 exported_program 以验证 ONNX 程序。"
)
超级().
初始化(
onnx 程序.
导出的程序.
模块())
自身.
_onnx 程序 =
onnx 程序
自身.
_onnx 值 =
_创建值映射(
onnx 程序.
模型.
图)
自身._args:
元组[
任意, ...] = ()
自身.
验证信息:
列表[
验证信息] = []
def run(
自身,
*参数:
任意,
初始环境:
字典[
火炬.fx.
节点,
任意] | None =
无,
启用 IO 处理:
布尔类型 = True,
) -> 任意:
使用给定的输入参数运行解释器。
此方法执行模型并填充每个值的验证信息到 :attr:`verification_infos` 属性。
以每个值的验证信息填充 :attr:`verification_infos` 属性。
参数:
输入参数:模型的输入参数。
初始环境:解释器的初始环境。
启用 IO 处理:是否启用 IO 处理。
返回:
任何:执行模型的结果。
```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)
```
自身.
验证信息 = []
自身._args = args
返回
超级().run(
*参数,
初始环境=
初始环境,
启用 IO 处理=
启用 IO 处理,
)
def 运行节点(
自身, n:
火炬.fx.
节点) ->
任意:
结果 =
超级().
运行节点(n)
如果 n.
操作符 !=
调用函数:
返回
结果
节点名称 = n.
名称
如果
节点名称 not
在
自身._onnx_values:
返回
结果
try:
(onnx 结果,) =
自身._onnx_program.
计算值([
节点名称
]
自身._args)
除了
异常
作为 e:
记录器.
警告(
无法计算节点值%s: %s",
节点名称,
e,
)
返回
结果
信息 =
验证信息.from_tensors(
名称=
节点名称,
预期=
结果,
实际=onnx_result,
)
自身.verification_infos.
追加(
信息)
如果
信息.max_abs_diff > 0.01
或
信息.
最大相对差异 > 0.1:
记录器.
警告(
节点验证信息%s
最大绝对差异:%s
,最大相对差异:%s",
节点名称,
信息.
最大绝对差,
信息.
最大相对差,
)
否则:
记录器.
信息(
节点验证信息%s
最大绝对差值:%s
, 最大相对差值:%s",
节点名称,
信息.
最大绝对差,
信息.
最大相对差,
)
返回
结果