AI 框架部署方案之模型量化的損失分析

語言: CN / TW / HK

0 前言

在模型實際的業務落地過程中,大家是否遇到過這樣的場景:千辛萬苦訓練出了精度滿足業務需求的模型,終於可以上線啦!經過模型轉換、量化、打包等等一系列複雜的流程後,成功部署到實際硬件平台上,模型終於可以運行了!然而一波操作猛如虎,模型精度與當初訓練結果相比卻下降了不少……一臉茫然,不知所措,完全不知該從何下手,也不知道怎樣才能有效的恢復模型的訓練精度。 本次分享將針對部署流程中導致精度下降的頭號疑犯——模型量化進行分析,帶大家一探精度損失的幕後真相。

歡迎關注開源深度學習推理引擎 - OpenPPL

本文將從以下三個方面進行“揭祕”: - 量化的基本過程 - 量化的損失來源 - 常見芯片的量化支持

1 量化的基本過程

模型量化粗略地可以分為在線量化(Quantization Aware Training, QAT)和離線量化(Post Training Quantization, PTQ)兩大類。 雖然在線量化可以對量化後的模型精度提供更高的保障,但因其與模型的訓練過程過於耦合,開發成本和門檻較高,在模型部署階段採用更多的是另一種方式——離線量化。因此,本文主要針對採用離線量化方式對模型量化後,產生的精度損失進行分析。

圖1 量化的三階段步驟示意圖

神經網絡的量化本質上是將數據從連續空間 [公式] 映射到離散空間 [公式] ,這個過程通常包括三個階段:數據縮放(scale),數據離散化(discretize),數據反向縮放(rescale)。 三個階段之間關係的可以形式化描述如下:

[公式]

輸入數據 [公式] 是來自連續空間 [公式] 的全精度值,即原始的浮點精度表示數據, [公式] 是量化後在離散空間 [公式] 中的對應值,即定點類型的數據。 首先,縮放函數 [公式] 將數據 [公式] 從原始空間 [公式] 的表示範圍縮放到離散空間 [公式] 。 然後,縮放後所得到的數據被離散化為屬於離散空間 [公式] 內的中間值 [公式] ,該階段最常用的離散函數是 [公式]。 最後,因為量化操作只改變數據的表示精度,不更改數據的表示範圍,所以離散化後的中間值 [公式] 需要使用反向縮放函數 [公式] ,重新調整 [公式] 到與 [公式] 在原始空間 [公式] 中相同的表示範圍。在不同的量化方法中,數據縮放函數和數據離散函數各不相同。

當前較為成熟且使用較多的量化機制為線性均勻量化,指縮放函數 [公式] 為線性映射函數,離散空間 [公式] 中的各個離散值之間的距離是均勻的。如使用的是線性量化,原始數據 [公式] 的數值表示範圍為 [公式] ,縮放函數可以表示為:

[公式]

在對稱量化中, [公式] 設置為 0,同時要求 [公式] ;在非對稱量化中,[公式] 會根據實際的數據範圍進行確定,通常會選取 [公式] 

2 量化的損失來源

實際部署過程中,為了獲得更大的壓縮率和更快的運行速度,或受限於部署平台的計算單元類型,模型通常需要對整個網絡模型的權值和激活值均進行定點量化。為了簡化討論,我們以一層卷積作為示例,採用線性均勻量化的方法,並假設 bias 已經通過優化融合到了 conv 的 weight 中。如下圖所示,整層網絡的計算誤差主要來自於三部分:

圖2 量化的誤差位置來源示意圖

  • [公式] 權值數據量化引入的誤差,主要來自於不同量化方法的選取產生的誤差。因 weights 的數值是確定可控的,其他來源的誤差(如取整舍入誤差)可以進行部分修正,屬於可控誤差,此處暫不進行詳細的分析。
  • [公式] 激活值數據量化引入的誤差,主要來自於不同量化方法產生的誤差和數值取整計算帶來的誤差。
  • [公式] 計算過程中數據累計計算的誤差,一方面來自多項數據進行累加和累乘等計算過程中,數值溢出和取整等計算帶來的誤差,另一方面來自定點芯片需要在定點域計算原本應該在浮點域計算的縮放函數和反向縮放函數,產生一定的計算誤差。

