• 文件 >
  • 使用 Qualcomm AI Engine Direct 後端建置並執行 ExecuTorch
捷徑

使用 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 進行驗證。

  • Qualcomm AI Engine Direct SDK

    • 點擊 "Get Software" 按鈕下載 QNN SDK 的版本。

    • 但是,在更新本教學時,上述網站沒有提供比 2.22.6 更新的 QNN SDK。

    • 以下是下載各種 QNN 版本的公開連結。 希望它們能盡快被公開發現。

    • QNN 2.26.0

已安裝的 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 環境變數

  1. $QNN_SDK_ROOT/lib/x86_64-linux-clang/

  2. $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_PATHLD_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 的 Android 示範應用程式執行模型

使用 Qualcomm AI Engine Direct Backend 的 Android 示範應用程式可以在 examples 中找到。 請參考 Android 示範應用程式的 教學

支援的模型清單

請參考 $EXECUTORCH_ROOT/examples/qualcomm/scripts/EXECUTORCH_ROOT/examples/qualcomm/oss_scripts/ 以取得支援的模型清單。

接下來會有什麼?

  • 改善 llama3-8B-Instruct 的效能並支援批次預填。

  • 我們將支援來自 Qualcomm AI Hub 的預先編譯的二進位檔。

常見問題 (FAQ)

如果您在重現本教學課程時遇到任何問題,請在 ExecuTorch repo 上提交一個 github issue,並標記 #qcom_aisw 標籤。

文件

存取 PyTorch 的完整開發者文件

檢視文件

教學

取得針對初學者和進階開發者的深入教學課程

檢視教學課程

資源

尋找開發資源並獲得問題解答

檢視資源