• 文档 >
  • 使用 ARM Ethos-U 后端构建和运行 ExecuTorch
快捷键

使用 ARM Ethos-U 后端构建和运行 ExecuTorch

您将在本教程中学习什么

在本教程中,您将学习如何将简单的 PyTorch 模型导出到 ExecuTorch Arm Ethos-u 后端委托并在 Corstone-300 FVP 模拟器上运行它。

警告

此 ExecuTorch 后端委托处于积极开发中。您可能会遇到一些粗糙的边缘和功能,这些功能可能已记录或计划但尚未实施。

提示

如果您已熟悉此委托,您可能希望直接跳转到示例源目录 - https://github.com/pytorch/executorch/tree/main/examples/arm

先决条件

在我们开始之前,让我们确保您拥有所需的一切。

硬件

要成功完成本教程,您需要一台基于 Linux 的主机,其处理器架构为 Arm aarch64 或 x86_64。

目标设备将是具有 Arm Cortex-M55 CPU 和 Ethos-U55 NPU(ML 处理器)的嵌入式平台。本教程将向您展示如何在两者上运行 PyTorch 模型。

我们将使用 固定虚拟平台 (FVP) 模拟 Corstone-300 (cs300) 系统。由于我们将使用 FVP(可以把它想象成虚拟硬件),因此本教程不需要任何真实的嵌入式硬件。

软件

首先,您需要安装 ExecuTorch。如果您还没有,请按照推荐的教程操作,以设置一个可用的 ExecuTorch 开发环境。

要生成可在嵌入式平台(真实或虚拟)上运行的软件,我们将需要一个用于交叉编译的工具链和 Arm Ethos-U 软件开发工具包,包括用于 Ethos-U NPU 的 Vela 编译器。

在以下部分中,我们将逐步介绍下载上面列出的每个依赖项的过程。

设置开发环境

在本节中,我们将进行一次性设置,例如下载和安装必要的软件,以获取在本教程中运行 ExecuTorch 程序所需的平台支持文件。

为此,我们将使用 examples/arm/setup.sh 脚本以自动化方式提取每个项目。建议在 conda 环境中运行脚本。执行成功后,您可以直接转到 下一步

如前所述,我们目前仅支持具有 x86_64 或 aarch64 处理器架构的基于 Linux 的平台。让我们确保我们确实在支持的平台上。

uname -s
# Linux

uname -m
# x86_64 or aarch64

接下来,我们将逐步介绍 setup.sh 脚本执行的步骤,以更好地了解开发设置。

下载和设置 Corstone-300 FVP

固定虚拟平台 (FVP) 是流行系统配置的预配置、功能精确的模拟。在本教程中,我们对 Corstone-300 系统感兴趣。我们可以从 Arm 网站下载它。

注意

通过下载和运行 FVP 软件,您将同意 FVP 最终用户许可协议 (EULA)

要下载,我们可以从 这里 下载 Corstone-300 Ecosystem FVP。或者,setup.sh 脚本将在 setup_fvp 函数下为您执行此操作。

下载和安装 Arm GNU AArch32 嵌入式工具链

与 FVP 类似,我们还需要一个工具链来交叉编译 ExecuTorch 运行时、executor-runner 嵌入式应用程序以及 Corstone-300 平台上可用的 Cortex-M55 CPU 的其余嵌入式堆栈。

这些工具链可在 这里 获得。在本教程中,我们将使用针对 arm-none-eabi 的 GCC 12.3。就像 FVP 一样,setup.sh 脚本将为您下载工具链。请参阅 setup_toolchain 函数。

设置 Arm Ethos-U 软件开发

此 Git 仓库是所有 Arm Ethos-U 软件的根目录。它旨在帮助我们下载所需的仓库并将它们放置在一个树状结构中。有关更多详细信息,请参见设置脚本的 setup_ethos_u 函数。

完成此操作后,您应该拥有一个可工作的 FVP 仿真器,一个用于交叉编译的功能完备的工具链,以及为裸机开发准备的 Ethos-U 软件开发环境。

安装 Vela 编译器

