• 文档 >
  • 通过 Inductor 使用 X86 后端进行 PyTorch 2 导出量化
快捷方式

PyTorch 2 导出量化与 X86 后端通过 Inductor

作者: Leslie Fang, Weiwen Xia, Jiong Gong, Jerry Zhang

先决条件

介绍

本教程介绍了使用 PyTorch 2 导出量化流程来生成针对 x86 inductor 后端定制的量化模型的步骤,并解释了如何将量化模型降低到 inductor 中。

pytorch 2 导出量化流程使用 torch.export 将模型捕获到图(graph)中,并在 ATen 图上执行量化转换。这种方法预计将具有显著更高的模型覆盖率、更好的可编程性以及简化的用户体验。TorchInductor 是新的编译器后端,它将 TorchDynamo 生成的 FX 图编译成优化的 C++/Triton 内核。

这种量化 2 与 Inductor 的流程支持静态量化和动态量化。静态量化最适用于 CNN 模型,例如 ResNet-50。而动态量化更适合 NLP 模型,例如 RNN 和 BERT。有关这两种量化类型的区别,请参阅以下页面

量化流程主要包括三个步骤:

  • 步骤 1:基于 torch 导出机制,从即时模式(eager)模型中捕获 FX 图。

  • 步骤 2:基于捕获的 FX 图应用量化流程,包括定义特定后端的量化器、生成带有观察器的准备模型、执行准备模型的校准或量化感知训练,以及将准备模型转换为量化模型。

  • 步骤 3:使用 API torch.compile 将量化模型降低到 inductor 中。

这个流程的高级架构可能如下所示

float_model(Python)                          Example Input
    \                                              /
     \                                            /
—--------------------------------------------------------
|                         export                       |
—--------------------------------------------------------
                            |
                    FX Graph in ATen
                            |            X86InductorQuantizer
                            |                 /
—--------------------------------------------------------
|                      prepare_pt2e                     |
|                           |                           |
|                     Calibrate/Train                   |
|                           |                           |
|                      convert_pt2e                     |
—--------------------------------------------------------
                            |
                     Quantized Model
                            |
—--------------------------------------------------------
|                    Lower into Inductor                |
—--------------------------------------------------------
                            |
                         Inductor

结合 PyTorch 2 导出和 TorchInductor 的量化,我们通过新的量化前端获得了灵活性和生产力,并通过编译器后端获得了出色的开箱即用性能。尤其是在 Intel 第四代 (SPR) Xeon 处理器上,可以通过利用高级矩阵扩展功能进一步提升模型性能。

训练后量化

现在,我们将通过一个分步教程,向您展示如何将它与 torchvision resnet18 模型 一起用于训练后量化。

1. 捕获 FX 图

我们将首先执行必要的导入,从即时模式(eager)模块中捕获 FX 图。

import torch
import torchvision.models as models
import copy
from torchao.quantization.pt2e.quantize_pt2e import prepare_pt2e, convert_pt2e
import torchao.quantization.pt2e.quantizer.x86_inductor_quantizer as xiq
from torchao.quantization.pt2e.quantizer.x86_inductor_quantizer import X86InductorQuantizer
from torch.export import export

# Create the Eager Model
model_name = "resnet18"
model = models.__dict__[model_name](pretrained=True)

# Set the model to eval mode
model = model.eval()

# Create the data, using the dummy data here as an example
traced_bs = 50
x = torch.randn(traced_bs, 3, 224, 224).contiguous(memory_format=torch.channels_last)
example_inputs = (x,)

# Capture the FX Graph to be quantized
with torch.no_grad():
    # Note: requires torch >= 2.6
    exported_model = export(
        model,
        example_inputs
    ).module()

接下来,我们将对 FX Module 进行量化。

2. 应用量化

捕获要量化的 FX Module 后,我们将导入 X86 CPU 的后端量化器,并配置如何量化模型。

quantizer = X86InductorQuantizer()
quantizer.set_global(xiq.get_default_x86_inductor_quantization_config())

注意

X86InductorQuantizer 中的默认量化配置对激活和权重均使用 8 位。

当向量神经网络指令不可用时,oneDNN 后端会默默地选择假定乘法为 7 位 x 8 位的内核。换句话说,在没有向量神经网络指令的 CPU 上运行时,可能会出现潜在的数值饱和和准确性问题。

默认情况下,量化配置是针对静态量化的。要应用动态量化,在获取配置时添加参数 is_dynamic=True

quantizer = X86InductorQuantizer()
quantizer.set_global(xiq.get_default_x86_inductor_quantization_config(is_dynamic=True))

导入特定于后端的 Quantizer 后,我们将准备模型以进行训练后量化。 prepare_pt2e 将 BatchNorm 算子折叠到前面的 Conv2d 算子中,并在模型中的适当位置插入观察器。

prepared_model = prepare_pt2e(exported_model, quantizer)

现在,在观察器被插入到模型后,我们将校准 prepared_model。此步骤仅对静态量化是必需的。

# We use the dummy data as an example here
prepared_model(*example_inputs)

# Alternatively: user can define the dataset to calibrate
# def calibrate(model, data_loader):
#     model.eval()
#     with torch.no_grad():
#         for image, target in data_loader:
#             model(image)
# calibrate(prepared_model, data_loader_test)  # run calibration on sample data

最后,我们将校准后的模型转换为量化模型。convert_pt2e 接受一个校准过的模型并生成一个量化模型。

converted_model = convert_pt2e(prepared_model)

