這一文件介紹些基礎概念吧,權當做一個階段整理,後面也會慢慢完善。
1. 量化分類:
根據谷歌量化白皮書中定義,分為PTQ和QAT兩種,而PTQ又包括兩種。
3種量化方式:QAT, PTQ Dynamic, PTQ Static。
1) 量化感知訓練 (Quant Aware Training, QAT):
量化訓練讓模型感知量化運算對模型精度帶來的影響,透過 finetune 訓練降低量化誤差。這種方法會降低訓練速度,但是能夠獲得更高的精度。
2) 動態離線量化 (Post Training Quantization Dynamic, PTQ Dynamic):
動態離線量化僅將模型中特定運算元的權重從FP32型別對映成 INT8/16 型別,bias和啟用函式 在推理過程中動態量化。但是對於不同的輸入值來說,其縮放因子是動態計算的(“動態”的由來)。動態量化是幾種量化方法中效能最差的。
動態量化常用於非常大的模型。
3) 靜態離線量化 (Post Training Quantization Static, PTQ Static):
靜態離線量化使用少量無標籤校準資料,採用 KL 散度等方法計算量化比例因子。靜態量化(Static quantization)與動態量化的區別在於其輸入的縮放因子計算方法不同,靜態量化的模型在使用前有“calibrate”的過程(校準縮放因子):準備部分輸入(對於影像分類模型就是準備一些圖片,其他任務類似),使用靜態量化後的模型進行預測,在此過程中量化模型的縮放因子會根據輸入資料的分佈進行調整。一旦校準完成後,權重和輸入的縮放因子都固定(“靜態”的由來)。靜態量化的效能一般比動態量化好,常用於中等模型和大模型。因此實際中基本都是在用靜態量化。 網址
靜態離線量化的目標是求取量化比例因子,主要透過對稱量化、非對稱量化方式來求,而找最大值或者閾值的方法又有MinMax、KLD、ADMM、EQ等方法,這些後文會講。
Tips:
a) feature map(fm)就是每一層網路的輸入tensor,模型最開始時將影像讀取後輸入到模型的那個tensor就是第一個featuremap,經過conv+relu等輸出後得到下一個featuremap;
b)靜態量化:對於權重而言,在模型訓練完成後數值就基本確定了,而對於 feature map(fm) 來說,卻沒法事先得知,因此會用一批校準資料 (通常就是驗證集的一小部分) 跑一遍網路,以此來統計每一層 feature map 的數值範圍。這裡的featuremap量化就是我們常說的啟用量化。
c) 由於訓練網路的時間和資源成本不低,以及通常是客戶訓練好網路,找我們公司來做硬體部署,因此都是先使用PTQ量化方法來部署,將各個tricks嘗試一遍後,基本也能滿足部署要求了,因此感覺QAT 量化感知訓練一般工業上很少用(至少目前我們還沒用到過)。
d)3種量化方式區分框圖及對比如下:
2. 量化方法:
量化方法:非對稱量化、對稱量化、隨機量化。
1)非對稱量化
假設輸入的浮點數範圍為(X_min, X_max),量化後的範圍為(0, N_levels - 1),對8bits量化來說N_levels為256,scale和零點的計算公式如下:
注:上述z要取一個round,zeropoint基於定點域平移, zeropoint是一個定點域的數。
得到了scale和零點後,對於任意的輸入x,量化計算過程為:
其中 clamp計算公式為:
對應的反量化計算公式為:
注意:對於單邊分佈如(2.5, 3.5),需要將其範圍放寬至(0, 3.5)再量化,在極端的單邊分佈的情況下會損失精度。
即若min大於0,就將範圍縮放到(0,max)。
2)對稱量化
scale(縮放因子)計算公式如下,對8bits量化來說N_levels為128:
對稱量化比較簡單,它限制零點值為0,量化公式如下:
反量化公式:
3)隨機量化
隨機量化與非對稱量化類似,區別在於量化時引入了噪音,其引數計算與反量化過程與非對稱量化相同,此處不再贅述。
Tips:
a)在硬體支援的情況下,量化時對啟用值X使用非對稱量化,對權重值W使用對稱量化,這是一種常用的量化方案(谷歌白皮書建議)
3. 量化工具/框架整理:
參考該作者內容,整理如下:
公司 | 量化工具 | 推理引擎 | 部署平臺 |
Intel | NNCF | OpenVino | x86 CPU |
高通 | AIMet | SNPE/QNN | 高通 DSP/NPU 等晶片 |
MTK | Neuropilot | Neuron SDK | 聯發科 APU 晶片 |
Nvidia | TensorRT(習慣稱為TRT) | TensorRT | 英偉達部分 GPU 晶片 |
商湯 | MQBench、PPL | PPL | -- |
騰訊 | NCNN | NCNN | 多種 CPU 平臺 |
Meta(臉書) | pytorch | Libtorch | arm/x86 CPU |
微軟 | NNI | ||
華為 | MindSpore Lite converter—PTQ MindSpore Lite—QAT |
MindSpore | 端、邊、雲 |
tvm團隊 | TVM | TVM | 端、邊、雲 |
百度 | PaddleSlim |
當然,還有其他廠家的未開源的工具,如芯原、寒武紀、地平線這些廠家都有自己的量化工具。
看了上面這張表後,大家可能也發現了:不同量化訓練工具能部署的推理框架和平臺,是不一樣的。這就意味著,如果你要把量化的模型部署到英偉達的 GPU平臺,你就得用英偉達的 TRT 進行量化訓練然後匯出模型。
同樣地,如果要用 華為的 NPU 來部署,就得用 MindSpore。
各家之間沒有相容,對部署工作很不方便。對於 fp32 的模型 (全精度模型),不管是用 tensorflow 還是 pytorch 訓練的模型,都可以很方便地轉換到 tflite/SNPE/NCNN 等平臺,做到一次訓練多平臺部署。為什麼到了量化這裡就不行了呢?
我個人覺得,一個很重要的原因是,量化這塊缺乏統一的標準,各個晶片產商都在搶奪市場,各自為戰,因此具體到量化推理框架上,各家的演算法實現也有很大差異。此外,量化推理和硬體本身有很強的關聯性,不同晶片由於支援的指令集和硬體單元的差異,也會導致量化演算法有一些差別。
有一點能肯定的是,各家量化工具基本都遵循谷歌量化白皮書,因此大體上量化演算法是類似的。
4. 量化方法中的量化策略
TRT量化思路:使用校準資料集,使得Kullback-Leibler divergence (KLD)為最小值來找到量化時的啟用層閾值,來量化啟用(也即特徵圖,featuremap),權重量化是使用的abs(值)的最大值(maximum absolute values)作為閾值量化的;
(也就是權重用的minmax,啟用用的KLD求解T)
TensorflowLite量化思路:使用abs(值)的最大值(maximum absolute values)作為閾值量化啟用,權重量化也是用maximum absolute values作為閾值,並且針對per-channel進行量化;
(也就是權重和啟用都是用的minmax)
常用幾種量化策略:MinMax、KLD、ADMM、EQ。更多PTQ中量化方式在該論文中的2.2介紹:EasyQuant: Post-training Quantization via Scale Optimization (EQ演算法出處)
EQ 量化⽅法的主要思想是:誤差累計、整⽹決策變成單⽹決策、以餘弦相似度為最佳化⽬標、交替最佳化權重縮放係數和啟用值縮放係數。
總結就是上面說到的權重量化和啟用量化時候的scale和bias怎麼選擇;通常是權重的引數直接計算出來,而啟用層更需要量化策略尋找最佳引數;
4.1 TRT量化
TensorRT的量化工具也比較成熟了。具體流程就是,我們匯出ONNX模型,轉換為TensorRT的過程中可以使用TRT提供的Calibration方法去校準,這個使用起來比較簡單。可以直接使用trt官方提供的trtexec命令去實現,也可以使用trt提供的python或者C++的API介面去量化。
TensorRT實現int8量化
- 對權重直接使用了最大值量化(非飽和量化);
- 對偏移直接忽略;
- 對啟用值採用飽和量化;
注: 我理解這裡提出的非飽和量化和飽和量化與上文中的對稱、非對稱量化可以類比,意思差不多。
a)非飽和量化方法(No Saturation):map |max| to 127
非飽和量化方法計算 FP32 型別 Tensor 中絕對值的最大值 abs_max,將其對映為 127,則量化比例因子等於 abs_max/127
b)飽和量化方法(Saturation):above |threshold| to 127
飽和量化方法使用 KL 散度計算一個合適的閾值 T(0 < T < mab_max),將其對映為 127,則量化比例因子等於 T/127
· 一般而言,對於待量化 OP 的權重 Tensor,採用非飽和量化方法,對於待量化 OP 的啟用 Tensor(包括輸入和輸出),採用飽和量化方法。
啟用為什麼用飽和量化?
因為啟用值通常分佈不均勻,直接使用非飽和量化會使得量化後的值都擠在一個很小的範圍從而浪費了INT8範圍內的其他空間,也就是說沒有充分利用INT8(-128~+127)的值域;
而進行飽和量化後,使得對映後的-128~+127範圍內分佈相對均勻,這相當於去掉了一些不重要的因素,保留了主要成分。
如何尋找這個閾值T就成了INT量化的關鍵,怎麼找呢?
不同模型的啟用值分佈差異很大,這就需要進行動態的量化。
於是,NVIDIA就選擇了KL散度也即相對熵來對量化前後的啟用值分佈進行評價,來找出使得量化後INT8分佈相對於原來的FP32分佈,資訊損失最小的那個閾值。
INT8量化校準過程
- 先在一個校準資料集上跑一遍原FP32的模型;
- 然後,對每一層都收集啟用值的直方圖(預設2048個bin),並生成在不同閾值下的飽和量化分佈;
- 最後,找出使得KL散度最小的那個閾值T,即為所求。
【TensorRT訓練中量化】
此外,訓練中量化(QAT)是TensorRT8新出的一個“新特性”,這個特性其實是指TensorRT有直接載入QAT模型的能力。實際上QAT過程和TensorRT沒有太大關係,trt只是一個推理框架,實際的訓練中量化操作一般都是在訓練框架中去做,比如我們熟悉的Pytorch。這一點跟TVM類似,都可以匯入提前量化好的模型來做後續部署。
後續繼續完善...
5. 參考文獻
1)量化方法介紹 【深度學習】模型量化-筆記/實驗
2)量化方法的綜合對比 模型量化(2):Paddle 模型的靜態量化和動態量化 - 飛槳AI Studio
6. 後續工作
6-1:實現 A developer-friendly guide to model quantization with PyTorch
6-2:實現introduction-to-quantization-on-pytorch:PyTorch
6-3:實現mobilenetv2在cifar10速度測試 pytorch量化測試程式碼
6-4:實現NCNN模型量化實戰-人像摳圖MODNet模型 【Matting】MODNet