讓深度學習進入移動端,蘑菇街在移動端的深度學習優化實踐

http://777n.com/keji/50566.html

http://777n.com/keji/50566.html

http://777n.com/keji/50566.html

深度學習是機器學習中一種基於對數據進行表徵學習的方法,與傳統靠手工設計特徵的機器學習算法不同,深度學習能根據不同任務自動學習數據的特徵。

目前深度學習在語音、圖像、視頻處理上已經取得了令人印象深刻的進步,但是它通常需要功能強大的電腦纔可以運行,如果它出現在我們的手機上呢?

2017 年 12 月 1 日-2 日,由 51CTO 主辦的 WOTD 全球軟件開發技術峯會在深圳中州萬豪酒店隆重舉行。

本次峯會以軟件開發爲主題,黃文波先生在軟件性能優化專場與來賓分享"深度學習在移動端的優化實踐"的主題演講,爲大家詳細闡述深度學習模型在移動端的設計和優化策略等問題。

討論涉及到如下三個方面:

  • 爲什麼做深度學習的優化

  • 深度學習在移動端的優化實踐

  • 總結

爲什麼做深度學習的優化?

深度學習近年來雖然特別火,但是由於計算量巨大,其對應的模型動輒就有上百兆。

要想把深度學習放在只能分配出幾十兆空間給單個 App 的手機上,我們就需要從算法層面上極致地縮小其模型。

這兩年來,深度學習的發展趨勢是將大部分應用都放置到雲端,而使用的設備一般是 GPU。

但是如果真要實現 AI,單靠雲端的算法是遠遠不夠的,因爲在一些應用的場景中,計算必須在本地進行。

例如:蘋果的 Face ID,如果僅放在雲端,那麼一旦手機沒有了信號,用戶豈不是無法使用手機了?

同理,無人駕駛需要及時響應外部環境,包括識別車外的人、交通燈等,那麼如果網絡發生了延遲,豈不是會發生交通事故?因此,許多應用都必須將計算放置在本地。

 

蘑菇街爲什麼會做深度學習的優化?主要原因有如下幾點:

  • 服務器:我們通過減少訓練、預測的時間,來縮小模型。節約 GPU 資源和省電,這對於深度學習來說是非常重要的因素。

    例如 Alpha GO 下一盤棋,需要 1920 個 CPU 到 280 多個 GPU,其耗費的電費約爲 3000 美元。同樣,蘑菇街就算使用的是 GPU,其電費也有上萬元。

  • 移動端:實時響應需求。通過本地化運行處理,我們不需要將圖片傳到服務器上,也不會侵犯用戶的隱私。

CNN(卷積神經網絡)基礎

 

深度學習對於圖像處理的理念是:經過層層濾波與篩選,最終得到結果。

它的典型流程爲:輸入(INPUT)經過卷積層(CONV)和激活層(RELU)的特徵提取,再經過池化層(Polling)的壓縮與降維,最後由全連接層(FC)連接所有的特徵,並輸出圖像歸屬類型的概率。

如上圖所示左邊的圖片經過了多層處理後,最後得出它屬於 car 的概率最高,因此我們可以認爲它是一輛車。

該示例只有 10 層左右,而在實際場景中,層數會更多,甚至能達到 100 多層。

對於上方簡易示圖的識別,實際上它經歷了 CNN 的成百上千萬次計算,才最終得到結果。

深度學習應用挑戰

深度學習領域的發展趨勢是:隨着其網絡日漸加深和大數據驅動所導致的數據激增,訓練結果的準確率也會越來越高。

伴隨着網絡越來越深,會出現一個問題:深度學習模型越來越大,用於計算所需耗費的資源也就越來越多。

與此同時,由於手機不像服務器可以使用性能強大的 GPU,因此手機的計算性能受到了限制。

另外,由於手機上 CPU 和電池容量暫時無法被突破,其功耗也會相應地受到限制。

深度學習在移動端的優化實踐

模型壓縮的兩類方式

將深度學習放置到手機上,可以從兩個方面入手:

  • 模型壓縮,現有的模型一般具有 100M~200M,其準確率非常高。因此我們在拿到模型後,需要進行壓縮。

  • 設計網絡,將網絡設計得非常小,同時保證網絡具有很強的表達能力。

在算法層面對模型的壓縮主要採取了三種方式:

  • 剪枝(Pruning)

  • 量化(Quantization)

  • 霍夫曼編碼(Huffman Encoding)

 

