# mypy: 允许未类型化装饰器
# mypy: 允许未类型化定义
导入
枚举
导入
操作符
来自
打字
导入
可调用,
可选,
联合
导入
火炬
导入 torch.ao.nn.intrinsic.quantized
作为 nniq
导入 torch.ao.nn.quantized
作为 nnq
导入 torch.nn
作为
然后
来自 torch.ao.quantization
导入
模拟量化基类, ObserverBase
来自 torch.ao.quantization.observer
导入
激活后处理
来自 torch.ao.quantization.utils
导入 getattr_from_fqn
来自 torch.fx
导入
图模块
来自 torch.fx.graph
导入
节点
来自 .ns_types
导入
NS 节点目标类型,
NS 结果类型
toq = 火炬.
操作.
量化
# TODO(future PR): 考虑删除此枚举并使用 torch 类型
这可能有点棘手,因为它不是一对一的映射。
类
节点输入或输出类型(
枚举.
枚举):
FP32 = 枚举.
自动() # torch.float
INT8 = 枚举.
自动()
# torch.qint8 或 torch.quint8
FP16 = 枚举.
自动() # torch.float16
未知 =
枚举.
自动()
# 无法确定输入/输出数据类型
# TODO(未来 PR): 当这些函数可以支持多种数据类型时,
# 为了数值调试的目的,我们希望获取实际的
# 模型中使用的数据类型。我们可能需要某种类型的传播来估计它。
# 以估计这个。
FP32_OR_INT8 = 枚举.
自动()
torch.float 或 torch.quint8 或 torch.qint8
TODO(未来 PRs): 动态量化、模拟量化等
def 获取节点第一个输入和输出类型(
节点:
节点,
gm: 图模块,
logger 类:
可调用,
节点类型到输入输出类型映射:
字典[
字符串,
集合[
NS 节点目标类型]],
) -> 元组[
节点输入或输出类型,
节点输入或输出类型
]:
TODO(未来 PR): 清理此代码
FUNS_IO_TYPE_FP32 = 节点类型到输入输出类型映射["funs_io_type_fp32"]
FUNS_IO_TYPE_FP16 = 节点类型到输入输出类型映射["funs_io_type_fp16"]
FUNS_IO_TYPE_INT8 = 节点类型到输入输出类型映射["funs_io_type_int8"]
FUNS_IO_TYPE_FP32_OR_INT8 = 节点类型到输入输出类型映射["funs_io_type_fp32_or_int8"]
MODS_IO_TYPE_FP32 = 节点类型到输入输出类型映射["mods_io_type_fp32"]
MODS_IO_TYPE_INT8 = 节点类型到输入输出类型映射["mods_io_type_int8"]
MODS_IO_TYPE_FP32_OR_INT8 = 节点类型到输入输出类型映射["mods_io_type_fp32_or_int8"]
METHS_IO_TYPE_FP32_OR_INT8 = 节点类型到输入输出类型映射["meths_io_type_fp32_or_int8"]
如果
节点.
操作符 ==
调用函数:
如果
节点.
目标
在 FUNS_IO_TYPE_FP32:
返回 (
节点输入或输出类型.FP32,
节点输入或输出类型.FP32)
如果
节点.
目标
在 FUNS_IO_TYPE_FP16:
返回 (
节点输入或输出类型.FP16,
节点输入或输出类型.FP16)
elif 节点.
目标
在 FUNS_IO_TYPE_INT8:
返回 (
节点输入或输出类型.INT8,
节点输入或输出类型.INT8)
elif 节点.
目标
在 FUNS_IO_TYPE_FP32_OR_INT8:
first_arg = 获取标准化第 n 个输入(
节点, gm, 0)
断言 isinstance(first_arg,
节点)
(
_prev_node_input_type,
prev_node_output_type,
) = 获取节点第一个输入和输出类型(
first_arg, gm, logger 类,
节点类型到输入输出类型映射
)
返回 (prev_node_output_type, prev_node_output_type)
否则:
返回 (
节点输入或输出类型.
未知,
节点输入或输出类型.
未知)
elif 节点.
操作符 ==
调用模块:
断言
节点.
操作符 ==
"调用模块"
断言 isinstance(
节点.
目标,
字符串)
修饰 = getattr_from_fqn(gm,
节点.
目标)
是否为已知的 fp32 或 int8 输入模块 =
任何(
isinstance(模块,
目标类型)
为
目标类型
在 MODS_IO_TYPE_FP32_OR_INT8 # type: ignore[arg-type]
)
如果 (
isinstance(模块, (
logger 类,
观察者基类,
模拟量化基类)) # type: ignore[arg-type]
或
是否为已知的 fp32 或 int8 输入模块
):
# 日志记录器或观察者的输入输出类型是前一个节点的输出
# 类型。
first_arg = 获取标准化第 n 个输入(
节点, gm, 0)
断言 isinstance(first_arg,
节点)
(
_prev_node_input_type,
prev_node_output_type,
) = 获取节点第一个输入和输出类型(
第一个参数, gm,
logger 类,
节点类型到输入输出类型映射
)
返回 (prev_node_output_type, prev_node_output_type)
是否为已知的 fp32 输入模块 =
任何(
isinstance(模块,
目标类型)
为
目标类型
在 MODS_IO_TYPE_FP32 # type: ignore[arg-type]
)
是否为已知的 int8 输入模块 =
任何(
isinstance(模块,
目标类型)
为
目标类型
在 MODS_IO_TYPE_INT8 # type: ignore[arg-type]
)
如果
是否为已知的 fp32 输入模块:
返回 (
节点输入或输出类型.FP32,
节点输入或输出类型.FP32)
elif 是否为已知的 int8 输入模块:
返回 (
节点输入或输出类型.INT8,
节点输入或输出类型.INT8)
否则:
返回 (
节点输入或输出类型.
未知,
节点输入或输出类型.
未知)
elif 节点.
操作符 ==
"调用方法":
如果
节点.
目标 ==
反量化:
# 反量化是一个特殊的节点,因为它允许多种输入类型。
# 因此,我们查找前一个节点的输出类型并返回该类型
# 作为此节点实例的输入类型。
前节点 =
获取第 n 个标准化输入(
节点, gm, 0)
断言 isinstance(
前节点,
节点)
(
_prev_node_input_type,
prev_node_output_type,
) = get_node_first_input_and_output_type(
prev_node, gm, logger 类,
节点类型到 IO 类型映射
)
返回 (
前节点输出类型,
节点输入或输出类型.FP32)
elif 节点.
目标 ==
适用于:
# to 是一个特殊节点,因为它允许多种输入类型。
所以,我们查找前一个节点的输出类型并返回
# 作为此节点实例的输入类型。我们还查找目标
数量,返回正确的输出类型。
前节点 =
获取标准化第 n 个输入(
节点, gm, 0)
断言 isinstance(
前节点,
节点)
(
_前节点输入类型,
前节点输出类型,
) = 获取节点第一个输入和输出类型(
前节点, gm,
logger 类,
节点类型到 IO 类型映射表
)
当前节点数据类型目标 =
获取标准化第 n 个输入(
节点, gm, 1)
断言 (
当前节点数据类型目标
是
火炬.float16
), f"{当前节点数据类型目标}
需要添加处理"
返回 (prev_node_output_type,
节点输入或输出类型.FP16)
elif 节点.
目标
在 METHS_IO_TYPE_FP32_OR_INT8:
第一个参数 =
获取第 n 个归一化输入(
节点, gm, 0)
断言 isinstance(
第一个参数,
节点)
(
_prev_node_input_type,
prev_node_output_type,
) = 获取节点第一个输入和输出类型(
第一个参数, gm,
logger 类,
节点类型到 IO 类型映射
)
返回 (
前节点输出类型, prev_node_output_type)
返回 (
节点输入或输出类型.
未知,
节点输入或输出类型.
未知)
否则:
返回 (
节点输入或输出类型.
未知,
节点输入或输出类型.
未知)
def 获取节点输入参数(
节点:
节点,
gm: 图模块,
节点类型到输入输出类型映射:
字典[
字符串,
集合[
NS 节点目标类型]],
) -> 可选[
元组[
联盟[
火炬.
张量, float
]
联盟[
火炬.
张量, int]]]:
""
返回`node`的第一个输入的 qparams(缩放比例、零点),如果它们可以从图中推断出来。
如果可以,则返回。
```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)
```
prev_node = 获取标准化第 n 个输入(
节点, gm, 0)
如果 not isinstance(
前节点,
节点):
返回 None
MODS_IO_TYPE_FP32_OR_INT8 = 节点类型到输入输出类型映射[
mods_io_type_fp32_or_int8]
def 从函数参数获取_zp 比例(
节点, gm,
比例参数索引,
zp 参数索引):
比例节点 =
获取标准化第 n 个输入(
节点, gm,
缩放参数索引)
zp 节点 =
获取标准化第 n 个输入(
节点, gm, zp_arg_idx)
断言 isinstance(scale_node,
节点)
和 isinstance(scale_node.
目标,
字符串)
断言 isinstance(zp_node,
节点)
和 isinstance(
zp 节点.
目标,
字符串)
缩放对象 =
从 FQN 获取属性(gm,
缩放节点.
目标)
zp_obj = getattr_from_fqn(gm, zp_node.目标)
返回 (scale_obj, zp_obj)
如果 prev_node.
操作符 ==
调用函数:
#量化 - 直接读取参数
如果 prev_node.
目标 ==
火炬.
按张量量化:
返回
从函数参数获取_zp 比例(
前一个节点, gm, 1, 2)
elif 前一个节点.
目标
在 (toq.
添加, toq.
添加 ReLU, toq.
多, toq.mul_relu):
返回
从函数参数获取_zp 比例(
前一个节点, gm, 2, 3)
返回 None
# TODO(未来 PR): 处理更多功能
# TODO(未来 PR): 处理从输入继承 qparams 的功能操作
elif 前一个节点.
操作符 ==
调用模块:
# 获取模块类型
断言 isinstance(
前一个节点.
目标,
字符串)
模块对象 =
从 FQN 获取属性(gm,
前节点.
目标)
如果 isinstance(
module_obj,
(
氮气.
线性,
氮气.
卷积 1D,
氮气.
卷积 2D,
nniq.ConvReLU2d,
氮气.
卷积 3D,
氮气.
批标准化 2d,
氮气.
批标准化 3d,
氮气.ConvTranspose1d,
氮气.ConvTranspose2d,
氮气.ELU,
氮气.
组归一化,
氮气.InstanceNorm1d,
氮气.
实例归一化 2d,
氮气.InstanceNorm3d,
氮气.
层归一化,
氮气.
硬 swish,
氮气.
漏波 ReLU,
氮气.ReLU6,
nniq.BNReLU2d,
nniq.BNReLU3d,
nniq.ConvReLU1d,
nniq.ConvReLU2d,
nniq.ConvReLU3d,
nniq.线性 ReLU,
),
):
返回 (module_obj.
比例, module_obj.
零点)
# 类型:忽略[返回值]
是否为已知的 fp32 或 int8 输入模块 =
任何(
isinstance(module_obj, 目标类型)
为
目标类型
在 MODS_IO_TYPE_FP32_OR_INT8 # type: ignore[arg-type]
)
如果
是否为已知的 fp32 或 int8 输入模块:
返回
获取节点输入参数(
前一个节点, gm,
节点类型到输入输出类型映射)
返回 None
def 返回第一个非观察者节点(
节点:
节点,
gm: 图模块,
) -> 节点:
""
如果节点不是观察者,则返回它。如果节点是观察者,
向上遍历图并返回第一个不是的父节点
观察者。例如,
图: (node_non_obs), 节点 = node_non_obs : 返回 node_non_obs
图:node_non_obs -> obs0,node = obs0:返回 node_non_obs
图:node_non_obs -> obs0 -> fq0,node = fq0:返回 node_non_obs
```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)
```
如果
节点.
操作符 ==
调用模块:
node_obj = 从 FQN 获取属性(gm,
节点.
目标) # type: ignore[arg-type]
如果 _is_activation_post_process(node_obj):
断言
长度(
节点.
参数) == 1
断言 isinstance(
节点.
参数[0
]
节点)
节点 =
节点.
参数[0]
# 代码重复,不值得重构
断言 isinstance(
节点.
目标,
字符串)
node_obj = getattr_from_fqn(gm, 节点.
目标)
如果 _is_activation_post_process(node_obj):
断言
长度(
节点.
参数) == 1
断言 isinstance(
节点.
参数[0
]
节点)
节点 =
节点.
参数[0]
返回
节点
def 获取非参数参数的数量(
节点:
节点,
gm: 图模块,
) -> int:
""
假设所有非参数参数都出现在前面。返回节点期望的非参数参数数量。
例如,对于 F.linear(x, weight, bias)
F.linear(x, weight, bias)
返回 1,因为 x 是非参数参数,权重和偏差是参数。
对于
lstm_mod(x, hid)
返回 2,因为 x 和 hid 都是非参数参数。
```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)
```
如果
节点.
操作符 ==
调用模块:
节点对象 =
从全名获取属性(gm,
节点.
目标) # type: ignore[arg-type]
如果 isinstance(
节点对象, nn.
长短期记忆网络):
返回 2
默认为 1
返回 1
def 获取需要记录输入的参数索引(
节点:
节点) ->
列表[int
]:
""
返回节点应附加日志记录器的参数索引。
如果启用了输入记录,则如此。
例如,
*对于(x + y),返回[0, 1]
*对于(1 + y),返回[1]
*对于(x + 1),返回[0]
*对于(linear(x, w, b)),返回[0]
默认返回[0]
```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)
```
如果
长度(
节点.
参数) == 0:
返回 []
如果
节点.
操作符 ==
调用函数
和 (
# TODO(未来 PR): 使用关系图代替硬编码
节点.
目标
在 (
火炬.
添加,
火炬.
操作.
量化.
添加,
操作符.
添加)
或
节点.
目标
在 (
火炬.
多,
火炬.
操作.
量化.
多,
操作符.
多)
):
结果 = [i
为 i
在
范围(2)
如果
类型(
节点.
参数[i]) ==
节点]
返回
结果
返回 [0]
def 获取目标类型字符串(
节点:
节点, gm:
图模块) ->
字符串:
""
返回函数或模块的类型字符串表示
指向此节点,或“”用于其他节点类型。
```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)
```
目标类型 =
请提供需要翻译的文本
如果
节点.
操作符
在 (
调用函数,
"调用方法"):
目标类型 =
火炬.
类型名(
节点.
目标)
elif 节点.
操作符 ==
调用模块:
断言 isinstance(
节点.
目标,
字符串)
目标模块 = getattr_from_fqn(gm,
节点.
目标)
目标类型 =
火炬.
类型名(
目标模块)
返回
目标类型
def 在模型节点名称上重新键入日志信息(
结果:
NS 结果类型,
模型名称:
字符串,
) -> NS 结果类型:
""
重新设置结果字典的层名为使用节点名称
从 `model_name`。
例如,转换
{'base_op_1_0': {'node_output': {'model_a':
[{'ref_node_name': 'linear1', ...}]}
进入
{'linear1': {'node_output': {'model_a':
[{'ref_node_name': 'linear1', ...}]}}
注意:我们无法直接使用这些节点名称,因为它们在模型之间不一定一致。
这就是为什么我们首先提取结果,然后再重新键的原因。
因此,我们首先提取结果,然后再重新键。
```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)
```
新结果 = {}
为
旧层名称,
结果类型到结果
在
结果.
项目():
新图层名称 = None
为
模型名称到结果
在
结果类型到结果.
值():
为
当前模型名称,
结果列表
在
模型名称到结果.
项目():
如果
当前模型名称 ==
模型名称:
断言
长度(
结果列表)
新层名称 =
结果列表[0
]
["引用节点名称"]
否则:
继续
如果
新图层名称
是 not
无:
新结果[
新图层名称] =
结果类型到结果
否则:
新结果[
旧层名称] =
结果类型到结果
返回
新结果
def 可能添加缺失的全限定名(
结果:
NS 结果类型) ->
无:
""
如果`results`中的某个模型填写了`fqn`条目,则复制
将它们传递给任何未填写它们的模型。
常见的用例之一是受益于比较经过量化的模型与量化模型。在这种情况下,经过量化的模型会有 `fqn` 条目,而量化模型则不会有。
在这个情况下,经过量化的模型会有 `fqn` 条目,而量化模型则不会有。
在这个情况下,经过量化的模型会有 `fqn` 条目,而量化模型则不会有。
```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)
```
检查第一个结果以找到任何具有 fqn 条目定义的模型。
带有 fqn 的模型名称 = None
为
结果类型到结果
在
结果.
值():
为
模型名称到结果
在
结果类型到结果.
值():
为
模型名称,
模型结果
在
模型名称到结果.
项目():
如果
长度(
模型结果) > 0:
如果
模型结果[0
]
["全限定名"]
是 not
无:
带全称的模型名称 =
模型名称
断开
断开
断开
如果
带全称的模型名称:
为
结果类型到结果
在
结果.
值():
为
模型名称到结果
在
结果类型到结果.
值():
参考模型结果 =
模型名称到结果[
带全称的模型名称]
为
模型名称,
模型结果
在
模型名称到结果.
项目():
如果
模型名称 ==
模型名称及全称:
继续
为 i
在
范围(
长度(
模型结果)):
完全限定名 =
参考模型结果[i
]
["全限定名"]
模型结果[i
]
["全限定名"] =
完全限定名
def 可能先对前两个张量参数进行去量化并处理元组(f):
def 内部(*
参数, **kwargs):
a0, a1, *a_other = args
如果 (isinstance(a0,
元组)
和 isinstance(a1,
元组))
或 (
isinstance(a0, 列表)
和 isinstance(a1,
列表)
):
结果 = []
为 el0, el1
在
压缩(a0, a1):
new_args = (el0, el1, *其他)
结果.
追加(
内部(*
新参数, **kwargs))
返回
结果
elif isinstance(a0, 火炬.
张量)
和 isinstance(a1,
火炬.
张量):
如果 a0.
是否量化:
a0 = a0.反量化()
如果 a1.
是否量化:
a1 = a1.反量化()
# 对于此实用工具,仅处理浮点数
如果 a0.dtype !=
火炬.
浮点数
或 a1.dtype !=
火炬.float:
返回 None
new_args = (a0, a1, *其他)
返回 f(*
新参数, **kwargs)
返回
内部
[文档]@可能先对前两个 tensor 参数进行反量化处理并处理元组
def compute_sqnr(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
"""
计算 x 和 y 之间的 SQNR。
Args:
xt: x: 张量或张量元组
y: 张量或张量元组
Return:
浮点数或浮点数元组
"""
Ps = torch.norm(x)
Pn = torch.norm(x - y)
返回 20 * torch.log10(Ps / Pn)
[文档]@可能先对前两个张量参数进行反量化处理并处理元组
def compute_normalized_l2_error(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
"""
计算 x 和 y 之间的归一化 L2 误差。
Args:
xt: x: 张量或张量元组
y: 张量或张量元组
Return:
浮点数或浮点数元组
"""
返回 torch.sqrt(((x - y) ** 2).sum() / (x**2).sum())
[文档]@可能先对前两个 tensor 参数进行反量化处理并处理元组
def compute_cosine_similarity(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
"""
计算 x 和 y 之间的余弦相似度。
Args:
xt: x: 张量或张量元组
y: 张量或张量元组
Return:
浮点数或浮点数元组
```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)
```
# 对于卷积,量化权重的形状多一个维度
与 fp32 权重的形状相比,维度。匹配形状
# 用于启用余弦相似度比较。
x = x.reshape(1, -1)
y = y.reshape(1, -1)
return torch.nn.functional.cosine_similarity(x, y)
def 操作类型支持遮蔽(
节点:
节点) ->
布尔:
如果
节点.
操作符 ==
调用函数:
如果
节点.
目标
在 (
火炬.
添加,
火炬.
多,
操作符.
添加,
操作符.
多,
火炬.
猫,
火炬.
栈,
):
# 多个张量输入的阴影遮蔽尚未实现
返回
假
返回
真实
def 获取标准化第 n 个输入(
节点:
节点, gm:
图模块,
索引: int) ->
节点:
""
给定一个节点,获取该节点的第 n 个输入,尽可能进行归一化
尽可能地归一化其参数和关键字参数。
```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)
```
try:
norm_args_and_kwargs = 节点.
标准化参数(
gm, 标准化以仅使用 kwargs=
真实
)
如果
标准化参数和 kwargs
是 not
无:
标准化参数,
标准化参数 =
标准化参数和关键字参数
断言
长度(
标准化参数) +
长度(
标准化关键字参数) >
索引
如果
索引 <
长度(
标准化参数):
返回
标准化参数[
索引]
否则:
# note: 在 Python 3.7+ 字典是有序的
返回
列表(
标准化关键字参数.
值
()
索引]
否则:
断言
长度(
节点.
参数) +
长度(
节点.kwargs) >
索引
如果
索引 <
长度(
节点.
参数):
返回
节点.
参数[
索引]
# 类型:忽略[返回值]
否则:
kwargs_idx = 索引 +
长度(
节点.
参数)
返回
列表(
节点.kwargs.
值
()kwargs_idx]
# 类型:忽略[返回值]
除了
运行时错误:
# 这个 RuntimeError 发生在节点参数归一化时
# 需要类型提示才能继续,例如对于 torch.add,其中
# 可能第一个、第二个或两个参数都是张量
断言
长度(
节点.
参数) +
长度(
节点.kwargs) >
索引
如果
索引 <
长度(
节点.
参数):
返回
节点.
参数[
索引]
# 类型:忽略[返回值]
否则:
kwargs_idx = 索引 +
长度(
节点.
参数)
返回
列表(
节点.kwargs.
值
()kwargs_idx]
# 类型:忽略[返回值]