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
标签。