Qualcomm AI Engine 後端¶
本教程將引導您完成為 Qualcomm AI Engine Direct 構建 ExecuTorch 並在其上執行模型的過程。
在原始碼和文件中,Qualcomm AI Engine Direct 也被稱為 QNN。
在本教程中,您將學習如何為 Qualcomm AI Engine Direct 進行模型 Lowering 並部署模型。
什麼是 Qualcomm AI Engine Direct?¶
Qualcomm AI Engine Direct 旨在為 AI 開發提供統一的底層 API。
開發者可以使用這套 API 與 Qualcomm 片上系統 (SoC) 上的各種加速器進行互動,包括 Kryo CPU、Adreno GPU 和 Hexagon 處理器。更多詳情請見此處。
目前,該 ExecuTorch 後端可以透過 Qualcomm AI Engine Direct API 將 AI 計算委託給 Hexagon 處理器。
先決條件(硬體和軟體)¶
主機作業系統¶
截至本教程更新時,QNN 後端驗證過的主機作業系統是 Ubuntu 22.04 LTS x64。通常,我們在 QNN 驗證過的相同作業系統版本上驗證該後端。版本記錄在 QNN SDK 中。
硬體:¶
您需要一臺已連線 adb,並在以下任一 Qualcomm 片上系統 (SoC) 上執行的 Android 智慧手機
SA8295
SM8450 (Snapdragon 8 Gen 1)
SM8475 (Snapdragon 8 Gen 1+)
SM8550 (Snapdragon 8 Gen 2)
SM8650 (Snapdragon 8 Gen 3)
SM8750 (Snapdragon 8 Elite)
SSG2115P
SSG2125P
SXR1230P
SXR2230P
SXR2330P
本示例已在 SM8550 和 SM8450 上驗證。
軟體:¶
遵循 ExecuTorch 推薦的 Python 版本。
一個用於編譯 AOT 部分的編譯器,例如 Ubuntu LTS 自帶的 GCC 編譯器。
Android NDK。本示例已在 NDK 26c 上驗證。
-
點選“獲取軟體”按鈕下載一個版本的 QNN SDK。
然而,截至本教程更新時,上述網站不提供高於 2.22.6 版本的 QNN SDK。
以下是下載各種 QNN 版本的公開連結。希望它們能儘快公開可見。
已安裝 Qualcomm AI Engine Direct SDK 的目錄結構類似
├── benchmarks
├── bin
├── docs
├── examples
├── include
├── lib
├── LICENSE.pdf
├── NOTICE.txt
├── NOTICE_WINDOWS.txt
├── QNN_NOTICE.txt
├── QNN_README.txt
├── QNN_ReleaseNotes.txt
├── ReleaseNotes.txt
├── ReleaseNotesWindows.txt
├── sdk.yaml
└── share
設定您的開發者環境¶
約定¶
$QNN_SDK_ROOT 指代 Qualcomm AI Engine Direct SDK 的根目錄,即包含 QNN_README.txt 的目錄。
$ANDROID_NDK_ROOT 指代 Android NDK 的根目錄。
$EXECUTORCH_ROOT 指代 executorch git 倉庫的根目錄。
設定環境變數¶
我們設定 LD_LIBRARY_PATH 以確保動態連結器能找到 QNN 庫。
此外,我們設定 PYTHONPATH 是為了更方便地開發和匯入 ExecuTorch Python API。
export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/:$LD_LIBRARY_PATH
export PYTHONPATH=$EXECUTORCH_ROOT/..
構建¶
以下構建說明的示例指令碼在此處here。我們建議使用該指令碼,因為 ExecuTorch 的構建命令可能會不時更改。上述指令碼正在積極使用中,其更新頻率高於本教程。示例用法如下:
cd $EXECUTORCH_ROOT
./backends/qualcomm/scripts/build.sh
# or
./backends/qualcomm/scripts/build.sh --release
AOT(預編譯)元件:¶
在 x64 上需要 Python API 來將模型編譯為 Qualcomm AI Engine Direct 二進位制檔案。
cd $EXECUTORCH_ROOT
mkdir build-x86
cd build-x86
# Note that the below command might change.
# Please refer to the above build.sh for latest workable commands.
cmake .. \
-DCMAKE_INSTALL_PREFIX=$PWD \
-DEXECUTORCH_BUILD_QNN=ON \
-DQNN_SDK_ROOT=${QNN_SDK_ROOT} \
-DEXECUTORCH_BUILD_DEVTOOLS=ON \
-DEXECUTORCH_BUILD_EXTENSION_MODULE=ON \
-DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON \
-DEXECUTORCH_ENABLE_EVENT_TRACER=ON \
-DPYTHON_EXECUTABLE=python3 \
-DEXECUTORCH_SEPARATE_FLATCC_HOST_PROJECT=OFF
# nproc is used to detect the number of available CPU.
# If it is not applicable, please feel free to use the number you want.
cmake --build $PWD --target "PyQnnManagerAdaptor" "PyQnnWrapperAdaptor" -j$(nproc)
# install Python APIs to correct import path
# The filename might vary depending on your Python and host version.
cp -f backends/qualcomm/PyQnnManagerAdaptor.cpython-310-x86_64-linux-gnu.so $EXECUTORCH_ROOT/backends/qualcomm/python
cp -f backends/qualcomm/PyQnnWrapperAdaptor.cpython-310-x86_64-linux-gnu.so $EXECUTORCH_ROOT/backends/qualcomm/python
# Workaround for fbs files in exir/_serialize
cp $EXECUTORCH_ROOT/schema/program.fbs $EXECUTORCH_ROOT/exir/_serialize/program.fbs
cp $EXECUTORCH_ROOT/schema/scalar_type.fbs $EXECUTORCH_ROOT/exir/_serialize/scalar_type.fbs
執行時:¶
可以使用一個示例 qnn_executor_runner 可執行檔案來執行已編譯的 pte 模型。
為 Android 構建 qnn_executor_runner 的命令
cd $EXECUTORCH_ROOT
mkdir build-android
cd build-android
# build executorch & qnn_executorch_backend
cmake .. \
-DCMAKE_INSTALL_PREFIX=$PWD \
-DEXECUTORCH_BUILD_QNN=ON \
-DQNN_SDK_ROOT=$QNN_SDK_ROOT \
-DEXECUTORCH_BUILD_DEVTOOLS=ON \
-DEXECUTORCH_BUILD_EXTENSION_MODULE=ON \
-DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON \
-DEXECUTORCH_ENABLE_EVENT_TRACER=ON \
-DPYTHON_EXECUTABLE=python3 \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake \
-DANDROID_ABI='arm64-v8a' \
-DANDROID_NATIVE_API_LEVEL=23
# nproc is used to detect the number of available CPU.
# If it is not applicable, please feel free to use the number you want.
cmake --build $PWD --target install -j$(nproc)
cmake ../examples/qualcomm \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake \
-DANDROID_ABI='arm64-v8a' \
-DANDROID_NATIVE_API_LEVEL=23 \
-DCMAKE_PREFIX_PATH="$PWD/lib/cmake/ExecuTorch;$PWD/third-party/gflags;" \
-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH \
-DPYTHON_EXECUTABLE=python3 \
-Bexamples/qualcomm
cmake --build examples/qualcomm -j$(nproc)
# qnn_executor_runner can be found under examples/qualcomm
# The full path is $EXECUTORCH_ROOT/build-android/examples/qualcomm/qnn_executor_runner
ls examples/qualcomm
注意: 如果您想構建釋出版本,請向 cmake 命令選項新增 -DCMAKE_BUILD_TYPE=Release。
在裝置上部署和執行¶
AOT 編譯模型¶
參考此指令碼瞭解確切的流程。本教程中我們以 deeplab-v3-resnet101 為例。執行以下命令進行編譯
cd $EXECUTORCH_ROOT
python -m examples.qualcomm.scripts.deeplab_v3 -b build-android -m SM8550 --compile_only --download
您可能會看到如下輸出
[INFO][Qnn ExecuTorch] Destroy Qnn context
[INFO][Qnn ExecuTorch] Destroy Qnn device
[INFO][Qnn ExecuTorch] Destroy Qnn backend
opcode name target args kwargs
------------- ------------------------ --------------------------- ----------------------------- --------
placeholder arg684_1 arg684_1 () {}
get_attr lowered_module_0 lowered_module_0 () {}
call_function executorch_call_delegate executorch_call_delegate (lowered_module_0, arg684_1) {}
call_function getitem <built-in function getitem> (executorch_call_delegate, 0) {}
call_function getitem_1 <built-in function getitem> (executorch_call_delegate, 1) {}
output output output ([getitem_1, getitem],) {}
已編譯的模型是 ./deeplab_v3/dlv3_qnn.pte。
在 QNN HTP 模擬器上測試模型推理¶
我們可以透過 HTP 模擬器在將模型部署到裝置之前測試模型推理。
讓我們為 x64 主機構建 qnn_executor_runner
# assuming the AOT component is built.
cd $EXECUTORCH_ROOT/build-x86
cmake ../examples/qualcomm \
-DCMAKE_PREFIX_PATH="$PWD/lib/cmake/ExecuTorch;$PWD/third-party/gflags;" \
-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH \
-DPYTHON_EXECUTABLE=python3 \
-Bexamples/qualcomm
cmake --build examples/qualcomm -j$(nproc)
# qnn_executor_runner can be found under examples/qualcomm
# The full path is $EXECUTORCH_ROOT/build-x86/examples/qualcomm/qnn_executor_runner
ls examples/qualcomm/
要執行 HTP 模擬器,動態連結器需要訪問 QNN 庫和 libqnn_executorch_backend.so。我們將以下兩個路徑設定為 LD_LIBRARY_PATH 環境變數
$QNN_SDK_ROOT/lib/x86_64-linux-clang/$EXECUTORCH_ROOT/build-x86/lib/
第一個路徑用於包含 HTP 模擬器的 QNN 庫。它已在 AOT 編譯部分中配置。
第二個路徑用於 libqnn_executorch_backend.so。
因此,我們可以透過以下方式執行 ./deeplab_v3/dlv3_qnn.pte
cd $EXECUTORCH_ROOT/build-x86
export LD_LIBRARY_PATH=$EXECUTORCH_ROOT/build-x86/lib/:$LD_LIBRARY_PATH
examples/qualcomm/qnn_executor_runner --model_path ../deeplab_v3/dlv3_qnn.pte
您應該會看到如下輸出。請注意,模擬器可能需要一些時間才能完成。
I 00:00:00.354662 executorch:qnn_executor_runner.cpp:213] Method loaded.
I 00:00:00.356460 executorch:qnn_executor_runner.cpp:261] ignoring error from set_output_data_ptr(): 0x2
I 00:00:00.357991 executorch:qnn_executor_runner.cpp:261] ignoring error from set_output_data_ptr(): 0x2
I 00:00:00.357996 executorch:qnn_executor_runner.cpp:265] Inputs prepared.
I 00:01:09.328144 executorch:qnn_executor_runner.cpp:414] Model executed successfully.
I 00:01:09.328159 executorch:qnn_executor_runner.cpp:421] Write etdump to etdump.etdp, Size = 424
[INFO] [Qnn ExecuTorch]: Destroy Qnn backend parameters
[INFO] [Qnn ExecuTorch]: Destroy Qnn context
[INFO] [Qnn ExecuTorch]: Destroy Qnn device
[INFO] [Qnn ExecuTorch]: Destroy Qnn backend
在搭載 Qualcomm 片上系統 (SoC) 的 Android 智慧手機上執行模型推理¶
步驟 1. 我們需要將所需的 QNN 庫推送到裝置上。
# make sure you have write-permission on below path.
DEVICE_DIR=/data/local/tmp/executorch_qualcomm_tutorial/
adb shell "mkdir -p ${DEVICE_DIR}"
adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtp.so ${DEVICE_DIR}
adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnSystem.so ${DEVICE_DIR}
adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtpV69Stub.so ${DEVICE_DIR}
adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtpV73Stub.so ${DEVICE_DIR}
adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtpV75Stub.so ${DEVICE_DIR}
adb push ${QNN_SDK_ROOT}/lib/hexagon-v69/unsigned/libQnnHtpV69Skel.so ${DEVICE_DIR}
adb push ${QNN_SDK_ROOT}/lib/hexagon-v73/unsigned/libQnnHtpV73Skel.so ${DEVICE_DIR}
adb push ${QNN_SDK_ROOT}/lib/hexagon-v75/unsigned/libQnnHtpV75Skel.so ${DEVICE_DIR}
步驟 2. 我們還需要透過設定 ADSP_LIBRARY_PATH 和 LD_LIBRARY_PATH 來指示 Android 和 Hexagon 上的動態連結器在哪裡找到這些庫。因此,我們可以像這樣執行 qnn_executor_runner
adb push ./deeplab_v3/dlv3_qnn.pte ${DEVICE_DIR}
adb push ${EXECUTORCH_ROOT}/build-android/examples/qualcomm/executor_runner/qnn_executor_runner ${DEVICE_DIR}
adb push ${EXECUTORCH_ROOT}/build-android/lib/libqnn_executorch_backend.so ${DEVICE_DIR}
adb shell "cd ${DEVICE_DIR} \
&& export LD_LIBRARY_PATH=${DEVICE_DIR} \
&& export ADSP_LIBRARY_PATH=${DEVICE_DIR} \
&& ./qnn_executor_runner --model_path ./dlv3_qnn.pte"
您應該會看到如下輸出
I 00:00:00.257354 executorch:qnn_executor_runner.cpp:213] Method loaded.
I 00:00:00.323502 executorch:qnn_executor_runner.cpp:262] ignoring error from set_output_data_ptr(): 0x2
I 00:00:00.357496 executorch:qnn_executor_runner.cpp:262] ignoring error from set_output_data_ptr(): 0x2
I 00:00:00.357555 executorch:qnn_executor_runner.cpp:265] Inputs prepared.
I 00:00:00.364824 executorch:qnn_executor_runner.cpp:414] Model executed successfully.
I 00:00:00.364875 executorch:qnn_executor_runner.cpp:425] Write etdump to etdump.etdp, Size = 424
[INFO] [Qnn ExecuTorch]: Destroy Qnn backend parameters
[INFO] [Qnn ExecuTorch]: Destroy Qnn context
[INFO] [Qnn ExecuTorch]: Destroy Qnn backend
模型僅被執行。如果我們要輸入真實輸入並獲取模型輸出,可以使用
cd $EXECUTORCH_ROOT
python -m examples.qualcomm.scripts.deeplab_v3 -b build-android -m SM8550 --download -s <device_serial>
透過 adb devices 命令可以找到 <device_serial>。
執行上述命令後,預處理的輸入和輸出將放在 $EXECUTORCH_ROOT/deeplab_v3 和 $EXECUTORCH_ROOT/deeplab_v3/outputs 資料夾中。
命令列引數寫在 utils.py 中。模型、輸入和輸出位置透過 --model_path、--input_list_path 和 --output_folder_path 引數傳遞給 qnn_executorch_runner。
支援的模型列表¶
請參考 $EXECUTORCH_ROOT/examples/qualcomm/scripts/ 和 EXECUTORCH_ROOT/examples/qualcomm/oss_scripts/ 檢視支援的模型列表。
未來展望?¶
改進 llama3-8B-Instruct 的效能並支援批次預填充。
我們將支援來自 Qualcomm AI Hub 的預編譯二進位制檔案。
常見問題¶
如果您在重現本教程時遇到任何問題,請在 ExecuTorch 倉庫上提交一個 GitHub issue 並使用 #qcom_aisw 標籤。