深度學習算法優化系列 | Google CVPR2018 int8量化算法

1. 前言
這是Google在CVPR 2018上發表的一篇int8量化的論文,題目爲《Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference》。也是入門量化最經典的論文之一。論文介紹了一種只使用整數運算的量化方式,相比於浮點數運算效率更高。一起先來看看這篇論文吧。論文的axriv地址可以在附錄中找到。

2. 背景
模型量化仍然屬於模型壓縮的範疇,而模型壓縮的目的是降低模型佔用的內存大小,加快模型推理速度。在這之前,主要有兩方面的研究用於減少模型的大小和前向推理的時間。一是在網絡結構上的改進,諸如MobileNet,SqueezeNet,ShuffleNet和DenseNet等等。二是量化權重和激活函數,將32位的浮點數用更低位的數來表示,如half-float,int,bit等等。然而這些方法並沒有在一個合理的BaseLine基礎上進行評估。這些網絡的BaseLine幾乎都是選在AlexNet,VGG16,GoogleNet這種大型網絡,而這些大型網絡在設計時爲了達到高準確率存在很多冗餘,所以在壓縮這些網絡時都有不小的效果體現。其二在於量化方法沒有在真正的硬件上進行有效性證明。有的方法只在權重上進行量化,僅僅關心設備的存儲,而不關心計算效率。有的方法如2-bit/3-bit權重網絡和bit-shifit網絡,它們把權重限制爲0或者2n2^n2 
n
 ,即把乘法操作用二進制移位來實現。但在某些硬件上,二進制移位實現並不比乘法,加法好。並且,只有當Bit大的時候,乘法操作才顯得比較"昂貴"。從上面的介紹引出這篇論文的目的,即是要將乘法的輸入:權重和激活值都量化成比較小的位寬,即int8量化。

同時,量化一般可以分爲兩種模式,即訓練後量化(post-training-quantizated)以及訓練時量化(quantization-aware-training)。訓練後量化比較容易理解,即將訓練後的模型中的權重從float32量化到int8,並以int8的形式保存,但在實際推理時,還需要反量化爲浮點數類型進行計算。這種量化方式在大模型上的效果很好,因爲大模型的抗噪能力很強,但在小模型上表現就比較差了。而訓練中量化意思是在訓練的過程中引入僞量化操作,即在前向傳播的時候,採用量化後的權重和激活值,但在反向傳播的時候仍然對float類型的權重進行梯度下降,前向推理時全部使用int8的方式進行計算。

3. 方法
這篇論文提出了一種將float32量化爲int8的方法,並給出了一個訓練和推理框架,推理框架使得模型可以在能執行整型運算的計算設備上高效運行,訓練框架和推理框架相輔相成,可以顯著降低量化過程中的精度損失。

3.1 量化推理
3.1.1 量化方案
首先,定義qqq代表量化後的值,rrr代表原始的float32值,這篇論文拋棄了之前使用查表的方式將浮點數映射爲整數的方法,而是直接引入了一個映射關係來表示,如公式(1)所示:


其中SSS代表縮放係數,ZZZ代表zero−pointzero-pointzero−point,即真實浮點數000映射到整數時所對應的值,和qqq的數據類型一致。對於int8量化,qqq就是8-bit整數,對於B-bit量化,q就是B-bit的實數,對於有bias的情況,就固定量化爲·32-bit的實數。其中SSS的計算方式爲:

S=arraymax−arraymin2bitwidth−1S=\frac{array_{max}-array_{min}}{2^{bit_width}-1}S= 

bit 
w
​    
 idth
 −1
array 
max
​    
 −array 
min
​    
 
​    
 

然後ZZZ可以表示爲:
Z=round(−arrayminS)Z = round(-\frac{array_{min}}{S})Z=round(− 
S
array 
min
​    
 
​    
 )

