天工開物|征程 6 啟航新章:量化流程PTQ篇
目前在 GPU 上訓(xùn)練的模型大部分都是浮點(diǎn)模型,即參數(shù)使用的是 float 類型存儲(chǔ)。而地平線 BPU 架構(gòu)的計(jì)算平臺(tái)使用的是 int8 的計(jì)算精度(業(yè)內(nèi)計(jì)算平臺(tái)的通用精度),能運(yùn)行定點(diǎn)量化模型。
地平線 征程 6 算法工具鏈(以下簡(jiǎn)稱工具鏈)作為專業(yè)量化工具,是一套完整的邊緣計(jì)算平臺(tái)算法落地解決方案,可以幫助您把浮點(diǎn)模型量化為定點(diǎn)模型,并在地平線計(jì)算平臺(tái)上快速部署自研算法模型。當(dāng)需要對(duì)量化后的參數(shù)進(jìn)行調(diào)整時(shí),又可以將量化方法分為訓(xùn)練后量化(PTQ)和量化感知訓(xùn)練(QAT)。
其中訓(xùn)練后量化 PTQ 是使用一批校準(zhǔn)數(shù)據(jù)對(duì)訓(xùn)練好的模型進(jìn)行校準(zhǔn),將訓(xùn)練過的FP32模型直接轉(zhuǎn)換為定點(diǎn)計(jì)算的模型,過程中無需對(duì)原始模型進(jìn)行任何訓(xùn)練。只對(duì)幾個(gè)超參數(shù)調(diào)整就可完成量化過程,過程簡(jiǎn)單快速,無需訓(xùn)練,此方法已被廣泛應(yīng)用于大量的端側(cè)和云側(cè)部署場(chǎng)景,我們優(yōu)先推薦您嘗試 PTQ 方法來查看是否滿足您的部署精度和性能要求 。如下即為 PTQ 流程所需數(shù)據(jù)和基本步驟:
本文章聚焦訓(xùn)練后量化(PTQ),展示 PTQ 基本流程以及和上一代計(jì)算平臺(tái)工具鏈之間的使用差異。
02 PTQ量化&編譯
征程 6 PTQ 的使用方式、yaml 配置參數(shù)等均和 征程 5 保持一致,詳細(xì)說明可見用戶手冊(cè)《6.2 PTQ 轉(zhuǎn)換工具》,下文將對(duì)其中部分功能點(diǎn)的使用方式做具體說明。
2.1 校準(zhǔn)數(shù)據(jù)
當(dāng)前版本已支持在 yaml 文件中配置 色彩轉(zhuǎn)換(如 nv12—>bgr)和 歸一化(mean & scale),同時(shí)在數(shù)據(jù)保存上,也支持復(fù)用征程 5 上使用的 bin 格式(np.tofile)。 與征程 5 不同的是,征程 6 的 PTQ 需要手動(dòng)對(duì)校準(zhǔn)數(shù)據(jù)做歸一化。 另外,征程 6 也支持使用 np.save 將數(shù)據(jù)保存為 npy 格式。
2.2 前處理節(jié)點(diǎn)
征程 6 PTQ 前處理節(jié)點(diǎn)的配置方式和征程 5 保持一致,可以完全復(fù)用。需要注意的是:
征程 5 的前處理節(jié)點(diǎn)在 *_original_float.onnx 階段就已經(jīng)插入;
征程 6 只在 *_quantized.bc 和 *.hbm 模型上插入,各階段 onnx 模型推理時(shí)的輸入數(shù)據(jù)則完全一致。另外,征程 6 也提供 HBRuntime 推理庫,使用同一套接口推理各階段 onnx 模型 以及 HBIR(*.bc) 模型,詳細(xì)說明可參考用戶手冊(cè)的HBRuntime推理庫章節(jié)。
2.2.1 input_type_rt
當(dāng)前版本已支持配置 input_type_rt: nv12:
input_parameters:
input_type_rt: 'nv12' # 配置為nv12或gray時(shí),input_source默認(rèn)自動(dòng)選擇為pyramid
以單輸入 nv12模型為例,在 X86 環(huán)境使用 hb_model_info 工具查看 *_quantized.bc 的模型信息,其輸入節(jié)點(diǎn)已經(jīng)被拆分成 y 和 uv 兩個(gè)分量;進(jìn)一步在 qemu 環(huán)境使用 hrt_model_exec model_info 工具查看 *.hbm 的模型信息,其 input_source 為 pyramid 類型。
*_quantized.bc | *.hbm |
hb_model_info *_quantized.bc -v 可以生成其可視化 onnx,如下圖所示,已插入前處理節(jié)點(diǎn)
2.2.2 數(shù)據(jù)歸一化
目前 PTQ 已支持配置歸一化參數(shù),其使用方式和 征程 5 保持一致,值得注意的是:校準(zhǔn)數(shù)據(jù)準(zhǔn)備時(shí)手動(dòng)在代碼中加入了歸一化操作,如果此處配置了歸一化參數(shù),為推理準(zhǔn)備數(shù)據(jù)如果參考校準(zhǔn)數(shù)據(jù)準(zhǔn)備流程是要移除校準(zhǔn)數(shù)據(jù)準(zhǔn)備中的重復(fù)操作:
input_parameters:
norm_type: 'data_mean_and_scale'
mean_value: 127 127 127
scale_value: 0.0078125 0.0078125 0.0078125
2.3 Resizer輸入
目前 PTQ 已支持配置 input_source 來指定是否為 resizer 輸入,參考如下:
input_parameters:
input_type_rt: 'nv12'
compiler_parameters:
input_source: {'input0':'resizer'} #resizer僅支持input_type_rt配置為nv12或gray
環(huán)境打印模型信息,確認(rèn)已成功編譯為 resizer 模型:
2.4 Batch輸入拆分
當(dāng)前 Alpha 版本在部署端,暫不支持 Batch>1 的 Pyramid/Resizer模型 以連續(xù)地址輸入數(shù)據(jù)進(jìn)行推理,因此需要將 batch 輸入顯式地拆分成 batch 份并提供獨(dú)立地址。當(dāng)前 PTQ 鏈路拆分模型 batch 輸入的方式包含如下幾種:
拆分方式一:
適用場(chǎng)景:適用于任何模型
正常轉(zhuǎn)換模型,并基于生成的 ptq_model.onnx,調(diào)用 hbdk python api 進(jìn)行 batch 拆分后重新編譯模型,參考代碼如下:
import onnx
from hbdk4.compiler.onnx import export
from hbdk4.compiler import convert, compile
ptq_onnx = onnx.load("./*_ptq_model.onnx")
ptq_bc = export(ptq_onnx)
# 將該模型第一個(gè)輸入節(jié)點(diǎn)按batch維度(0)做拆分,方式同QAT
func = ptq_bc.functions[0]
# 依據(jù)部署需要,維護(hù)一個(gè)獨(dú)立地址部署的輸入節(jié)點(diǎn)名稱列表,或index列表
# 輸入節(jié)點(diǎn)名采用上一節(jié)的方式進(jìn)行了自定義修改
batch_input = ["input_name1"]
for input in func.inputs[::-1]:
for name in batch_input[::-1]:
if name in input.name:
input.insert_split(dim=0)
# ps:
# 1.insert_split會(huì)導(dǎo)致節(jié)點(diǎn)數(shù)變多,因此建議從后往前拆
# 2.就算batch=1,該接口也會(huì)嘗試拆分,會(huì)導(dǎo)致輸入節(jié)點(diǎn)的name增加 “_0” 后綴
quantized_bc = convert(ptq_bc, "nash-e")
compile(
quantized_bc,
march="nash-e",
path="./*.hbm"
)
拆分方式二:
適用場(chǎng)景:當(dāng)前版本僅適用于 單輸入 模型,暫不支持多輸入模型 直接使用 separate_batch 拆分 batch(要求原始模型為單輸入且 batch=1,并配合使用input_batch配置 batch 數(shù)):
input_parameters:
input_batch: 16
separate_batch: True
拆分方式三:
適用場(chǎng)景:在 DL 框架內(nèi)拆分 batch 輸入后重新導(dǎo)出 ONNX 模型
2.5 刪除指定節(jié)點(diǎn)
當(dāng)前版本已支持刪除指定名稱/類型的節(jié)點(diǎn),其支持刪除的算子類型和 yaml 配置方式和征程 5 保持一致。 需要注意:與 征程 5 不同,征程 6 沒有提供 **hb_model_modifier** 工具用于修改模型節(jié)點(diǎn)。
model_parameters:
remove_node_type: Quantize;Transpose;Dequantize;Cast;Reshape;Softmax
#remove_node_name:
03 性能評(píng)估
性能測(cè)試可以分為靜態(tài)測(cè)試和動(dòng)態(tài)測(cè)試兩種模式或階段。
3.1 靜態(tài)評(píng)估
靜態(tài)評(píng)估是編譯器根據(jù)模型的結(jié)構(gòu)和計(jì)算平臺(tái)架構(gòu)通過靜態(tài)的分析預(yù)估出的模型 BPU 部分的性能情況。需要注意因?yàn)樵u(píng)估需要計(jì)算平臺(tái)深層的架構(gòu)信息做支撐,目前靜態(tài)評(píng)估的結(jié)果僅 BPU 部分,不含 CPU/DSP 等性能情況。如果想獲取全面的性能數(shù)據(jù)還需要通過動(dòng)態(tài)評(píng)估。 靜態(tài)評(píng)估是通過地平線提供的 hb_compile 工具進(jìn)行的,該工具集成了模型編譯與性能分析的功能。在模型轉(zhuǎn)換編譯完成后,會(huì)在yaml文件配置的 working_dir 路徑下生成編譯器預(yù)估的模型BPU部分的模型靜態(tài)評(píng)估文件:model.html(可讀性更好)和model.json。用戶可通過他們了解模型的靜態(tài)評(píng)估結(jié)果。 另外,如果用戶需要,也可以通過 python 組件的hbdk4.compiler主動(dòng)進(jìn)行性能評(píng)估,參考代碼如下:
from hbdk4.compiler import hbm_perf
hbm_perf("model.hbm")
3.2 動(dòng)態(tài)測(cè)試
動(dòng)態(tài)評(píng)估是通過測(cè)試工具hrt_model_exec實(shí)際在板端運(yùn)行被測(cè)試模型最終獲取性能結(jié)果的過程。因?yàn)闇y(cè)試過程就是模型推理過程的真實(shí)在線,因此是對(duì)模型推理過程所需的系統(tǒng)依賴的一個(gè)全面評(píng)估,該過程有效彌補(bǔ)了靜態(tài)評(píng)估僅針對(duì) BPU 部分測(cè)評(píng)的不足。 hrt_model_exec工具的具體使用方法可以參考用戶使用手冊(cè) 和 部署部分的文章,這里不單獨(dú)贅述。
04 精度評(píng)測(cè)
精度評(píng)測(cè)是通過一批測(cè)試數(shù)據(jù)集(包含真值)、推理腳本以及結(jié)果后處理程序獲取優(yōu)化前后模型的精度信息,進(jìn)而了解模型從浮點(diǎn)模型量化為定點(diǎn)模型過程中帶來的精度損失情況。需要用戶了解的是后量化方式是基于幾十或上百張校準(zhǔn)數(shù)據(jù)實(shí)現(xiàn)的模型從浮點(diǎn)到定點(diǎn)轉(zhuǎn)換過程,無論是數(shù)據(jù)的規(guī)模還是模型參數(shù)的表達(dá)寬度都與原始模型訓(xùn)練過程有很大的差距,精度損失在一定程度上是不可避免地。地平線轉(zhuǎn)換工具經(jīng)過大量實(shí)際生產(chǎn)經(jīng)驗(yàn)驗(yàn)證和優(yōu)化,在大部分情況下可以將精度損失保持在1%以內(nèi),這在業(yè)界已經(jīng)是很牛的存在。 通過模型編譯過程我們了解*_quantized_model.bc是過程的產(chǎn)物之一,雖然最后的hbm模型才是將部署到計(jì)算平臺(tái)的模型,考慮到方便在Ubuntu開發(fā)機(jī)上完成精度評(píng)測(cè),我們一般通過bc模型文件來進(jìn)行精度評(píng)測(cè)過程。模型推理參考代碼如下: 下方示例代碼不僅適用于quantized模型,對(duì)original和optimized等onnx模型同樣適用(替換模型文件即可),根據(jù)模型的輸入類型和layout要求準(zhǔn)備數(shù)據(jù)即可。
import numpy as np
# 加載地平線依賴庫
from horizon_tc_ui.hb_runtime import HBRuntime
# 準(zhǔn)備模型運(yùn)行的輸入,此處`input.npy`為處理好的數(shù)據(jù)
data = np.load("input.npy")
# 加載模型文件,根據(jù)實(shí)際模型進(jìn)行設(shè)置
# ONNX模型
sess = HBRuntime("model.onnx")
# HBIR模型
sess = HBRuntime("model.bc")
# 獲取輸入&輸出節(jié)點(diǎn)名稱
input_names = sess.input_names
output_names = sess.output_names
# 準(zhǔn)備輸入數(shù)據(jù),根據(jù)實(shí)際輸入類型和layout進(jìn)行準(zhǔn)備,配置格式要求為字典形式,輸入名稱和輸入數(shù)據(jù)組成鍵值對(duì)
# 如模型僅有一個(gè)輸入
input_feed = {input_names[0]: data}
# 如模型有多個(gè)輸入
input_feed = {input_names[0]: data1, input_names[1]: data2}
# 進(jìn)行模型推理,推理的返回值是一個(gè)list,依次與output_names指定名稱一一對(duì)應(yīng)
output = sess.run(output_names, input_feed)
當(dāng)然,這里主要描述了模型精度分析基本流程和推理代碼,如果評(píng)估發(fā)現(xiàn)結(jié)果不符合預(yù)期,可以參考用戶手冊(cè)中的 PTQ 模型精度調(diào)優(yōu) 章節(jié)的內(nèi)容嘗試調(diào)優(yōu),其中 PTQ 精度debug 工具 征程6 與征程 5 使用方式一致,精度分析推薦流程也一致,具體請(qǐng)參考社區(qū)文章 精度驗(yàn)證及調(diào)優(yōu)建議流程。主要區(qū)別就是征程 6 平臺(tái)的性能評(píng)估過程是通過(*_quantized_model.bc)hbir 格式
*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。