• 文档 >
  • 模块代码 >
  • torch >
  • torch.ao.ns._numeric_suite_fx
快捷键

torch.ao.ns._numeric_suite_fx 的源代码

# mypy: 允许未类型化定义
""
本模块包含用于比较模型权重和激活的工具
在模型之间。示例用法:

导入 copy
导入 torch
导入 torch.ao.quantization.quantize_fx 作为 quantize_fx
导入 torch.ao.ns._numeric_suite_fx 作为 ns

    m = torch.nn.Sequential(torch.nn.Conv2d(1, 1, 1)).eval()
    mp = quantize_fx.prepare_fx(m, {'': torch.ao.quantization.default_qconfig})
# 我们转换一个副本,因为我们需要原始的已准备模型
# 用于比较,并且 `quantize_fx.convert_fx` 是原地转换。
    mq = quantize_fx.convert_fx(copy.deepcopy(mp))

    #
# 比较权重
    #

# 提取权重对
    weight_comparison = ns.extract_weights('a', mp, 'b', mq)

为每个比较添加 SQNR,就地
    ns.extend_logger_results_with_comparison(
        weight_comparison, 'a', 'b', torch.ao.ns.fx.utils.compute_sqnr,
        'sqnr')

# 权重比较包含从 `mp` 和 `mq` 存储的权重,并可用于进一步分析。
以成对形式出现,可用于进一步分析。


    #
比较激活,带有错误传播
    #

添加日志记录器
    mp_ns, mq_ns = ns.add_loggers(
'a', 深拷贝(mp),
'b', 深拷贝(mq),
        ns.OutputLogger)

# 发送一个示例数据以捕获中间激活
    datum = torch.randn(1, 1, 1, 1)
mp_ns(数据)
mq_ns(数据)

提取中间激活
    act_comparison = ns.extract_logger_info(
        mp_ns, mq_ns, ns.OutputLogger, 'b')

为每个比较添加 SQNR,就地
    ns.extend_logger_results_with_comparison(
        act_comparison, 'a', 'b', torch.ao.ns.fx.utils.compute_sqnr,
        'sqnr')

# act_comparison 包含了 `mp_ns` 和 `mq_ns` 的激活,并存储成对,可用于进一步分析。
以成对形式出现,可用于进一步分析。

    #
# 比较激活,不进行错误传播
    #

创建阴影模型
    mp_shadows_mq = ns.add_shadow_loggers(
'a', 深拷贝(mp),
'b', 深拷贝(mq),
        ns.OutputLogger)

# 发送一个示例数据以捕获中间激活
    datum = torch.randn(1, 1, 1, 1)
    mp_shadows_mq(datum)

提取中间激活
    shadow_act_comparison = ns.extract_shadow_logger_info(
        mp_shadows_mq, ns.OutputLogger, 'b')

为每个比较添加 SQNR,就地
    ns.extend_logger_results_with_comparison(
        shadow_act_comparison, 'a', 'b', torch.ao.ns.fx.utils.compute_sqnr,
        'sqnr')

# shadow_act_comparison 包含从 `mp_ns` 和 `mq_ns` 存储的激活
以成对形式出现,可用于进一步分析。

""

导入 集合
 打字 导入 任何, 可调用, 可选, 类型检查

导入 火炬
导入 torch.ao.quantization.quantize_fx  量化效果
导入 torch.nn  神经网络
from torch.ao.ns.fx.graph_matcher 导入 获取匹配的子图对
from torch.ao.ns.fx.mappings 导入 获取基本名称到相关操作集合的映射
from torch.ao.ns.fx.n_shadows_utils 导入 (
    获取去重子图,
    创建添加日志记录器的图,
    创建 n 个转换并记录的子图副本,
    创建结果比较,
    提取权重比较,
    按子图分组结果,
    输出属性,
    打印 n 个阴影摘要,
    阴影包装节点名称前缀,
)
from torch.ao.ns.fx.qconfig_multi_mapping 导入 QConfigMultiMapping
from torch.ao.quantization 导入 QConfig 映射
from torch.ao.quantization.backend_config 导入 后端配置
from torch.ao.quantization.backend_config.utils 导入 (
    get_fusion_pattern_to_root_node_getter,
)
from torch.ao.quantization.fx.graph_module 导入 _get_observed_graph_module_attr
from torch.ao.quantization.fx.量化匹配工具 导入 _find_matches
from torch.ao.quantization.fx.量化配置映射工具 导入 (
    _generate_node_name_to_qconfig,
)
from torch.ao.quantization.fx.quantize_handler 导入 _get_pattern_to_quantize_handlers
from torch.fx 导入 图模块
 torch.fx.graph 导入 节点

来自 .fx.graph_passes 导入 add_loggers_to_model, 创建阴影_b
from .fx.ns_types 导入 NS 节点目标类型, NS 结果类型, NSSingleResultValuesType
from .fx.utils 导入 (
    获取目标类型字符串,
    可能添加缺失的全限定名,
    在模型节点名称上重新键入日志信息,
)
from .fx.权重工具 导入 从节点提取权重


如果 类型检查:
    from torch.ao.quantization.qconfig 导入 QConfigAny

RNN 返回类型 = 元组[火把.张量, 元组[火把.张量, 火把.张量]]


