close

點擊下方卡片,關注「新機器視覺」公眾號

視覺/圖像重磅乾貨,第一時間送達


作者:知乎-揚易(@xieyangyi) 簡楓 千瞳

地址:https://zhuanlan.zhihu.com/p/138059904

整理:人工智能前沿講習

本文僅作學術交流,如有侵權,請聯繫刪文



前言

近年來深度學習模型在計算機視覺、自然語言處理、搜索推薦廣告等各種領域,不斷刷新傳統模型性能,並得到了廣泛應用。隨着移動端設備計算能力的不斷提升,移動端AI落地也成為了可能。相比於服務端,移動端模型的優勢有:

減輕服務端計算壓力,並利用雲端一體化實現負載均衡。特別是在雙11等大促場景,服務端需要部署很多高性能機器,才能應對用戶流量洪峰。平時用戶訪問又沒那麼集中,存在巨大的流量不均衡問題。直接將模型部署到移動端,並在置信度較高情況下直接返回結果,而不需要請求服務端,可以大大節省服務端計算資源。同時在大促期間降低置信度閾值,平時又調高,可以充分實現雲端一體負載均衡。
實時性好,響應速度快。在feed流推薦和物體實時檢測等場景,需要根據用戶數據的變化,進行實時計算推理。如果是採用服務端方案,則響應速度得不到保障,且易造成請求過於密集的問題。利用端計算能力,則可以實現實時計算。
穩定性高,可靠性好。在斷網或者弱網情況下,請求服務端會出現失敗。而採用端計算,則不會出現這種情況。在無人車和自動駕駛等可靠性要求很高的場景下,這一點尤為關鍵,可以保證在隧道、山區等場景下仍能穩定運行。
安全性高,用戶隱私保護好。由於直接在端上做推理,不需要將用戶數據傳輸到服務端,免去了網絡通信中用戶隱私泄露風險,也規避了服務端隱私泄露問題
移動端部署深度學習模型也有很大的挑戰。主要表現在,移動端等嵌入式設備,在計算能力、存儲資源、電池電量等方面均是受限的。故移動端模型必須滿足模型尺寸小、計算複雜度低、電池耗電量低、下發更新部署靈活等條件。因此模型壓縮和加速就成為了目前移動端AI的一個熱門話題。
模型壓縮和加速不僅僅可以提升移動端模型性能,在服務端也可以大大加快推理響應速度,並減少服務器資源消耗,大大降低成本。結合移動端AI模型和服務端模型,實現雲端一體化,是目前越來越廣泛採用的方案。
模型壓縮和加速是兩個不同的話題,有時候壓縮並不一定能帶來加速的效果,有時候又是相輔相成的。壓縮重點在於減少網絡參數量,加速則側重在降低計算複雜度、提升並行能力等。模型壓縮和加速可以從多個角度來優化。總體來看,個人認為主要分為三個層次:
算法層壓縮加速。這個維度主要在算法應用層,也是大多數算法工程師的工作範疇。主要包括結構優化(如矩陣分解、分組卷積、小卷積核等)、量化與定點化、模型剪枝、模型蒸餾等。
框架層加速。這個維度主要在算法框架層,比如tf-lite、NCNN、MNN等。主要包括編譯優化、緩存優化、稀疏存儲和計算、NEON指令應用、算子優化等
硬件層加速。這個維度主要在AI硬件芯片層,目前有GPU、FPGA、ASIC等多種方案,各種TPU、NPU就是ASIC這種方案,通過專門為深度學習進行芯片定製,大大加速模型運行速度。
下面也會分算法層、框架層和硬件層三個方面進行介紹。


算法層壓縮加速