完成此操作后,脚本将通过为您安装 Vela 编译器来完成设置,详细信息请参见 setup_vela 函数。

安装 TOSA 参考模型

这是设置过程的最后一步,使用 setup_tosa_reference_model 函数 setup.sh 脚本将为您安装 TOSA 参考模型。

在设置结束时,如果一切顺利,您的顶级开发目录可能看起来像这样,

.
├── arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi # for x86-64 hosts
├── ethos-u
│   ├── core_platform
│   ├── core_software
│   ├── fetch_externals.py
│   └── [...]
├── ethos-u-vela
├── FVP
│   ├── FVP_Corstone_SSE-300.sh
│   └── [...]
├── FVP_cs300.tgz
├── gcc.tar.xz
└── reference_model

将 PyTorch 模型转换为 .pte 文件

.pte 是由 ExecuTorch Ahead-of-Time (AoT) 管道通过接收 PyTorch 模型(一个 torch.nn.Module)、导出它、运行各种传递并最终将其序列化为 .pte 文件格式生成的二进制文件。此二进制文件通常由 ExecuTorch 运行时使用。此 文档 详细介绍了 ExecuTorch 软件堆栈,涵盖 AoT 和运行时。

在本节中,我们将主要关注 AoT 流程,最终目标是生成一个 .pte 文件。有一组导出配置可用于在运行时定位不同的后端。对于每种配置,AoT 流程都会生成一个唯一的 .pte 文件。我们将探索几种不同的配置,这些配置会生成不同的 .pte 文件,特别是对于我们的 Corstone-300 系统和可用的处理元素来说,这一点非常有趣。

在我们开始之前,让我们先讨论一下我们将要使用的 PyTorch 模块。

PyTorch 示例模块

我们将使用几个简单的 PyTorch 模块来探索端到端流程。这些模块将在整个教程中以不同的方式使用,并通过其 <class_name> 来引用它们。

SoftmaxModule

这是一个非常简单的 PyTorch 模块,只有一个 Softmax 操作符。

import torch

