QNN 执行提供者
ONNX Runtime 的 QNN 执行提供者支持在高通芯片组上进行硬件加速执行。它使用高通 AI 引擎直接 SDK (QNN SDK) 从 ONNX 模型构建 QNN 图,该图可由受支持的加速器后端库执行。OnnxRuntime QNN 执行提供者可用于配备高通骁龙 SOC 的 Android 和 Windows 设备。
目录
- 安装先决条件(仅限从源代码构建)
- 构建(Android 和 Windows)
- 预构建包(仅限 Windows)
- Qualcomm AI Hub
- 配置选项
- 支持的 ONNX 运算符
- 使用 QNN EP 的 HTP 后端运行模型 (Python)
- QNN 上下文二进制缓存功能
- QNN EP 权重共享
- 使用方法
- 错误处理
- 在 QNN EP 中添加新运算符支持
- 混合精度支持
- LoRAv2 支持
安装先决条件(仅限从源代码构建)
如果您从源代码构建 QNN 执行提供者,您应该首先从 https://qpm.qualcomm.com/#/main/tools/details/Qualcomm_AI_Runtime_SDK 下载高通 AI 引擎直接 SDK (QNN SDK)。
QNN 版本要求
ONNX Runtime QNN 执行提供者已使用 QNN 2.22.x 和高通 SC8280、SM8350、骁龙 X SOC 在 Android 和 Windows 上构建并测试。
构建(Android 和 Windows)
有关构建说明,请参阅构建页面。
预构建包(仅限 Windows)
注意:从 1.18.0 版本开始,您无需单独下载和安装 QNN SDK。所需的 QNN 依赖库已包含在 OnnxRuntime 包中。
- NuGet package
- Microsoft.ML.OnnxRuntime.QNN 每夜构建包的源可在此处找到
- Python 包
- 要求
- Windows ARM64(用于在高通 NPU 的本地设备上进行推理)
- Windows X64(用于模型量化。请参阅生成量化模型)
- Python 3.11.x
- Numpy 1.25.2 或 >= 1.26.4
- 安装:
pip install onnxruntime-qnn
- 安装每夜构建包
python -m pip install --pre --extra-index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/ORT-Nightly/pypi/simple onnxruntime-qnn
- 要求
Qualcomm AI Hub
Qualcomm AI Hub 可用于在高通托管设备上优化和运行模型。OnnxRuntime QNN 执行提供者是 Qualcomm AI Hub 中支持的运行时。
配置选项
QNN 执行提供者支持多种配置选项。这些提供者选项以键值字符串对的形式指定。
EP 提供者选项
"backend_type" | 描述 |
---|---|
‘cpu’ | 启用 CPU 后端。对集成测试很有用。CPU 后端是 QNN 运算符的参考实现。 |
‘gpu’ | 启用 GPU 后端。 |
‘htp’ | 启用 HTP 后端。将计算卸载到 NPU。默认值。 |
‘saver’ | 启用 Saver 后端。 |
"backend_path" | 描述 |
---|---|
‘libQnnCpu.so’ 或 ‘QnnCpu.dll’ | 启用 CPU 后端。请参阅 backend_type ‘cpu’。 |
‘libQnnHtp.so’ 或 ‘QnnHtp.dll’ | 启用 HTP 后端。请参阅 backend_type ‘htp’。 |
注意: backend_path
是 backend_type
的替代方案。两者中最多只能指定一个。backend_path
需要平台特定的路径(例如,libQnnCpu.so
与 QnnCpu.dll
),但也允许指定任意路径。
"profiling_level" | 描述 |
---|---|
‘off’ | 默认值。 |
‘basic’ | |
‘detailed’ |
"profiling_file_path" | 描述 |
---|---|
‘your_qnn_profile_path.csv’ | 指定用于转储 QNN 性能分析事件的 csv 文件路径 |
有关性能分析的更多信息,请参阅性能分析工具
除了在编译时设置 profiling_level 外,还可以通过 ETW (Windows) 动态启用性能分析。有关详细信息,请参阅跟踪
"rpc_control_latency" | 描述 |
---|---|
微秒(字符串) | 允许客户端以微秒为单位设置 RPC 控制延迟 |
"vtcm_mb" | 描述 |
---|---|
大小(MB)(字符串) | QNN VTCM 大小(MB),默认为 0(未设置) |
"htp_performance_mode" | 描述 |
---|---|
‘burst’ | |
‘balanced’ | |
‘default’ | 默认值。 |
‘high_performance’ | |
‘high_power_saver’ | |
‘low_balanced’ | |
‘low_power_saver’ | |
‘power_saver’ | |
‘sustained_high_performance’ |
"qnn_saver_path" | 描述 |
---|---|
‘QnnSaver.dll’ 或 ‘libQnnSaver.so’ 的文件路径 | QNN Saver 后端库的文件路径。将 QNN API 调用转储到磁盘以供重放/调试。 |
"qnn_context_priority" | 描述 |
---|---|
‘low’ | |
‘normal’ | 默认值。 |
‘normal_high’ | |
‘high’ |
"htp_graph_finalization_optimization_mode" | 描述 |
---|---|
‘0’ | 默认值。 |
‘1’ | 更快的准备时间,但图优化程度较低。 |
‘2’ | 更长的准备时间,但图优化程度更高。 |
‘3’ | 最长的准备时间,最有可能生成更优化的图。 |
"soc_model" | 描述 |
---|---|
型号(字符串) | SoC 型号。有关有效值,请参阅 QNN SDK 文档。默认为“0”(未知)。 |
"htp_arch" | 描述 |
---|---|
硬件架构 | HTP 架构编号。有关有效值,请参阅 QNN SDK 文档。默认(无) |
"device_id" | 描述 |
---|---|
设备 ID(字符串) | 设置 htp_arch 时要使用的设备 ID。默认为“0”(对于单个设备)。 |
"enable_htp_fp16_precision" | 描述 示例 |
---|---|
‘0’ | 禁用。如果是 fp32 模型,则使用 fp32 精度进行推理。 |
‘1’ | 默认。启用 float32 模型以使用 fp16 精度进行推理。 |
"offload_graph_io_quantization" | 描述 |
---|---|
‘0’ | 禁用。QNN EP 将处理图 I/O 的量化和反量化。 |
‘1’ | 默认。启用。将图 I/O 的量化和反量化卸载到 CPU EP。 |
"enable_htp_shared_memory_allocator" | 描述 |
---|---|
‘0’ | 默认。禁用。 |
‘1’ | 启用 QNN HTP 共享内存分配器。需要 libcdsprpc.so/dll 可用。代码示例 |
运行选项
"qnn.lora_config" | 描述 |
---|---|
配置文件路径 | LoRAv2 配置文件路径。配置的格式将在 LoRAv2 支持中提及。 |
支持的 ONNX 运算符
运算符 | 备注 |
---|---|
ai.onnx:Abs | |
ai.onnx:Add | |
ai.onnx:And | |
ai.onnx:ArgMax | |
ai.onnx:ArgMin | |
ai.onnx:Asin | |
ai.onnx:Atan | |
ai.onnx:AveragePool | |
ai.onnx:BatchNormalization | 自 1.18.0 起支持 fp16 |
ai.onnx:Cast | |
ai.onnx:Clip | 自 1.18.0 起支持 fp16 |
ai.onnx:Concat | |
ai.onnx:Conv | 自 1.18.0 起支持 3D |
ai.onnx:ConvTranspose | 自 1.18.0 起支持 3D |
ai.onnx:Cos | |
ai.onnx:DepthToSpace | |
ai.onnx:DequantizeLinear | |
ai.onnx:Div | |
ai.onnx:Elu | |
ai.onnx:Equal | |
ai.onnx:Exp | |
ai.onnx:Expand | |
ai.onnx:Flatten | |
ai.onnx:Floor | |
ai.onnx:Gather | 仅支持正索引 |
ai.onnx:Gelu | |
ai.onnx:Gemm | |
ai.onnx:GlobalAveragePool | |
ai.onnx:Greater | |
ai.onnx:GreaterOrEqual | |
ai.onnx:GridSample | |
ai.onnx:HardSwish | |
ai.onnx:InstanceNormalization | |
ai.onnx:LRN | |
ai.onnx:LayerNormalization | |
ai.onnx:LeakyRelu | |
ai.onnx:Less | |
ai.onnx:LessOrEqual | |
ai.onnx:Log | |
ai.onnx:LogSoftmax | |
ai.onnx:LpNormalization | p == 2 |
ai.onnx:MatMul | HTP 后端支持的输入数据类型:(uint8, uint8), (uint8, uint16), (uint16, uint8) |
ai.onnx:Max | |
ai.onnx:MaxPool | |
ai.onnx:Min | |
ai.onnx:Mul | |
ai.onnx:Neg | |
ai.onnx:Not | |
ai.onnx:Or | |
ai.onnx:Prelu | 自 1.18.0 起支持 fp16, int32 |
ai.onnx:Pad | |
ai.onnx:Pow | |
ai.onnx:QuantizeLinear | |
ai.onnx:ReduceMax | |
ai.onnx:ReduceMean | |
ai.onnx:ReduceMin | |
ai.onnx:ReduceProd | |
ai.onnx:ReduceSum | |
ai.onnx:Relu | |
ai.onnx:Resize | |
ai.onnx:Round | |
ai.onnx:Sigmoid | |
ai.onnx:Sign | |
ai.onnx:Sin | |
ai.onnx:Slice | |
ai.onnx:Softmax | |
ai.onnx:SpaceToDepth | |
ai.onnx:Split | |
ai.onnx:Sqrt | |
ai.onnx:Squeeze | |
ai.onnx:Sub | |
ai.onnx:Tanh | |
ai.onnx:Tile | |
ai.onnx:TopK | |
ai.onnx:Transpose | |
ai.onnx:Unsqueeze | |
ai.onnx:Where | |
com.microsoft:DequantizeLinear | 提供 16 位整数反量化支持 |
com.microsoft:Gelu | |
com.microsoft:QuantizeLinear | 提供 16 位整数量化支持 |
支持的数据类型因运算符和 QNN 后端而异。有关更多信息,请参阅 QNN SDK 文档。
使用 QNN EP 的 HTP 后端运行模型 (Python)
QNN HTP 后端仅支持量化模型。具有 32 位浮点激活和权重的模型必须首先进行量化以使用较低的整数精度(例如,8 位或 16 位整数)。
本节提供了量化模型以及随后使用 Python API 在 QNN EP 的 HTP 后端上运行量化模型的说明。有关量化概念的更广泛概述,请参阅量化页面。
模型要求
QNN EP 不支持具有动态形状的模型(例如,动态批处理大小)。动态形状必须固定到特定值。有关更多信息,请参阅固定动态输入形状的文档。
此外,QNN EP 支持 ONNX 运算符的子集(例如,不支持循环和条件语句)。请参阅支持的 ONNX 运算符列表。
生成量化模型(仅限 x64)
ONNX Runtime Python 包通过 onnxruntime.quantization
导入提供了量化 ONNX 模型的实用程序。由于在 ARM64 上安装 onnx
包存在问题,量化实用程序目前仅在 x86_64 上受支持。因此,建议要么使用 x64 机器量化模型,要么在 Windows ARM64 机器上使用单独的 x64 Python 安装。
安装 ONNX Runtime x64 Python 包。(请注意,您必须使用 x64 包进行模型量化。使用 arm64 包进行推理和利用 HTP/NPU)
python -m pip install onnxruntime-qnn
QNN EP 的量化需要使用校准输入数据。使用能代表典型模型输入的校准数据集对于生成准确的量化模型至关重要。
以下代码片段定义了一个示例 DataReader
类,用于生成随机 float32 输入数据。请注意,使用随机输入数据很可能会生成不准确的量化模型。有关如何创建从磁盘图像文件提供输入的 CalibrationDataReader
的一个示例,请参阅 Resnet 数据读取器的实现。
# data_reader.py
import numpy as np
import onnxruntime
from onnxruntime.quantization import CalibrationDataReader
class DataReader(CalibrationDataReader):
def __init__(self, model_path: str):
self.enum_data = None
# Use inference session to get input shape.
session = onnxruntime.InferenceSession(model_path, providers=['CPUExecutionProvider'])
inputs = session.get_inputs()
self.data_list = []
# Generate 10 random float32 inputs
# TODO: Load valid calibration input data for your model
for _ in range(10):
input_data = {inp.name : np.random.random(inp.shape).astype(np.float32) for inp in inputs}
self.data_list.append(input_data)
self.datasize = len(self.data_list)
def get_next(self):
if self.enum_data is None:
self.enum_data = iter(
self.data_list
)
return next(self.enum_data, None)
def rewind(self):
self.enum_data = None
以下代码片段预处理原始模型,然后将预处理后的模型量化为使用 uint16
激活和 uint8
权重。尽管量化实用程序公开了 uint8
、int8
、uint16
和 int16
量化数据类型,但 QNN 运算符通常支持 uint8
和 uint16
数据类型。有关每个 QNN 运算符的数据类型要求,请参阅 QNN SDK 运算符文档。
# quantize_model.py
import data_reader
import numpy as np
import onnx
from onnxruntime.quantization import QuantType, quantize
from onnxruntime.quantization.execution_providers.qnn import get_qnn_qdq_config, qnn_preprocess_model
if __name__ == "__main__":
input_model_path = "model.onnx" # TODO: Replace with your actual model
output_model_path = "model.qdq.onnx" # Name of final quantized model
my_data_reader = data_reader.DataReader(input_model_path)
# Pre-process the original float32 model.
preproc_model_path = "model.preproc.onnx"
model_changed = qnn_preprocess_model(input_model_path, preproc_model_path)
model_to_quantize = preproc_model_path if model_changed else input_model_path
# Generate a suitable quantization configuration for this model.
# Note that we're choosing to use uint16 activations and uint8 weights.
qnn_config = get_qnn_qdq_config(model_to_quantize,
my_data_reader,
activation_type=QuantType.QUInt16, # uint16 activations
weight_type=QuantType.QUInt8) # uint8 weights
# Quantize the model.
quantize(model_to_quantize, output_model_path, qnn_config)
运行 python quantize_model.py
将生成一个名为 model.qdq.onnx
的量化模型,该模型可以通过 ONNX Runtime 的 QNN EP 在 Windows ARM64 设备上运行。
有关量化实用程序用法的更多信息,请参阅以下页面
- CPU EP 上 mobilenet 的量化示例
- quantization/execution_providers/qnn/preprocess.py
- quantization/execution_providers/qnn/quant_config.py
在 Windows ARM64 上运行量化模型 (onnxruntime-qnn 版本 >= 1.18.0)
安装适用于 QNN EP 的 ONNX Runtime ARM64 Python 包(需要 Python 3.11.x 和 Numpy 1.25.2 或 >= 1.26.4)
python -m pip install onnxruntime-qnn
以下 Python 代码片段创建了一个带有 QNN EP 的 ONNX Runtime 会话,并在 HTP 后端上运行量化模型 model.qdq.onnx
。
# run_qdq_model.py
import onnxruntime
import numpy as np
options = onnxruntime.SessionOptions()
# (Optional) Enable configuration that raises an exception if the model can't be
# run entirely on the QNN HTP backend.
options.add_session_config_entry("session.disable_cpu_ep_fallback", "1")
# Create an ONNX Runtime session.
# TODO: Provide the path to your ONNX model
session = onnxruntime.InferenceSession("model.qdq.onnx",
sess_options=options,
providers=["QNNExecutionProvider"],
provider_options=[{"backend_path": "QnnHtp.dll"}]) # Provide path to Htp dll in QNN SDK
# Run the model with your input.
# TODO: Use numpy to load your actual input from a file or generate random input.
input0 = np.ones((1,3,224,224), dtype=np.float32)
result = session.run(None, {"input": input0})
# Print output.
print(result)
运行 python run_qdq_model.py
将在 QNN HTP 后端上执行量化的 model.qdq.onnx
模型。
请注意,会话已选择性配置为在整个模型无法在 QNN HTP 后端上执行时引发异常。这对于验证量化模型是否完全受 QNN EP 支持非常有用。可用的会话配置包括
- session.disable_cpu_ep_fallback: 禁用将不受支持的运算符回退到 CPU EP。
- ep.context_enable: 启用 QNN 上下文缓存功能以转储模型的缓存版本,从而缩短会话创建时间。
上述代码片段仅指定了 backend_path
提供者选项。有关所有可用 QNN EP 提供者选项的列表,请参阅配置选项部分。
QNN 上下文二进制缓存功能
QNN 上下文在模型转换、编译和最终确定后包含 QNN 图。QNN 可以将上下文序列化为二进制文件,这样用户就可以直接使用它进行后续推理(无需 QDQ 模型),从而降低模型加载成本。QNN 执行提供者支持多种会话选项来配置此功能。
转储 QNN 上下文二进制文件
- 创建会话选项,将“ep.context_enable”设置为“1”以启用 QNN 上下文转储。键“ep.context_enable”在 onnxruntime_session_options_config_keys.h 中定义为 kOrtSessionOptionEpContextEnable。
- 使用第 1 步中创建的会话选项创建带有 QDQ 模型的会话,并使用 HTP 后端。一旦会话创建/初始化,将创建一个带有 QNN 上下文二进制文件的 Onnx 模型。无需运行会话。QNN 上下文二进制文件的生成可以在具有 HTP 的高通设备上使用 Arm64 构建完成。也可以在 x64 机器上使用 x64 构建完成(由于没有 HTP 设备,无法运行)。
生成的带有 QNN 上下文二进制文件的 Onnx 模型可以部署到生产/实际设备以运行推理。该 Onnx 模型被 QNN 执行提供者视为普通模型。推理代码与在 HTP 后端上使用 QDQ 模型进行推理的代码保持相同。
#include "onnxruntime_session_options_config_keys.h"
// C++
Ort::SessionOptions so;
so.AddConfigEntry(kOrtSessionOptionEpContextEnable, "1");
// C
const OrtApi* g_ort = OrtGetApiBase()->GetApi(ORT_API_VERSION);
OrtSessionOptions* session_options;
CheckStatus(g_ort, g_ort->CreateSessionOptions(&session_options));
g_ort->AddSessionConfigEntry(session_options, kOrtSessionOptionEpContextEnable, "1");
# Python
import onnxruntime
options = onnxruntime.SessionOptions()
options.add_session_config_entry("ep.context_enable", "1")
配置上下文二进制文件路径
如果用户未指定路径,生成的带有 QNN 上下文二进制文件的 Onnx 模型默认名为 [input_QDQ_model_name]_ctx.onnx。用户可以使用键“ep.context_file_path”在会话选项中设置路径。以下是代码示例
// C++
so.AddConfigEntry(kOrtSessionOptionEpContextFilePath, "./model_a_ctx.onnx");
// C
g_ort->AddSessionConfigEntry(session_options, kOrtSessionOptionEpContextFilePath, "./model_a_ctx.onnx");
# Python
options.add_session_config_entry("ep.context_file_path", "./model_a_ctx.onnx")
启用嵌入模式
QNN 上下文二进制内容默认不嵌入到生成的 Onnx 模型中。将单独生成一个 bin 文件。文件名为 [input_model_file_name]QNN[hash_id].bin。该名称由 Ort 提供并在生成的 Onnx 模型中跟踪。如果对 bin 文件进行任何更改,将导致问题。此 bin 文件需要与生成的 Onnx 文件放在一起。用户可以通过将“ep.context_embed_mode”设置为“1”来启用此功能。在这种情况下,上下文二进制内容将嵌入到 Onnx 模型内部。
// C++
so.AddConfigEntry(kOrtSessionOptionEpContextEmbedMode, "1");
// C
g_ort->AddSessionConfigEntry(session_options, kOrtSessionOptionEpContextEmbedMode, "1");
# Python
options.add_session_config_entry("ep.context_embed_mode", "1")
QNN EP 权重共享
注意:QNN EP 需要 Linux x86_64 或 Windows x86_64 平台。
此外,如果用户使用 QNN 工具链 (qnn-context-binary-generator
) 创建带有权重共享的 QNN 上下文二进制文件 (qnn_ctx.bin
),他们可以使用脚本从该上下文生成包装 Onnx 模型:gen_qnn_ctx_onnx_model.py。该脚本会创建多个 model_x_ctx.onnx
文件,每个文件都包含一个引用共享 qnn_ctx.bin
文件的 EPContext
节点。每个 EPContext
节点指定一个唯一的节点名称,引用 QNN 上下文中的不同 Qnn 图。
用法
C++
C API 详情请见此处。
Ort::Env env = Ort::Env{ORT_LOGGING_LEVEL_ERROR, "Default"};
std::unordered_map<std::string, std::string> qnn_options;
qnn_options["backend_path"] = "QnnHtp.dll";
Ort::SessionOptions session_options;
session_options.AppendExecutionProvider("QNN", qnn_options);
Ort::Session session(env, model_path, session_options);
Python
import onnxruntime as ort
# Create a session with QNN EP using HTP (NPU) backend.
sess = ort.InferenceSession(model_path, providers=['QNNExecutionProvider'], provider_options=[{'backend_path':'QnnHtp.dll'}])`
推理示例
使用 QNN 执行提供者在 CPP 中使用 QNN CPU 和 HTP 后端进行 Mobilenetv2 图像分类
错误处理
HTP 子系统重启 - SSR
QNN EP 返回 StatusCode::ENGINE_ERROR,表示 QNN HTP SSR 问题。如果在会话运行期间检测到此错误,上层框架/应用程序应重新创建 Onnxruntime 会话。
在 QNN EP 中添加新运算符支持
要在 EP 中启用新运算符支持,需要关注以下方面
- QDQ 脚本是否支持此操作?代码示例
- Onnxruntime QDQ 节点单元是否支持此操作?代码示例
- 它是否是布局敏感运算符?
- 是否在 LayoutTransformer 中注册?代码示例
- NHWC 运算符模式已注册?示例错误消息
::operator ()] 模型 face_det_qdq 加载失败:致命错误:com.ms.internal.nhwc:BatchNormalization(9) 不是已注册的函数/操作 [示例 PR](https://github.com/microsoft/onnxruntime/pull/15278)
启用新运算符的 PR 示例
-
非布局敏感运算符。 使用 SDK 直接支持为 QNN EP 启用 Hardsigmoid
混合精度支持
下图展示了一个混合精度模型的示例。
混合精度 QDQ 模型由具有不同激活/权重量化数据类型的区域组成。区域之间的边界使用 DQ 到 Q 序列在激活量化数据类型(例如,uint8 到 uint16)之间进行转换。
指定不同量化数据类型区域的能力使得探索精度和延迟之间的权衡成为可能。更高的整数精度可能会提高精度,但会牺牲延迟,因此有选择地将某些区域提升到更高的精度有助于在关键指标上实现理想的平衡。
下图显示了一个模型,其中一个区域已从默认的 8 位激活类型提升到 16 位。
该模型量化为 uint8 精度,但张量“Op4_out”量化为 16 位。这可以通过指定以下初始张量量化覆盖来实现
# Op4_out could be an inaccurate tensor that should be upgraded to 16bit
initial_overrides = {"Op4_out": [{"quant_type": QuantType.QUInt16}]}
qnn_config = get_qnn_qdq_config(
float_model_path,
data_reader,
activation_type=QuantType.QUInt8,
weight_type=QuantType.QUInt8,
init_overrides=initial_overrides, # These initial overrides will be "fixed"
)
上述代码片段生成以下“固定”覆盖(通过 qnn_config.extra_options[“TensorQuantOverrides”] 获取)
overrides = {
“Op2_out”: [{“quant_type”: QUInt8, “convert”: {“quant_type”: QUInt16, “recv_nodes”: {“Op4”}}}],
“Op3_out”: [{“quant_type”: QUInt8, “convert”: {“quant_type”: QUInt16, “recv_nodes”: {“Op5”}}}],
“Op4_out”: [{“quant_type”: QUInt16}],
“Op5_out”: [{“quant_type”: QUInt16, “convert”: {“quant_type”: QUInt8, “recv_nodes”: {“Op6”}}}]
}
覆盖后,模型工作方式如下
- Op2 的输出由 Op4、Op7 和 Op8 消耗。Op4 消耗转换后的 u16 类型,而 Op7 和 Op8 消耗原始的 u8 类型。
- Op3 的输出从 u8 转换为 u16。Op5 消耗转换后的 u16 类型。
- Op4 的输出只是 u16(未转换)。
- Op5 的输出从 u16 转换为 u8。Op6 消耗 u8 类型。
LoRAv2 支持
目前,仅支持带有 EPContext 节点的预编译模型。参考示例脚本为 gen_qnn_ctx_onnx_model.py。在使用 QNN SDK 应用 LoRAv2 模型后,将生成一个主 QNN 上下文二进制文件和多个适配器二进制段。我们使用 LoRAv2 配置并将其放入 RunOptions 中进行推理。
- LoRAv2 配置的格式
- graph name: QNN 预构建上下文二进制文件中的 QNN 图。
- adapter binary section path: 由 qnn-context-binary-generator 生成的二进制段 ```