深度學習模型輕量化(上)

深度學習模型輕量化(上)

移動端模型必須滿足模型尺寸小、計算複雜度低、電池耗電量低、下發更新部署靈活等條件。

模型壓縮和加速是兩個不同的話題,有時候壓縮並不一定能帶來加速的效果,有時候又是相輔相成的。壓縮重點在於減少網絡參數量,加速則側重在降低計算複雜度、提升並行能力等。模型壓縮和加速可以從多個角度來優化。總體來看,個人認爲主要分爲三個層次:

  1. 算法層壓縮加速。這個維度主要在算法應用層,也是大多數算法工程師的工作範疇。主要包括結構優化(如矩陣分解、分組卷積、小卷積核等)、量化與定點化、模型剪枝、模型蒸餾等。

  2. 框架層加速。這個維度主要在算法框架層,比如tf-lite、NCNN、MNN等。主要包括編譯優化、緩存優化、稀疏存儲和計算、NEON指令應用、算子優化等

  3. 硬件層加速。這個維度主要在AI硬件芯片層,目前有GPU、FPGA、ASIC等多種方案,各種TPU、NPU就是ASIC這種方案,通過專門爲深度學習進行芯片定製,大大加速模型運行速度。

下面也會分算法層、框架層和硬件層三個方面進行介紹。

2 算法層壓縮加速

2.1 結構優化

2.1.1 矩陣分解

舉個例子,將MN的矩陣分解爲MK + K*N,只要讓K<<M 且 K << N,就可以大大降低模型體積。比如在ALBERT的embedding層,就做了矩陣分解的優化。如下圖所示

在這裏插入圖片描述

其中M爲詞表長度,也就是vocab_size,典型值爲21128。N爲隱層大小,典型值爲1024,也就是hidden_size。K爲我們設置的低維詞嵌入空間,可以設置爲128。

  1. 分解前:矩陣參數量爲 (M * N)

  2. 分解後:參數量爲 (MK + KN)

  3. 壓縮量:(M * N) / (MK + KN), 由於M遠大於N,故可近似爲 N / k,當N=2014,k=128時,可以壓縮8倍

2.1.2 權值共享

相對於DNN全連接參數量過大的問題,CNN提出了局部感受野和權值共享的概念。在NLP中同樣也有類似應用的場景。比如ALBert中,12層共用同一套參數,包括multi-head self attention和feed-forward,從而使得參數量降低到原來的1/12。這個方案對於模型壓縮作用很大,但對於推理加速則收效甚微。因爲共享權值並沒有帶來計算量的減少。

2.1.3 分組卷積

在視覺模型中應用較爲廣泛,比如shuffleNet,mobileNet等。我們以mobileNet爲例。對於常規的M輸入通道,N輸出通道,dkdk的kernel
size的卷積,需要參數量爲 M
Ndkdk。這是因爲每個輸入通道,都會抽取N種特徵(對應輸出通道數),不同的輸入通道需要不同的kernel來做抽取,然後疊加起來。故M個輸入通道,N個輸出通道,就需要M*N個kernel了。

mobileNet對常規卷積做了優化,每個輸入通道,僅需要一個kernel做特徵提取,這叫做depth wise。如此M個通道可得到M個feature map。但我們想要的是N通道輸出,怎麼辦呢?mobileNet採用一個常規11卷積來處理這個連接,從而轉化到N個輸出通道上。總結下來,mobileNet利用一個dkdk的depth wise卷積和一個1*1的point wise卷積來實現一個常規卷積。

  1. 分組前:參數量 (MNdk*dk)

  2. 分組後:參數量 (Mdkdk + MN1*1)

  3. 壓縮量:(Mdkdk + MN11) / (MNdkdk),近似爲
    1/(dkdk)。dk的常見值爲3,也就是33卷積,故可縮小約9倍

如下圖所示:
在這裏插入圖片描述

2.1.4 分解卷積

  1. 使用兩個串聯小卷積核來代替一個大卷積核。InceptionV2中創造性的提出了兩個3x3的卷積核代替一個5x5的卷積核。在效果相同的情況下,參數量僅爲原先的 3x3x2 / 5x5 =
    18/25

  2. 使用兩個並聯的非對稱卷積核來代替一個正常卷積核。InceptionV3中將一個7x7的卷積拆分成了一個1x7和一個7x1, 卷積效果相同的情況下,大大減少了參數量,同時還提高了卷積的多樣性。

2.1.5 其他

  1. 全局平均池化代替全連接層。這個纔是大殺器!AlexNet和VGGNet中,全連接層幾乎佔據了90%的參數量。inceptionV1創造性的使用全局平均池化來代替最後的全連接層,使得其在網絡結構更深的情況下(22層,AlexNet僅8層),參數量只有500萬,僅爲AlexNet的1/12

  2. 1x1卷積核的使用。1x1的卷積核可以說是性價比最高的卷積了,沒有之一。它在參數量爲1的情況下,同樣能夠提供線性變換,relu激活,輸入輸出channel變換等功能。VGGNet創造性的提出了1x1的卷積核

  3. 使用小卷積核來代替大卷積核。VGGNet全部使用3x3的小卷積核,來代替AlexNet中11x11和5x5等大卷積核。小卷積核雖然參數量較少,但也會帶來特徵面積捕獲過小的問題。inception net認爲越往後的卷積層,應該捕獲更多更高階的抽象特徵。因此它在靠後的卷積層中使用的5x5等大面積的卷積核的比率較高,而在前面幾層卷積中,更多使用的是1x1和3x3的卷積核。

2.2 量化

2.2.1 僞量化

