QNN 執行提供程式

ONNX Runtime 的 QNN 執行提供程式 (Execution Provider) 支援在 Qualcomm 晶片組上進行硬體加速執行。它使用 Qualcomm AI Engine Direct SDK (QNN SDK) 從 ONNX 模型構建 QNN 計算圖,該計算圖可由受支援的加速器後端庫執行。OnnxRuntime QNN 執行提供程式可用於搭載 Qualcomm Snapdragon SoC 的 Android 和 Windows 裝置。

內容

安裝前提條件(僅限從原始碼構建)

如果您從原始碼構建 QNN 執行提供程式,請首先從 https://qpm.qualcomm.com/#/main/tools/details/Qualcomm_AI_Runtime_SDK 下載 Qualcomm AI Engine Direct SDK (QNN SDK)。

QNN 版本要求

ONNX Runtime QNN 執行提供程式已在 QNN 2.22.x 以及 Android 和 Windows 上的 Qualcomm SC8280、SM8350、Snapdragon X SoC 上進行過構建和測試。

構建(Android 和 Windows)

有關構建說明,請參閱 構建頁面

預構建包(僅限 Windows)

注意:從 1.18.0 版本開始,您無需單獨下載和安裝 QNN SDK。所需的 QNN 依賴庫已包含在 OnnxRuntime 包中。

  • NuGet 包
    • Microsoft.ML.OnnxRuntime.QNN 的夜間構建包源可以在此處找到。
  • Python 包
    • 要求
      • Windows ARM64(用於在搭載 Qualcomm 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 可用於在 Qualcomm 託管的裝置上最佳化和執行模型。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’。
‘libQnnGpu.so’ 或 ‘QnnGpu.dll’ 啟用 GPU 後端。請參閱 backend_type ‘gpu’。

注意:backend_pathbackend_type 的替代選項。兩者最多隻能指定其中之一。backend_path 需要特定於平臺的路徑(例如 libQnnCpu.soQnnCpu.dll),但也允許指定任意路徑。

"profiling_level" 描述
‘off’ 預設值。
‘basic’  
‘detailed’  
‘optrace’ 需要 QAIRT 2.39 或更高版本
"profiling_file_path" 描述
‘your_qnn_profile_path.csv’ 指定用於轉儲 QNN 分析事件的 CSV 檔案路徑。

有關分析的更多資訊,請參閱 profiling-tools
除了在編譯時設定 profiling_level 外,還可以使用 ETW (Windows) 動態啟用分析。更多詳細資訊請參閱 tracing

"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)

Offline workflow for quantizing an ONNX model for use on QNN EP

QNN HTP 後端僅支援量化模型。具有 32 位浮點啟用和權重的模型必須先量化為較低的整數精度(例如 8 位或 16 位整數)。

本節提供了量化模型並在 Python API 中使用 QNN EP 的 HTP 後端執行該量化模型的說明。關於量化概念的更廣泛概述,請參考量化頁面

模型要求

QNN EP 不支援具有動態形狀(例如動態批次大小)的模型。動態形狀必須固定為特定值。有關更多資訊,請參考固定動態輸入形狀的文件。

此外,QNN EP 僅支援一部分 ONNX 運算元(例如不支援 Loops 和 Ifs)。請參考支援的 ONNX 運算元列表

生成量化模型(僅限 x64)

ONNX Runtime Python 包透過 onnxruntime.quantization 匯入提供用於量化 ONNX 模型的工具。由於在 ARM64 上安裝 onnx 包存在問題,目前量化工具僅在 x86_64 上受支援。因此,建議使用 x64 機器進行模型量化,或者在 Windows ARM64 機器上使用單獨的 x64 Python 環境。

安裝 ONNX Runtime x64 Python 包。(請注意,您必須使用 x64 包進行模型量化。推理和使用 HTP/NPU 時請使用 arm64 包。)

python -m pip install onnxruntime-qnn

QNN EP 的量化需要使用校準輸入資料。使用代表典型模型輸入的校準資料集對於生成精確的量化模型至關重要。

以下程式碼片段定義了一個生成隨機 float32 輸入資料的示例 DataReader 類。請注意,使用隨機輸入資料極有可能會產生不精確的量化模型。參考 Resnet 資料讀取器實現,瞭解如何建立提供磁碟影像檔案輸入的 CalibrationDataReader

# 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, uint16int16 量化資料型別,但 QNN 運算元通常只支援 uint8uint16 資料型別。有關每個 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 裝置上執行。

有關量化工具使用方法的更多資訊,請參考以下頁面:

在 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 支援非常有用。可用的會話配置包括:

上述程式碼片段僅指定了 backend_path 提供程式選項。有關所有可用 QNN EP 提供程式選項的列表,請參考配置選項部分

使用 QNN EP 的 GPU 後端執行模型

QNN GPU 後端可以執行具有 32 位/16 位浮點啟用和權重的模型,無需預先量化。與 32 位版本相比,16 位浮點模型通常可以在 GPU 上更快地執行推理。為了幫助減小大型模型的體積,還支援將權重量化為 uint8,同時將啟用保持為浮點數。

除上述 HTP 後端部分提到的量化模型要求外,所有其他要求對於 GPU 後端也同樣適用。模型推理示例程式碼也是如此,只需修改指定後端的部分即可。

# Create an ONNX Runtime session.
# TODO: Provide the path to your ONNX model
session = onnxruntime.InferenceSession("model.onnx",
                                       sess_options=options,
                                       providers=["QNNExecutionProvider"],
                                       provider_options=[{"backend_path": "QnnGpu.dll"}]) # Provide path to Gpu dll in QNN SDK

QNN 上下文二進位制快取功能

QNN 上下文包含了轉換、編譯、終結模型後的 QNN 計算圖。QNN 可以將上下文序列化為二進位制檔案,以便使用者直接將其用於後續推理(無需 QDQ 模型),從而降低模型載入成本。QNN 執行提供程式支援多種會話選項來配置此功能。

匯出 QNN 上下文二進位制檔案

  1. 建立會話選項,將“ep.context_enable”設定為“1”以啟用 QNN 上下文匯出。“ep.context_enable”鍵在 onnxruntime_session_options_config_keys.h 中定義為 kOrtSessionOptionEpContextEnable。
  2. 使用步驟 1 中建立的會話選項和 HTP 後端建立 QDQ 模型會話。一旦會話建立/初始化,就會建立一個包含 QNN 上下文二進位制檔案的 Onnx 模型。無需執行會話。QNN 上下文二進位制檔案的生成可以在搭載 HTP 的 Qualcomm 裝置上使用 Arm64 構建完成。也可以在 x64 機器上使用 x64 構建完成(但由於沒有 HTP 裝置,無法在 x64 上執行)。

生成的包含 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 分析 (Profiling)

HTP 後端提供分析資料。啟用 QNN 分析將生成一個使用者可讀的 .csv 檔案,其中包含初始化、執行和反初始化過程的資訊。

如果 onnxruntime 是使用較新的 QAIRT SDK(2.39 或更高版本)編譯的,則在 .csv 檔案旁邊還會生成一個 _qnn.log 檔案。該 .log 檔案可由 SDK 提供的 qnn-profile-viewer 解析。

常規用法

要使用 QNN 分析,只需將 EP 選項 profiling_level 設定為 basic、detailed 或 optrace。此外,必須將 EP 選項 profiling_file_path 設定為您希望將資料寫入的輸出 .csv 檔案路徑。

# Python on Windows on Snapdragon device
import onnxruntime as ort
import numpy as np

provider_options = [
    "backend_path": "path/to/QnnHtp.dll", # Use libQnnHtp.so if on Linux
    "htp_performance_mode": "burst",
    "device_id": "0",
    "htp_graph_finalization_optimization_mode":"3",
    "soc_model": "60",
    "htp_arch": "73",
    "vtcm_mv": "8",
    "profiling_level": "basic",
    "profiling_file_path": "output.csv"
]

sess_options = ort.SessionOptions()

session = ort.InferenceSession(
    "model.onnx",
    sess_options=sess_options,
    providers=["QNNExecutionProvider"],
    provider_options=provider_options
)

input0 = np.ones((1,2,3,4), dtype=np.float32)
result = session.run(None, {"input": input0})

使用上面的示例,將生成一個名為“output.csv”的檔案,其中包含分析資料。此外,如果使用 QAIRT 2.39 SDK 或更高版本,還會生成另一個檔案“output_qnn.log”。

隨後可以使用相應的 qnn-profile-viewer 二進位制檔案解析“output_qnn.log”。

> qnn-profile-viewer.exe --input_log .\output_qnn.log --output output_2.csv

上述操作將輸出基本資訊,例如最快和最慢執行的分析資料以及平均情況。也可以用這種方式生成 .csv 檔案,儘管其中的資訊可能與“output.csv”沒有區別。

此外,如果將 profiling_level 設定為“detailed”或“optrace”,則會顯示每個網路層的額外資料。

Optrace 級分析

Optrace 級分析生成一個分析 .log 檔案,其中包含 Qualcomm Hexagon Tensor Processor 分析摘要 (QHAS) 資料。這些資料可用於生成 chrometraces,並提供對使用者友好的瀏覽器 UI 來視覺化資料。

此功能僅適用於 QAIRT 2.39 SDK 及更高版本。

Optrace 設定

要使用此功能,必須在執行前生成上下文二進位制檔案。

# Python on Windows on Snapdragon device
import onnxruntime as ort
import numpy as np

provider_options = [
    "backend_path": "path/to/QnnHtp.dll", # Use libQnnHtp.so if on Linux
    "htp_performance_mode": "burst",
    "device_id": "0",
    "htp_graph_finalization_optimization_mode":"3",
    "soc_model": "60",
    "htp_arch": "73",
    "vtcm_mv": "8",
    "profiling_level": "optrace",   # Set profiling_level to optrace
    "profiling_file_path": "optrace.csv"
]

sess_options = ort.SessionOptions()

# Enable context bin generation
sess_options.add_session_config_entry("ep.context_embed_mode", "0")
sess_options.add_session_config_entry("ep.context_enable", "1")

session = ort.InferenceSession(
    "model.onnx",
    sess_options=sess_options,
    providers=["QNNExecutionProvider"],
    provider_options=provider_options
)

成功建立會話後,將生成三個檔案:

  • model_ctx.onnx
  • model_qnn.bin
  • QNNExecutionProvider_QNN_<number>_schematic.bin

model_ctx.onnx 是一個 Onnx 模型,其中包含指向 model_qnn.bin 上下文二進位制檔案的節點,HTP 後端將使用該檔案進行執行。_schematic.bin 檔案將由 qnn-profile-viewer 使用以生成 QHAS 資料。

生成 QHAS 資料

之前對於一般分析資料,我們使用“model.onnx”建立並執行會話。但是,現在有一個新的 _ctx.onnx 模型,它使用新生成的上下文二進位制檔案。因此,必須使用新的 _ctx.onnx 模型建立新的推理會話。

# Continuing from Optrace Setup:

sess_options.add_session_config_entry("ep.context_enable", "0")

optrace_session = ort.InferenceSession(
    "model_ctx.onnx",
    sess_options=sess_options,
    providers=["QNNExecutionProvider"],
    provider_options=provider_options
)

input0 = np.ones((1,2,3,4), dtype=np.float32)
result = optrace_session.run(None, {"input": input0})

與“常規用法”中一樣,會生成一個 .csv 檔案 (optrace.csv) 和一個 _qnn.log 檔案 (optrace_qnn.log)。

qnn-profile-viewer 可以配合不同的引數和檔案使用,以解析寫入 optrace_qnn.log 的所有資料。

> qnn-profile-viewer.exe --config .\config.json --reader .\QnnHtpOptraceProfilingReader.dll --input_log .\optrace_qnn.log  --schematic .\QNNExecutionProvider_QNN_12345_schematic.bin --output optrace.json

請注意:

  • 使用了三個新檔案:
    • config.json:請參考此頁面上的“後處理(Chrometrace 生成)”部分。
    • QnnHtpOptraceProfilingReader.dll:作為 QAIRT SDK 的一部分提供。Linux 的對應檔案為 libQnnHtpOptraceProfilingReader.so。
    • QNNExecutionProvider_QNN_12345_schematic.bin:名稱會有所不同。該檔案必須與“Optrace 設定”下上下文二進位制檔案旁邊生成的那個檔案一致。
  • 輸出檔案現在是一個包含 chrometrace 資料的 .json 檔案。可以使用 Perfetto Trace Vizualizer 或 chrome://tracing 開啟此 .json 檔案。

執行 qnn-profile-viewer 後,您應該會看到幾個與 --output 檔名引數具有相同字首的 .json 檔案。您還應該會看到生成了一個 .html 檔案。該 .html 檔案可以由 Chrome 開啟,以便在更友好的 GUI 中檢視 chrometrace。

其他參考資料

有關如何解釋 QHAS 資料的更多資訊,請參考此頁面

有關 optrace 分析收集的資料的更多資訊,請參考此頁面

QNN EP 權重共享

請參考 EPContext 設計文件

注意:QNN EP 需要 Linux x86_64Windows 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'}])`

推理示例

在 C++ 中使用 QNN 執行提供程式(搭配 QNN CPU 和 HTP 後端)進行 Mobilenetv2 影像分類。

錯誤處理

HTP 子系統重啟 - SSR

QNN EP 會針對 QNN HTP SSR 問題返回 StatusCode::ENGINE_ERROR。如果在會話執行期間檢測到此錯誤,上層框架/應用程式應重新建立 Onnxruntime 會話。

在 QNN EP 中新增新運算元支援

要在 EP 中啟用新運算元支援,需要關注以下區域:

  • QDQ 指令碼是否支援此運算元?程式碼示例
  • Onnxruntime QDQ 節點單元是否支援此運算元?程式碼示例
  • 它是否是佈局敏感型 (layout sensitive) 運算元?
    • 是否在 LayoutTransformer 中註冊?程式碼示例
    • 是否註冊了 NHWC 運算元方案?示例錯誤訊息:::operator ()] Model face_det_qdq failed to load:Fatal error: com.ms.internal.nhwc:BatchNormalization(9) is not a registered function/op 示例 PR

啟用新運算元的示例 PR

混合精度支援

下圖展示了一個混合精度模型的示例。

mixed precision model

混合精度 QDQ 模型由具有不同啟用/權重量化資料型別的區域組成。區域之間的邊界使用 DQ 到 Q 的序列在啟用量化資料型別(例如 uint8 到 uint16)之間進行轉換。

指定具有不同量化資料型別區域的能力,使得能夠探索精度與延遲之間的權衡。更高的整數精度可能會以增加延遲為代價提高精度,因此有選擇地將某些區域提升到更高的精度有助於在關鍵指標之間取得理想的平衡。

下圖顯示了一個模型,其中一個區域已從預設的 8 位啟用型別提升為 16 位。

mixed precision layers

該模型量化為 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 生成的二進位制部分路徑。
; ```