目录

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

我们建议您在此之前完成教程:
您将在本教程中学到什么:

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

警告

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

提示

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

先决条件

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

硬件

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

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

我们将使用固定虚拟平台 (FVP) 来模拟 Corstone-300(cs300) 和 Corstone-320(cs320) 系统。由于我们将使用 FVP(将其视为虚拟硬件),因此本教程不需要任何真正的嵌入式硬件。

软件

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

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

在以下部分中,我们将演练下载上面列出的每个依赖项的步骤。

设置开发人员环境

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

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

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

uname -s
# Linux

uname -m
# x86_64 or aarch64

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

下载并设置 Corstone-300 和 Corstone-320 FVP

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

注意

下载和运行 FVP 软件,即表示您同意 FVP 最终用户许可协议 (EULA)。

要下载,我们可以下载 并从 这里.或 script 在 function 下为您执行此操作。Corstone-300 Ecosystem FVPCorstone-320 Ecosystem FVPsetup.shsetup_fvp

下载并安装 Arm GNU AArch32 裸机工具链

与 FVP 类似,我们还需要一个工具链来交叉编译 ExecuTorch 运行时、执行程序运行程序裸机应用程序,以及 Corstone-300/Corstone-320 平台上可用的 Cortex-M55/M85 CPU 裸机堆栈的其余部分。

这些工具链可在此处获得。我们将在本教程中使用 GCC 12.3 定位。就像 FVP 一样,script 将为你提供工具链。请参阅函数。arm-none-eabisetup.shsetup_toolchain

设置 Arm Ethos-U 软件开发

此 git 存储库是所有 Arm Ethos-U 软件的根目录。这是为了帮助我们下载所需的存储库并将它们放在树结构中。有关更多详细信息,请参阅 function of the setup script 。setup_ethos_u

完成此操作后,您应该有一个可用的 FVP 模拟器、一个用于交叉编译的功能性工具链,以及用于裸机开发的 Ethos-U 软件开发设置。

安装 Vela 编译器

完成此操作后,脚本将通过为您安装 Vela 编译器来完成设置,详细信息在功能中。setup_vela

安装 TOSA 参考模型

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

在设置结束时,如果一切顺利,您的顶级开发目录可能如下所示:

.
├── 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-corstone300
│   ├── FVP_Corstone_SSE-300.sh
│   └── [...]
├── FVP-corstone320
│   ├── FVP_Corstone_SSE-320.sh
│   └── [...]
├── FVP_cs300.tgz
├── FVP_cs320.tgz
├── gcc.tar.xz
└── reference_model

将 PyTorch 模型转换为文件.pte

.pte是由 ExecuTorch 预先 (AoT) 管道生成的二进制文件,方法是接收 PyTorch 模型 (torch.nn.Module),将其导出,运行各种传递,最后将其序列化为文件格式。此二进制文件通常由 ExecuTorch 运行时使用。本文档更深入地介绍了 AoT 和 Runtime 的 ExecuTorch 软件堆栈。.pte

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

在开始之前,我们先谈谈我们将使用的 PyTorch 模块。

PyTorch 示例模块

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

Softmax模块

这是一个非常简单的 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 提供了各种不同的后端。选择 backend 是可选的,通常是为了针对给定模型计算要求的特定加速模式或硬件。如果没有任何后端,ExecuTorch 运行时将回退到使用一组高度可移植的运算符(默认情况下可用)。

预计在具有专用加速的平台上(如 Ethos-U55),非委托流将用于两种主要情况:

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

  2. 当网络混合了可以针对 NPU 的操作和不能针对 NPU 的操作时,例如 Ethos-U55 支持整数运算,因此浮点 softmax 将回退到 CPU 上执行。

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

以下脚本将用作帮助程序实用程序,帮助我们生成文件。这在目录中可用。.pteexamples/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--delegateto_backend

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

委托量化工作流程

在为 MobileNetV2 等委托量化网络生成文件之前,我们需要构建.ptequantized_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}"

cmake --build cmake-out-aot-lib --parallel -- 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 后端委托。

  • 第三个包含量化的 MV2Model,并且还启用了 Arm Ethos-U 后端委托。

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

获取裸机可执行文件

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

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

下面的框图概括地演示了如何生成各种构建工件并将其链接在一起以生成最终的裸机可执行文件。

提示

script 中的函数根据通过 input 参数提供的模型生成文件generate_pte_filerun.sh.pte--model_name

生成 ExecuTorch 库

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

本文档提供了每个构建件的详细概述。要运行文件的任一变体,我们需要一组核心库。这是一个列表,.pte

  • libexecutorch.a

  • libportable_kernels.a

  • libportable_ops_lib.a

要使用 Arm 后端委托调用指令运行文件,我们需要 Arm 后端委托运行时库,即.pte

  • libexecutorch_delegate_ethos_u.a

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

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

例如,在上面的命令行中,为了运行 SoftmaxModule,我们只包含 softmax CPU 运算符。同样,要以非委托方式运行 AddModule,您需要 add op 等。您可能已经意识到,对于将由 Arm 后端委托执行的委托运算符,我们不需要在此列表中包含这些运算符。这仅适用于非委托运算符

提示

该脚本接受 option,它提供了一种提供要包含的可移植内核的逗号分隔列表的方法。run.sh--portable_kernels

构建 executor_runner 裸机应用程序

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

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

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

提示

该脚本采用 option,它提供了一种提供特定目标的方法,即 Corstone-300(ethos-u55-128) 或 Corstone-320(ethos-u85-128)run.sh--target

在 Corstone FVP 平台上运行

准备好 elf 后,无论文件变体如何,都使用裸机 elf 生成。以下命令用于在 Corstone-320 FVP 上运行 MV2Model.pte