[文档] 输出记录器(神经网络.模块): "" 捕获中间值的基类。 "文档" 统计: 列表[torch.张量] 统计 RNN: 列表[RNN 返回类型] # 标记为不纯,以便在 DCE 期间调用它时不会被删除。 _is_impure = 真实 def __init__( , 参考节点名称: 字符串, 前节点名称: 字符串, 模型名称: 字符串, 参考名称: 字符串, 前节点目标类型: 字符串, 引用节点目标类型: 字符串, 结果类型: 字符串, 参数内索引: 整数, 索引参数: 整数, 完全限定名: 可选[字符串], qconfig 字符串: 可选[字符串] = 输入文本翻译为简体中文为:"", ): 超级().__init__() .统计: 列表[PyTorch.张量] = 输入文本为空,请提供需要翻译的文本 .统计 RNN: 列表[RNN 返回类型] = 输入文本为空,请提供需要翻译的文本 负责添加此日志记录器的节点名称 备注: - 如果我们正在记录节点输出,则此名称与 prev_node_name 相同 - 如果我们正在记录节点输入,则此名称为节点名称 # 该记录器记录的输入。 # # 例如,logger1 记录 op1 的输入,logger2 记录 # op1 的输出: # # x1 -> logger1 -> op1 -> logger2 -> x2 # # 在此示例中, # - logger1 的前节点名称是 x1,引用节点名称是 op1 # - logger2 的前节点名称是 op1,引用节点名称是 op1 .节点引用名称 = 节点引用名称 # 该 Logger 捕获输出的节点名称 .前节点名称 = 前节点名称 模型节点的来源名称 .模型名称 = 模型名称 # 参考名称,用于匹配来自不同模型的记录器 互相 .引用名称 = 引用名称 该节点输出此记录器记录的目标的类型 .prev_node_target_type = prev_node_target_type # 节点负责添加此内容的类型 # 日志记录器 .引用节点目标类型 = 引用节点目标类型 # 统计中包含哪些值 .结果类型 = 结果类型 # 输入/输出节点参数中的此节点索引 # 例如,在 cat([x1, x2, x3], dim=0) 中,x2 的 index_within_arg 等于 1 .输入索引 = 输入索引 # 输入/输出节点参数中的节点索引 # 例如,在 add(x1, x2) 中,x2 的 index_of_arg 等于 1 .索引参数 = 索引参数 完全限定名称 .完全限定名 = 完全限定名 如果在 prepare_fx 之前添加了日志记录器,但我们不想 仅收集校准结果,仅收集 convert_fx 后的结果 因此,我们添加一个标志来控制这个日志记录器是否收集数据 .启用 = 真实 qconfig 的字符串表示 .qconfig_str = qconfig_str # 这可以在校准期间关闭以减少内存使用 .保存激活 = 真实 # 注意:无法注释 x 的类型,因为 TorchScript 不支持 # 联合类型。
[文档] def forward(self, x): # fmt: off """ """ # 空文档块以使自动文档功能满意 # fmt: 开启格式化 # TODO(future PR): 考虑设计得更好,因为差异 这两个标志之间的差异微妙且不明显。 如果未 self.enabled: return x if not self.save_activations: return x # TODO(future PR): consider refactoring this to better reuse the parent 类 如果 isinstance(x, torch.Tensor): self.stats.append(x.detach()) elif isinstance(x, tuple) and len(x) == 2 and len(x[1]) == 2: new_res = (x[0].detach(), (x[1][0].detach(), x[1][1].detach())) self.stats_rnn.append(new_res) return x
def __repr__(): 清理字典 = { k: v for k, v
.字典.项目() # 跳过 nn.Module 键 如果 (k != "训练") 并且 k.以...开头(“_”) } 返回 f"输出记录器("{清洁字典})"
[文档]类 OutputComparisonLogger(OutputLogger): """ 与 OutputLogger 相同,但还需要原始激活 为了在标定时间计算比较 ```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 __init__(self, *参数, **关键字参数): super().__init__(*args, **kwargs) # TODO(未来 PR): 使比较函数可配置 self.comparison_fn = torch.ao.ns.fx.utils.compute_sqnr self.comparison_fn_name = "sqnr" # 预计算的日志输出与参考的比较 self.comparisons = [] # 预计算的比较函数
[文档] def forward(self, x, x_ref): # type: ignore[override] # fmt: off """ """ # 空的文档块以使自动文档满意 # 开启格式化 如果没有 self.enabled: return x assert isinstance(x, torch.Tensor), "非张量输入尚未支持" if self.save_activations: # 保存激活,用于调试 self.stats.append(x.detach()) # 保存比较 self.comparisons.append(self.comparison_fn(x, x_ref)) return x
def __repr__(self): clean_dict = { k: v for k, v in self.__dict__.items() # 跳过 nn.Module 键 if (k != "training") and not k.startswith("_") } return f"OutputComparisonLogger({clean_dict})"
[文档]class NSTracer(quantize_fx.量化追踪器): """ 就像常规的 FX 量化追踪器一样,但处理观察者和 fake_quantize 模块作为叶子模块。 """
[文档] def is_leaf_module(self, m: torch.nn.Module, module_qualified_name: str) -> bool: # fmt: off """ """ """ # 空的文档块以使自动文档满意 # 开启格式化 如果 isinstance(m, torch.ao.quantization.ObserverBase): 返回 True 如果 isinstance(m, torch.ao.quantization.FakeQuantizeBase): 返回 True 返回 super().is_leaf_module(m, module_qualified_name)
def _从单个模型提取权重( 模型名称
: 字符串, 模型: GraphModule, nodes_and_names_to_instrument: 列表[元组[节点, 字符串]], 结果: NS 结果类型, op_to_type_to_weight_extraction_fn: 可选[ 字典[字符串, 字典[可调用, 可调用]] ] = , ) -> : 火把._C._log_api_usage_once( "量化_api._数值套件_fx._从单一模型提取权重" ) for node, 引用名称 nodes_and_names_to_instrument: 资源类型 = 单一结果值类型.重量.value 提取的重量 = 从节点提取权重( node, 模型, 操作符到类型到权重提取函数 ) 如果 提取重量: 如果 参考名称 结果: 结果[参考名称] = {资源类型: {}} 结果[参考名称] [资源类型] [模型名称] = [提取权重] def _提取权重实现( 模型名_a: 字符串, gm_a: 图模块, 模型名称_b: 字符串, gm_b: GraphModule, 基础名称到相关操作集合映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = , 无法匹配的类型映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = , op_to_type_to_weight_extraction_fn: 可选[ 字典[字符串, 字典[可调用, 可调用]] ] = , ) -> NS 结果类型: 火把._C._log_api_usage_once( "量化 API._数值套件 FX._提取权重实现" ) 匹配的子图对 = 获取匹配的子图对( gm_a, gm_b, 基础名称到相关操作集合映射, 无法匹配的类型映射 ) 将子图对分割成每个模型的一个数据结构 要监控的节点和名称: 列表[元组[节点, 字符串]] = 输入文本为空,请提供需要翻译的文本 节点与名称要检测_b: 列表[元组[节点, 字符串]] = 输入文本为空,请提供需要翻译的文本 for 匹配名称, 匹配 匹配的子图对.项目(): subgraph_a, 子图_b = 匹配 要监控的节点和名称.append((subgraph_a.基础操作节点, 匹配名称)) 节点与名称要检测_b.append((subgraph_b.基础操作节点, 匹配名称)) # 逐个模型填充结果 结果: NS 结果类型 = {} _从单个模型提取权重( 模型名_a, gm_a, 要监控的节点和名称, 结果, op_to_type_to_weight_extraction_fn, ) _从单个模型提取权重( 模型名_b, gm_b, 节点与名称要检测_b, 结果, op_to_type_to_weight_extraction_fn, ) # 填充缺失的 fqn 条目 可能添加缺失的全限定名(结果) 在 gm_b 中对节点名称进行重新键值 结果 = 在模型节点名称上重新键值 logger_info(结果, 模型名_b) 返回 结果
[文档]定义 提取权重( 模型名_a: 字符串, 模型_a: 神经网络.模块, 模型名称_b: 字符串, 模型_b: 神经网络.模块, 基础名称到相关操作集合映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = , 无法匹配的类型映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = , op_to_type_to_weight_extraction_fn: 可选[ 字典[字符串, 字典[可调用, 可调用]] ] = , ) -> NS 结果类型: "" 从模型 A 和模型 B 中提取权重,并返回比较。 参数: model_name_a: 使用在结果中的模型 A 的字符串名称 model_a: 模型 A model_name_b: 使用在结果中的模型 B 的字符串名称 model_b: 模型 B base_name_to_sets_of_related_ops: 可选覆盖子图基本节点,可能发生变化 unmatchable_types_map: 可选覆盖不可匹配的类型,可能发生变化 op_to_type_to_weight_extraction_fn: 可选覆盖提取类型的权重的函数 从类型中提取权重,可能发生变化 返回: 包含权重比较的 NSResultsType "文档" torch._C._log_api_usage_once("quantization_api._numeric_suite_fx.extract_weights") if 基本名称到相关操作集合 : 基本名称到相关操作集合 = 获取相关操作集的基本名称() # TODO(未来 PR): 公开这些 跳过的模块名称: 列表[字符串] = 输入文本为空,请提供需要翻译的文本 跳过的模块类: 列表[可调用] = 输入文本为空,请提供需要翻译的文本 tracer_a = NS 跟踪器(跳过的模块名称, 跳过的模块类) 跟踪器_b = NS 跟踪器(跳过的模块名称, 跳过的模块类) gm_a = GraphModule(model_a, 跟踪器_a.跟踪(model_a)) maybe_model_a 节点名称到作用域 = _获取观察到的图模块属性( model_a, "节点名称到作用域" ) 如果 maybe_model_a 节点名称到作用域 None: gm_a._节点名称到作用域_ = maybe_model_a 节点名称到作用域 gm_b = GraphModule(模型_b, 跟踪器_b.跟踪(模型_b)) maybe_model_b_node_name_to_scope = _获取观察到的图模块属性( 模型_b, "节点名称到作用域" ) 如果 maybe_model_b_node_name_to_scope None: gm_b._节点名称到作用域_ = maybe_model_b_node_name_to_scope 返回 _提取权重实现( 模型名_a, gm_a, 模型名_b, gm_b, 基础名称到相关操作集合映射, 无法匹配的类型映射, op_to_type_to_weight_extraction_fn, )
def _添加日志器_单个模型( 模型名称: 字符串, 模型: GraphModule, 要监控的节点和名称: 列表[元组[节点, 字符串, 字符串]], 要仪器化的节点和名称: 列表[元组[节点, 字符串, 字符串]], 日志类: 可调用, ) -> 神经网络.模块: PyTorch._C._log_api_usage_once( "量化 API._numeric_suite_fx._添加单个模型的日志记录器" ) # TODO(未来 PR): 不要观察我们不关心的节点 # 关于(包括 fp32、拒绝列表等) 将节点到仪器输入映射到参考名称: 字典[节点, 元组[字符串, 字符串]] = {} 节点将输出映射到引用名称: 字典[节点, 元组[字符串, 字符串]] = {} node, 参考名称, 参考节点类型 要监控的节点和名称: 将节点到仪器输入映射到参考名称[node] = (参考名称, 参考节点类型) node, 参考名称, 参考节点类型 要仪器化的节点和名称: 节点将输出映射到引用名称[node] = (参考名称, 参考节点类型) 模型 = add_loggers_to_model( 模型, 将节点到仪器输入映射到参考名称, 节点将输出映射到引用名称, 日志类, 模型名称, ) 返回 模型 def _添加日志实现( name_a: 字符串, gm_a: GraphModule, name_b: 字符串, gm_b: GraphModule, 日志类: 可调用, 应记录输入: 布尔, 基础名称到相关操作集合映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = None, 无法匹配的类型映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = None, ) -> 元组[神经网络.模块, 神经网络.模块]: PyTorch._C._log_api_usage_once("quantization_api._numeric_suite_fx._add_loggers_impl") 匹配的子图对 = 获取匹配的子图对( gm_a, gm_b, 基础名称到相关操作集合映射, 无法匹配的类型映射 ) 节点和名称到要仪器化的输入的映射_a = 输入文本为空,请提供需要翻译的文本 节点与名称到要监控的输入 = 输入文本为空,请提供需要翻译的文本 节点与名称到要监控的输出_a = 输入文本为空,请提供需要翻译的文本 节点与名称到要监控的输出_b = 输入文本为空,请提供需要翻译的文本 匹配名称, (subgraph_a, subgraph_b) 匹配的子图对.项目(): ref_node_type_a = 获取目标类型字符串(subgraph_a.基础操作节点, gm_a) ref_node_type_b = 获取目标类型字符串(subgraph_b.基础操作节点, gm_b) # 注意:对于匹配输入,我们使用起始节点,例如观察 # 线性-relu 中的线性输入 如果 应记录输入: 节点及名称到仪器输入_a.append( (subgraph_a.开始节点, 匹配名称, 参考节点类型_a) ) 节点与名称到仪器输入_b.append( (subgraph_b.开始节点, 匹配名称, 参考节点类型 B) ) # 注意:对于匹配激活,我们始终使用 end_node, # 例如观察线性-relu 的输出中的 relu, 节点及名称到仪器输出_a.append( (subgraph_a.结束节点, 匹配名称, 参考节点类型_a) ) 节点与名称到仪器输出_b.append( (subgraph_b.结束节点, 匹配名称, 参考节点类型 B) ) 新模型 A = _添加日志器_单个模型( name_a, gm_a, 节点及名称到仪器输入_a, 节点及名称到仪器输出_a, 日志类, ) 新模型_b = _添加日志器_单个模型( name_b, gm_b, 节点与名称到仪器输入_b, 节点与名称到仪器输出_b, 日志类, ) 返回 (新模型_a, 新模型_b)
[文档]def 添加日志记录器( name_a: 字符串, model_a: 神经网络.模块, name_b: 字符串, 模型_b: 神经网络.模块, logger 类: 可调用, 应记录输入: 布尔类型 = 错误, 基础名称到相关操作集合: 可选[字典[字符串, 设置[NS 节点目标类型]]] = , 无法匹配的类型映射: 可选[字典[字符串, 集合[NS 节点目标类型]]] = , ) -> 元组[神经网络.模块, 神经网络.模块]: "" 使用记录器对仪器模型 A 和模型 B 进行建模。 参数: name_a: 字符串 模型 A 在结果中使用的名称 model_a: 模型 A name_b: 字符串 模型 B 在结果中使用的名称 model_b: 模型 B logger_cls: 要使用的 Logger 类 base_name_to_sets_of_related_ops: 可选覆盖子图基本节点,可能发生变化 unmatchable_types_map: 可选覆盖不可匹配的类型,可能发生变化 返回: 返回一个包含(model_a_with_loggers, model_b_with_loggers)的元组。就地修改两个模型。 "文档" torch._C._log_api_usage_once(quantization_api._numeric_suite_fx.add_loggers) # TODO(未来 PR): 公开这些 跳过的模块名称: 列表[字符串] = 输入文本为空,请提供需要翻译的文本 跳过的模块类: 列表[可调用] = 输入文本为空,请提供需要翻译的文本 tracer_a = NS 跟踪器(跳过的模块名称, 跳过的模块类) 跟踪器_b = NS 跟踪器(跳过的模块名称, 跳过的模块类) gm_a = GraphModule(model_a, 跟踪器_a.跟踪(model_a)) maybe_model_a 节点名称到作用域 = _获取观察到的图模块属性( model_a, "节点名称到作用域" ) if maybe_model_a 节点名称到作用域 None: gm_a._节点名称到作用域_ = maybe_model_a 节点名称到作用域 gm_b = GraphModule(模型_b, 跟踪器_b.跟踪(模型_b)) maybe_model_b_node_name_to_scope = _获取观察到的图模块属性( 模型_b, "节点名称到作用域" ) 如果 maybe_model_b_node_name_to_scope None: gm_b._节点名称到作用域_ = maybe_model_b_node_name_to_scope 返回 _添加日志实现( name_a, gm_a, name_b, gm_b, 日志类, 应记录输入=应记录输入, 基础名称到相关操作集合映射=基础名称到相关操作集合映射, 无法匹配的类型映射=无法匹配的类型映射, )
def _提取一个模型的信息( 模型: 神经网络.模块, 结果: NS 结果类型, 日志类: 可调用, ) -> None: PyTorch._C._log_api_usage_once( "quantization_api._numeric_suite_fx._extract_logger_info_one_model" ) for _gm_name, 修饰 模型.命名模块(): # TODO(未来 PR): 更好的脚本检查 is_logger = isinstance(模块, 日志类) 或者 ( # type: ignore[arg-type] isinstance(模块, 火把.算子.递归脚本模块) 并且 模块.原始名称 == 输出记录器 ) 如果 是否为记录器: key = 模块.引用名称 如果 key 结果: 结果[] = {} 断言 ( 模块.模型名称 结果[] ), f"{模块.模型名称}已存在于结果中 如果 模块.结果类型 结果[]: 结果[] [模块.结果类型] = {} 如果 模块.模型名称 结果[] [模块.结果类型] 结果[] [模块.结果类型] [模块.模型名称] = 输入文本为空,请提供需要翻译的文本 可用统计 = 模块.统计 如果 长度(模块.统计 RNN) > 0: 可用统计 = 模块.stats_rnn 数据 = { 类型: 模块.结果类型, "值": 要使用的统计数据, "引用节点名称": 模块.参考节点名称, "节点目标类型": 模块.引用节点目标类型, "前节点名称": 模块.前节点名称, "prev_node_target_type": 模块.前节点目标类型, "index_within_arg": 模块.参数内索引, "参数索引": 模块.索引参数, "全限定名": 模块.完全限定名, "qconfig 字符串": 模块.qconfig 字符串, } if 有属性(模块, "comparisons"): 数据["comparisons"] = 模块.比较 数据["comparison_fn_name"] = 模块.comparison_fn_name 否则: 数据["comparisons"] = 输入文本为空,请提供需要翻译的文本 数据["comparison_fn_name"] = 请提供需要翻译的文本 结果[] [模块.结果类型] [模块.模型名称].追加(数据) # 确保列表保持排序 结果[] [模块.结果类型] [模块.模型名称].排序( =lambda 资源: f"{资源[参数索引]}:{资源[参数内索引]}" ) # TODO(未来 PR): 在命名上达成一致 这部分相当于 `ns.compare_model_outputs` 的比较提取部分
[文档]def extract_logger_info( model_a: nn.Module, model_b: nn.Module, logger_cls: 可调用对象, model_name_to_use_for_layer_names: 字符串类型, ) -> NSResultsType: """ 遍历`model_a`和`model_b`中的所有记录器,并提取记录的内容 信息。 参数: model_a: 模型 A model_b: 模型 B logger_cls: 要使用的 Logger 类 model_name_to_use_for_layer_names: 输出层名称所使用的模型名称 layer_names_in_the_output: 输出层名称 返回: 包含已记录比较的 NSResultsType """ torch._C._log_api_usage_once( quantization_api._numeric_suite_fx.extract_logger_info ) results: NSResultsType = {} for model in (model_a, model_b): _extract_logger_info_one_model(model, results, logger_cls) # 填充缺失的完全限定名称条目 maybe_add_missing_fqns(results) # 根据模型 b 的名称重新键 results = rekey_logger_info_on_node_name_of_model( results, model_name_to_use_for_layer_names ) return results
def _add_shadow_loggers_impl( name_a: 字符串, gm_a: GraphModule, name_b: 字符串, gm_b: GraphModule, 日志类
: 可调用, 应记录输入: 布尔, 基础名称到相关操作集合映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = None, 节点类型到输入输出类型映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = None, 无法匹配的类型映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = None, ) -> 神经网络.模块: PyTorch._C._log_api_usage_once( "quantization_api._numeric_suite_fx._add_shadow_loggers_impl" ) 匹配的子图对 = 获取匹配的子图对( gm_a, gm_b, 基础名称到相关操作集合映射, 无法匹配的类型映射 ) gm_a_shadows_b = 创建阴影_b( name_a, gm_a, name_b, gm_b, 匹配的子图对, 日志类, 应记录输入=应记录输入, 节点类型到输入输出类型映射=节点类型到输入输出类型映射, ) 返回 gm_a_shadows_b
[文档]def 添加阴影记录器( name_a: 字符串, model_a: 神经网络.模块, name_b: 字符串, 模型_b: 神经网络.模块, 日志类: 可调用, 应记录输入: 布尔 = 假的, 基础名称到相关操作集合映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = None, 节点类型到输入输出类型映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = None, 无法匹配的类型映射: 可选[字典[字符串, 设置[NS 节点目标类型]]] = None, ) -> 神经网络.模块: "" 使用阴影记录器对 A 型仪器和 B 型仪器进行模型测试。 参数: name_a: 字符串 模型 A 在结果中使用的名称 model_a: 模型 A name_b: 字符串 模型 B 在结果中使用的名称 model_b: 模型 B logger_cls: 要使用的 Logger 类 should_log_inputs: 是否记录输入 base_name_to_sets_of_related_ops: 可选覆盖子图基本节点,可能发生变化 unmatchable_types_map: 可选覆盖不可匹配的类型,可能发生变化 "文档" PyTorch._C._log_api_usage_once( "量化 API._numeric_suite_fx 添加阴影记录器" ) # TODO(未来 PR): 公开这些 跳过的模块名称: 列表[字符串] = 输入文本为空,请提供需要翻译的文本 跳过的模块类: 列表[可调用] = 输入文本为空,请提供需要翻译的文本 tracer_a = NS 跟踪器(跳过的模块名称, 跳过的模块类) 跟踪器_b = NS 跟踪器(跳过的模块名称, 跳过的模块类) gm_a = GraphModule(model_a, 跟踪器_a.跟踪(model_a)) maybe_model_a 节点名称到作用域 = _获取观察到的图模块属性( model_a, "节点名称到作用域" ) 如果 maybe_model_a 节点名称到作用域 None: gm_a._节点名称到作用域_ = maybe_model_a 节点名称到作用域 gm_b = GraphModule(模型_b, 跟踪器_b.跟踪(模型_b)) maybe_model_b_node_name_to_scope = _获取观察到的图模块属性( 模型_b, "节点名称到作用域" ) 如果 maybe_model_b_node_name_to_scope None: gm_b._节点名称到作用域_ = maybe_model_b_node_name_to_scope 返回 _add_shadow_loggers_impl( name_a, gm_a, name_b, gm_b, 日志类, 应记录输入=应记录输入, 基础名称到相关操作集合映射=基础名称到相关操作集合映射, 节点类型到输入输出类型映射=节点类型到输入输出类型映射, 无法匹配的类型映射=无法匹配的类型映射, )
[文档]def extract_shadow_logger_info( model_a_shadows_b: nn.Module, logger_cls: Callable, model_name_to_use_for_layer_names: str, ) -> NSResultsType: ```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) ``` 遍历影子模型中的所有记录器,并提取记录内容 信息。 参数: model_a_shadows_b: 影子模型 logger_cls: 要使用的 Logger 类 model_name_to_use_for_layer_names: 输出层名称中要使用的模型名称 layer_names_in_the_output: 输出层名称 返回: 包含已记录比较的 NSResultsType """ torch._C._log_api_usage_once( quantization_api._numeric_suite_fx.extract_shadow_logger_info ) results: NSResultsType = collections.defaultdict(dict) _extract_logger_info_one_model(model_a_shadows_b, results, logger_cls) 填充缺失的 fqn 条目 可能添加缺失的 fqn(results) 在模型 b 的名称上重新键 results = 在模型节点的名称上重新键入日志信息(rekey_logger_info_on_node_name_of_model( 结果,用于层名称的模型名称 ) 返回字典(results)
[文档]def 扩展日志结果以包含比较( 结果: NS 结果类型, 模型名称_1: 字符串, 模型名称_2: 字符串, 比较函数: 可调用[[PyTorch.张量, PyTorch.张量], PyTorch.张量], 比较名称: 字符串, ) -> None: "" 将 `model_name_2` 的日志值与 `model_name_1` 中相应的值进行比较 使用 `comparison_fn` 记录结果 在`model_name_2`的`comparison_name`结果下。就地修改`results`。 参数: 结果:来自`extract_logger_info`或`extract_shadow_logger_info`的结果数据结构。 `extract_shadow_logger_info`。 model_name_1:模型 1 的字符串名称。 模型名称_2:模型 2 的字符串名称 比较函数:比较两个张量的函数 比较名称:用于比较的模型的字符串名称 输出层的名称 "文档" 结果类型到结果 结果.(): 模型名称到结果 结果类型到结果.(): 断言 ( 模型名称_1 模型名称到结果 ), f"{模型名称_1}未在结果中找到 断言 ( 模型名称_2 模型名称到结果 ), f"{模型名称 2}未在结果中找到 结果_1 = 模型名称到结果[模型名称 1] 结果 2 = 模型名称到结果[模型名称_2] 结果_2 结果_2: 参数内的索引_2 = 结果_2["index_within_arg"] arg_2 的索引 = 结果_2["参数索引"] 查找对应的结果_1 结果_1 = 当前结果_1 结果_1: 参数_1 内的索引 = 当前结果_1["index_within_arg"] 参数_1 的索引 = cur_result_1["参数索引"] 如果 (index_within_arg_1 == index_within_arg_2) ( index_of_arg_1 == 参数 2 的索引 ): 结果 1 = 当前结果 1 断开 断言 结果 1 否。 values_1 = result_1["值"] values_2 = 结果_2["值"] 结果_2[比较名称] = 输入文本为空,请提供需要翻译的文本 value_1, value_2 zip(values_1, values_2): 比较结果 = 比较函数(value_1, value_2) 结果_2[比较名称].追加(比较结果)
[文档]def 准备_n_shadows 模型( 模型: 火把.神经网络.模块, 示例输入: 任何, qconfig 多映射: QConfig 多映射, 后端配置: 后端配置, 自定义准备函数: 可选[可调用] = None, 自定义准备参数: 可选[字典[字符串, 任何]] = None, custom_tracer 自定义追踪器: 任何 = None, ) -> GraphModule: "" 给定一个包含 M 个操作的图模型, args_kwargs_m -> op_m -> output_m 并且为每个操作创建一组 N 个 qconfig,创建一个新的模型, 将`op_m`的每个子图转换为 .. 代码块:: |---------> op_m_n -> log_m_n | / args_kwargs_m ---------> op_m -> log_m_0 其中 op_m_n 是将 op_m 封装在子模块中并进行转换的 qconfig_n 及其内部图看起来像 .. 代码块:: args_m -------- op_m_prepared_with_qconfig_n -> out_m_n / kwargs_m --- 这对于测试多层不同量化很有用 一次通过模型。 未来 Pull Requests 的高级待办事项: 找出更好的命名输出结构的方法 返回一个结果数据结构而不是打印出来 添加示例到文档块 "文档" 如果 custom_tracer 自定义跟踪器 None: 追踪器 = 量化效果.量化追踪器[] [] 否则: 追踪器 = custom_tracer 自定义跟踪器 沉浸式翻译 = PyTorch.fx.GraphModule(模型, 追踪器.跟踪(模型)) 这是必要的,以确保记录器 FQNs 被填充。 沉浸式翻译._节点名称到作用域_ = 追踪器.节点名称到范围映射 # 类型:忽略[赋值] 运行示例输入传播,我们需要这个来调用 prepare_fx 单个子图 输出传播 = 输出属性(沉浸式翻译) 输出属性.传播(*示例输入) 找到原始图中我们需要 考虑。 模块 = 字典(沉浸式翻译.命名模块(删除重复项=假的)) 模式 = _获取量化处理器的模式(后端配置) 根节点获取器映射 = get_fusion_pattern_to_root_node_getter(后端配置) 独立模块名称: 列表[字符串] = 输入文本为空,请提供需要翻译的文本 独立模块类: 列表[类型] = 输入文本为空,请提供需要翻译的文本 自定义模块类: 列表[类型] = 输入文本为空,请提供需要翻译的文本 匹配项 = _find_matches( 沉浸式翻译., 模块, 模式, 根节点获取映射, 独立模块名称, 独立模块类, 自定义模块类, ) 子图去重: 字典[字符串, 列表[节点]] = 获取去重子图(匹配) 为每个子图生成节点到 qconfig # TODO(未来 PR): 去重重复条目 节点名到 qconfig 的列表: 列表[字典[字符串, QConfigAny]] = 输入文本为空,请提供需要翻译的文本 qconfig 映射 qconfig 多映射.Q 配置映射列表: 节点名称到 qconfig = _generate_node_name_to_qconfig( 沉浸式翻译, 模块, 沉浸式翻译., QConfig 映射, 追踪器.节点名称到范围映射 ) 节点名到 qconfig 的列表.追加(节点名称到 Q 配置映射) 对于模型中的每个区域,执行以下操作: 对于该区域的每个 qconfig,执行以下操作: 1. 创建一个模块包装区域的副本 2. 传递原始参数、原始关键字参数和预期输出到模块 3. 添加一个输出比较记录器并将其连接到比较 实际输出到预期输出 在模块上运行 `prepare_fx` subgraph_idx, (匹配名称, 节点数) 列举( 子图去重.项目() ): 创建 n 个转换并记录的子图副本( 沉浸式翻译, subgraph_idx, 匹配名称, 节点数, qconfig 多映射.Q 配置映射列表, 节点名到 qconfig 的列表, 自定义准备函数, 自定义准备参数, # type: ignore[arg-type] ) 返回 沉浸式翻译
# TODO(future PR): 我们应该重新思考所有 PNP API 的命名 def _准备_n_阴影添加日志记录器模型( 模型: PyTorch.神经网络.模块, 示例输入: 任何, QConfig 映射: QConfig 映射, 后端配置: 后端配置, ) -> PyTorch.神经网络.模块: r"" 注意:此 API 不建议广泛使用,它仅 为需要从 `add_loggers` 迁移的客户提供 API. 这将创建一个模型,该模型为以下问题提供日志记录: 问题是:如果我们使用 `qconfig_mapping` 对 `model` 进行量化并输入 通过两种模型进行相同输入,记录比较结果。 对应的中间层。 问题通过单个模型解决。具体来说,我们将模型 `model` 划分为 N 个子图,为每个相关的子图创建一个副本。 将 `model` 划分为 N 个子图,为每个相关的子图创建一个副本。 将子图封装在模块中,并应用量化 API 将模块连接到日志记录器以测量比较 示例起始图: x0 -> op0 -> x1 -> op1 -> x2 示例配置:将 op0 量化为 int8,对 op1 不做任何操作。 以下将创建的图: .. 代码块:: x0_0 -> op0_0 -> x1_0 -> log -----> op1_0 -> x2_0 -> log \ \ \ # noqa: W605 ---> op0_1 -> x1_1 ----> clog -> op1_0 -> x2_1 ----> clog 其中 op0_0 是 op0,op0_1 是封装在子模块中的 op0 并进行了量化 op1_0 是 op1(在图中出现两次),log 是记录器, 并且 clog 是一个比较日志记录器。 "文档" 追踪器 = 量化效果.量化追踪器([], [] 沉浸式翻译 = PyTorch.fx.GraphModule(模型, 追踪器.跟踪(模型)) 这是必要的,以确保记录器 FQNs 被填充。 沉浸式翻译._节点名称到作用域_ = 追踪器.节点名称到范围映射 # 类型:忽略[赋值] 运行示例输入传播,我们需要这个来调用 prepare_fx 单个子图 输出传播 = 输出属性(沉浸式翻译) 输出属性.传播(*示例输入) 找到原始图中我们需要 考虑。 模块 = 字典(沉浸式翻译.命名模块(删除重复项=假的)) 模式 = _获取量化处理器的模式(后端配置) 根节点获取器映射 = get_fusion_pattern_to_root_node_getter(后端配置) 独立模块名称: 列表[字符串] = 输入文本为空,请提供需要翻译的文本 独立模块类: 列表[类型] = 输入文本为空,请提供需要翻译的文本 自定义模块类: 列表[类型] = 输入文本为空,请提供需要翻译的文本 匹配项 = _find_matches( 沉浸式翻译., 模块, 模式, 根节点获取映射, 独立模块名称, 独立模块类, 自定义模块类, ) 子图去重: 字典[字符串, 列表[节点]] = 获取去重子图(匹配) 为每个子图生成节点到 qconfig 节点名称到 qconfig = _generate_node_name_to_qconfig( 沉浸式翻译, 模块, 沉浸式翻译., QConfig 映射, 追踪器.节点名称到范围映射 ) 现在,将图变异为具有传播的 add_loggers 图 错误。 创建添加日志记录器的图(沉浸式翻译, 子图去重, QConfig 映射, 节点名称到 Q 配置映射) 返回 沉浸式翻译 # TODO(future PR): 我们应该重新思考所有 PNP API 的命名 def _n_shadows_compare_weights( 模型: PyTorch.神经网络.模块, 示例输入: 任何, QConfig 映射: QConfig 映射, 后端配置: 后端配置, ) -> NS 结果类型: "" 注意:此 API 不建议广泛使用,它仅 为需要从 `add_loggers` 迁移的客户提供 API. "文档" qconfig 多映射 = QConfig 多映射.从列表中获取 QConfig 映射( [QConfig 映射] ) mp = 准备_n_shadows 模型( 模型, 示例输入, qconfig 多映射, backend_config ) # 通过模型传递输入是填充 # 观察具有实数值的权重观察者 mp(*示例输入) mq = 转换_n_阴影模型(mp) 重量比较 = 提取权重比较(mq) 返回 重量比较 # TODO(future PR): 考虑将 API 签名与其他类似量化对齐 # 函数(enable_fake_quant 等)
[文档]def loggers_set_enabled(model: torch.nn.Module, enabled: bool) -> None: """ 设置`model`的日志记录器的`enabled`设置 """ for _, child in model.named_modules(): if isinstance(child, OutputLogger): child.enabled = enabled
# TODO(future PR): 考虑将 API 签名与其他类似量化对齐 # 函数(enable_fake_quant 等)
[文档]def loggers_set_save_activations( 模型: torch.nn.Module, save_activations: 布尔型, ) -> None: """ 设置`model`的日志记录器的`save_activations`设置 """ for _name, child in model.named_modules(): if isinstance(child, OutputLogger): child.save_activations = save_activations
[文档]def convert_n_shadows_model( model: 图模块, custom_convert_fn: 可选[可调用对象] = None, custom_convert_kwargs: 可选[dict[str, Any]] = None, ) -> 图模块: "" 给定一个来自 `prepare_n_shadows_model` 的模型,运行 `convert_fx` 在每个阴影子模块上。 "" for node in model.graph.nodes: # TODO(future PR): 考虑以更安全的方式匹配 # 节点名字符串匹配 if node.name.startswith(SHADOW_WRAPPER_NODE_NAME_PREFIX): orig_mod = getattr(model, node.name) if custom_convert_fn is None: converted_mod = torch.ao.quantization.quantize_fx.convert_fx(orig_mod) else: 如果 custom_convert_kwargs 为 None: custom_convert_kwargs = {} converted_mod = custom_convert_fn(orig_mod, **custom_convert_kwargs) setattr(model, node.name, converted_mod) 返回模型
[文档]def extract_results_n_shadows_model(model: torch.nn.Module) -> NSResultsType: """ 从 `model` 中提取日志结果。 "``" results: NSResultsType = {} _extract_logger_info_one_model(model, results, OutputLogger) return results

© 版权所有 PyTorch 贡献者。

使用 Sphinx 构建,并使用 Read the Docs 提供的主题。

文档

查看 PyTorch 的全面开发者文档

查看文档

教程

深入了解初学者和高级开发者的教程

查看教程

资源

查找开发资源,获取您的疑问解答

查看资源