2.1 結構優化
2.1.1 矩陣分解
舉個例子,將M*N的矩陣分解為M*K + K*N,只要讓K<<M 且 K << N,就可以大大降低模型體積。比如在ALBERT的embedding層,就做了矩陣分解的優化。如下圖所示
其中M為詞表長度,也就是vocab_size,典型值為21128。N為隱層大小,典型值為1024,也就是hidden_size。K為我們設置的低維詞嵌入空間,可以設置為128。
分解前:矩陣參數量為(M * N)
分解後:參數量為(M*K + K*N)
壓縮量:(M * N) / (M*K + K*N), 由於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輸出通道,dk*dk的kernel size的卷積,需要參數量為 M*N*dk*dk。這是因為每個輸入通道,都會抽取N種特徵(對應輸出通道數),不同的輸入通道需要不同的kernel來做抽取,然後疊加起來。故M個輸入通道,N個輸出通道,就需要M*N個kernel了。
mobileNet對常規卷積做了優化,每個輸入通道,僅需要一個kernel做特徵提取,這叫做depth wise。如此M個通道可得到M個feature map。但我們想要的是N通道輸出,怎麼辦呢?mobileNet採用一個常規1*1卷積來處理這個連接,從而轉化到N個輸出通道上。總結下來,mobileNet利用一個dk*dk的depth wise卷積和一個1*1的point wise卷積來實現一個常規卷積。
分組前:參數量(M*N*dk*dk)
分組後:參數量(M*dk*dk + M*N*1*1)
壓縮量:(M*dk*dk + M*N*1*1) / (M*N*dk*dk),近似為1/(dk*dk)。dk的常見值為3,也就是3*3卷積,故可縮小約9倍
如下圖所示:
2.1.4 分解卷積
使用兩個串聯小卷積核來代替一個大卷積核。InceptionV2中創造性的提出了兩個3x3的卷積核代替一個5x5的卷積核。在效果相同的情況下,參數量僅為原先的 3x3x2 / 5x5 = 18/25
使用兩個並聯的非對稱卷積核來代替一個正常卷積核。InceptionV3中將一個7x7的卷積拆分成了一個1x7和一個7x1, 卷積效果相同的情況下,大大減少了參數量,同時還提高了卷積的多樣性。
2.1.5 其他
全局平均池化代替全連接層。這個才是大殺器!AlexNet和VGGNet中,全連接層幾乎占據了90%的參數量。inceptionV1創造性的使用全局平均池化來代替最後的全連接層,使得其在網絡結構更深的情況下(22層,AlexNet僅8層),參數量只有500萬,僅為AlexNet的1/12
1x1卷積核的使用。1x1的卷積核可以說是性價比最高的卷積了,沒有之一。它在參數量為1的情況下,同樣能夠提供線性變換,relu激活,輸入輸出channel變換等功能。VGGNet創造性的提出了1x1的卷積核
使用小卷積核來代替大卷積核。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等聚類算法,步驟如下:
將大小相近的參數聚在一起,分為一類。
每一類計算參數的平均值,作為它們量化後對應的值。
每一類參數存儲時,只存儲它們的聚類索引。索引和真實值(也就是類內平均值)保存在另外一張表中
推理時,利用索引和映射表,恢復為真實值。
過程如下圖所示,
從上可見,當只需要4個類時,我們僅需要2bit就可以實現每個參數的存儲了,壓縮量達到16倍。推理時通過查找表恢復為浮點值,精度損失可控。結合霍夫曼編碼,可進一步優化存儲空間。一般來說,當聚類數為N時,我們壓縮量為log(N)/ 32。
2.2.3 定點化
與偽量化不同的是,定點化在推理時,不需要還原為浮點數。這需要框架實現算子的定點化運算支持。目前MNN、XNN等移動端AI框架中,均加入了定點化支持。
2.3 剪枝
2.3.1 剪枝流程
剪枝歸納起來就是取其精華去其糟粕。按照剪枝粒度可分為突觸剪枝、神經元剪枝、權重矩陣剪枝等。總體思想是,將權重矩陣中不重要的參數設置為0,結合稀疏矩陣來進行存儲和計算。通常為了保證performance,需要一小步一小步地進行迭代剪枝。步子大了,容易那個啥的,大家都懂的哈。
常見迭代剪枝流程如下圖所示
訓練一個performance較好的大模型。
評估模型中參數的重要性。常用的評估方法是,越接近0的參數越不重要。當然還有其他一些評估方法,這一塊也是目前剪枝研究的熱點。
將不重要的參數去掉,或者說是設置為0。之後可以通過稀疏矩陣進行存儲。比如只存儲非零元素的index和value。
訓練集上微調,從而使得由於去掉了部分參數導致的performance下降能夠儘量調整回來。
驗證模型大小和performance是否達到了預期,如果沒有,則繼續迭代進行。
2.3.2 突觸剪枝
突觸剪枝剪掉神經元之間的不重要的連接。對應到權重矩陣中,相當於將某個參數設置為0。常見的做法是,按照數值大小對參數進行排序,將大小排名最後的k%置零即可,k%為壓縮率。具體流程可以參考下面的圖例:
2.3.3 神經元剪枝
神經元剪枝則直接將某個節點直接去掉。對應到權重矩陣中,相當於某一行和某一列置零。常見做法是,計算神經元對應的一行和一列參數的平方和的根,對神經元進行重要性排序,將大小排名最後的k%置零。具體流程可以參考下面的圖例:
2.3.4 權重矩陣剪枝
除了將權重矩陣中某些零散的參數,或者整行整列去掉外,我們能否將整個權重矩陣去掉呢?答案是肯定的,目前也有很多這方面的研究。NeurIPS 2019有篇文章,Are Sixteen Heads Really Better than One?(https://arxiv.org/abs/1905.10650),深入分析了BERT多頭機制中每個頭到底有多大用,結果發現很多頭其實沒啥卵用。他在要去掉的head上,加入mask,來做每個頭的重要性分析。
作者先分析了單獨去掉每層每個頭,WMT任務上BLEU的改變。發現,大多數head去掉後,對整體影響不大。如下圖所示
然後作者分析了,每層只保留一個最重要的head後,ACC的變化。可見很多層只保留一個head,performance影響不大。如下圖所示
由此可見,直接進行權重矩陣剪枝,也是可行的方案。相比突觸剪枝和神經元剪枝,壓縮率要大很多。
2.4 蒸餾
2.4.1 蒸餾流程
蒸餾本質是student對teacher的擬合,從teacher中汲取養分,學到知識,不僅僅可以用到模型壓縮和加速中。蒸餾常見流程如下圖所示
老師和學生可以是不同的網絡結構,比如BERT蒸餾到BiLSTM網絡。但一般相似網絡結構,蒸餾效果會更好。
總體loss為 soft_label_loss + hard_label_loss。soft_label_loss可以用KL散度或MSE擬合
soft label為teacher模型的要擬合的對象。可以是模型預測輸出,也可以是embeddings, 或者hidden layer和attention分布。
針對軟標籤的定義,蒸餾的方案也是百花齊放,下面分享兩篇個人認為非常經典的文章。
2.4.2 distillBERT
DistillBERT: A distilled version of BERT: smaller, faster, cheaper and lighter(https://arxiv.org/abs/1910.01108)
DistillBERT由大名鼎鼎的HuggingFace出品。主要創新點為:
Teacher 12層,student 6層,每兩層去掉一層。比如student第二層對應teacher第三層
Loss= 5.0 * Lce+2.0 * Lmlm+1.0 * Lcos
Lce:soft_label 的KL散度
Lmlm:mask LM hard_label 的交叉熵
Lcos:hidden state 的餘弦相似度
DistilBERT 比 BERT 快 60%,體積比 BERT 小 60%。在glue任務上,保留了 95% 以上的性能。在performance損失很小的情況下,帶來了較大的模型壓縮和加速效果。
2.4.3 TinyBERT
TinyBERT: Distilling BERT for Natural Language Understanding(https://arxiv.org/abs/1909.10351)
總體結構
重點來看下 TinyBERT,它是由華為出品,非常值得深入研究。TinyBERT 對 embedding 層,transformer層(包括hidden layer和attention)和 prediction 層均進行了擬合。如下圖所示。
TinyBERT 蒸餾過程
其中Embeddings採用MSE, Prediction採用KL散度, Transformer層的hidden layer和attention,均採用MSE。loss如下
其中m為層數。
效果分析
表2: glue任務上的performance。在glue任務上,可達到bert-base的96%,幾乎無損失。表3: tinyBERT模型大小和推理速度。縮小7.5倍,加速9.4倍。壓縮和加速效果十分明顯。
消融分析
表6:分析embedding、prediction、attention、hidden layer軟標籤作用,其中attention和hidden layer作用最大。這個也很好理解,transformer層本來就是整個BERT中最關鍵的部分。
表7:分析老師學生不同層對應方法的效果,uniform為隔層對應,top為全部對應老師頂部幾層,bottom為全部對應老師底部幾層。Uniform效果明顯好很多。這個也很好理解,淺層可以捕捉低階特徵,深層可以捕捉高階特徵。全是低階或者高階顯然不合適,我們要儘量葷素搭配。


框架層加速


3.1 手機端AI能力
目前移動端AI框架也比較多,包括谷歌的tf-lite,騰訊的NCNN,阿里的MNN,百度的PaddleLite, 小米的MACE等。他們都不同程度的進行了模型壓縮和加速的支持。特別是端上推理的加速。這個可以參考「手機端AI性能排名「(http://ai-benchmark.com/ranking.html)。
3.2 端側AI框架加速優化方法
個人總結的主要方法如下,可能有遺漏哈,各位看官請輕拍:
基於基本的C++編譯器優化。
打開編譯器的優化選項,選擇O2等加速選項。
小函數內聯,概率大分支優先,避免除法,查表空間換時間,函數參數不超過4個等。
利用C,而不是C++,C++有不少冗餘的東西。
緩存優化
小塊內存反覆使用,提升cache命中率,儘量減少內存申請。比如上一層計算完後,接着用作下一層計算。
連續訪問,內存連續訪問有利於一次同時取數,相近位置cache命中概率更高。比如縱向訪問數組時,可以考慮轉置後變為橫向訪問。
對齊訪問,比如224*224的尺寸,補齊為256*224,從而提高緩存命中率。
緩存預取,CPU計算的時候,preload後面的數據到cache中。
多線程
為循環分配線程。
動態調度,某個子循環過慢的時候,調度一部分循環到其他線程中。
稀疏化
稀疏索引和存儲方案,採用eigen的sparseMatrix方案。
內存復用和提前申請
掃描整個網絡,計算每層網絡內存復用的情況下,最低的內存消耗。推理剛開始的時候就提前申請好。避免推理過程中反覆申請和釋放內存,避免推理過程中因為內存不足而失敗,復用提升內存訪問效率和cache命中率。
ARM NEON指令的使用,和ARM的深度融合。NEON可以單指令多取值(SIMD),感興趣可針對學習,這一塊水也很深。
手工匯編,畢竟機器編譯出來的代碼還是有不少冗餘的。可以針對運行頻次特別高的代碼進行手工匯編優化。當然如果你匯編功底驚天地泣鬼神的強,也可以全方位手工匯編。
算子支持:比如支持GPU加速,支持定點化等。有時候需要重新開發端側的算子。


硬件層加速


硬件層加速比較硬核,小編就連半瓢水都達不到了,為了保證整個方案的全面性,還是硬着頭皮東施效顰下。目前AI芯片廠家也是百花齊放,誰都想插一腳,不少互聯網公司也來趕集,如下圖所示。
AI 芯片目前三種方案。GPU目前被英偉達和AMD牢牢把控。ASIC目前最火,TPU、NPU等屬於ASIC範疇。


總結


這篇文章我們對深度學習模型壓縮和加速的幾類常用的方法進行了介紹,如果有讀者對模型壓縮加速也感覺興趣的話,歡迎一起來討論。

參考文獻
1.https://arxiv.org/abs/1909.11942
2.https://arxiv.org/abs/1704.04861
3.https://arxiv.org/abs/1905.10650
4.https://arxiv.org/abs/1910.01108
5.https://arxiv.org/abs/1910.01108
6.http://ai-benchmark.com/ranking.html

本文僅做學術分享,如有侵權,請聯繫刪文。

—THE END—
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()