构建并使用带有 Qualcomm AI 引擎 Direct 后端的 ExecuTorch¶
在本教程中,我们将引导您完成开始构建用于 Qualcomm AI 引擎 Direct 的 ExecuTorch 并在其上运行模型的过程。
Qualcomm AI 引擎 Direct 在源代码和文档中也称为 QNN。
在本教程中,您将学习如何降低模型并将其部署到 Qualcomm AI 引擎 Direct。
什么是 Qualcomm AI 引擎 Direct?¶
Qualcomm AI 引擎 Direct 旨在为 AI 开发提供统一的底层 API。
开发者可以使用这些 API 集合与 Qualcomm SoC 上的各种加速器进行交互,包括 Kryo CPU、Adreno GPU 和 Hexagon 处理器。更多详细信息请参见此处。
目前,此 ExecuTorch 后端可以通过 Qualcomm AI 引擎 Direct API 将 AI 计算委托给 Hexagon 处理器。
先决条件(硬件和软件)¶
主机操作系统¶
QNN 后端经过验证的 Linux 主机操作系统是在更新本教程时的 Ubuntu 22.04 LTS x64。通常,我们在 QNN 经过验证的相同操作系统版本上验证后端。该版本记录在 QNN SDK 中。
硬件:¶
您将需要一部通过 adb 连接的 Android 智能手机,该手机运行在以下 Qualcomm SoC 之一上
SM8450 (骁龙 8 Gen 1)
SM8475 (骁龙 8 Gen 1+)
SM8550 (骁龙 8 Gen 2)
SM8650 (骁龙 8 Gen 3)
此示例已通过 SM8550 和 SM8450 验证。
软件:¶
遵循 ExecuTorch 推荐的 Python 版本。
用于编译 AOT 部分的编译器,例如,GCC 编译器随 Ubuntu LTS 一起提供。
Android NDK。此示例已通过 NDK 26c 验证。
-
点击“获取软件”按钮下载 QNN SDK 的版本。
但是,在更新本教程时,上述网站未提供高于 2.22.6 的 QNN SDK。
以下是下载各种 QNN 版本的公共链接。希望它们能很快被公开发现。
安装的 Qualcomm AI 引擎 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 引擎 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 (Ahead-of-time) 组件:¶
x64 上的 Python API 是将模型编译为 Qualcomm AI 引擎 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>
<device_serial>
可以通过 adb devices
命令找到。
在执行上述命令后,预处理后的输入和输出将放在 $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 repo 上提交 github issue 并标记使用 #qcom_aisw
标签