剪枝的方法較爲直觀,它的思想是:在訓練神經網絡時,每個神經元會有一個權重,而權重具有大小之分。其中權重小的表示對最終結果的影響力非常小。

因此在 2015 年剛開始研究時,有人提出在不會影響到最終結果的情況下,將這些小的權重砍掉(Remove)了。

 

前面提到的“砍”權重的做法對於內存是很不友好的。例如卷積裏有 3×3 的矩陣,如果僅部分被砍的話,實際計算還要去往被砍處,從而造成了內存的不連續。

因此有人提出將 Filter 一併砍掉,在將整個 3×3 全部砍掉之後再予以訓練,以達到較好的效果。

最簡單的一種想法:直接刪掉參數矩陣的某一列,對應的就是刪掉一個filter,相應的輸出特徵圖將少一個通道。而在下一個卷積操作時,這個輸出特徵圖將變成輸入特徵圖,輸入矩陣少一個通道即少一個列,那麼爲了保證矩陣乘法正確,參數矩陣必須要對應刪去一行,即所有filter都要刪去一個通道。從上述介紹我們發現:若對某層卷積以filter單位裁枝,那麼到下一層一定會有以channel單位的裁枝,這就是filter-level裁枝。

備註:即刪除當前層的一個 filter, 刪除下一層的所有filter的某一個通道。

2017 年,有人提出通過對每個通道添加一個 scale 因子對網絡進行訓練,然後選擇把 scale 值比較小的卷積全部砍掉(如上圖中橙色處所示),以方便對內存進行高效地操作。

 

除了砍掉網絡,我們還可以通過量化來將其做得更小。如上圖所示的 3×3 權重,在實際存儲時權重都是浮點數(Float),而存儲每個浮點數都需要 32 比特位。

因此量化的思想是把這 9 個浮點值進行聚類,分別聚到四個類中,那麼我們在存浮點時就只需要存這四個浮點數即可。

對於這些值的表達,我們可以通過 Index 來實現。由於此處已聚了四個類,我們在 Index 時,只需兩個便可以表達了,即 2 的 2 次方正好是 4,正好表達出了四個數。

 

想必大家在學習數據結構時都瞭解過霍夫曼編碼。它的思想是:由於部分權重的出現次數遠高於其他權重,因此對於出現次數較多的權重,我們可以用更少的比特位來編碼。

而對於出現次數較少的權重,則用較大的比特位來表達。那麼該方法可以在總體 Index 爲已知的角度,直接用固定的位數進行存儲,從而節約了空間。

 

 

 

另一個壓縮的思路是設計小網絡,它主要有三種方式:

  • SqueezeNet

  • MobileNet

  • ShuffleNet

 

SqueezeNet 的核心思想是在做下一個 3×3 的卷積時,先進行一個 1×1 的卷積,將以前的 64 維降到 16 維,然後在此 16 維的基礎上再進行 3×3 的卷積。

這樣就相當於在做 3×3 的卷積之處比原來降低了 4 倍,也就是將模型降低到原來的四分之一。

 

谷歌於 2017 年 3 月提出了一個較小的模型 MobileNets。它主要採取了 Group 的策略,核心思想是:不再與前面的所有層進行操作,只跟對應的上一層通道做卷積。

例如:以前的權重是 3×3×32,再乘以前面的 32 個通道,就是有兩個 32 相乘。

通過使用 Group,我們只需一個 32,因爲後面乘的是 1(跟每一個通道相乘),此處比原來減少了 32 倍。

如果後面的通道數越多,如 512,則會比原來相應地減少 512 個,所以這是非常可觀的。

 

2017 年 7 月,Face++ 團隊提出了更進一步的做法 ShuffleNet。考慮到在通道大的時候,1×1 的卷積到 1024 或者 2048 的計算量也會很大,因此它將 1×1 的卷積也進行了 Group 操作。

在分組之後,我們單獨地進行 1×1 的卷積,並且隨即將順序打亂。這樣便可以把通道與通道之間的關係表達進去。

 

這個是我們在公開數據集 ImageNet 上的數據集實驗。我們將原大小爲 98M 的模型,通過模型壓縮後降到了 49M,而在權重的量化之後,繼續減到了 15M。

在整個過程中,Top-1 從 75% 變成 72.4%,該降幅比較少,而業界一般準確率是在 75%~76%。可見這個實驗是比較可觀和可用的。

 

