PTQ 精度 Debug 工具
01 前言
使用 PTQ 后量化的模型量化方案,可以幫助用戶(hù)非常簡(jiǎn)單便捷地完成從浮點(diǎn)模型到地平線(xiàn)混合異構模型的轉換,模型轉換工具會(huì )基于用戶(hù)提供的校準樣本對模型進(jìn)行校準量化并保障模型高效地部署在地平線(xiàn)計算平臺上。
但是在模型轉換的過(guò)程中,不可避免地會(huì )因為浮點(diǎn)高精度到定點(diǎn)低精度的量化過(guò)程而引入精度損失,因此為了幫助用戶(hù)準確快速地定位模型精度損失的主要原因,地平線(xiàn)工具鏈提供了一套 PTQ 精度 Debug 工具。
本文將基于 征程5 工具鏈 1.1.62 版本的 OpenExplorer 開(kāi)發(fā)包來(lái)詳細介紹這些工具的使用方法和使用流程,以及對輸出結果進(jìn)行解讀,來(lái)幫助用戶(hù)快速上手。
對于 XJ3 工具鏈,PTQ 精度 Debug 工具的使用方法一致,但是暫不支持命令行配置和 runall 功能。
在本文開(kāi)始之前,需要用戶(hù)參考文章圖片校準數據準備問(wèn)題介紹與處理和模型精度驗證及調優(yōu)建議,掌握正確處理校準數據以及評測模型精度的方法,并且排除因輸入數據預處理不當和欠佳的推理結果后處理等非模型自身造成的精度問(wèn)題。
02 基礎介紹
在 PTQ 模型后量化過(guò)程中,通常情況下造成精度損失的主要原因可能有以下幾點(diǎn):
敏感節點(diǎn)量化問(wèn)題。模型中的一部分節點(diǎn)對量化比較敏感會(huì )引入較大誤差;
節點(diǎn)量化誤差累積問(wèn)題。模型中各個(gè)節點(diǎn)的量化誤差累積導致模型整體出現較大的校準誤差,主要包含:權重量化導致的誤差累積、激活量化導致的誤差累積以及全量量化導致的誤差累積。
針對上述情況,地平線(xiàn)工具鏈提供的精度 Debug 工具將協(xié)助用戶(hù)自主定位模型量化過(guò)程中產(chǎn)生的精度問(wèn)題,對校準模型進(jìn)行節點(diǎn)粒度的量化誤差分析并快速定位出現精度異常的節點(diǎn)。
PTQ 精度 Debug 工具提供的功能包括:
獲取節點(diǎn)量化敏感度;
獲取模型累積誤差曲線(xiàn);
獲取指定節點(diǎn)的數據分布;
獲取指定節點(diǎn)輸入數據通道間數據分布箱線(xiàn)圖等。
2.1 基本流程
使用精度 Debug 工具進(jìn)行精度 Debug 的基本流程為:
1.校準模型和校準數據的準備。校準模型在模型轉換過(guò)程中為常態(tài)化保存,無(wú)需用戶(hù)額外操作,為模型轉換的輸出目錄model_output下的calibrated_model;校準數據的保存需要用戶(hù)在 Yaml 文件中的模型參數組中增加如下參數配置(配置后精度 Debug 功能同時(shí)開(kāi)啟,但需要用戶(hù)重新編譯一次模型,編譯等級可以配置為 O0 以減少編譯時(shí)間):
model_parameters:
debug_mode: "dump_calibration_data"
2.通過(guò)import horizon_nn.debug as dbg導入 Debug 模塊(使用命令行則無(wú)需導入),加載校準模型和校準數據;
3.通過(guò)精度 Debug 工具提供的 API 或者命令行,對精度損失明顯的模型進(jìn)行分析。(詳細的流程以及分析見(jiàn)后文。)
2.2 校準數據
校準數據(calibration_data)在模型轉換的校準階段使用,模型通過(guò)對這些數據進(jìn)行前向推理來(lái)獲取每個(gè)被量化節點(diǎn)的量化參數,量化參數包括:縮放因子(Scale)和閾值(Threshold)。
值得注意的是,此處保存的校準數據是經(jīng)過(guò)顏色空間轉換以及預處理之后的 npy 格式的數據,該數據可以通過(guò)np.load()直接送入校準模型進(jìn)行推理,不同于在模型轉換前數據準備階段使用02_preprocess.sh腳本生成的校準數據。
使用腳本生成的校準數據是 bgr 顏色空間的數據,在工具鏈內部會(huì )將數據從 bgr 轉換到 yuv444/gray 等模型實(shí)際輸入的格式。
按照上述精度 Debug 流程中提到的操作保存校準數據后,自動(dòng)在model_output目錄下生成的calibration_data文件夾結構如下所示:
|-- calibration_data #校準數據
|---- input1 #文件夾名為模型的輸入節點(diǎn)并保存對應的輸入數據
|-------- 0.npy
|-------- 1.npy
|-------- ...
|---- input2 #對于多輸入模型將保存多個(gè)文件夾
|-------- 0.npy
|-------- 1.npy
|-------- ...
|---- ...
2.3 校準模型
校準模型(calibrated_model.onnx)是將在校準階段計算得到的每個(gè)被量化節點(diǎn)的量化參數保存在校準節點(diǎn)中,從而得到的模型。
模型轉換過(guò)程中,模型轉換工具將浮點(diǎn)模型進(jìn)行結構優(yōu)化后,通過(guò)校準數據計算得到每個(gè)節點(diǎn)對應的量化參數并將其保存在校準節點(diǎn)中,形成了校準模型。
因此校準模型是一種中間產(chǎn)物,主要特點(diǎn)是模型中包含校準節點(diǎn),校準節點(diǎn)的節點(diǎn)類(lèi)型為 HzCalibration。
校準節點(diǎn)主要分為兩類(lèi):
激活(activation)校準節點(diǎn)。激活校準節點(diǎn)的輸入是當前節點(diǎn)的上一個(gè)節點(diǎn)的輸出,并基于當前激活校準節點(diǎn)中保存的量化參數對輸入數據進(jìn)行量化和反量化后輸出;
權重(weight)校準節點(diǎn)。權重校準節點(diǎn)的輸入是模型的原始浮點(diǎn)權重,并基于當前權重校準節點(diǎn)中保存的量化參數對輸入的原始浮點(diǎn)權重進(jìn)行量化和反量化后輸出。
除了上述的校準節點(diǎn),校準模型中的其他節點(diǎn)稱(chēng)為普通節點(diǎn)(node),普通節點(diǎn)的類(lèi)型包括:Conv、Mul 和 Add 等。
下圖直觀(guān)地展示了校準節點(diǎn)和普通節點(diǎn)在校準模型中扮演的角色:
03 一鍵運行
為了方便用戶(hù)快速獲取 PTQ Debug 工具的結果,這里推薦使用一鍵運行功能,可以一鍵運行 PTQ Debug 工具中的全部功能。每個(gè)功能的詳細解讀可以參考第三部分功能詳解。
根據經(jīng)驗,校準數據的數量對 PTQ Debug 的結果不會(huì )有太大影響,為了加速工具運行,建議用戶(hù)使用一份校準數據即可,配置詳見(jiàn)參數介紹。
此外 PTQ 精度 Debug 工具可以使用 GPU 進(jìn)行計算加速,用戶(hù)需要進(jìn)入 GPU Docker 中先卸載預安裝的 CPU 版本horizon-nn,再安裝對應版本的horizon-nn-gpu,安裝所需的 wheel 文件在路徑ddk/package/host/ai_toolchain下,這樣可以保證完整覆蓋。
注意:因涉及 ONNXRuntime 版本問(wèn)題,使用 GPU 加速計算僅作為嘗試,如遇報錯,建議回退 CPU 進(jìn)行計算。
3.1 使用方法
1. API 使用
# 導入debug模塊
import horizon_nn.debug as dbg
dbg.runall(model_or_file='./calibrated_model.onnx',
calibrated_data='./calibration_data',)
2. 命令行使用
# 例如
hmct-debugger runall calibrated_model.onnx calibration_data
可通過(guò)hmct-debugger runall -h/--help查看相關(guān)參數。
3. 參數介紹
詳細的 API 和命令行參數說(shuō)明以及配置介紹,閱讀原文后至社區搜索《 J5 算法工具鏈—PTQ Debug 工具使用》。
4. 執行順序
PTQ Debug 工具各個(gè)功能的運行順序如下:
分別獲取權重校準節點(diǎn)和激活校準節點(diǎn)的量化敏感度;
根據 step1 的結果,分別取權重校準節點(diǎn)的 top5 和激活校準節點(diǎn)的 top5 繪制其數據分布;
針對 step2 獲取的節點(diǎn),分別繪制其通道間數據分布的箱線(xiàn)圖;
繪制分別只量化權重和只量化激活的累積誤差曲線(xiàn)。
注:當指定node_type='node'時(shí),工具會(huì )獲取 top5 節點(diǎn),并分別找到每個(gè)節點(diǎn)對應的校準節點(diǎn),并獲取其校準節點(diǎn)的數據分布和箱線(xiàn)圖。
3.2 Debug 分析
PTQ Debug 分析的流程如下所示:
首先根據累積誤差曲線(xiàn)判斷模型量化掉點(diǎn)是由激活量化導致的還是由權重量化導致的,確定是激活量化問(wèn)題還是權重量化問(wèn)題。如果激活和權重的單獨量化誤差不明顯,可以判斷是兩者共同作用導致的。
對于激活量化問(wèn)題,根據激活節點(diǎn)的量化敏感度排序確定敏感節點(diǎn);對于權重量化問(wèn)題,根據權重節點(diǎn)的量化敏感度排序確定敏感節點(diǎn);對于激活和權重量化問(wèn)題,計算普通節點(diǎn)的量化敏感度并排序,確定敏感節點(diǎn)。
獲取量化敏感節點(diǎn)后,進(jìn)行部分節點(diǎn)以較高精度量化或部分節點(diǎn)不量化測試。在 J5 工具鏈上,可以對敏感節點(diǎn)設置 Int16 量化,Int16 量化調優(yōu)工具的使用參考社區文章 PTQ 精度調優(yōu)手段—設置 Int16 量化,同時(shí)也可以讓敏感節點(diǎn)run_on_cpu。XJ3 工具鏈暫不支持用戶(hù)手動(dòng)設置節點(diǎn)以 Int16 精度量化,可以讓敏感節點(diǎn)run_on_cpu。
同時(shí)還可以根據敏感節點(diǎn)數據分布直方圖以及通道間數據分布的箱線(xiàn)圖來(lái)明確優(yōu)化方向,例如激活敏感節點(diǎn)數據分布不均勻則可以通過(guò)優(yōu)化模型結構來(lái)改善,權重敏感節點(diǎn)數據分布不均勻則建議使用 QAT 等。
04 功能詳解
接下來(lái)將針對 PTQ 精度 Debug 工具提供的功能和對應的 API 或命令行使用方法,逐一進(jìn)行詳解。
4.1 獲取累積誤差曲線(xiàn)
通過(guò)只量化浮點(diǎn)模型中的某一個(gè)節點(diǎn),并依次計算該模型中每個(gè)節點(diǎn)與浮點(diǎn)模型中節點(diǎn)輸出的誤差,獲得累積誤差曲線(xiàn)。
API 使用方法:
# 導入debug模塊
import horizon_nn.debug as dbg
dbg.plot_acc_error(
save_dir='./',
calibrated_data='./calibration_data/',
model_or_file='./calibrated_model.onnx',
quantize_node=['weight', 'activation'],
metric='cosine-similarity',
average_mode=False)
命令行使用方法:
# 例如
hmct-debugger plot-acc-error calibrated_model.onnx calibration_data -q ['weight', 'activation']
可通過(guò)hmct-debugger plot-acc-error -h/--help查看相關(guān)參數。
參數介紹:
詳細的 API 和命令行參數說(shuō)明以及配置介紹,閱讀原文后到社區搜索《 J5 算法工具鏈—PTQ Debug 工具使用》。
配置方式與 Debug 結果分析:
指定單節點(diǎn)量化/不量化:
如下配置意為分別只量化Conv_2和Conv_90并保持其他節點(diǎn)不量化,計算累積誤差曲線(xiàn);或分別解除量化Conv_2和Conv_90并保持其他節點(diǎn)量化,計算累積誤差曲線(xiàn)。
API 配置方式為:
quantize_node=['Conv_2', 'Conv_90']/non_quantize_node=['Conv_2', 'Conv_90']
命令行配置方式為:
-q ['Conv_2', 'Conv_90']/-nq ['Conv_2', 'Conv_90']
指定多個(gè)節點(diǎn)量化/不量化:如下配置意為分別只量化Conv_2以及只量化Conv_2和Conv_90并保持其他節點(diǎn)不量化,計算累積誤差曲線(xiàn);或分別解除量化Conv_2以及解除量化Conv_2和Conv_90并保持其他節點(diǎn)量化,計算累積誤差曲線(xiàn)。
API 配置方式為:
quantize_node=[['Conv_2'], ['Conv_2', 'Conv_90']]/non_quantize_node=[['Conv_2'], ['Conv_2', 'Conv_90']]
命令行配置方式為:
-q [['Conv_2'], ['Conv_2', 'Conv_90']]/-nq [['Conv_2'], ['Conv_2', 'Conv_90']]
按照量化敏感度排序選擇節點(diǎn)量化/不量化:
注:non_quantize_node設置不量化的節點(diǎn)等價(jià)于讓其運行在 CPU 上。
測試部分量化精度時(shí),可以按照量化敏感度排序進(jìn)行多組量化策略的精度對比,此時(shí)可以參考以下用法:
# 導入debug模塊
import horizon_nn.debug as dbg
# 首先使用量化敏感度排序函數獲取模型中節點(diǎn)的量化敏感度排序,或者可以直接加載保存的量化敏感度
node_message = dbg.get_sensitivity_of_nodes(
model_or_file='./calibrated_model.onnx',
metrics='cosine-similarity',
calibrated_data='./calibration_data/',
output_node=None,
node_type='node',
verbose=False,
interested_nodes=None)
# node_message為字典類(lèi)型,其key值為節點(diǎn)名稱(chēng)
nodes = list(node_message.keys())
# 通過(guò)nodes來(lái)指定不量化節點(diǎn),可以方便使用
dbg.plot_acc_error(
save_dir='./',
calibrated_data='./calibration_data/',
model_or_file='./calibrated_model.onnx',
non_quantize_node=[nodes[:1],nodes[:2]], #分別解除量化敏感度排序靠前的節點(diǎn)
metric='cosine-similarity',
average_mode=True)
可視化結果:
針對部分量化精度分析的累積誤差曲線(xiàn)圖,在分析時(shí),用戶(hù)應當更關(guān)注模型輸出位置(曲線(xiàn)尾部)的量化相似度,例如下圖,qmodel_1 模型的精度要好于 qmodel_0:
指定激活節點(diǎn)/權重節點(diǎn)分別量化:
如下配置意為分別只量化權重節點(diǎn)和激活節點(diǎn)并保持其他節點(diǎn)不量化,計算累積誤差曲線(xiàn)。
API 配置方式為:quantize_node=['weight', 'activation']
命令行配置方式為:-q ['weight', 'activation']
可視化結果:通過(guò)下圖可以分析出激活節點(diǎn)量化引入了大量的量化累積誤差,而權重節點(diǎn)量化對模型精度無(wú)負面影響。
##
4.2 獲取節點(diǎn)量化敏感度
API 使用方法:
# 導入debug模塊
import horizon_nn.debug as dbg
# 導入log日志模塊
import logging
# 若verbose=True時(shí),需要先設置log level為INFO
logging.getLogger().setLevel(logging.INFO)
# 獲取節點(diǎn)量化敏感度
node_message = dbg.get_sensitivity_of_nodes(
model_or_file='./calibrated_model.onnx',
metrics=['cosine-similarity', 'mse'],
calibrated_data='./calibration_data/',
output_node=None,
node_type='node',
data_num=None,
verbose=True,
interested_nodes=None)
命令行使用方法:
# 例如
hmct-debugger get-sensitivity-of-nodes calibrated_model.onnx calibration_data -m ['cosine-similarity','mse']
可通過(guò)hmct-debugger get-sensitivity-of-nodes -h/--help查看相關(guān)參數
參數介紹:
詳細的 API 和命令行參數說(shuō)明以及配置介紹,見(jiàn) J5 算法工具鏈—PTQ Debug 工具使用。
Debug 結果分析:
首先您通過(guò)node_type設置需要計算敏感度的節點(diǎn)類(lèi)型,然后工具獲取校準模型中所有符合node_type的節點(diǎn),并獲取這些節點(diǎn)的量化敏感度。當verbose設置為 True 時(shí),工具會(huì )將節點(diǎn)量化敏感度進(jìn)行排序后打印在終端,排序越靠前,說(shuō)明該節點(diǎn)量化引入的量化誤差越大。
同時(shí)對于不同的node_type,工具會(huì )顯示不同的節點(diǎn)量化敏感度信息,設置verbose=True,結果如下:
# node_type='node'
=================node sensitivity=================
node cosine-similarity mse
---------------------------------------------------
Conv_60 0.77795 68.02103
...
# node_type='weight'
# weight:權重校準節點(diǎn)名
# node:權重校準節點(diǎn)對應的普通節點(diǎn)名,即權重校準節點(diǎn)的輸出為其輸入
====================================node sensitivity====================================
weight node cosine-similarity mse
-----------------------------------------------------------------------------------------
471_HzCalibration Conv_2 0.99978 0.07519
...
# node_type='activation'
# activation:激活校準節點(diǎn)名
# node:激活校準節點(diǎn)后的普通節點(diǎn),即激活校準節點(diǎn)的輸出為其輸入
# threshold:校準閾值,若有多個(gè)閾值則取最大值
# bit:量化比特
===================================node sensitivity===================================
activation node threshold bit cosine-similarity mse
---------------------------------------------------------------------------------------
406_HzCalibration Conv_60 0.91501 8 0.77851 67.82422
...
此外,調用 API 時(shí),API 還將以字典格式(Key 為節點(diǎn)名稱(chēng),Value 為節點(diǎn)的量化敏感度信息)返回節點(diǎn)量化敏感度信息,以供用戶(hù)后續使用,返回格式如下:
node_message:
{'Conv_60': {'cosine-similarity': 0.77795, 'mse': 68.02103},
'Conv_48': {'cosine-similarity': 0.78428, 'mse': 64.36318},
...
}
量化敏感度保存與加載:
由于模型的量化敏感度在 PTQ 精度 Debug 過(guò)程中扮演著(zhù)重要的角色,累積誤差曲線(xiàn)的計算分析中會(huì )用到,因此建議用戶(hù)及時(shí)保存模型量化敏感度,直接加載以節省時(shí)間,可以參考如下代碼進(jìn)行量化敏感度的保存與加載:
注意:命令行暫時(shí)無(wú)法手動(dòng)保存量化敏感度。
# node_message保存為text文件
filename = open('sensitivity_of_nodes.txt', 'w')
for node, sensitivity in node_message.items():
filename.write(str(node) + ':' + str(sensitivity))
filename.write('\n')
filename.close()
# 加載保存的text文件
node_message = {}
filename = open('sensitivity_of_nodes.txt')
for line in filename:
line_split = line.split(':', 1) #按照第一個(gè)出現的':'進(jìn)行分割
node_message[line_split[0]] = line_split[1] #{str: str,...}
filename.close()
# node_message保存為json文件(推薦)
import json
save_json = json.dumps(node_message, sort_keys=False, indent=4, separators=(',', ': '))
filename = open('sensitivity_of_nodes.json', 'w')
filename.write(save_json)
filename.close()
# 加載保存的json文件
filename = open('sensitivity_of_nodes.json', 'r')
node_message = json.load(filename)
filename.close()
4.3 獲取節點(diǎn)數據分布
指定節點(diǎn),分別獲取該節點(diǎn)在浮點(diǎn)模型和校準模型中的輸出,得到輸出數據分布。另外,將兩個(gè)輸出結果做差,獲取兩個(gè)輸出之間的誤差分布。
API 使用方法:
# 導入debug模塊
import horizon_nn.debug as dbg
dbg.plot_distribution(
save_dir='./',
model_or_file='./calibrated_model.onnx',
calibrated_data='./calibration_data',
nodes_list=['317_HzCalibration', #激活節點(diǎn)
'471_HzCalibration', #權重節點(diǎn)
'Conv_2']) #普通節點(diǎn)
命令行使用方法:
hmct-debugger plot-distribution calibrated_model.onnx calibration_data -n ['317_HzCalibration','471_HzCalibration','Conv_2']
可通過(guò)hmct-debugger plot-distribution -h/--help查看相關(guān)參數。
參數介紹:
詳細的 API 和命令行參數說(shuō)明以及配置介紹,閱讀原文后至社區搜索《 J5 算法工具鏈—PTQ Debug 工具使用》。
Debug 結果分析:
可視化結果:
注:藍色三角表示數據絕對值的最大值;紅色虛線(xiàn)表示最大的校準閾值。
數據分布結果的分析標準為是否滿(mǎn)足對量化友好的正態(tài)分布,只要分布中有一個(gè)很明顯的單峰就認為滿(mǎn)足正態(tài)分布,不需要嚴格滿(mǎn)足正態(tài)分布公式,例如可視化結果中的第一張圖,輸入數據分布相對比較集中,對于激活節點(diǎn)而言輸入數據分布是友好的;同理第二張圖校準前后權重節點(diǎn)的數據分布也是友好的。
第三張圖是普通節點(diǎn)校準前后的輸出數據分布以及量化誤差分布,也符合正態(tài)分布,當節點(diǎn)輸出不符合正態(tài)分布時(shí),用戶(hù)可以嘗試在模型中增加 BatchNorm 層并重新訓練,然后再進(jìn)行 PTQ 量化。
4.4 獲取節點(diǎn)通道間數據分布
繪制指定校準節點(diǎn)輸入數據通道間數據分布的箱線(xiàn)圖。
API 使用方法:
# 導入debug模塊
import horizon_nn.debug as dbg
dbg.get_channelwise_data_distribution(
save_dir='./',
model_or_file='./calibrated_model.onnx',
calibrated_data='./calibration_data',
nodes_list=['317_HzCalibration'],
axis=None)
命令行使用方法:
hmct-debugger get-channelwise-data-distribution calibrated_model.onnx calibration_data -n ['317_HzCalibration']
可通過(guò)hmct-debugger get-channelwise-data-distribution -h/--help查看相關(guān)參數。
參數介紹:
詳細的API和命令行參數說(shuō)明以及配置介紹,見(jiàn)。
Debug結果分析:
可視化結果:
注:橫坐標表示節點(diǎn)輸入數據的通道數;縱坐標表示每個(gè) channel 的數據分布范圍,其中紅色實(shí)線(xiàn)表示該 channel 數據的中位數,藍色虛線(xiàn)表示均值。
通過(guò)箱線(xiàn)圖可以直觀(guān)地了解當前數據每個(gè)通道之間的數據分布情況。通過(guò)觀(guān)察箱線(xiàn)圖縱坐標確認數據分布范圍,當某一個(gè)通道有異常值時(shí)(即數值極大或極小,例如上圖中第 21 通道),認為當前節點(diǎn)采用 per-tensor 量化會(huì )有較大的量化風(fēng)險,需要嘗試使用 per-channel 量化去減少量化誤差。箱線(xiàn)圖的閱讀可以參考下圖:
05 實(shí)例參考
我們還提供了三篇社區文章,來(lái)展示如何在具體模型上使用 PTQ Debug 工具進(jìn)行分析:
【PTQ 精度 debug 示例】mnasnet_1.0_96 精度問(wèn)題分析
【PTQ 精度 debug 示例】repvgg_b2_deploy 精度問(wèn)題分析
【PTQ 精度 debug 示例】MobileVit_s 精度問(wèn)題分析
*博客內容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀(guān)點(diǎn),如有侵權請聯(lián)系工作人員刪除。