其中roundroundround算子表示:
round(x)=⎧⎩⎨0,⌊x⌉,2n−1x<00<⌊x⌉<2n−1x>2n−1round(x)= \left\{\begin{aligned}0 ,& &x<0\\⌊x⌉ ,& & 0<⌊x⌉ <2^n-1\\2^n-1& & x>2^n-1\end{aligned}\right.
round(x)= 





​    
  
0,
⌊x⌉,

n
 −1
​    
  
​    
  
x<0
0<⌊x⌉<2 
n
 −1
x>2 
n
 −1
​    
 

再從公式(1)推導得到反量化公式,這是訓練的時候反向傳播要用到的:
q=round(Z+rS)q=round(Z+\frac{r}{S})q=round(Z+ 
S
r
​    
 )

如果我們用C++裏面的結構體來表示這個數據結構,那麼就可以寫成下面的形式:

可以將卷積層的量化過程總結如下,這部分借鑑了一篇CSDN博主的流程,鏈接放在附錄的參考博客1了。卷積層的量化過程表示爲:

1、輸入 量化的特徵圖lhs_quantized_val, uint8類型, 偏移量 lhs_zero_point, int32類型。

2、輸入 量化的卷積核rhs_quantized_val, uint8類型, 偏移量 rhs_zero_point, int32類型。

3、轉換uint8到int32類型。

4、每一塊卷積求和,注意int32_accumulator求和有溢出的風險,可以換成固定點小數乘法。這部分公式表示爲:
int32_accumulator += (lhs_quantized_val(i, j) - lhs_zero_point) * (rhs_quantized_val(j, k) - rhs_zero_point)。

5、輸入量化的乘子quantized_multiplier, int32類型和右移次數記錄right_shift, int類型。將int32_accumulator右移right_shift位。

6、計算乘法,得到int32結果,仍有溢出風險,可以換爲固定點小數乘法。這部分公式表示爲:quantized_multiplier * int32_accumulator。

7、加上結果的偏移量 result_zero_point。

8、左移right_shift位還原,得到int32的結果。

9、將int32類型結果 限幅到[0, 255], 再強制轉換到 uint8類型。

10、之後再反量化到浮點數,更新統計輸出值分佈信息max和min。

11、再量化回uint8。

12、之後量化激活層。

13、最後反量化到浮點數,即卷積層的輸出。

14、進入下一層,循環執行1-13步驟。

值得注意的一點事,如果有連續的層需要進行量化操作時,就沒有必要反量化了,如上面的10->11步驟,但這很有可能帶來乘加累積導致的溢出,所以每層量化似乎似乎是比較穩妥的做法。

3.1.2 純整數算術矩陣乘法
從公式(1)可以看到,每個arrayarrayarray中的實數rir_ir 
i
​    
 都表示帶有一對參數SSS和ZZZ的實數qiq_iq 
i
​    
 。則對實數矩陣R1R_1R 
1
​    
 ,R2R_2R 
2
​    
 做乘法,其結果矩陣的每個實數可以用下面的公式表示:


這個公式可以重寫爲:


其中:


可以看到MMM是式子(3)中唯一不是整數的值,並且經驗發現MMM的值總是在(0,1)(0,1)(0,1)中,所以可以將MMM表示爲下面的式子:


其中nnn是非負整數,M0M_0M 
0
​    
 是一個整數。這樣實數運算就變成了整數運算,同時2−n2^{-n}2 
−n
 可以用移位運算。這個nnn就是上面介紹的卷積層量化過程中的右移參數。

注意,這裏還有一個關鍵點就是在預測階段,權重矩陣的量化係數SSS可以通過已有的參數統計出來。而激活層的量化參數是大量訓練數據指數移動均值計算出來的,所以這裏纔會有q3q_3q 
3
​    
 沒出來,但先使用了S3S_3S 
3
​    
 。

3.1.3 零點的有效處理
在上面的公式(4)中因爲兩個矩陣都需要減去各自的零點Z值,減法運算後得到的值可能會突破int8範圍,到時候就需要int16來存儲,但整個運算爲了控制在int8的類型下計算,論文做了下面的變換。


這樣可以有效的避免計算過程中的值溢出int8範圍。但可以發現,這個等效變換仍然沒有改變整個計算的複雜度,都爲O(2N3)O(2N^3)O(2N 
3
 )。

3.1.4 融合一個層
前面描述了權重的矩陣計算,但在神經網絡中還有偏置bias和激活函數的映射,因爲int8類型的運算完之後的值應該是在int32之內的,所以bias選擇int32的類型,這樣的選擇一是因爲bias在整個神經網絡中只佔據極少的一部分,此外bias的作用其實非常重要,高精度的bias可以降低模型的偏差。因此加上bias之後就變成了int32,我們需要再次轉換成int8類型(反量化),之後再進入到激活中。具體如下圖所示:


再用公式詳細表達一下,定義bias的量化:
其中,SbiasS_{bias}S 
bias
​    
 用int32表示。將weights和input執行矩陣乘法後加上bias,公式表達爲:
得到了int32之後的結果後需要再次轉換成int8類型(反量化),之後再執行激活函數的操作。

4. 模擬量化訓練
在介紹中提到,後處理量化過程適合大模型,而小模型會導致精度損失比較大。論文認爲後處理量化主要存在兩點問題:

同一層不同通道的權重分佈尺度差很多(超過100x)
離散的權重會導致所有剩餘權重的精度下降
因此,論文提出了一種在前向傳播階段模擬量化的方法,反向傳播和平常一樣,所有的權重和biases都用浮點數保存以微調小變化。具體的量化方法如下:
1、 weights再輸入進行卷積之前就開始量化,如果有bn層,將bn層融入到weights中。
2、 激活在激活函數執行完之後再量化。

如下圖所示:


量化的公式如下:

這和上面介紹的推理階段的量化公式完全一致,我就不再贅述了。

4.1 學習量化範圍
對於上面的量化範圍(a, b),weight和activation是不一樣的,對於weight來說很簡單,就是該權重中最大最小值,但是對於activation是不太一樣的, 對於activation採用了EMA(滑動平均)來對輸入中的最大最小值計算得到的,但是在訓練初期,因爲輸入的值變化較大,會影響到滑動平均的值,因此在初期不對activation做量化,而是在網絡穩定之後再引入。

4.2 摺疊BN
對於bn層,在訓練時是一個單獨的層存在,但是在前向推理時爲了提升效率是融合到卷積或全連接層的權重和偏置中的,如下圖:

所以,爲了模擬推斷過程,訓練時需要把BN層考慮到權重中,公式如下:


考慮了fold bn之後,最終可以用下圖來表示訓練過程中的量化:

後記
今天解讀了CVPR 2018 《Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference》,對int8量化了有了基本認識,這兩天隨緣更新一個實戰篇吧。

附錄
論文原文:https://arxiv.org/pdf/1712.05877.pdf

參考博客1:https://blog.csdn.net/qq_19784349/article/details/82883271

參考博客2:https://blog.csdn.net/holmosaint/article/details/82423610
————————————————
版權聲明:本文爲CSDN博主「just_sort」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/just_sort/article/details/103704975

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章