ARM Ethos-U 後端¶
在本教程中,您將學習如何為 ExecuTorch Arm Ethos-U 後端委託匯出簡單的 PyTorch 模型,並在 Corstone FVP 模擬器上執行它。
警告
此 ExecuTorch 後端委託正在積極開發中。您可能會遇到一些粗糙之處以及可能已文件化或計劃但尚未實現的功能。
提示
如果您已經熟悉此委託,可以直接跳轉到示例原始碼目錄 - https://github.com/pytorch/executorch/tree/main/examples/arm
先決條件¶
在開始之前,請確保您已具備所有必需條件。
硬體¶
要成功完成本教程,您需要一臺基於 Linux 的主機,其處理器架構為 Arm aarch64 或 x86_64。
目標裝置將是帶有 Arm Cortex-M CPU 和 Ethos-U NPU(機器學習處理器)的嵌入式平臺。本教程將向您展示如何在兩者上執行 PyTorch 模型。
我們將使用固定虛擬平臺 (FVP),模擬 Corstone-300(cs300) 和 Corstone-320(cs320) 系統。由於我們將使用 FVP(可以將其視為虛擬硬體),因此本教程不需要任何實際的嵌入式硬體。
軟體¶
首先,您需要安裝 ExecuTorch。如果您尚未安裝,請按照推薦的教程進行操作,以設定可用的 ExecuTorch 開發環境。
為了生成可在嵌入式平臺(實際或虛擬)上執行的軟體,我們需要一個用於交叉編譯的工具鏈以及一個 Arm Ethos-U 軟體開發套件,其中包括用於 Ethos-U NPU 的 Vela 編譯器。
在以下各節中,我們將逐步介紹下載上面列出的每個依賴項的步驟。
設定開發者環境¶
在本節中,我們將進行一次性設定,例如下載和安裝必要的軟體,以便在本教程中執行 ExecuTorch 程式所需的平臺支援檔案。
為此,我們將使用 examples/arm/setup.sh 指令碼以自動化方式拉取每個專案。建議在 conda 環境中執行此指令碼。成功執行後,您可以直接進入下一步。
如前所述,我們目前僅支援基於 Linux 且處理器架構為 x86_64 或 aarch64 的平臺。請確保您確實在使用受支援的平臺。
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)。
要下載,您可以從此處下載 Corstone-300 Ecosystem FVP 和 Corstone-320 Ecosystem FVP,或者 setup.sh 指令碼會在 setup_fvp 函式下為您完成此操作。
下載並安裝 Arm GNU AArch32 裸機工具鏈¶
與 FVP 類似,我們還需要一個工具鏈來交叉編譯 ExecuTorch 執行時、executor-runner 裸機應用,以及 Corstone-300/Corstone-320 平臺上可用的 Cortex-M55/M85 CPU 的其餘裸機棧。
這些工具鏈可從此處獲取。在本教程中,我們將使用針對 arm-none-eabi 的 GCC 13.3.rel1。就像 FVP 一樣,setup.sh 指令碼將為您下載工具鏈。請參閱 setup_toolchain 函式。
設定 Arm Ethos-U 軟體開發¶
此 git 倉庫是所有 Arm Ethos-U 軟體的根目錄。它旨在幫助我們下載所需的倉庫並將其放置在樹形結構中。有關更多詳細資訊,請參閱 setup 指令碼的 setup_ethos_u 函式。
完成此步驟後,您應該擁有一個可用的 FVP 模擬器、一個功能正常的交叉編譯工具鏈,以及已準備好進行裸機開發的 Ethos-U 軟體開發設定。
安裝 Vela 編譯器¶
完成此步驟後,指令碼將透過為您安裝 Vela 編譯器來完成設定,詳細資訊請參閱 setup_vela 函式。
安裝 TOSA 參考模型¶
這是安裝過程的最後一步,setup.sh 指令碼將使用 setup_tosa_reference_model 函式為您安裝 TOSA 參考模型。
設定結束時,如果一切順利,您的頂級開發目錄可能看起來像這樣:
.
├── arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi # for x86-64 hosts
├── arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz
├── ethos-u
│ ├── core_platform
│ ├── core_software
│ ├── fetch_externals.py
│ └── [...]
├── FVP-corstone300
│ ├── FVP_Corstone_SSE-300.sh
│ └── [...]
├── FVP-corstone320
│ ├── FVP_Corstone_SSE-320.sh
│ └── [...]
├── FVP_corstone300.tgz
├── FVP_corstone320.tgz
└── setup_path.sh
注意:¶
setup.sh 指令碼已生成一個 setup_path.sh 指令碼,每次重新啟動 shell 時都需要 source 它。
例如,執行 source executorch/examples/arm/ethos-u-scratch/setup_path.sh
由於 setup.sh 將下載並設定所需的 Arm 工具鏈,請確保透過呼叫以下命令來使用它:
which arm-none-eabi-gcc
它應該在 executorch 專案中顯示 arm-none-eabi-gcc,而不是 /usr/bin 中的任何內容,類似於:
<EXECUTORCH_ROOT>/examples/arm/ethos-u-scratch/arm-gnu-toolchain-13.3.rel1-aarch64-arm-none-eabi/bin/arm-none-eabi-gcc 或 <EXECUTORCH_ROOT>/examples/arm/ethos-u-scratch/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-gcc
如果不是,您可能需要解除安裝 arm-none-eabi-gcc 或確保在您的 $PATH 環境變數中,專案中的那個工具鏈優先順序更高。
將 PyTorch 模型轉換為 .pte 檔案¶
.pte 是 ExecuTorch 預編譯 (AoT) 流水線透過接收 PyTorch 模型(一個 torch.nn.Module)、匯出它、執行各種 Pass,最後將其序列化為 .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)上,非委託流程預計用於兩種主要情況:
當網路設計得非常小,最適合單獨在 Cortex-M 上執行時。
當網路包含可針對 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_arm_ethos-u55-128.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_ethos-u55-128.pte
委託量化工作流程¶
在為 MobileNetV2 等委託量化網路生成 .pte 檔案之前,我們需要構建 quantized_ops_aot_lib
您可以直接執行 backends/arm/scripts/build_quantized_ops_aot_lib.sh 指令碼為您構建它,或者像這樣自己構建。
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 \
-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_ethos-u55-128.pte
最後,我們應該有三個不同的 .pte 檔案。
第一個檔案包含 SoftmaxModule,不使用任何後端委託。
第二個檔案包含 AddModule,並啟用了 Arm Ethos-U 後端委託。
第三個檔案包含 量化後的 MV2Model,同樣啟用了 Arm Ethos-U 後端委託。
現在讓我們嘗試在 Corstone-300 和 Corstone-320 平臺上的裸機環境中執行這些 .pte 檔案。
獲取裸機可執行檔案¶
在本節中,我們將介紹構建執行時應用程式所需的步驟。然後將其執行在目標裝置上。在 executorch 倉庫中,我們有一個功能正常的指令碼,它執行完全相同的步驟。該指令碼位於 executorch/examples/arm/run.sh。我們將使用它來構建必要的元件,最後在 FVP 上執行之前生成的 PTE 檔案。
預設情況下,run.sh 將使用 arm_test/ 作為構建和輸出資料夾,您將在其下找到構建產物。如果需要,可以使用 --et_build_root 和 --output 標誌來控制/覆蓋此設定。
例如,執行 examples/arm/run.sh --model_name=add --target=ethos-u85-128 將生成一個 pte 和 elf 檔案,如下所示:
arm_test/add/add_arm_delegate_ethos-u85-128.pte
arm_test/add/cmake-out/arm_executor_runner
此外,在開始之前,請確保您已完成 ExecuTorch cmake 構建設定,並按照前面描述的說明設定了開發環境。
下面的框圖在高層次上演示了各種構建產物如何生成並連結在一起,以生成最終的裸機可執行檔案。
提示
run.sh 指令碼中的 generate_pte_file 函式根據透過 --model_name 輸入引數提供的模型生成 .pte 檔案。
生成 ExecuTorch 庫¶
ExecuTorch 的 CMake 構建系統生成了一組構建元件,這些元件對於在 Ethos-U SDK 為 Corstone FVP 提供的裸機環境中包含並執行 ExecuTorch 執行時至關重要。
此文件詳細介紹了每個單獨的構建元件。為了執行任何一個 .pte 檔案變體,我們將需要一套核心庫。列表如下:
libexecutorch.alibportable_kernels.alibportable_ops_lib.a
要執行包含 Arm 後端委託呼叫指令的 .pte 檔案,我們需要 Arm 後端委託執行時庫,即:
libexecutorch_delegate_ethos_u.a
這些庫是由 run.sh 指令碼呼叫的 backends/arm/scripts/build_executorch.sh`、backends/arm/scripts/build_portable_kernels.sh 和 backends/arm/scripts/build_quantized_ops_aot_lib.sh 指令碼生成的。
--portable_kernels 標誌可用於在執行 backends/arm/scripts/build_portable_kernels.sh 時設定構建標誌 EXECUTORCH_SELECT_OPS_LIST,該標誌將決定構建中包含的行動式運算子數量以及在執行時可用的運算子。它必須與 .pte 檔案的要求相匹配,否則您將在執行時遇到 Missing Operator 錯誤。
例如,在上面的命令列中,為了執行 SoftmaxModule,我們只包含了 softmax CPU 運算子。類似地,要以非委託方式執行 AddModule,您將需要 add 運算子等等。正如您可能已經意識到的那樣,對於委託運算子(將由 Arm 後端委託執行),我們不需要將這些運算子包含在此列表中。這僅適用於*非委託*運算子。
構建 executor_runner 裸機應用¶
SDK 目錄與前面準備的目錄相同。並且,我們將傳遞上面生成的 .pte 檔案(其中任何一個)。
注意,如果您想更改模型或 .pte 檔案,則必須生成新的 executor-runner 二進位制檔案。此限制源於我們為 Corstone-300/Corstone-320 平臺提供的受限裸機執行時環境。
這是由從 run.sh 執行的 backends/arm/scripts/build_executorch_runner.sh 指令碼執行的。
提示
run.sh 指令碼接受 --target 選項,該選項提供了指定特定目標(Corstone-300(ethos-u55-128) 或 Corstone-320(ethos-u85-128))的方式。
在 Corstone FVP 平臺上執行¶
一旦 elf 準備好,無論使用哪個 .pte 檔案變體生成裸機 elf,run.sh 將透過 backends/arm/scripts/run_fvp.sh 指令碼為您執行 FVP,但您也可以直接執行它。
以下命令用於在 Corstone-320 FVP 上執行 MV2Model
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]
注意
run.sh 指令碼提供了各種選項,用於選擇特定的 FVP 目標、使用所需的模型、選擇行動式核心,可以使用 --help 引數進行探索。
要點總結¶
透過本教程,我們學習瞭如何使用 ExecuTorch 軟體,既可以從 PyTorch 匯出標準模型,又可以在緊湊且功能完備的 ExecuTorch 執行時上執行它,為將模型從 PyTorch 解除安裝到基於 Arm 的平臺提供了一條順暢的路徑。
總結一下,主要有兩種流程:
一種直接流程,它利用構建到 ExecuTorch 中的庫將工作解除安裝到 Cortex-M 上。
一種委託流程,它將圖劃分成用於 Cortex-M 的部分以及可在 Ethos-U 硬體上解除安裝和加速的部分。
這兩種流程都在不斷發展,以實現更多用例和更好的效能。