為了進一步考慮上述誤差是如何在計算過程中引入的,我們固定量化的比特位數 [公式] 。根據量化的三個計算階段我們可以看到,均勻線性量化過程中涉及到的可能引入誤差的操作主要有三個:Round 操作,Clip 操作,scaling factor 的選取。Round 和 Clip 操作引入的即為前文所指的各類取整和溢出誤差,不同的線性均勻量化方法和優化策略本質上即為不同 scaling factor 的選取。三者對誤差的引入和影響是緊耦合在一起的,互相作用和影響,需要進行一定的 trade-off。

Round 操作實現數據的離散化,將原始連續空間的數值映射到距離它數值最近的離散點。原始數據 [公式] 和其映射後所對應的離散值 [公式] 之間的差值 [公式] 即為 Round 操作引入的量化誤差。我們可以將誤差項近似看做服從均值為 0 的正態分佈,則有:

[公式]

對於每一部分的量化誤差, 其最大值不會超過兩個相鄰的量化離散值的間距,根據公式可知離散量化值之間的間距由原始數據的動態表示範圍 [公式] 決定。在 [公式] 固定的情況下,動態表示範圍越大,由 Round 操作引發的量化損失也就越大。

Clip 操作 [公式] 外的數據進行截取,使得所有的數據都在動態表示範圍內。這些超出範圍的數據 [公式] 需要截取映射到 [公式] 或者 [公式] , 之間的差值即為 Clip 操作引入的量化誤差。

[公式]

我們來看一個服從正態分佈的浮點數據進行線性均勻量化的過程示意:

圖3 浮點數據離散化示意圖

在圖3(a) 中,我們選取原始數據的真實最小值和最大值作為動態表示範圍 [公式] ,因此不存在超出範圍的數值點,即 [公式] ;因動態表示範圍過大,各個離散數據值之間的距離較大,會導致 [公式] 偏大。在圖3(b) 中,我們選取了較為合適的動態表示範圍來儘可能減小 [公式] ,使得較多的數據分佈在 [公式] 外圍,這些數據會引入較大的 [公式] ,在長尾分佈中引入的誤差尤為明顯。當前不同的量化算法和優化策略往往是尋找一個恰當的 [公式] ,使得 [公式] 較小。

Scaling factor 和動態表示範圍在不同的量化方法中有着不同的計算方法,例如在線性均勻量化中,二者之間的關係為:

[公式]

通常確定了二者中的一個,另外一個可以根據公式計算得來。除了上述提到的 [公式]  [公式] , scaling facor 引入的量化誤差主要來源於各個離散數值利用率的平衡性。例如在圖3(a) 中,動態範圍選取較大帶來的另外一個弊端,即為離散數值利用率不均衡的問題。對於 [公式] 的量化,離散數值共有 [公式] 種選擇(在部分量化策略中會強制保留 [公式] 表示數值 0,則離散數值的選擇剩餘 [公式] 種)。神經網絡的原始浮點數據通常服從長尾正態分佈,大部分的數值分佈在均值附近,量化後映射到 0 值附近;小部分數值會分佈在遠離均值的位置,帶有一定信息量無法完全直接捨棄。較大的動態範圍使得離散數值之間的間距較大,大部分分佈在均值附近的數據只能映射到少量的離散值上,導致 [公式] 的實際表示能力會損失 [公式] 甚至更多。 如何平衡各個離散數值之間的數據分佈和利用率,是當前一些量化策略減少量化損失的方向。

3 常見芯片的量化支持

