一、高通ai 軟件的介紹
Qualcomm?AI Engine Direct SDK(qnn)
提供較低級別、高度可定制的統一API,通過單獨的庫加速所有AI加速器核心上的AI模型, 可以直接用于針對特定的加速器核心或從流行的運行時(包括Qualcomm Neural Processing SDK、TensorFlow Lite和ONNX運行時)委派工作負載。
Qualcomm 神經處理SDK
一個支持異構計算、系統級配置的一體化SDK,旨在將AI工作負載引導到我們平臺上的所有加速器核心。為開發人員提供靈活性,包括核心間協作支持和其他高級功能。
AIMET- AI模型效率工具包
是一個為訓練好的神經網絡模型提供高級量化和壓縮技術的庫。
Qualcomm AI Hub
Qualcomm于2024年2月發布 平臺,旨在為開發者提供優化和驗證過的AI模型,以便在Snapdragon?和Qualcomm?設備上運行。這個平臺不僅支持移動計算、汽車、物聯網等多個領域,還提供一個強大的模型庫,截至目前為止包括Whisper、ControlNet、Stable Diffusion和Baichuan 7B等超過109個熱門AI模型,這些模型覆蓋了視覺、音頻和語音應用,并且已經在Qualcomm AI Hub、GitHub和Hugging Face上提供。
本次主要是介紹QNN SDK.
QNN SDK 可以將模型文件(例如從 pytorch 構建)轉換成高通的指定格式文件,然后可以在高通的多種各種設備處理器(CPU、GPU、DSP、HTP 或 LPAI)上運行我們轉換好的模型。
先看下高通的軟件整體架構圖。
可以看到支持很多的后端CPU, GPU, HTP, cDSP, HTA,這些后端在QNN中都支持模型的運行。
這個是QNN的軟件架構
device設備
硬件加速器平臺的軟件抽象。提供關聯首選硬件加速器資源以執行用戶組合圖所需的所有構造。平臺可能分為多個設備。設備可能有多個核心。基于不同的硬件后端,qnn 提供統一的接口實現host和device之間的操作。支持的設備類型有windows,linux, android, qnx(臺架)。
Backend 后端
后端是一個頂級 API 組件,它托管和管理圖形組合和執行所需的大部分后端資源,包括存儲所有可用操作的操作注冊表。
Context 上下文
表示維持用戶應用程序所需的所有 Qualcomm? AI Engine Direct 組件的構造。托管用戶提供的網絡并允許將構造的實體緩存到序列化對象中以供將來使用。它通過提供可共享的內存空間來實現多個圖形之間的互操作性,在該空間中,可以在圖形之間交換張量。
Graph 圖形
Qualcomm? AI Engine Direct 表示可加載網絡模型的方式。由表示操作的節點和將它們互連以組成有向無環圖的張量組成。 Qualcomm? AI Engine Direct 圖形構造支持執行網絡模型初始化、優化和執行的 API。
Operation Package registry 操作包注冊表
維護可用于執行模型的所有操作記錄的注冊表。這些操作可以是內置的,也可以由用戶作為自定義操作提供。
二、QNN詳解
2.1 整體流程圖
這是qnn sdk 將一個模型加載到運行的流程圖。那從圖中可以知道,首先我們需要離線的將模型轉換成qnn可以運行的文件,這個轉換過程可以在windows 或者linux(wsl也可以)進行模型的離線轉換。
?
2.2 軟件包的構成
Qnn的軟件包下載路徑,這里面可以下載各個版本的qnn-sdk.
https://code.qualcomm.com/nextev-usa-inc/automotive-ai-sdk-for-qualcomm-neural-networks-zip-2-0_qti-tools_oem/tree/r23104.2
先看下解壓的sdk的目錄構成,
2.2.1 SDK的目錄構成
Benchmarks: 這個是測試模型的性能數據的庫。
Bin: 這里面主要是qnn的Android, linux, windows各個平臺,各個高通后端cpu, gpu, npu等平臺的可執行文件,主要是執行進行模型的轉換,量化等一系列操作。
Docs: 主要是sdk的說明文檔。
Example: 這里主要是實現的例子,轉換模型,數據處理,模型運行的時候可以參考
Include: 主要是各個頭文件,高通沒有開放源碼,只是提供了頭文件,方便接口查看。
Lib: 這個主要是各個so 文件,在轉換,執行時需要的庫文件。
share: 這個應該是一些共享的文件之類。
2.2.2 bin文件夾
在 x86 的 linux 端可以看到其提供的相關功能,在 bin/x86_64-linux-clang 如下所示,可以根據自己平臺決定如何使用, android, Windows等是類似的結構。
#模型編譯和運行
qnn-net-run #用于運行 QNN 模型,并測試其推理性能
qnn-model-lib-generator # 用于生成 QNN 兼容的模型庫(.so 文件)
qnn-throughput-net-run #類似 qnn-net-run,但專注于吞吐量測試
#調試 & 解析
qnn-architecture-checker # 檢查模型是否適用于 QNN 硬件架構
qnn-profile-viewer #分析 QNN 運行時的性能數據
qnn-platform-validator #驗證 QNN 運行環境是否正確配置
#模型格式轉換
qnn-onnx-converter 將 ONNX 模型轉換為 QNN 兼容格式。
qnn-tflite-converter 轉換 TensorFlow Lite (TFLite) 模型為 QNN 兼容格式。
qnn-tensorflow-converter
qnn-pytorch-converter
#模型量化 & 解析
qnn-quantization-checker #檢查模型的量化信息,確保適用于 QNN 運行環境
qnn-context-binary-generator #生成 QNN 運行時所需的 context 文件
qnn-context-binary-utility #解析和管理 QNN context 二進制文件
#精度分析
qnn-accuracy-debugger # 調試和分析模型精度問題
qnn-accuracy-evaluator #評估 QNN 模型的精度
qnn-hypertuner #用于調整 QNN 運行參數,以優化性能
#其他
qnn-netron 可視化 QNN 計算圖(類似 Netron)
qnn-rpc-server #用于遠程調用 QNN 運行時
qnn-op-package-generator # 用于自定義算子(Op)封裝
# Hexagon DSP(高通的專用 DSP 處理器)相關
hexagon-llvm-objcopy # 用于處理 Hexagon 目標文件
hexagon-llvm-objdump #用于處理 Hexagon 目標文件
hexagon-readelf #用于查看 Hexagon ELF 文件的信息
2.3 模型轉換
這個是模型轉換的實現整體流程示意圖。
qnn_core_api關鍵的類與關系如下:
?
QnnModel:該類類似于給定上下文中的 QnnGraph 及其張量。初始化時應提供上下文,并在其中創建新的 QnnGraph。有關這些類 API 的更多詳細信息,請參閱 QnnModel.hpp、QnnWrapperUtils.hpp
GraphConfigInfo:此結構用于從客戶端傳遞 QNN 圖配置列表(如果適用)。有關可用圖配置選項的詳細信息,請參閱 QnnGraph API。
GraphInfo:此結構用于將構造的圖及其輸入和輸出張量傳達給客戶端。
QnnModel_composeGraphs:負責使用 QnnModel 類在提供的 QNN 后端上構造qnn 圖。它將通過 graphsInfo 返回構造的圖。
QnnModel_freeGraphsInfo:僅在不再使用圖時才應調用。
2.4 qnn 的后端
Qnn 支持的后端設備有CPU, GPU, HTP, DSP,HTA, (HTP MCP 支持在linux 和qnx 上運行)
QNN HTP 支持在所有 Qualcomm SoC 上運行量化 8 位和量化 16 位網絡。QNN HTP Quant 運行時支持的操作列表可在“支持的操作”中的“后端支持 HTP”列下查看,QNN HTP 支持在部分 Qualcomm SoC 上使用 float16 數學運行 float32 網絡。
QNN 僅支持每個進程獨享一個后端庫。這些庫必須是相同的 SDK 版本,并且具有匹配的 Hexagon 架構版本。為了便于說明,圖表中將使用 V73 Hexagon 架構庫;非 V73 工件也適用相同的準則。QNN 支持的庫布局如下所示。
為防止 QNN 庫布局不正確,Qualcomm 建議如下:
-
每個庫的一份副本應存在于單個進程(后端、接口、骨架等)中。
-
后端庫 (libQnnHtp.so) 應使用 dlopen 明確加載,而不是作為依賴項動態鏈接。
-
庫應彼此位于同一目錄中(只要正確設置 ADSP_LIBRARY_PATH 以查找庫,stub就是例外)。
-
不要重命名庫以加載多個副本,因為不支持此操作。
QNN 不支持在單個進程中訪問 QNN 后端庫 (libQnnHtp.so) 的多個副本。下面描述了兩種不同的布局,其中設備上存在多個后端庫。
?對于要加載的兩個后端庫,要么明確地從與第一個庫所在目錄不同的目錄中加載庫的第二個副本,要么在進程執行期間創建重復的文件系統(對于 Android 目標,則為 adb remount)。無論哪種情況,都不支持上面顯示的兩種工件布局。
2.5 專業名稱解釋
cdsp:
高通CDSP(Compute Digital Signal Processor)是高通Snapdragon平臺上專門用來做通用計算的硬件加速單元,與主機CPU相比,它通常以較低的時鐘速度運行,并提供更多的并行指令級。這使得DSP在功耗方面成為CPU的更好替代品。因此,將盡可能多的大型計算密集型任務移植到DSP上,可以降低設備的整體功耗。
CDSP的主要用途包括攝像頭、視頻的圖像增強相關處理,計算機視覺、增強、虛擬現實處理,以及深度學習硬件加速。在SOC架構中,CDSP位于紅色框內。CDSP的硬件架構包括Hexagon Scalar Core,其中包含4個或更多的DSP硬件線程,每個DSP硬件線程都可以訪問Hexagon標量單元。此外,Qualcomm Hexagon Vector eXtensions-HVX(“Hexagon矢量擴展”)是Hexagon 680 DSP的典型特性,能夠在執行圖像處理應用中的計算負載中發揮重要作用,比如虛擬現實、增強現實、圖像處理、視頻處理、計算視覺等。
高通SoC的AI加速硬件都是基于DSP的,指令方面則是SIMD擴展。高通自2013年導入DSP加速,一直沿用至今。DSP是通用的加速器,可以對應標量(Scalar)、向量(Vector)和張量(Tensor)。而我們所說的AI芯片一般只對應張量。標量運算一般對應的是CPU,通常是串行數據。向量和張量對應的并行數據計算,傳統的CPU不太勝任。
HVX(Hexagon Vector eXtensions)
高通在第六代Hexagon DSP中引入的一種矢量處理功能,它為標量DSP單元添加了128字節的矢量處理功能,即在HVX編程的時候很多處理都要128對齊。標量硬件線程通過訪問HVX寄存器文件(也稱為HVX上下文)來使用HVX協處理器。HVX意味著你可以將視頻和攝像機任務從CPU轉移到Hexagon DSP,以實現低功耗的快速圖像處理。長期以來,用于寬矢量處理的新型Hexagon VX內核一直很吸引人的用例,因為它們消耗大量電能,因此可以從CPU上卸載計算機視覺(CV)和視頻。借助HVX內核,Hexagon的設計師增加了寬矢量處理,以實現更高的性能和更低的功耗。
HMX(Hexagon Matrix eXtensions)
是高通HTP(Hexagon Tensor Processor)中的一部分,作為HTP上專門用于深度學習MAC計算的硬件模塊暫時不對客戶開放進行可編程。HMX是高通繼續改進HTA(Hexagon Tensor Accelerator),改進為HTP時加入的,所謂HTP就是加入了HMX即Hexagon Matrix eXtensions,仍然是基于標量DSP而加入的協處理器。
三、模型精度和量化
3.1 量化模式
非量化模型文件使用 32 位浮點表示模型權重參數。量化模型文件使用定點表示網絡參數,通常為 8 位權重和 8 位或 32 位的偏置。定點表示與 Tensorflow 量化模型中使用的相同。
在量化或非量化模型之間進行選擇:
GPU - 選擇非量化模型。量化模型目前與 GPU 后端不兼容。
DSP - 選擇量化模型。在 DSP 后端運行時需要量化模型。
HTP - 選擇量化模型。在 HTP 后端運行時需要量化模型。
HTA - 選擇量化模型。在 HTA 后端運行時需要量化模型。以下是圖片中的文字內容:
對高通SM8650平臺的AI Engine中各設備支持的數據精度如下:
CPU: INT8, FP32
GPU: FP16, FP32
HTP: INT4, INT8, INT16(W8A16/W16A16), FP16, 混合精度(INT4-INT8-INT16-FP16)
在量化方面,QNN對模型使用的是定點數(Fixed-point)量化,對weight可使用4bit/8bit量化,對activation可使用8bit/16bit量化,對bias可使用8bit/32bit量化(注意bias不支持16bit)。
注意:定點數據類型在DSP領域很常用,包含了整數部分和小數部分(都用INT來表示),用一部分bit表示整數部分,一部分表示小數部分,因此并不等同于INT數據類型,也不同于FLOAT數據類型。
這個是8bit的量化示意圖
?
針對數據差異,建議權重做對稱量化,激活輸入做非對稱量化,可以獲取更好的效果。
原因:在神經網絡模型的量化過程中,常采用對權重量化使用對稱量化,而對激活輸入量化使用非對稱量化的策略。這種組合方式有助于在模型精度和計算效率之間取得平衡。
對稱量化是指將數據的正負范圍以零為中心進行均勻量化,適用于數據分布接近對稱的情況。模型的權重通常呈現對稱分布,因此對稱量化能夠有效地減少量化誤差,并簡化計算復雜度。
非對稱量化允許數據在非對稱范圍內進行量化,適用于數據分布偏移的情況。激活輸入的數據分布往往是不對稱的,可能存在偏移。采用非對稱量化可以更準確地捕捉這些偏移,減少量化引入的誤差,從而提高模型的精度。然而,非對稱量化需要額外存儲零點信息,并在推理時進行相應的計算。
對于qnn 自身的量化能力其實是比較弱的,想要得到更好的量化效果,可以使用AIMET,利用aimet得到的encodings 文件,作為qnn-onnx-converter的輸入,可以得到更好的量化效果。
當精度值設置為 QNN_PRECISION_FLOAT16 時,QNN HTP 后端會將 QnnGraph_execute() 中用戶提供的 float32 輸入轉換為 float16,并使用 float16 數學運算執行圖形。最終輸出以 float32 輸出結果。
注意QNN HTP 不支持將精度值設置為 QNN_PRECISION_FLOAT32, 如果未設置精度值,QNN HTP 后端會假定客戶端希望運行量化網絡。
3.2 量化注意要點
-
如果不配置量化的算法和精度,默認使用的min-max 量化方法,采用的是W8A8的量化精度。
-
可以在配置文件中針對某些算子進行數據類型的設置,和量化位數的設置,沒有設置的算子就是默認使用fp32數據類型和默認的量化位數進行量化。
?
3. converter模型需要加上--input_list.txt 這個文件是指定量化數據的路徑,得到的就是量化模型。不指定這個參數就是得到非量化模型。
4. 當在converter的時候,如果在coverter的命令行中指定了–act_bw/–weight_bw/–bias_bw, 那么配置文件中的配置不會生效,以顯式指定的這個參數為準。
5. Qnn支持混合精度的量化,類似下面的這個示意圖:
6. 當加上converter的時候, 加上–float_bw 16時,所有的權重和激活數據類型都會被設置成fp16, 配置文件中的數據類型會被忽略,也就是不進行模型量化。
7. 量化精度支持到0.0001
8. 支持的量化方式有 tf(非對稱量化), symmetric, enhanced(對稱量化), and tf adjusted(非對稱量化),量化方法都是min_max的計算方式。
9. 量化參數設置:
偏置參數設置--bias_bw 8(32), --act_bw 8 (16), --weight_bw 8.(這個是qnn sdk 2.12 的參數說明,不同版本可能會有差異。)
四、模型Benchmarking
1. 說明
提供了腳本qnn_bench.py 進行模型運行性能的測試。能快速比較不同模型的整體性能,對比 CPU vs GPU vs DSP 后端的整體推理時間,比較不同 batch size、量化方式的性能差異。從而可以知道各個配置的性能結果。
Profiling主要是性能分析,知道每一個模型,每一層網絡結果,每一個算子的性能數據,為性能分析提供詳實的數據。benchmark 主要是獲取模型的整體數性能數據。
usage: qnn_bench.py [-h] -c CONFIG_FILE [-o OUTPUT_BASE_DIR_OVERRIDE][-v DEVICE_ID_OVERRIDE] [-r HOST_NAME][-t DEVICE_OS_TYPE_OVERRIDE] [-d] [-s SLEEP][-n ITERATIONS] [-p PERFPROFILE][--backend_config BACKEND_CONFIG] [-l PROFILINGLEVEL][-json] [-be BACKEND [BACKEND ...]] [--htp_serialized][--dsp_type {v65,v66,v68,v69,v73}][--arm_prepare] [--use_signed_skel] [--discard_output][--test_duration TEST_DURATION] [--enable_cache][--shared_buffer] [--clean_artifacts] [--cdsp_id {0,1}]Run the qnn_benchrequired arguments:-c CONFIG_FILE, --config_file CONFIG_FILEPath to a valid config fileRefer to sample config file config_help.json present at <SDK_ROOT>/benchmarks/QNN/to know details on how to fill parameters in config fileoptional arguments:-o OUTPUT_BASE_DIR_OVERRIDE, --output_base_dir_override OUTPUT_BASE_DIR_OVERRIDESets the output base directory.-v DEVICE_ID_OVERRIDE, --device_id_override DEVICE_ID_OVERRIDEUse this device ID instead of the one supplied in config file.-r HOST_NAME, --host_name HOST_NAMEHostname/IP of remote machine to which devices are connected.-t DEVICE_OS_TYPE_OVERRIDE, --device_os_type_override DEVICE_OS_TYPE_OVERRIDESpecify the target OS type, valid options are['aarch64-android', 'aarch64-windows-msvc', 'aarch64-qnx','aarch64-oe-linux-gcc9.3', 'aarch64-oe-linux-gcc8.2', 'aarch64-ubuntu-gcc7.5']-d, --debug Set to turn on debug log-s SLEEP, --sleep SLEEPSet number of seconds to sleep between runs e.g. 20 seconds-n ITERATIONS, --iterations ITERATIONSSet the number of iterations to execute for calculatin
2. 這個是qnx的配置文件示例:?
{"Name":"InceptionV3","HostRootPath": "inception_v3.repo","HostResultsDir":"inception_v3.repo/results","DevicePath":"/data/local/tmp/qnnbm.repo","Devices":["e9b27753"],"Runs":3,"Model": {"Name": "INCEPTION_V3","qnn_model": "../../examples/Models/InceptionV3/model_libs/aarch64-qnx/libInception_v3.so","InputList": "../../examples/Models/InceptionV3/data/target_raw_list.txt","Data": ["../../examples/Models/InceptionV3/data/cropped"]},"Backends":["CPU"],"Measurements": ["timing"]}
3. 運行示例
Running inceptionV3_sample.json (CPU backend)
cd $QNN_SDK_ROOT/benchmarks/QNN
python3.6 qnn_bench.py -c inceptionV3_sample.json# Running inceptionV3_quantized_sample.json (HTP backend by generating context binary on X86)
cd $QNN_SDK_ROOT/benchmarks/QNN
python3.6 qnn_bench.py -c inceptionV3_quantized_sample.json --dsp_type v68 --htp_serialized