上圖的結果源自蘑菇街自己的實際數據。當前我們的數據樣本大致是 1200 萬,通過“剪枝”之後,Top-1 基本保持在 48%。

而 Top-5 降低了 1 個點,從 82.2% 降至 81.5%,但是模型的大小則從 86M 降到了 31M;同時 Inference Time 爲 45 毫秒。這就意味着效率提升了一倍。

 

另一個嘗試是語意分割網絡。蘑菇街基於服裝的特點對人體的各個部位進行了“分割”,包括手、腳、鞋子、衣服、褲子等。該語義分割模型的基礎網絡爲 MobileNet,最終模型只有 13M。

移動端優化實踐

在將模型做得足夠小之後,我們又是如何讓它跑在手機之上的呢?

 

在手機上做深度學習時,由於計算量非常大,我們不應該將訓練放在手機上,而是仍然交給 GPU 來實現。而在訓練完成之後,我們再將模型部署到手機端。

 

如今業界常用且好用的深度學習框架包括:

  • Facebook 推出的 Caffe2,亞馬遜選用的 MXNet。不過我們試用下來發現,它們在手機上的實際性能表現卻不盡人意,對於一張圖的識別可能需要 8~9 秒。

  • NCNN 是騰訊開源的框架,而 MDL 則是百度開源的移動端深度學習框架。

  • CoreML 是蘋果在 2017 年 WWDC 上發佈的在手機上的深度學習框架。

  • Tensorflow Lite 是谷歌在 2017 年 I/O 大會上發佈的開源產品。

那麼對於一個網絡,我們是否非要將 Inference 與訓練網絡做得一樣呢?如今業界大部分框架的做法的確如此,例如 NCNN 和 MDL,它們都是直接把訓練好的網絡轉到手機上運行。

但是我們發現在訓練的時候,需要做一些梯度計算和反向傳播,而在 Inference 時,我們實際上並沒有必要做反向傳播。

上圖中是一個典型 CNN 網絡裏的單個 Block(塊),從 Convolution 到 BN(BachNormalization)再到 Relu。

這三層在存儲時對於內存的需求非常大,實際上我們完全可以將它們合爲一層,從而減少內存的使用,並加快速度。

在具體實現過程中,我們將 BN 放到 Convolution 裏的轉變是不需要改動框架代碼的。但是如果要把 Relu 放入 Convolution,則需要修改此框架的源代碼。

優化卷積計算

 

 

由於深度學習在處理圖像時,大部分的計算都涉及到卷積,因此比較直觀的做法就是直接進行 3×3 Filter。

因爲數據和圖像在內存裏的存儲是連續的,從而導致了讀取時經常需要到各處跳轉,這造成了指針跨度巨大,極大降低了 cache 命中率。

所以大部分的卷積算法優化都採取將 Filter 乘法轉化成傳統的矩陣乘法。如上圖右側所示,原來 7x7 矩陣和 3x3 矩陣分別被轉化成了 25×9 的矩陣和 9×1 矩陣。

我們通過直接對大型矩陣進行乘法操作,便可得到結果,且該結果跟原來是一模一樣的。這也是目前許多針對矩陣運算的加速庫所普遍採用的優化方式。

2017 年 MEC 算法被推出,由於原來 7×7 矩陣的轉化率 25×9 中存在着冗餘和複製,該算法把它變換成爲 5×21。就數量級而言,該 5×21 比 25×9 降低約一倍的內存,其性能更爲直觀。

浮點運算定點化

 

由於深度學習模型的權重和特徵圖的值是浮點數,而計算機對於浮點的運算能力遠不及定點的運算,例如:計算 3+2 和 3.0+2.0 的速度肯定是不一樣的,因此我們需要將傳過來的浮點數先給轉化成定點數。

例如:如果權重的大部分都是 0.1 或 0.2 的話,那麼我們通過求最小、最大值的方式將其映射到了 0 到 2 上,而 0 到 2 正好是一個字節,因此一個 8 位就能夠予以表達了。

如此,我們在計算 Convolution 矩陣相乘時,完全可以直接使用典型的矩陣來進行計算,其速度會比使用浮點數計算快很多。

當然,在計算完成之後,我們還需將結果轉換成浮點數予以輸出。

 