完成这些步骤后,我们就完成了量化流程的运行,并将获得量化模型。

3. 降低到 Inductor

获得量化模型后,我们将进一步将其降低到 inductor 后端。默认的 Inductor Wrapper 会生成 Python 代码来调用生成的内核和外部内核。此外,Inductor 支持 C++ Wrapper,它可以生成纯 C++ 代码。这允许无缝集成生成的内核和外部内核,有效减少 Python 开销。未来,利用 C++ Wrapper,我们可以扩展其功能以实现纯 C++ 部署。有关 C++ Wrapper 的更全面细节,请参阅关于Inductor C++ Wrapper 教程的专用教程。

# Optional: using the C++ wrapper instead of default Python wrapper
import torch._inductor.config as config
config.cpp_wrapper = True
with torch.no_grad():
    optimized_model = torch.compile(converted_model)

    # Running some benchmark
    optimized_model(*example_inputs)

在一个更高级的场景中,int8-mixed-bf16 量化发挥了作用。在这种情况下,卷积或 GEMM 算子会产生 BFloat16 输出数据类型,而不是 Float32,前提是没有后续的量化节点。随后,BFloat16 张量会无缝地传播到后续的逐点算子中,从而有效减少内存使用量并可能提高性能。使用此功能与常规 BFloat16 Autocast 的用法类似,只需将脚本包装在 BFloat16 Autocast 上下文中即可。

with torch.autocast(device_type="cpu", dtype=torch.bfloat16, enabled=True), torch.no_grad():
    # Turn on Autocast to use int8-mixed-bf16 quantization. After lowering into Inductor CPP Backend,
    # For operators such as QConvolution and QLinear:
    # * The input data type is consistently defined as int8, attributable to the presence of a pair
        of quantization and dequantization nodes inserted at the input.
    # * The computation precision remains at int8.
    # * The output data type may vary, being either int8 or BFloat16, contingent on the presence
    #   of a pair of quantization and dequantization nodes at the output.
    # For non-quantizable pointwise operators, the data type will be inherited from the previous node,
    # potentially resulting in a data type of BFloat16 in this scenario.
    # For quantizable pointwise operators such as QMaxpool2D, it continues to operate with the int8
    # data type for both input and output.
    optimized_model = torch.compile(converted_model)

    # Running some benchmark
    optimized_model(*example_inputs)

将所有这些代码放在一起,我们就会得到一个玩具示例代码。请注意,由于 Inductor 的 freeze 功能默认尚未开启,请使用 TORCHINDUCTOR_FREEZING=1 运行您的示例代码。

例如

TORCHINDUCTOR_FREEZING=1 python example_x86inductorquantizer_pytorch_2_1.py

通过 PyTorch 2.1 版本,TorchBench 测试套件中的所有 CNN 模型都经过了测量,并被证明与 Inductor FP32 推理路径相比有效。有关详细的基准测试数据,请参阅本文档

量化感知训练

PyTorch 2 导出量化感知训练 (QAT) 现在通过 X86InductorQuantizer 在 X86 CPU 上得到支持,之后将量化模型降低到 Inductor 中。要更深入地了解 PT2 导出量化感知训练,我们建议参考专门的PyTorch 2 导出量化感知训练

PyTorch 2 导出 QAT 流程与 PTQ 流程大体相似。

import torch
from torchao.quantization.pt2e.quantize_pt2e import (
  prepare_qat_pt2e,
  convert_pt2e,
)
import torchao.quantization.pt2e.quantizer.x86_inductor_quantizer as xiq
from torchao.quantization.pt2e.quantizer.x86_inductor_quantizer import X86InductorQuantizer

class M(torch.nn.Module):
   def __init__(self):
      super().__init__()
      self.linear = torch.nn.Linear(1024, 1000)

   def forward(self, x):
      return self.linear(x)

example_inputs = (torch.randn(1, 1024),)
m = M()

# Step 1. program capture
exported_model = torch.export.export(m, example_inputs).module()
# we get a model with aten ops

# Step 2. quantization-aware training
# Use Backend Quantizer for X86 CPU
# To apply dynamic quantization, add an argument ``is_dynamic=True`` when getting the config.
quantizer = X86InductorQuantizer()
quantizer.set_global(xiq.get_default_x86_inductor_quantization_config(is_qat=True))
prepared_model = prepare_qat_pt2e(exported_model, quantizer)

# train omitted

converted_model = convert_pt2e(prepared_model)
# we have a model with aten ops doing integer computations when possible

# move the quantized model to eval mode, equivalent to `m.eval()`
torchao.quantization.pt2e.move_exported_model_to_eval(converted_model)

# Lower the model into Inductor
with torch.no_grad():
  optimized_model = torch.compile(converted_model)
  _ = optimized_model(*example_inputs)

请注意,Inductor 的 freeze 功能默认未启用。要使用此功能,您需要使用 TORCHINDUCTOR_FREEZING=1 运行示例代码。

例如

TORCHINDUCTOR_FREEZING=1 python example_x86inductorquantizer_qat.py

结论

通过本教程,我们介绍了如何在 PyTorch 2 量化中使用 Inductor 与 X86 CPU。用户可以了解如何使用 X86InductorQuantizer 来量化模型并将其降低到使用 X86 CPU 设备的 inductor 中。

文档

访问全面的 PyTorch 开发者文档

查看文档

教程

为初学者和高级开发者提供深入的教程

查看教程

资源

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

查看资源