目录

ExecuTorch XNNPACK 委托

这是 ExecuTorch XNNPACK 后端委托的高级概述。这种高性能委托旨在减少 ExecuTorch 模型的 CPU 推理延迟。我们将简要介绍 XNNPACK 库,并探索委托的整体架构和预期用例。

什么是 XNNPACK?

XNNPACK 是一个高度优化的神经网络运算符库,适用于 Android、iOS、Windows、Linux 和 macOS 环境中的 ARM、x86 和 WebAssembly 架构。这是一个开源项目,您可以在 github 上找到有关它的更多信息。

什么是 ExecuTorch 代表?

委托是后端处理和执行 ExecuTorch 程序各部分的入口点。ExecuTorch 模型的委托部分将执行移交给后端。XNNPACK 后端委托是 ExecuTorch 中提供的众多委托之一。它利用 XNNPACK 第三方库在各种 CPU 上高效加速 ExecuTorch 程序。有关代表和开发您自己的代表的更多详细信息,请点击此处。建议您先熟悉该内容,然后再继续 Architecture 部分。

建筑

高级 XNNPACK 委托体系结构

提前

在 ExecuTorch 导出流程中,降低到 XNNPACK 委托发生在该阶段。在此阶段中,模型由 .图形的分区部分将转换为特定于 XNNPACK 的图形,然后通过 flatbuffer 进行序列化。然后,序列化的 flatbuffer 就可以在运行时由 XNNPACK 后端反序列化和执行了。to_backend()XnnpackPartitioner

ExecuTorch XNNPACK 委托导出流程

分区程序

分区器由 backend delegate 实现,用于标记适合降低的节点。lowers 使用 node targets 和 module metadata。可以在此处找到分区程序的更多参考XnnpackPartitioner

基于模块的分区

source_fn_stack嵌入在节点的元数据中,并提供有关这些节点来源的信息。例如,像 capture 和 exported 这样的模块会生成节点组进行计算。与计算线性模块相关的节点组则具有 source_fn_stack' 允许我们识别可通过 XNNPACK 降低的节点组。torch.nn.Linearto_edgesource_fn_stacktorch.nn.Linear. Partitioning based on

例如,捕获后,您将在与 linear 关联的 addmm 节点的元数据中找到以下键:torch.nn.Linear

>>> print(linear_node.meta["source_fn_stack"])
'source_fn_stack': ('fn', <class 'torch.nn.modules.linear.Linear'>)
基于操作的分区

还使用 op 目标进行分区。它遍历图形并识别可降低到 XNNPACK 的各个节点。基于模块的分区的缺点是可以跳过来自分解的运算符。例如,运算符 like 分解为 add、muls、divs 和 clamps。虽然 hardsigmoid 不可降低,但我们可以降低分解的运算。依赖元数据会跳过这些 lowerable,因为它们属于不可 lowerable 模块,因此为了提高模型性能,我们根据 op 目标以及 .XnnpackPartitionertorch.nn.Hardsigmoidsource_fn_stacksource_fn_stack

通过

在进行任何序列化之前,我们会在子图上应用传递来准备图形。这些传递实质上是有助于提高代理性能的图形转换。我们在下面概述了最重要的递次及其功能。有关所有通道的描述,请参见此处

  • 通道上次重塑

    • ExecuTorch 张量在传递到委托之前往往是连续的,而 XNNPACK 只接受 channels-last 内存布局。此传递最大限度地减少了插入的排列运算符的数量,以 channels-last 内存格式传递。

  • Conv1d 到 Conv2d

    • 允许我们通过将 Conv1d 节点转换为 Conv2d 来委托这些节点

  • Conv 和 BN 融合

    • 将 batch norm 操作与前面的卷积节点融合

序列化

从模型中分割可降低的子图后,XNNPACK 委托对这些子图进行预处理,并通过 XNNPACK 后端的 flatbuffer 对其进行序列化。

序列化架构

XNNPACK 委托使用 flatbuffer 进行序列化。为了提高运行时性能,XNNPACK 委托的 flatbuffer 架构镜像了 XNNPACK 库的图形级 API 调用。序列化数据是 XNNPACK API 的参数,因此在运行时,可以通过连续调用 XNNPACK 的 API 来高效地创建 XNNPACK 执行图。

运行

XNNPACK 后端的运行时通过 custom 和 function 与 ExecuTorch 运行时交互。每个委托的子图都包含在一个单独序列化的 XNNPACK blob 中。初始化模型时,ExecuTorch 会调用所有 XNNPACK Blob 以从序列化的 flatbuffer 加载子图。之后,当模型执行时,每个子图都通过自定义函数通过后端执行。要了解有关委托运行时如何与 ExecuTorch 交互的更多信息,请参阅此资源initexecuteinitexecute

XNNPACK 库

XNNPACK 委托支持多个平台上的 CPU;有关支持的硬件体系结构的更多信息,请参见 XNNPACK 库的 README。

初始化