ethos_u_build_dir=examples/arm/executor_runner/

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

FVP_Corstone_SSE-320_Ethos-U85                          \
    -C mps4_board.subsystem.ethosu.num_macs=${num_macs} \
    -C mps4_board.visualisation.disable-visualisation=1 \
    -C vis_hdlcd.disable_visualisation=1                \
    -C mps4_board.telnetterminal0.start_telnet=0        \
    -C mps4_board.uart0.out_file='-'                    \
    -C mps4_board.uart0.shutdown_on_eot=1               \
    -a "${elf}"                                         \
    --timelimit 120 || true # seconds- after which sim will kill itself

如果成功,模拟器应在 shell 上生成类似于以下内容的内容:

I [executorch:arm_executor_runner.cpp:364] Model in 0x70000000 $
I [executorch:arm_executor_runner.cpp:366] Model PTE file loaded. Size: 4425968 bytes.
I [executorch:arm_executor_runner.cpp:376] Model buffer loaded, has 1 methods
I [executorch:arm_executor_runner.cpp:384] Running method forward
I [executorch:arm_executor_runner.cpp:395] Setup Method allocator pool. Size: 62914560 bytes.
I [executorch:arm_executor_runner.cpp:412] Setting up planned buffer 0, size 752640.
I [executorch:ArmBackendEthosU.cpp:79] ArmBackend::init 0x70000070
I [executorch:arm_executor_runner.cpp:445] Method loaded.
I [executorch:arm_executor_runner.cpp:447] Preparing inputs...
I [executorch:arm_executor_runner.cpp:461] Input prepared.
I [executorch:arm_executor_runner.cpp:463] Starting the model execution...
I [executorch:ArmBackendEthosU.cpp:118] ArmBackend::execute 0x70000070
I [executorch:ArmBackendEthosU.cpp:298] Tensor input/output 0 will be permuted
I [executorch:arm_perf_monitor.cpp:120] NPU Inferences : 1
I [executorch:arm_perf_monitor.cpp:121] Profiler report, CPU cycles per operator:
I [executorch:arm_perf_monitor.cpp:125] ethos-u : cycle_cnt : 1498202 cycles
I [executorch:arm_perf_monitor.cpp:132] Operator(s) total: 1498202 CPU cycles
I [executorch:arm_perf_monitor.cpp:138] Inference runtime: 6925114 CPU cycles total
I [executorch:arm_perf_monitor.cpp:140] NOTE: CPU cycle values and ratio calculations require FPGA and identical CPU/NPU frequency
I [executorch:arm_perf_monitor.cpp:149] Inference CPU ratio: 99.99 %
I [executorch:arm_perf_monitor.cpp:153] Inference NPU ratio: 0.01 %
I [executorch:arm_perf_monitor.cpp:162] cpu_wait_for_npu_cntr : 729 CPU cycles
I [executorch:arm_perf_monitor.cpp:167] Ethos-U PMU report:
I [executorch:arm_perf_monitor.cpp:168] ethosu_pmu_cycle_cntr : 5920305
I [executorch:arm_perf_monitor.cpp:171] ethosu_pmu_cntr0 : 359921
I [executorch:arm_perf_monitor.cpp:171] ethosu_pmu_cntr1 : 0
I [executorch:arm_perf_monitor.cpp:171] ethosu_pmu_cntr2 : 0
I [executorch:arm_perf_monitor.cpp:171] ethosu_pmu_cntr3 : 503
I [executorch:arm_perf_monitor.cpp:178] Ethos-U PMU Events:[ETHOSU_PMU_EXT0_RD_DATA_BEAT_RECEIVED, ETHOSU_PMU_EXT1_RD_DATA_BEAT_RECEIVED, ETHOSU_PMU_EXT0_WR_DATA_BEAT_WRITTEN, ETHOSU_PMU_NPU_IDLE]
I [executorch:arm_executor_runner.cpp:470] model_pte_loaded_size:     4425968 bytes.
I [executorch:arm_executor_runner.cpp:484] method_allocator_used:     1355722 / 62914560  free: 61558838 ( used: 2 % ) 
I [executorch:arm_executor_runner.cpp:491] method_allocator_planned:  752640 bytes
I [executorch:arm_executor_runner.cpp:493] method_allocator_loaded:   966 bytes
I [executorch:arm_executor_runner.cpp:494] method_allocator_input:    602116 bytes
I [executorch:arm_executor_runner.cpp:495] method_allocator_executor: 0 bytes
I [executorch:arm_executor_runner.cpp:498] temp_allocator_used:       0 / 1048576 free: 1048576 ( used: 0 % ) 
I executorch:arm_executor_runner.cpp:152] Model executed successfully.
I executorch:arm_executor_runner.cpp:156] 1 outputs:
Output[0][0]: -0.749744
Output[0][1]: -0.019224
Output[0][2]: 0.134570
...(Skipped)
Output[0][996]: -0.230691
Output[0][997]: -0.634399
Output[0][998]: -0.115345
Output[0][999]: 1.576386
I executorch:arm_executor_runner.cpp:177] Program complete, exiting.
I executorch:arm_executor_runner.cpp:179]

注意

该脚本提供了各种选项来选择特定的 FVP 目标、使用所需的模型、选择可移植内核,并且可以使用参数run.sh--help

要点

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

概括地说,有两个主要流程:

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

  • 一个委托流,将图形划分为 Cortex-M 部分和可在 Ethos-U 硬件上卸载和加速的部分。

这两个流程都在不断发展,从而支持更多的用例和更好的性能。

常见问题

如果您在本教程后遇到任何错误或问题,请在 Github 上提交错误/问题。

文档

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

查看文档

教程

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

查看教程

资源

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

查看资源