使用 Qualcomm AI Engine Direct 後端建置並執行 ExecuTorch¶
在本教學中,我們將引導您開始建構用於 Qualcomm AI Engine Direct 的 ExecuTorch,並在其上執行模型。
Qualcomm AI Engine Direct 在原始碼和文件中也稱為 QNN。
在本教學中,您將學習如何降低模型複雜度並部署到 Qualcomm AI Engine Direct 上。
什麼是 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 後端驗證過的 Linux 主機作業系統是 Ubuntu 22.04 LTS x64 (撰寫本教學時)。 通常,我們會使用 QNN 驗證過的相同作業系統版本來驗證後端。 該版本記錄在 QNN SDK 中。
硬體:¶
您需要一台已透過 adb 連接的 Android 智慧型手機,並執行下列 Qualcomm SoC 之一
SM8450 (Snapdragon 8 Gen 1)
SM8475 (Snapdragon 8 Gen 1+)
SM8550 (Snapdragon 8 Gen 2)
SM8650 (Snapdragon 8 Gen 3)
此範例已使用 SM8550 和 SM8450 進行驗證。
軟體:¶
請遵循 ExecuTorch 建議的 Python 版本。
用於編譯 AOT 部分的編譯器,例如,GCC 編譯器隨附於 Ubuntu LTS。
Android NDK。 此範例已使用 NDK 26c 進行驗證。
-
點擊 "Get Software" 按鈕下載 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/..
建構¶
以下建構指示的範例腳本位於這裡。 我們建議使用該腳本,因為 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
注意:如果您想要建構發布版本,請將 -DCMAKE_BUILD_TYPE=Release
新增到 cmake
指令選項中。
在裝置上部署和執行¶
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 的預先編譯的二進位檔。
常見問題 (FAQ)¶
如果您在重現本教學課程時遇到任何問題,請在 ExecuTorch repo 上提交一個 github issue,並標記 #qcom_aisw
標籤。