调用 XNNPACK 委托时,我们通过 flatbuffer 反序列化预处理过的 blob。我们定义节点(运算符)和边(中间张量),以使用我们提前序列化的信息构建 XNNPACK 执行图。正如我们前面提到的,大部分处理都是提前完成的,因此在运行时,我们只需连续调用带有序列化参数的 XNNPACK API。当我们将静态数据定义到执行图中时,XNNPACK 在运行时执行权重打包,以准备权重和偏差等静态数据,以实现高效执行。创建执行图后,我们创建运行时对象并将其传递给 。initexecute

由于权重打包在 XNNPACK 中创建了权重的额外副本,因此我们在预处理的 XNNPACK Blob 中释放了权重的原始副本,这允许我们消除一些内存开销。

执行

在执行 XNNPACK 子图时,我们准备张量输入和输出并将它们馈送到 XNNPACK 运行时图。执行运行时图后,输出指针将填充计算出的张量。

分析

我们已经为 XNNPACK 委托启用了基本性能分析,可以使用以下编译器标志来启用它。通过 ExecuTorch 的开发人员工具集成,您现在还可以使用开发人员工具对模型进行性能分析。您可以按照使用 ExecuTorch 开发人员工具分析模型中的步骤来分析如何分析 ExecuTorch 模型,并使用开发人员工具的检查器 API 查看 XNNPACK 的内部分析信息。-DENABLE_XNNPACK_PROFILING

量化

XNNPACK 委托也可以用作后端来执行对称量化模型。对于量化模型委托,我们使用 . 是特定于后端的,这意味着 被配置为量化模型以利用 XNNPACK 库提供的量化运算符。我们不会详细介绍如何实现自定义量化器,您可以按照此处的文档进行操作。但是,我们将简要概述如何量化模型以利用 XNNPACK 委托的量化执行。XNNPACKQuantizerQuantizersXNNPACKQuantizer

配置 XNNPACKQuantizer

from torch.ao.quantization.quantizer.xnnpack_quantizer import (
  XNNPACKQuantizer,
  get_symmetric_quantization_config,
)
quantizer = XNNPACKQuantizer()
quantizer.set_global(get_symmetric_quantization_config())

在这里,我们初始化 并将量化配置设置为对称量化。对称量化是指使用 和 对称量化权重,这会强制量化零点为零。 可以使用以下参数进行配置:XNNPACKQuantizerqmin = -127qmax = 127get_symmetric_quantization_config()

  • is_per_channel

    • 权重跨通道量化

  • is_qat

    • 量化感知训练

  • is_dynamic

    • 动态量化

然后,我们可以根据需要配置 。我们设置以下配置作为示例:XNNPACKQuantizer

quantizer.set_global(quantization_config)
    .set_object_type(torch.nn.Conv2d, quantization_config) # can configure by module type
    .set_object_type(torch.nn.functional.linear, quantization_config) # or torch functional op typea
    .set_module_name("foo.bar", quantization_config)  # or by module fully qualified name

使用 XNNPACKQuantizer 量化模型

配置量化器后,我们现在可以量化模型了

from torch.export import export_for_training

exported_model = export_for_training(model_to_quantize, example_inputs).module()
prepared_model = prepare_pt2e(exported_model, quantizer)
print(prepared_model.graph)

Prepare 执行一些 Conv2d-BN 融合,并在适当的位置插入量化观察器。对于 Post-Training Quantization,我们通常在此步骤之后校准我们的模型。我们通过 运行示例示例来观察 Tensor 的统计数据,以计算量化参数。prepared_model

最后,我们在此处转换我们的模型:

quantized_model = convert_pt2e(prepared_model)
print(quantized_model)

您现在将看到模型的 Q/DQ 表示,这意味着入到量化的运算符输入处,并插入到运算符输出处。例:torch.ops.quantized_decomposed.dequantize_per_tensortorch.ops.quantized_decomposed.quantize_per_tensor

def _qdq_quantized_linear(
    x_i8, x_scale, x_zero_point, x_quant_min, x_quant_max,
    weight_i8, weight_scale, weight_zero_point, weight_quant_min, weight_quant_max,
    bias_fp32,
    out_scale, out_zero_point, out_quant_min, out_quant_max
):
    x_fp32 = torch.ops.quantized_decomposed.dequantize_per_tensor(
        x_i8, x_scale, x_zero_point, x_quant_min, x_quant_max, torch.int8)
    weight_fp32 = torch.ops.quantized_decomposed.dequantize_per_tensor(
        weight_i8, weight_scale, weight_zero_point, weight_quant_min, weight_quant_max, torch.int8)
    out_fp32 = torch.ops.aten.linear.default(x_fp32, weight_fp32, bias_fp32)
    out_i8 = torch.ops.quantized_decomposed.quantize_per_tensor(
        out_fp32, out_scale, out_zero_point, out_quant_min, out_quant_max, torch.int8)
    return out_i8

您可以在此处阅读有关 PyTorch 2 量化的更多深入说明。

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源