前言
Quantization 作為模型加速一個非常重要的演算法,
這邊整理一下
Quantization 用途
Quantization 就是一個把連續值變成離散的過程,
2.123 變成 2 之類的
優點
將 float 轉成 int,
- 可以達到計算加速
- 需要的儲存空間減少 (例如:float32, 4 bytes 變 int8, 1 bytes)
- 功耗減少
但相對的當然也有缺點
缺點
- 因為有轉換的過程,就會產生誤差需要處理
(這其中也包含著轉成 int 後的數字有沒有足夠的解釋原始 float 數據的能力,此與分布有關係)
Quantization 分類
主要分為兩類
- Post-training Quantization
- Quantization aware training
Post-training Quantization
看到 「post-」,就知道這個是後處理的部分,通常是拿已經訓練好的 model 直接進行 Quantization
Quantization aware training (QAT)
對比上面,這個就是邊訓練邊做 Quantization
粗略比較兩種 Quantization
Post-training Quantization 當然是最泛用,可以直接現有的就直接轉,
但考慮到裡面資料的分布,直接作 Quantization 有可能導致解釋力不足,
(可以設想一個情況,資料分布非常不平均,在 Quantization 後可能導致資料都被過度的集中,變成反應不出原始資料的分布)
Quantization aware training 基本上就是訓練時就邊做 Quantization,
比較麻煩,但出來的結果相對品質會更好 (如果追求 Quantization 後還要高的 accuary,也許就會採取這個策略)
Quantization 基本概念
分成 Symmetric Quantization 和 Asymmetric Quantization (也有中文翻譯作對稱/非對稱)
Symmetric Quantization (對稱)
Symmetric 重點在公式皆滿足
- Quantize 公式:「X_quant = round(X_原始 / scale)」
- 依照下圖公式 (Dequantize):「X_原始 = X_quant * scale」
(這也表示 zero-point 被限制等於 0)
zero-point 正如其名,用來解釋「原始的哪個數值」quant 後會到 0 的位置,
Symmetric Quantization 的好處就是在「我們不用處理 offset, zero-point 等額外的運算」,相對就比較簡化
* Reference: https://arxiv.org/pdf/2106.08295
這裡也分為 unsigned int8 (0-255) 與 signed int8(-128 ~ 127)
不過我們應該是可以先不用管這麼多,基本上原文提到 unsigned 是專門用來處理 ReLU 那種只有單邊數值的數據。
很明顯的缺點就是如果左右的數據一邊大一邊小,比較小那邊的解析度會被另外一邊害到 (因為 0 已經被固定)
Asymmetric Quantization (非對稱,最常見,重點!!!)
- Quantize 公式:「X_quant = round((X_原始 / scale) + zero_point)」
- 依照下圖公式 (Dequantize):「X_原始 = (X_quant – zero_point) * scale」
相比 Symmetric,從整個數據的分布去考量,zero point 不在限制為 0,
因此整個數據的分佈可以更合理!
* Reference: https://arxiv.org/pdf/2106.08295
這邊我們推算一下,
如果 Quantize 公式是 「X_quant = round((X_原始 / scale) + zero_point)」,
原始的最小值,在除以 scale 後加上 zero_point 應該要位於 quant 後最小的位置,也就是 0
所以 0 = round((X_原始 / scale) + zero_point
「X_原始_min = -zs」就這樣推算出來
而我們也可以知道
「zero_point= -round((X_原始 / scale) 」
但這樣好像有點難看,於是又有了一個 offset 的詞,像是把原來的範圍移動的感覺,
因此 offset = round((X_原始 / scale) = -(zero_point)
名詞解釋
zero-point
zero-point 正如其名,用來解釋「原始的哪個數值」quant 後會到 0 的位置,
Symmetric,zero-point 被固定在 0
Asymmetric,zero-point 可以反應在「X_原始 / scale」要加上的偏移量 (移動的感覺)
留意 Asymmetric 公式:「X_quant = round((X_原始 / scale) + zero_point)」
offset
- offset = -(zero-point)
offset,中文是偏移量,但中文不重要我自己多了中文的解釋反而更亂,解釋是用作調整數據範圍「至新的中心點」
說實在我也常被這個詞越搞越亂,現在我也只記他好像是 zero-point 的相反,
然後把 zero-point 定義記好 「原始數據 quant 後 0 的位置!」
然後 X_原始_min = -sz ,這樣 quant 後才會等於 0。
bit-width
有幾個 bit 能用來表示,常見的 b = 8 (bit)
能表示範圍 = 2^8 – 1 = 255 個數值
scale
縮放的比例,因為概念相對直覺,這邊比較晚提到,
通常計算是
scale =(max – min) / (2^b -1)
- max-min 可以算出全部的數值範圍
- 2^b -1 代表著用於表示的全部數字
除完就可以得到每一個間距大概要抓多少了。
dynamic range
直翻可能是動態範圍,不過中文不重要,
這表示範圍太大可能導致解析度差(表示力不足),
簡單想像:「超級大的範圍只用 0-255 表示?」
dynamic range 與 scale 的關係
想一下就會發現 dynamic range 其實跟 scale 息息相關,
因為 scale 大才能反應出大的 dynamic range,
通常可以有個判斷的依據 (但實際還是要是情況而定)
scale > 0.5 (dynamic range 太大了)
scale < 0.01 (dynamic range 很小,有可能還滿精準的)
(警告) 此區對觀念沒把握的不要亂看,是我自己的混淆區,亂看可能跟著混淆
Quantization 與 Normalization?
- Quantization 的結果是離散的 (discrete)
- Normalization 的結果依然是連續的 (continuous)
- Quantization 的目的是讓連續的值,轉換到「有限範圍的離散值」,目標是減少儲存空間、加速運算等
- Normalization 的目的是調整數據的範圍,有可能是轉換到「0-1 或 -1~1」,目的是確保「在不同的維度上有一致性」,也可以是為了權重的相等
- Quantization 的「離散」結果,會產生一些資訊上的誤差
- Normalization 的結果只是比例上的縮放,讓資料範圍保持一致,資訊保持著一定的比例,很少會額外增加誤差
Reference
- https://docs.nvidia.com/deeplearning/tensorrt/tensorflow-quantization-toolkit/docs/docs/intro_to_quantization.html
- https://intellabs.github.io/distiller/algo_quantization.html
- https://chih-sheng-huang821.medium.com/ai%E6%A8%A1%E5%9E%8B%E5%A3%93%E7%B8%AE%E6%8A%80%E8%A1%93-%E9%87%8F%E5%8C%96-quantization-966505128365
- https://arxiv.org/pdf/2106.08295