使用 XNNPACK 后端构建和运行 ExecuTorch¶
以下教程将带您熟悉利用 ExecuTorch XNNPACK 委托来加速您的 ML 模型使用 CPU 硬件。它将介绍如何将模型导出并序列化为二进制文件,以 XNNPACK 委托后端为目标,并在支持的目标平台上运行模型。为了快速入门,请使用 ExecuTorch 仓库中的脚本,其中包含有关导出和生成用于演示流程的几个示例模型的二进制文件的说明。
在本教程中,您将学习如何导出 XNNPACK 降低的模型并在目标平台上运行它
将模型降低到 XNNPACK¶
import torch
import torchvision.models as models
from torch.export import export, ExportedProgram
from torchvision.models.mobilenetv2 import MobileNet_V2_Weights
from executorch.backends.xnnpack.partition.xnnpack_partitioner import XnnpackPartitioner
from executorch.exir import EdgeProgramManager, ExecutorchProgramManager, to_edge_transform_and_lower
from executorch.exir.backend.backend_api import to_backend
mobilenet_v2 = models.mobilenetv2.mobilenet_v2(weights=MobileNet_V2_Weights.DEFAULT).eval()
sample_inputs = (torch.randn(1, 3, 224, 224), )
exported_program: ExportedProgram = export(mobilenet_v2, sample_inputs)
edge: EdgeProgramManager = to_edge_transform_and_lower(
exported_program,
partitioner=[XnnpackPartitioner()],
)
我们将通过从 TorchVision 库下载的 MobileNetV2 预训练模型来完成此示例。模型降低的流程从导出模型 to_edge
开始。我们使用 XnnpackPartitioner
调用 to_backend
API。分区器会识别适合 XNNPACK 后端委托消费的子图。之后,标识出的子图将被序列化为 XNNPACK 委托 flatbuffer schema,并且每个子图将被替换为对 XNNPACK 委托的调用。
>>> print(edge.exported_program().graph_module)
GraphModule(
(lowered_module_0): LoweredBackendModule()
(lowered_module_1): LoweredBackendModule()
)
def forward(self, b_features_0_1_num_batches_tracked, ..., x):
lowered_module_0 = self.lowered_module_0
lowered_module_1 = self.lowered_module_1
executorch_call_delegate_1 = torch.ops.higher_order.executorch_call_delegate(lowered_module_1, x); lowered_module_1 = x = None
getitem_53 = executorch_call_delegate_1[0]; executorch_call_delegate_1 = None
aten_view_copy_default = executorch_exir_dialects_edge__ops_aten_view_copy_default(getitem_53, [1, 1280]); getitem_53 = None
aten_clone_default = executorch_exir_dialects_edge__ops_aten_clone_default(aten_view_copy_default); aten_view_copy_default = None
executorch_call_delegate = torch.ops.higher_order.executorch_call_delegate(lowered_module_0, aten_clone_default); lowered_module_0 = aten_clone_default = None
getitem_52 = executorch_call_delegate[0]; executorch_call_delegate = None
return (getitem_52,)
我们会在上面降低模型后打印图,以显示为调用 XNNPACK 委托而插入的新节点。被委托给 XNNPACK 的子图是每个调用站点上的第一个参数。可以看出,大部分 convolution-relu-add
块和 linear
块都可以被委托给 XNNPACK。我们还可以看到无法降低到 XNNPACK 委托的运算符,例如 clone
和 view_copy
。
exec_prog = edge.to_executorch()
with open("xnnpack_mobilenetv2.pte", "wb") as file:
exec_prog.write_to_file(file)
降低到 XNNPACK Program 后,我们可以将其准备好用于 executorch,并将模型保存为 .pte
文件。.pte
是一种二进制格式,用于存储序列化的 ExecuTorch 图。
将量化模型降低到 XNNPACK¶
XNNPACK 委托还可以执行对称量化模型。要了解量化流程并学习如何量化模型,请参考 自定义量化 说明。在本教程中,我们将利用方便地添加到 executorch/executorch/examples
文件夹的 quantize()
Python 辅助函数。
from torch.export import export_for_training
from executorch.exir import EdgeCompileConfig, to_edge_transform_and_lower
mobilenet_v2 = models.mobilenetv2.mobilenet_v2(weights=MobileNet_V2_Weights.DEFAULT).eval()
sample_inputs = (torch.randn(1, 3, 224, 224), )
mobilenet_v2 = export_for_training(mobilenet_v2, sample_inputs).module() # 2-stage export for quantization path
from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_pt2e
from executorch.backends.xnnpack.quantizer.xnnpack_quantizer import (
get_symmetric_quantization_config,
XNNPACKQuantizer,
)
def quantize(model, example_inputs):
"""This is the official recommended flow for quantization in pytorch 2.0 export"""
print(f"Original model: {model}")
quantizer = XNNPACKQuantizer()
# if we set is_per_channel to True, we also need to add out_variant of quantize_per_channel/dequantize_per_channel
operator_config = get_symmetric_quantization_config(is_per_channel=False)
quantizer.set_global(operator_config)
m = prepare_pt2e(model, quantizer)
# calibration
m(*example_inputs)
m = convert_pt2e(m)
print(f"Quantized model: {m}")
# make sure we can export to flat buffer
return m
quantized_mobilenetv2 = quantize(mobilenet_v2, sample_inputs)
量化需要分两个阶段进行导出。首先,我们使用 export_for_training
API 在将模型提供给 quantize
实用函数之前捕获模型。在执行量化步骤后,我们现在可以利用 XNNPACK 委托来降低量化后的导出模型图。从这里开始,过程与非量化模型降低到 XNNPACK 的过程相同。
# Continued from earlier...
edge = to_edge_transform_and_lower(
export(quantized_mobilenetv2, sample_inputs),
compile_config=EdgeCompileConfig(_check_ir_validity=False),
partitioner=[XnnpackPartitioner()]
)
exec_prog = edge.to_executorch()
with open("qs8_xnnpack_mobilenetv2.pte", "wb") as file:
exec_prog.write_to_file(file)
使用 aot_compiler.py
脚本进行降低¶
我们还提供了一个脚本,用于快速降低和导出几个示例模型。您可以运行该脚本来生成降低后的 fp32 和量化模型。此脚本仅用于方便起见,并执行与前两节中列出的所有步骤相同的操作。
python -m examples.xnnpack.aot_compiler --model_name="mv2" --quantize --delegate
在上面的示例中请注意,
-—model_name
指定要使用的模型-—quantize
标志控制是否应量化模型-—delegate
标志控制我们是否尝试将图的一部分降低到 XNNPACK 委托。
生成的模型文件将根据提供的参数命名为 [model_name]_xnnpack_[qs8/fp32].pte
。
使用 CMake 运行 XNNPACK 模型¶
导出 XNNPACK 委托模型后,我们现在可以使用 CMake 尝试使用示例输入运行它。我们可以构建和使用 executor_runner,这是一个 ExecuTorch Runtime 的示例包装器。XNNPACK 后端通过编译标志 -DEXECUTORCH_BUILD_XNNPACK=ON
启用。我们首先像这样配置 CMake 构建
# cd to the root of executorch repo
cd executorch
# Get a clean cmake-out directory
./install_executorch.sh --clean
mkdir cmake-out
# Configure cmake
cmake \
-DCMAKE_INSTALL_PREFIX=cmake-out \
-DCMAKE_BUILD_TYPE=Release \
-DEXECUTORCH_BUILD_EXECUTOR_RUNNER=ON \
-DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=ON \
-DEXECUTORCH_BUILD_EXTENSION_MODULE=ON \
-DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON \
-DEXECUTORCH_BUILD_XNNPACK=ON \
-DEXECUTORCH_ENABLE_LOGGING=ON \
-DPYTHON_EXECUTABLE=python \
-Bcmake-out .
然后您可以使用以下命令构建运行时组件
cmake --build cmake-out -j9 --target install --config Release
现在您应该可以在 ./cmake-out/executor_runner
找到构建的可执行文件,您可以像这样使用您生成的模型来运行可执行文件
./cmake-out/executor_runner --model_path=./mv2_xnnpack_fp32.pte
# or to run the quantized variant
./cmake-out/executor_runner --model_path=./mv2_xnnpack_q8.pte
使用 XNNPACK 后端构建和链接¶
您可以构建 XNNPACK 后端 CMake 目标,并将其与您的应用程序二进制文件(例如 Android 或 iOS 应用程序)链接。有关此内容的更多信息,您可以接下来查看此 资源。
分析¶
要在 executor_runner
中启用分析,请将标志 -DEXECUTORCH_ENABLE_EVENT_TRACER=ON
和 -DEXECUTORCH_BUILD_DEVTOOLS=ON
传递给构建命令(添加 -DENABLE_XNNPACK_PROFILING=ON
以获取更多详细信息)。这将启用运行时推理时的 ETDump 生成,并启用用于分析的命令行标志(有关详细信息,请参阅 executor_runner --help
)。