class SoftmaxModule(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.softmax = torch.nn.Softmax()

    def forward(self, x):
        z = self.softmax(x)
        return z

使用 Python 环境(在同一开发 Linux 机器上)运行它,我们获得了预期的输出。

>>> m = SoftmaxModule()
>>> m(torch.ones(2,2))
tensor([[0.5000, 0.5000],
        [0.5000, 0.5000]])

AddModule

让我们编写另一个简单的 PyTorch 模块,它只有一个 Add 操作符。

class AddModule(torch.nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, x):
        return x + x

使用 Python 环境(在同一开发 Linux 机器上)运行它,正如预期的那样,1 + 1 确实产生 2。

>>> m = AddModule()
>>> m(torch.ones(5, dtype=torch.int32)) # integer types for non-quantized Ethos-U delegation
tensor([2, 2, 2, 2, 2], dtype=torch.int32)

请牢记这些模块的输入和输出。当我们降低并通过替代方式运行它,而不是在该 Linux 机器上运行时,我们将使用相同的输入,并期望输出与这里显示的输出匹配。

提示

我们需要了解在 Ethos-U55 上运行网络的数据类型,因为它是一个仅支持整数的处理器。在此示例中,我们显式地使用整数类型,对于此类流程的典型使用,网络是在浮点中构建和训练的,然后被量化为整数以进行高效的推理。

MobileNetV2 模块

MobileNetV2 是一个常用在生产环境中的网络,适用于边缘和移动设备。它也是 torchvision 中的默认模型,因此我们可以使用以下示例代码加载它。

from torchvision.models import mobilenet_v2  # @manual
from torchvision.models.mobilenetv2 import MobileNet_V2_Weights

mv2 = mobilenet_v2(weights=MobileNet_V2_Weights.DEFAULT)

有关更多详细信息,您可以参考以下代码片段 这里

非委托工作流程

在 ExecuTorch AoT 管道中,其中一个选项是选择后端。ExecuTorch 提供了各种不同的后端。选择后端是可选的,通常是为了针对特定模式的加速或硬件,以满足给定模型的计算需求。如果没有后端,ExecuTorch 运行时将回退到使用默认情况下可用的高度可移植的操作符集。

预计在拥有专门加速功能的平台(例如 Ethos-U55)上,非委托流程将用于以下两种主要情况:

  1. 当网络设计为非常小并且最适合在 Cortex-M 上运行时。

  2. 当网络包含一部分可以针对 NPU 的操作以及一部分不能针对 NPU 的操作时,例如,Ethos-U55 支持整数运算,因此浮点 Softmax 将回退到在 CPU 上执行。

在此流程中,没有任何后端委托,为了说明 ExecuTorch 运行时以及操作符库的可移植性,我们将跳过在 .pte 生成期间指定后端。

以下脚本将用作一个帮助程序实用程序,帮助我们生成 .pte 文件。它位于 examples/arm 目录中。

python3 -m examples.arm.aot_arm_compiler --model_name="softmax"
# This should produce ./softmax.pte

委托工作流程

与 Arm 合作,我们为 ExecuTorch 引入了一个新的 Arm 后端委托。该后端正在积极开发中,截至撰写本文时,其功能有限。

通过在 ExecuTorch AoT 导出管道中包含以下步骤来生成 .pte 文件,我们可以启用此后端委托。

from executorch.backends.arm.arm_backend import generate_ethosu_compile_spec

graph_module_edge.exported_program = to_backend(
    model.exported_program,
    ArmPartitioner(generate_ethosu_compile_spec("ethos-u55-128")))

与非委托流程类似,相同的脚本将用作一个帮助程序实用程序,帮助我们生成 .pte 文件。请注意,--delegate 选项用于启用 to_backend 调用。

python3 -m examples.arm.aot_arm_compiler --model_name="add" --delegate
# should produce ./add_arm_delegate.pte

委托量化工作流程

在为委托量化网络(例如 MobileNetV2)生成 .pte 文件之前,我们需要构建 quantized_ops_aot_lib

SITE_PACKAGES="$(python3 -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())')"
CMAKE_PREFIX_PATH="${SITE_PACKAGES}/torch"

cd <executorch_root_dir>
mkdir -p cmake-out-aot-lib
cmake -DCMAKE_BUILD_TYPE=Release \
    -DEXECUTORCH_BUILD_XNNPACK=OFF \
    -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \
    -DEXECUTORCH_BUILD_KERNELS_QUANTIZED_AOT=ON \
    -DCMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH" \
    -DPYTHON_EXECUTABLE=python3 \
-Bcmake-out-aot-lib \
    "${et_root_dir}"

n=$(nproc)
cmake --build cmake-out-aot-lib -j"$((n - 5))" -- quantized_ops_aot_lib

quantized_ops_aot_lib 构建完成后,我们可以运行以下脚本生成 .pte 文件

python3 -m examples.arm.aot_arm_compiler --model_name="mv2" --delegate --quantize --so_library="$(find cmake-out-aot-lib -name libquantized_ops_aot_lib.so)"
# should produce ./mv2_arm_delegate.pte.pte

最后,我们应该拥有三个不同的 .pte 文件。

  • 第一个包含 SoftmaxModule,没有任何后端委托。

  • 第二个包含 AddModule,启用了 Arm Ethos-U 后端委托。

  • 第三个包含 quantized MV2Model,同样也启用了 Arm Ethos-U 后端委托。

现在让我们尝试在 Corstone-300 平台的裸机环境中运行这些 .pte 文件。

获取裸机可执行文件

在本节中,我们将介绍构建运行时应用程序所需经历的步骤。然后,它将在目标设备上运行。在 executorch 存储库中,我们有一个功能完备的脚本,它执行完全相同的步骤。它位于 executorch/examples/arm/run.sh。我们将使用它来构建必要的部件,最后将之前生成的 PTE 文件运行在 FVP 上。

此外,在我们开始之前,请确保您已完成 ExecuTorch cmake 构建设置,以及前面描述的设置开发环境的说明 earlier

下方的框图从高层展示了如何生成各种构建工件,以及如何将它们链接在一起以生成最终的裸机可执行文件。

生成 ExecuTorch 库

ExecuTorch 的 CMake 构建系统生成了一组构建部件,这些部件对于我们在裸机环境中包含和运行 ExecuTorch 运行时至关重要,该环境是我们在 Ethos-U SDK 中为 Corstone-300 提供的。

This 文档详细概述了每个构建部件。为了运行两种变体之一的 .pte 文件,我们将需要一组核心库。以下是列表:

  • libexecutorch.a

  • libportable_kernels.a

  • libportable_ops_lib.a

为了运行具有 Arm 后端委托调用指令的 .pte 文件,我们将需要 Arm 后端委托运行时库,即:

  • libexecutorch_delegate_ethos_u.a

这些库是在 run.sh 脚本的 build_executorch 函数中生成的。

在此函数中,EXECUTORCH_SELECT_OPS_LIST 将决定构建中包含的可移植操作符数量,这些操作符在运行时可用。它必须与 .pte 文件的要求匹配,否则您将在运行时收到 Missing Operator 错误。

例如,在上面的命令行中,为了运行 SoftmaxModule,我们只包含了 softmax CPU 操作符。类似地,为了以非委托方式运行 AddModule,您将需要添加操作符,依此类推。正如您可能已经意识到的,对于委托操作符(将由 Arm 后端委托执行),我们不需要将这些操作符包含在此列表中。这仅适用于非委托操作符。

构建 executor_runner 裸机应用程序

SDK 目录与我们之前准备的目录相同 earlier。并且,我们将传递上面生成的 .pte 文件(其中任意一个)。

请注意,如果您要更改模型或 .pte 文件,则必须生成一个新的 executor-runner 二进制文件。此约束来自我们在 Corstone-300 平台上拥有的受限裸机运行时环境。

这是由 run.sh 中的 build_executorch_runner 函数执行的。

在 Corstone-300 FVP 平台上运行

一旦 ELF 文件准备就绪,无论使用哪种 .pte 文件变体生成裸机 ELF 文件,您都可以使用以下命令运行它:

ethos_u_build_dir=examples/arm/executor_runner/

elf=$(find ${ethos_u_build_dir} -name "arm_executor_runner")

FVP_Corstone_SSE-300_Ethos-U55                          \
    -C ethosu.num_macs=128                              \
    -C mps3_board.visualisation.disable-visualisation=1 \
    -C mps3_board.telnetterminal0.start_telnet=0        \
    -C mps3_board.uart0.out_file='-'                    \
    -a "${elf}"                                         \
    --timelimit 10 # seconds - after which sim will kill itself

如果成功,模拟器应该在 shell 上输出类似以下内容:

    Ethos-U rev 136b7d75 --- Apr 12 2023 13:44:01
    (C) COPYRIGHT 2019-2023 Arm Limited
    ALL RIGHTS RESERVED

I executorch:runner.cpp:64] Model PTE file loaded. Size: 960 bytes.
I executorch:runner.cpp:70] Model buffer loaded, has 1 methods
I executorch:runner.cpp:78] Running method forward
I executorch:runner.cpp:95] Setting up planned buffer 0, size 32.
I executorch:runner.cpp:110] Method loaded.
I executorch:runner.cpp:112] Preparing inputs...
I executorch:runner.cpp:114] Input prepared.
I executorch:runner.cpp:116] Starting the model execution...
I executorch:runner.cpp:121] Model executed successfully.
I executorch:runner.cpp:125] 1 outputs:
Output[0][0]: 0.500000
Output[0][1]: 0.500000
Output[0][2]: 0.500000
Output[0][3]: 0.500000
Application exit code: 0.

