构建并运行 QNN 示例应用
qnn-sample-app 位于 ${QNN_SDK_ROOT}/examples/QNN/SampleApp,
其中 QNN_SDK_ROOT 指 QNN SDK 解压所在的路径。
设置 QAIRT SDK
要为 QNN 示例应用设置工具链,请完成以下步骤:- 下载 Qualcomm AI Runtime SDK。
-
提取并解压 SDK。
-
安装 SDK。
按照 Build QIM SDK 安装 SDK,其中包含
所需的交叉编译工具链。
- 这些库使用 GCC-11.2 编译。
- 使用 SDK 安装路径设置 SDK_PATH 环境变量。后续步骤会使用该安装路径(/path/to/extracted/toolchain)进行编译。
构建 QNN 示例应用
完成以下步骤以构建 QNN 示例应用。-
进入示例应用目录。
-
为 GCC 工具链设置环境变量。
-
构建应用。
这会创建两个文件夹。
bin:在各平台对应的目录中包含qnn-sample-app二进制文件。obj:包含构建和链接可执行文件时使用的所有目标文件。
在 Linux(基于 Yocto)上运行 QNN 示例应用
构建好的qnn-sample-app 可执行文件可以使用任何
QNN 后端运行模型。对于基于 Yocto scarthgap 的设备,后端可用于
aarch64-oe-linux-gcc11.2。
-
将工件推送到目标设备。
如果设备上尚不存在
/etc/apps/目录,请创建它。 -
在主机上,使用 AI Hub 导出模型。
例如,要导出 InceptionV3 QNN 模型,请运行以下命令:
请为目标设备上使用的相同 SDK 版本生成上下文二进制文件。
-
将导出的 InceptionV3 QNN 模型推送到目标设备。
将模型保存到
export_assets/inception_v3-qnn_context_binary-w8a8-<CHIPSET>。以下示例使用QCS6490作为芯片组。出现输入密码的提示时,输入oelinux123。 -
在主机上,生成用于推理的虚拟输入文件并将其传输到目标设备。
a. 在 Python 环境中运行以下命令。
b. 将
input.raw文件传输到目标设备: -
从主机通过 SSH 登录到目标设备。
-
创建
input_list.txt。 -
运行应用。
要获取帮助信息,请运行:请根据所选模型更新模型名称和 input_list。
命令行参数
必需参数--model:QNN 网络模型的路径。与--retrieve_context互斥。--retrieve_context:用于加载已保存上下文和执行图的 缓存二进制文件的路径。与--model互斥。--backend:用于运行模型的 QNN 后端的路径。--input_list:列出网络输入的文件的路径。对于多个 图,请提供以逗号分隔的输入文件列表。
--debug:保存所有网络层的输出。--output_dir:输出目录(默认:./output)。--output_data_type:输出数据类型(float_only、native_only、float_and_native)。--input_data_type:输入数据类型(float 或 native)。--op_packages:以逗号分隔的算子包和接口提供程序列表。--profiling_level:性能分析级别(basic 或 detailed)。--save_context:将后端上下文和图元数据保存到二进制文件。--num_inferences:要执行的推理次数。--log_level:最大日志级别(error、warn、info、verbose)。--system_library:用于在上下文加载期间使用反射 API 的 libQnnSystem.so 的路径。--version:打印 QNN SDK 版本。--help:显示帮助信息。
工作流和 API 用法
使用以下推荐模式,通过 QNN API 开发 C++ 应用。- 加载先决共享库。
-
使用 QNN API。
a. 使用 QNN 接口获取函数指针。
b. 设置日志记录。
c. 初始化后端。
d. 初始化性能分析。
e. 创建设备。
f. 注册算子包。
g. 创建上下文。
h. 准备图。
i. 完成图。
j. 将上下文保存到二进制文件。
k. 从缓存的二进制文件加载上下文。
l. 运行图。
m. 释放上下文。
n. 终止后端。
加载先决共享库
QNN SDK 提供了各种共享库用于访问后端, 应用必须根据需要加载它们才能运行网络。 可通过以下方式之一在 QNN 中创建网络。- 使用 QNN API 直接在您的应用中构建网络。
- 使用 QNN 转换器生成 QNN 网络的共享库。
qnn-sample-app 使用共享库方式。该网络可以
使用 SDK 中提供的任一 QNN 转换器生成,并
使用 qnn-model-lib-generator 编译为共享库。
对于 Windows 用户,请在以下说明中将所有
.so 文件替换为
对应的 .dll 文件。有关更多详情,请参阅
平台差异。加载后端
QNN SDK 中提供了包括 CPU、GPU、HTP 和 DSP 在内的各种后端的 共享库。每个实现了 QNN API 的后端 都会暴露所有必要的符号,可通过动态加载 机制进行访问。 假设有一个名为 libQnnSampleBackend.so 的示例后端共享库, 可以按如下方式动态加载它:解析共享库中的符号
共享库成功加载后,我们可以继续 解析访问 QNN API 所需的所有符号。 以下代码片段展示了一个在共享库中解析符号的 模板:${QNN_SDK_ROOT}/examples/QNN/SampleApp/SampleApp/src/SampleApp.hpp 中找到。
本教程的其余部分将假定存在一个名为
m_qnnFunctionPointers、类型为 QnnFunctionPointers 的变量,
其中包含有效的函数指针。
QNN API 的用法
本节演示 QNN API 在客户端应用中的用法。使用 QNN 接口获取函数指针
可以使用 QNN 接口机制来设置指向后端中 QNN API 的函数 指针表,而不必手动逐个解析 每个 API 的符号,这使得符号解析变得简单。QNN 接口的使用方式如下:设置日志记录
可以在初始化后端之前、动态加载后端 共享库之后设置日志记录。 要初始化日志记录,必须定义一个类型为 QnnLog_Callback_t 的 回调。示例定义如下:初始化后端
成功初始化日志记录后,可以按如下方式初始化 后端:初始化性能分析
如果需要性能分析,在初始化后端之后,可以设置一个性能分析 句柄。此性能分析句柄可以在之后 用于任何支持性能分析的 API。 可以在后端以 basic 性能分析级别创建性能分析句柄, 如下所示:创建设备
可以按如下方式创建设备:注册算子包
算子包是向后端提供包含算子的库的一种方式。 可以按如下方式注册它们:创建上下文
可以在后端中按如下方式创建上下文:准备图
qnn-sample-app 依赖某个转换器的输出在后端中 创建 QNN 网络。composeGraphsFnHandle 映射到 模型共享库中的 QnnModel_composeGraphs API,该 API 以 qnn_wrapper_api::GraphInfo_t*** 作为参数之一。 函数 composeGraphsFnHandle 会对后端进行必要的调用 以创建网络。它还会将执行图所需的所有必要 信息(例如与图相关的输入和输出张量的信息)写入 结构体 graphsInfo,如以下代码块所示:完成图
可以按如下方式完成上一步中添加的 图:将上下文保存到二进制文件
当上下文中的所有图都完成后,用户应用 可以选择将上下文保存为二进制文件以供将来使用。保存 上下文的优点是,将来可以检索它 以执行其中包含的图,而无需再次完成 它们。这将大大节省网络执行期间的 初始化时间。 可以按如下方式保存上下文:从缓存的二进制文件加载上下文
像上一步那样保存为二进制文件的上下文,可以 被加载,作为每次创建新上下文的替代方案。 以下代码片段演示了此步骤:运行图
在创建了上下文、添加并完成了图之后, 或者在从二进制文件检索了上下文之后,可以执行上下文中的一个 或多个图。 运行图涉及以下操作:- 设置输入和输出张量。
- 将输入数据填充到输入张量中。
- 调用后端中的执行方法。
- 获取输出并保存。
${QNN_SDK_ROOT}/examples/QNN/SampleApp/SampleApp/src/Utils/IOTensor.cpp。
它公开了一些有助于执行图的方法,
这些方法在前面的代码片段中已被使用:
- setupInputAndOutputTensors,用于设置与输入 和输出张量相关的结构。
- populateInputTensors,用于将输入数据复制到输入张量 结构中。
- tearDownInputAndOutputTensors,用于清理与 输入和输出张量关联的资源。

