Qualcomm AI Engine Backend¶
在本教程中,我们将引导您完成为 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 与高通 SoC 上的各种加速器进行交互,包括 Kryo CPU、Adreno GPU 和 Hexagon 处理器。更多详情可在此处找到此处。
目前,此 ExecuTorch Backend 可以通过 Qualcomm AI Engine Direct API 将 AI 计算委托给 Hexagon 处理器。
先决条件(硬件和软件)¶
主机操作系统¶
在更新本教程时,QNN Backend 已通过 Ubuntu 22.04 LTS x64 验证。通常,我们会在 QNN 验证所使用的相同操作系统版本上验证后端。版本在 QNN SDK 中有说明。
硬件:¶
您需要一部连接 adb 的 Android 智能手机,该手机运行以下 Qualcomm SoC 之一:
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/..
构建¶
下面构建说明的一个示例脚本可以在此处找到。我们推荐使用该脚本,因为 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
# 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_PLATFORM=android-30
# 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_PLATFORM=android-30 \
-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/executor_runner/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/executor_runner
# The full path is $EXECUTORCH_ROOT/build-x86/examples/qualcomm/executor_runner/qnn_executor_runner
ls examples/qualcomm/executor_runner
要运行 HTP 模拟器,动态链接器需要访问 QNN 库和 libqnn_executorch_backend.so
。我们将以下两个路径设置为 LD_LIBRARY_PATH
环境变量:
$QNN_SDK_ROOT/lib/x86_64-linux-clang/
$EXECUTORCH_ROOT/build-x86/lib/
第一个路径用于 QNN 库,包括 HTP 模拟器。它已在 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/executor_runner/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
标签。