需要進行量化的模型,其部署目標平台多為邊緣側/終端側,即對模型的大小和運行速度有一定限制和要求的移動端和嵌入式設備。較為常用的典型硬件平台有 NVIDIA Jetson 系列 GPU、Qualcomm Hexagon 系列 DSP、以及新興的神經網絡加速器。在加速器系列中,我們選取寒武紀(Cambricon) 的推理芯片為例,各類硬件平台對量化的支持情況如下表所示:

硬件平台 後端推理庫 權值數據量化支持類型 是否支持外部參數寫入
NVIDIA Jetson [1] TensorRT per-tensor & per-channel Yes
Quanlcomm Hexagon DSP [2] SNPE per-tensor No
Cambricon MLU [3] CNRT per-tensor & per-channel Yes

其中 per-tensor 的量化方法也稱 per-layer,指對於給定的網絡層的 weight tensor 中的所有數據共享一個量化參數;per-channel 也稱 per-axis,指對於給定網絡層的 weight tensor,每一個 axis 對應一個 channel,每個 channel 有自己獨立的量化參數。通常情況下,per-channel 因為量化的粒度更細緻,量化參數的自由度更高,往往更優於 per-tensor 的量化精度。 NVIDIA TensorRT 在對權值(weights) 的量化上支持 per-tensor 和 per-channel 兩種方式,採用對稱最大值的方法[4];對於激活值(activations) 只支持 per-tensor 的方式,採用 KL-divergence 的方法進行量化;量化參數既支持內部自動生成,也支持外部指定,支持 weights 和 activations 採用不同的指定方式。更為詳細的機制解析和使用方式已有官方技術文檔[5]和很多文章[6]進行了分析講解,感興趣的同學可以自行閲讀,此處不再贅述。 Quanlcomm Hexagon DSP 的官方後端推理庫 SNPE 只支持 per-tensor 的量化方式,採用非對稱最大最小值的方法[7],並提供 Enhanced Quantization Mode 進行精度優化,暫不支持外部量化參數寫入。為了補償SNPE在使用時的量化精度損失,Quanlcomm 推出了自己的模型量化壓縮工具 AIMET[8],支持 DFQ[9]、AdaRound[10]等量化算法,依託於 AIMET 也可以實現部分外部量化參數的寫入。通過對AIMET源碼的閲讀,我們也可以獲得如何把量化參數寫入SNPE後端的方法。寒武紀 MLU 系列加速器的官方後端為 CNRT,同樣支持 per-tensor 和 per-channel 兩種量化方法,也支持外部參數的寫入,但只支持 weights 和 activations 採用同樣的指定方式,即同時由內部生成或同時由外部指定。我們可以看到,當前的硬件平台除了自身的工具鏈提供的原生量化方法外,大部分均支持外部參數的寫入,通過指定 scaling_factor 或動態範圍 [公式] 二者之一,另外一個在內部通過計算得到。當原生量化工具無法滿足模型部署精度需求時,我們可以通過誤差分析,自行調整量化參數,從而達到恢復精度的目的。

4 結語

本文對量化過程中的誤差來源進行了粗略的歸類,理解這些誤差如何在計算過程中引入,並對各個誤差項之間的耦合和影響關係進行了分析。當前很多量化算法和優化策略本質上還是在尋找一些減小這些誤差項的方法,並在其中尋找平衡點。例如,AdaRound 算法不再採用四捨五入的方法,而是採用一種自使用的策略動態決定向上取整還是向下取整,從而減小 Round 操作引入的誤差項 [公式] ;DFQ 算法通過計算恆等性修改權值數據的分佈,減少長尾部分的數據,從而減少Clip操作引入的誤差項 [公式] ,同時也可以有效降低 [公式] 和離散值利用率低的問題;ACIQ[11] 算法直接通過對優化問題求解,得到最優的動態範圍,從而降低上述誤差項,等等。本文對量化誤差背後的基本原理進行了介紹,關於如何評估哪類誤差對整個網絡最終的結果起到了更大的影響,如何根據損失去選擇合適的量化算法等問題,歡迎持續關注 AI 框架技術分享模型部署專題系列。