深度學習模型參數通常是32bit浮點型,我們能否使用16bit,8bit,甚至1bit來存儲呢?答案是肯定的。常見的做法是保存模型每一層時,利用低精度來保存每一個網絡參數,同時保存拉伸比例scale和零值對應的浮點數zero_point。推理階段,利用如下公式來網絡參數還原爲32bit浮點:

在這裏插入圖片描述

這個過程被稱爲僞量化。

僞量化之所以得名,是因爲存儲時使用了低精度進行量化,但推理時會還原爲正常高精度。爲什麼推理時不仍然使用低精度呢?這是因爲一方面框架層有些算子只支持浮點運算,需要專門實現算子定點化才行。另一方面,高精度推理準確率相對高一些。僞量化可以實現模型壓縮,但對模型加速沒有多大效果。

2.2.2 聚類與僞量化

一種實現僞量化的方案是,利用k-means等聚類算法,步驟如下:

  1. 將大小相近的參數聚在一起,分爲一類。

  2. 每一類計算參數的平均值,作爲它們量化後對應的值。

  3. 每一類參數存儲時,只存儲它們的聚類索引。索引和真實值(也就是類內平均值)保存在另外一張表中

  4. 推理時,利用索引和映射表,恢復爲真實值。

過程如下圖所示,

從上可見,當只需要4個類時,我們僅需要2bit就可以實現每個參數的存儲了,壓縮量達到16倍。推理時通過查找表恢復爲浮點值,精度損失可控。結合霍夫曼編碼,可進一步優化存儲空間。一般來說,當聚類數爲N時,我們壓縮量爲 log(N) / 32。

2.2.3 定點化

與僞量化不同的是,定點化在推理時,不需要還原爲浮點數。這需要框架實現算子的定點化運算支持。目前MNN、XNN等移動端AI框架中,均加入了定點化支持。

2.3 剪枝

2.3.1 剪枝流程

剪枝歸納起來就是取其精華去其糟粕。按照剪枝粒度可分爲突觸剪枝、神經元剪枝、權重矩陣剪枝等。總體思想是,將權重矩陣中不重要的參數設置爲0,結合稀疏矩陣來進行存儲和計算。通常爲了保證performance,需要一小步一小步地進行迭代剪枝。步子大了,容易那個啥的,大家都懂的哈。

常見迭代剪枝流程如下圖所示

在這裏插入圖片描述

從上可見,當只需要4個類時,我們僅需要2bit就可以實現每個參數的存儲了,壓縮量達到16倍。推理時通過查找表恢復爲浮點值,精度損失可控。結合霍夫曼編碼,可進一步優化存儲空間。一般來說,當聚類數爲N時,我們壓縮量爲 log(N) / 32。

2.2.3 定點化

與僞量化不同的是,定點化在推理時,不需要還原爲浮點數。這需要框架實現算子的定點化運算支持。目前MNN、XNN等移動端AI框架中,均加入了定點化支持。

2.3 剪枝

2.3.1 剪枝流程

剪枝歸納起來就是取其精華去其糟粕。按照剪枝粒度可分爲突觸剪枝、神經元剪枝、權重矩陣剪枝等。總體思想是,將權重矩陣中不重要的參數設置爲0,結合稀疏矩陣來進行存儲和計算。通常爲了保證performance,需要一小步一小步地進行迭代剪枝。步子大了,容易那個啥的,大家都懂的哈。

常見迭代剪枝流程如下圖所示

在這裏插入圖片描述

  1. 訓練一個performance較好的大模型。

  2. 評估模型中參數的重要性。常用的評估方法是,越接近0的參數越不重要。當然還有其他一些評估方法,這一塊也是目前剪枝研究的熱點。

  3. 將不重要的參數去掉,或者說是設置爲0。之後可以通過稀疏矩陣進行存儲。比如只存儲非零元素的index和value。

  4. 訓練集上微調,從而使得由於去掉了部分參數導致的performance下降能夠儘量調整回來。

  5. 驗證模型大小和performance是否達到了預期,如果沒有,則繼續迭代進行。

2.3.2 突觸剪枝

在這裏插入圖片描述

突觸剪枝剪掉神經元之間的不重要的連接。對應到權重矩陣中,相當於將某個參數設置爲0。常見的做法是,按照數值大小對參數進行排序,將大小排名最後的k%置零即可,k%爲壓縮率。具體流程可以參考下面的圖例:

剪枝後

在這裏插入圖片描述

2.3.3 神經元剪枝
在這裏插入圖片描述

神經元剪枝則直接將某個節點直接去掉。對應到權重矩陣中,相當於某一行和某一列置零。常見做法是,計算神經元對應的一行和一列參數的平方和的根,對神經元進行重要性排序,將大小排名最後的k%置零。具體流程可以參考下面的圖例:

在這裏插入圖片描述

剪枝後

在這裏插入圖片描述

2.3.4 權重矩陣剪枝

除了將權重矩陣中某些零散的參數,或者整行整列去掉外,我們能否將整個權重矩陣去掉呢?答案是肯定的,目前也有很多這方面的研究。NeurIPS 2019有篇文章,Are Sixteen Heads Really
Better than One?,深入分析了BERT多頭機制中每個頭到底有多大用,結果發現很多頭其實沒啥卵用。他在要去掉的head上,加入mask,來做每個頭的重要性分析。

作者先分析了單獨去掉每層每個頭,WMT任務上BLEU的改變。發現,大多數head去掉後,對整體影響不大。如下圖所示

在這裏插入圖片描述

然後作者分析了,每層只保留一個最重要的head後,ACC的變化。可見很多層只保留一個head,performance影響不大。如下圖所示

在這裏插入圖片描述

由此可見,直接進行權重矩陣剪枝,也是可行的方案。相比突觸剪枝和神經元剪枝,壓縮率要大很多。

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