EXITTHESIM

Info: Simulation is stopping. Reason: CPU time has been exceeded.

在本例中,我们使用 softmax.pte 文件运行 executor_runner 二进制文件,该文件是为 SoftmaxModule 生成的,我们确实看到了在 FVP 模拟器上的 Corstone-300 虚拟硬件上运行的裸机二进制文件生成的预期结果。

如果您使用为 AddModule 生成的委托 .pte 文件(即 add_arm_delegate.pte)重新运行相同的 FVP 命令,您可能会看到类似以下内容,同样是预期结果。请注意以 ArmBackend:: 为前缀打印的消息,它们表明后端已成功初始化,并且来自我们 .pte 文件中的 AddModule 的 add 运算符已在 Ethos-U55 NPU 上执行。

    Ethos-U rev 136b7d75 --- Apr 12 2023 13:44:01
    (C) COPYRIGHT 2019-2023 Arm Limited
    ALL RIGHTS RESERVED

I executorch:runner.cpp:64] Model PTE file loaded. Size: 2208 bytes.
I executorch:runner.cpp:70] Model buffer loaded, has 1 methods
I executorch:runner.cpp:78] Running method forward
I executorch:runner.cpp:95] Setting up planned buffer 0, size 64.
I executorch:ArmBackendEthosU.cpp:51] ArmBackend::init 0x11000050
I executorch:runner.cpp:110] Method loaded.
I executorch:runner.cpp:112] Preparing inputs...
I executorch:runner.cpp:114] Input prepared.
I executorch:runner.cpp:116] Starting the model execution...
I executorch:ArmBackendEthosU.cpp:103] ArmBackend::execute 0x11000050
I executorch:runner.cpp:121] Model executed successfully.
I executorch:runner.cpp:125] 1 outputs:
Output[0][0]: 2
Output[0][1]: 2
Output[0][2]: 2
Output[0][3]: 2
Output[0][4]: 2
Application exit code: 0.