除了上述提到的優化卷積的核心方法,我們還能怎麼進化呢?

  • 再牛的核心算法,都不如硬件實現來得直接。此處主要是針對蘋果產品,蘋果在做圖像識別時使用的就是自己開發的帶有卷積乘法的 GPU 硬件。

    我們在二次開發時可以直接調用它提供的基礎卷積操作,而不必使用任何前面提到的算法。

  • 另外,前面提到的許多框架都是通用的優化算法。但是在實際深度學習中,我們根本不需要那麼多具有通用性的卷積。

    例如:剛纔列出的很多網絡,要麼是 1×1 的卷積,要麼是 3×3 的卷積,基本上不會出現 2×2 的卷積。

    因此我們只需要使用 3×3 的卷積優化便可。正如騰訊 NCNN 所採用的特定卷積策略,僅優化 3×3 和 1×1 的卷積。我們同樣可以不必考慮其他的矩陣相乘方式,如此便可提高實現速度。

  •  

  • 通過深入分析,我們發現:騰訊與百度在安卓上的效果差不多。如前所述,由於騰訊針對 3×3 和 1×1 優化採取的是特定卷積,而百度採取的是通用做法,所以後者更耗內存。

    當然兩者性能都在 200 毫秒左右,而對於開源的 Tensorflow Lite,由於它將浮點型轉爲整型進行運算,其性能會比上述兩者更快,只需 85 毫秒,基本可以滿足實時性的要求。

 

針對深度學習,蘋果於 2017 年發佈了 CoreML。它在網上被炒得特別火,其框架如上圖所示,最下面被分化出了負責加速的一層 BNNS,它是用 C 語言寫的機器學習庫。

旁邊的 Metal Performance Shaders 屬於蘋果自己的硬件,它封裝好了與機器學習相關的底層 API。

二次開發人員可以在 CoreML 的底層基礎上,進行適當的應用添加。不過我們並沒有採用該 CoreML,原因如下:

  • 由於蘋果比較封閉,它只能提供現成的框架和既定的模型。而計算機視覺的算法開發領域發展速度非常快,我們經常需要開發出一些新的層(layer)。因此 CoreML 無法滿足我們的算法要求。

  • CoreML 的庫需要調用最新的 iOS 包,而許多蘋果手機的 iOS 版本並未升級到 iOS11 以上。

 

所以在 iOS 上,我們是在 MPSCNN 層實現計算卷積的,好處在於:

  • Metal 的機制充分利用了 GPU 資源,而在 iOS 上不會搶佔 CPU 資源。

  • 運用蘋果自己的 Metal 語言去開發新的一層會非常的方便。

同時需要注意如下兩點:

  • Metal 實現的是 16 位 Float 數的計算,並非 32 位,因此屬於半精度。

  • 其權重的格式是 NHWC。

有人可能會質疑半精度的計算準確率,然而,由於深度學習有着非常強的泛化能力,就算減少計算精度,受到的影響基本上也並不大,同樣可以完成任務。

 

 

 

就自行搭建深度學習框架而言,我們需要注意如下的策略方面:

  • 優化 Inference 網絡結構。請牢記 Inference 網絡與 Training 的不同之處。如前所述,通過將傳統的三層合併爲一層,我們能夠大幅降低開銷。

  • GPU 加速。由於蘋果使用 Metal 進行封閉存儲,因此對於 GPU 的加速在 iOS 上做得比較好。而其他非 iOS 的安卓生態,目前尚無較好的 GPU 加速硬件。

  • 指令加速。如今 99% 以上的安卓手機裏都是使用的 ARM 芯片,該芯片能夠提供一些統一的指令集,以供我們實現底層的加速。

  • 鑑於 CPU 普遍爲多核的特點,我們也可以採取多線程的方式進行加速。

  • 採取內存佈局優化,將傳統的 NCHW(N:number、C:channel、H:height、W:width)多維方式中的 channel 維度放到最後,變成 NHWC 以提高速度。

  • 將浮點運算轉到定點化,以提升計算速度。

基於 NCNN 的工具包框架

 

Mogu Deep Learning Toolkit 是蘑菇街於 2016 開發的僅供公司內部使用的深度學習工具包。

由於各層都被做得十分專業極致,其高內聚低耦合的特點在網絡設計上顯得非常靈活,對於專業人士來說也比較好用。

 

總結

 

 

要想把深度學習做到移動端上,一定要將算法與工程相結合。

 

 

http://777n.com/keji/50566.html

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