EXITTHESIM

Info: Simulation is stopping. Reason: CPU time has been exceeded.

类似地,我们可以获得运行 MV2Model 时的以下输出:

    Ethos-U rev 136b7d75 --- Apr 12 2023 13:44:01
    (C) COPYRIGHT 2019-2023 Arm Limited
    ALL RIGHTS RESERVED

I executorch:arm_executor_runner.cpp:60] Model in 0x70000000 $
I executorch:arm_executor_runner.cpp:66] Model PTE file loaded. Size: 4556832 bytes.
I executorch:arm_executor_runner.cpp:77] Model buffer loaded, has 1 methods
I executorch:arm_executor_runner.cpp:85] Running method forward
I executorch:arm_executor_runner.cpp:109] Setting up planned buffer 0, size 752640.
I executorch:ArmBackendEthosU.cpp:49] ArmBackend::init 0x70000060
I executorch:arm_executor_runner.cpp:130] Method loaded.
I executorch:arm_executor_runner.cpp:132] Preparing inputs...
I executorch:arm_executor_runner.cpp:141] Input prepared.
I executorch:arm_executor_runner.cpp:143] Starting the model execution...
I executorch:ArmBackendEthosU.cpp:87] ArmBackend::execute 0x70000060
I executorch:ArmBackendEthosU.cpp:234] Tensor input 0 will be permuted
I executorch:arm_executor_runner.cpp:152] Model executed successfully.
I executorch:arm_executor_runner.cpp:156] 1 outputs:
Output[0][0]: -0.639322
Output[0][1]: 0.169232
Output[0][2]: -0.451286
...(Skipped)
Output[0][996]: 0.150429
Output[0][997]: -0.488894
Output[0][998]: 0.037607
Output[0][999]: 1.203430
I executorch:arm_executor_runner.cpp:177] Program complete, exiting.
I executorch:arm_executor_runner.cpp:179]

结论

通过本教程,我们了解了如何使用 ExecuTorch 软件导出 PyTorch 的标准模型,并在紧凑且功能齐全的 ExecuTorch 运行时上运行它,从而为将模型从 PyTorch 迁移到 Arm 平台提供了一条平滑的路径。

回顾一下,有两种主要流程:

  • 直接流程将工作卸载到 Cortex-M,使用 ExecuTorch 中内置的库。

  • 委托流程将图划分成 Cortex-M 的部分和可以卸载到 Ethos-U 硬件上加速的部分。

这两种流程都在不断发展,支持更多用例并提高性能。

常见问题解答

如果您在遵循本教程时遇到任何错误或问题,请在此处提交错误/问题:Github

文档

访问 PyTorch 的全面开发者文档

查看文档

教程

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

查看教程

资源

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

查看资源