當前位置:
首頁 > 知識 > 向手機端神經網路進發:MobileNet壓縮指南

向手機端神經網路進發:MobileNet壓縮指南

選自Machine Think

機器之心編譯

參與:機器之心編輯部

隨著 MobileNet 等面向移動設備的模型不斷出現,機器學習正在走向實用化。不過,由於深度學習等方法需要消耗大量計算資源的因素,目前我們距離真正的移動端人工智慧應用還有一段距離。在硬體之外,我們也需要對模型本身進行壓縮,最近,荷蘭工程師 Matthijs Hollemans 向我們展示了他壓縮 MobileNet 的方法:通過刪除卷積層的部分濾波器,他在保證準確性不變的情況下,讓模型體量縮小了 25%,讓我們看看他是怎麼做的。

隨著機器學習技術向移動設備滲透的趨勢,人們正在越來越注重於尋找讓深度神經網路更快、更簡潔的方式。

一種方法是提出更智能化的神經網路設計。例如:MobileNet 可以在獲得相同結果的情況下比 VGG-16 小 32 倍,速度快上 10 倍。

另一個方法是採用現有的神經網路,並用刪除與結果無關的神經元的方法來壓縮它。本文會著重介紹這種方法。

我們將著手改進 MobileNet-224,讓它的體量減小 25%,換句話說,我們要把它的參數從 400 萬個減少到 300 萬個——同時不損失模型的準確性(好吧…只有一點點)。

如何做到更好

鑒於 MobileNet 比 VGG16 要小 32 倍,而準確性相同,前者捕獲知識的效率顯然更高。

的確,VGG 模型中的神經網路連接比我們所需要的多很多。斯坦福大學韓松等人 2015 年在論文《Deep Compression: Compressing Deep Neural Networks with Pruning, Trained Quantization and Huffman Coding》中提出的壓縮網路方法展示了通過剪枝不必要的神經網路連接讓 VGG16 縮小 49 倍,並保持準確性的方法。

現在問題來了:MobileNet 里還有不必要的連接接嗎?即使這個模型已足夠小,但我們能不能讓它變得更小且不損失準確性呢?

當你試圖壓縮一個神經網路,需要權衡的問題是模型尺寸與準確性。通常,網路越小,速度就越快(當然也耗電更少),但預測出來的結果也會越差。例如,MobileNet 的性能要好過 SqueezeNet,同時前者也比後者大上 3.4 倍。

在理想情況下,我們總是希望找到儘可能小的神經網路——不過它們必須為我們傳遞準確的結果。這在機器學習中是一個開放問題,在正確的理論出現之前,讓我們先試著從大模型開始剪枝吧。

在這個項目中,我使用了 Keras 2.0.7 版中預訓練的 MobileNet,並運行在 TensorFlow1.0.3 上。如果使用 ImageNet ILSVRC2012 驗證集來測試,它的得分是:

Top-1 accuracy over 50000 images = 68.4%

Top-5 accuracy over 50000 images = 88.3%

這意味著它有 68.4% 的幾率一次性給出正確結果,如果範圍擴大到排名前五的結果,則準確率則為 88.3%。我們希望壓縮這個模型,並讓它保持以上分數。

注意:MobileNet 論文(MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications)聲稱它在 ImageNet 上的準確率是 70.6%,VGG16 是 71.5%,GoogleNet 是 69.8%。我們還不知道這個數據是來自 ImageNet 還是驗證集。無論如何,Keras 版的 MobileNet 在驗證集上的得分是 68.4%,我們將會使用這個數字作為基準。

如何壓縮一個卷積神經網路

就像多數的現代神經網路一樣,MobileNet 有著許多卷積層。將每層的權重由小至大排列並剔除帶有最小權重的連接就是一種壓縮卷積層的方法。

這種方法是由 Han 等人在將 VGG 壓縮 49 倍時提出的。聽起來是個好方法但也存在著大缺陷:它造成了稀疏連接。不幸的是,GPUs 並不擅長處理稀疏矩陣,並且在計算上花費了更多的時間,儘管網路得到了壓縮,你並未由此而省時。在這種情況下,壓縮得更小並不意味著會更快。

但這對於追求魔鬼速度的我們並不奏效:小而快才是我們所追求的。我們將移除複雜的卷積濾波器,而不是修剪掉單個連接。這讓我們保持連接緊密的同時也不會給 GPU 帶來麻煩。回想一下一個卷積層產出一個帶有一組特定數量的輸出通道的圖像。每一個輸出通道都包含了單一卷積濾波器帶來的影響。這樣一個濾波器接管了所有來自於輸入通道的加權和,並將這一加權和寫入單一的輸出通道。

我們要找出那種最不重要的卷積濾波器,並且將其輸出通道從層中移除。

例如,MobileNet 中的層 conv_pw_12 有 1024 個輸出通道。我們會捨棄這其中的 256 個通道使得被壓縮版的 conv_pw_12 只有 768 個輸出通道。

注意:為了迎合 Metal,我們應該以每次移除四個輸出通道的速度進行。因為 Metal 實際上是一個圖形 API,它用紋理(texture)來描述神經網路的圖像數據,而每一個紋理為四個連續的通道保存數據。所以,如果我們只移除一個輸出通道,Metal 仍然需要處理其他三個通道的紋理。考慮到 Metal 的這一特點,我們只有以四的倍數來移除通道才會使得壓縮層有意義。

現在問題是:我們可以移除哪些濾波器和輸出通道?我們僅希望在不影響太多性能的情況下移除一些輸出通道。

我們可以使用不同的度量方案來估計濾波器的相關性,但是我們也可以選擇非常簡單的方法:濾波器權重的 L1 範數,即所有濾波器權重的絕對值之和。

例如以下是 MobileNet 前面幾個卷積層(32 個濾波器)的 L1 範數:

如圖所示,L1 範數非常小、接近於零的第一個層有 10 個濾波器。我們或許可以去除這些濾波器。但是由於我們的目標是使用帶有 Metal 的網路,因此去除 10 個濾波器沒有意義。我們必須去除 8 個或 12 個濾波器。

我首先嘗試去除 8 個最小的濾波器。效果很好,準確率完全沒有損失,因此我決定去除 12 個。稍後你可以看到,它的效果依然很好。這意味著我們實際上能夠去除該網路第一個卷積層中 37.5% 的濾波器,而網路性能不會變差!

這裡有 MobileNet 所有卷積層的 L1 範數示圖。你可以看到很多層的濾波器對網路幾乎沒有貢獻(低 L1 範數)。

註:由於並非所有層都具備相同數量的輸出通道,上圖中所有數據都按同樣的標準進行歸一化處理。橫軸代表通道(按 L1 範數的值從低到高),縱軸代表實際的 L1 範數(同樣是歸一化處理後的)。

你可以在 Li et al 所寫的論文《Pruning Filters For Efficient Convnets》中獲取該方法的更多詳情。

從一個層中去除濾波器意味著該層輸出通道的數量變少。自然而然,這對該網路下一層也有影響,因為下一層現在的輸入通道變少了。

因此,我們還必須從那一層去除對應輸入通道。當卷積之後是批量歸一化(BN)時,我們還必須從批量歸一化參數中去除這些通道。

MobileNet 事實上有三種卷積層:

一個常規的 3×3 卷積(第一層)

Depth-wise 卷積

1×1 卷積(又名 pointwise convolution)

我們僅從 3×3 和 1×1 卷積中去除濾波器,而非 Depth-wise 卷積。一個 Depth-wise 卷積必須具備相同數量的輸出通道和輸入通道。壓縮可能沒有什麼收穫,而且 Depth-wise 卷積很快(因為它們的工作比常規卷積要少)。因此我們主要將注意力集中在帶有 3×3 和 1×1 卷積的層。

再訓練

因為從層中刪除濾波器會讓網路的準確性變差——畢竟,你在丟棄神經網路學習到的東西,即使它可能不是非常重要——你需要做一點重新訓練,這樣才能彌補丟棄造成的損失。

在訓練意味著你需要再次調用 model.fit()。一點小小的試錯後,我們就會把學習率定在 0.00001——一個非常小的數字,任何稍大的訓練參數都會讓結果超出控制。學習率如此之小的原因是,在這裡,大部分網路已經被訓練過了,我們只想進行小改進來提升結果。

過程是這樣:

1. 在層中 4 倍並行地移除濾波器(即輸出通道)

2. 再訓練該神經網路幾個 epoch

3. 對驗證集進行評估,以檢查該神經網路的準確性是否恢復至之前水準

4. 移動到下一層並重複這些步驟

正如你所看到的,這個過程費時費力,因為我們每次只能改動一層,而修改之後每次又要重新訓練神經網路。每個層中,可以丟棄的濾波器都是不太一樣的。

使用訓練子集

MobileNet 已在 ILSVRC 比賽數據集(也就是 ImageNet)中進行過預訓練了。這是一個巨大的數據集,其中包含超過 120 萬張訓練圖片。

依靠最近裝配的深度學習機器(只有一塊英偉達 GTX 1080Ti 的 Linux 系統),每個 epoch 需要訓練兩小時之久。即使用 5 萬張圖像驗證集來做這件事也需要 3 分鐘。

毫無疑問,這樣的硬體讓快速迭代變得難以實現。我可不想每天盯著屏幕兩個小時,只為看到模型出現一點點小變化。所以我們得在樣本上找辦法,而不是用完整的數據集,我在數據集 1000 個類別中每類隨機找出五張圖片(這多少有點代表性),形成了 5000 張圖片的訓練子集。現在每個 epoch 只需要 30 秒鐘了。這要比兩個小時方便多了!

為了進行驗證,我從完整驗證集中隨機抽取了 1000 張圖片作為驗證集,用它來評估網路性能只需要畫上 3 秒鐘。

看來,使用樣本的方法很有效。

壓縮第一個卷積層

如你所見,第一個卷積層有 10 個非常小的 L1 規範濾波器。因為對於 Metal,我們需要以 4 的倍數來去除濾波器,所以我刪除了具有最小 L1 規範的 12 個濾波器。

最初,我還沒有從神經網路中刪除任何濾波器,只是將他們的連接權重設置為 0。理論上,這樣的事情可以讓 Top-1 準確率從 69.4% 降到 68.7%——有一點損失,不過沒有什麼不是在訓練不能解決的。

接下來,我創建了一個與原始層相同的新模型,並在這裡刪除了濾波器,所以在第一個卷積層中,實際上只有 24 個輸出通道(而不是原來的 36 個)。但是現在準確率評分變得很低了:29.9%,發生了什麼?

理論上,將連接權重設置為 0 和刪除連接應該可以獲得相同的結果,但實踐中卻出了差錯:我忘了將下一層相應輸入通道的權重設置為 0。而更糟的是,因為下一層是深度卷積,我還得設置相應的參數,讓該層的批量歸一化為 0。

教訓:從一層中去除濾波器也會對其它層產生影響。而這些變化會影響評分。

所以刪除第一層中的濾波器損失 37.5% 的準確率不太值得?在檢查整個模型後,我發現問題在於第二個批量範數(batch norm)層上 12 個偏置值:當它們變成其他任何數字後,其他的東西都歸零了。

看起來,是這 12 個數字讓 68.7% 的識別率變成了 29.9%,真的嗎?這個神經網路有 400 萬個參數,12 個數字肯定不能決定一切,在這樣的思路下,我覺得我可以做點什麼了。

我用 5000 張圖片的子集重訓練了神經網路 10 個 epoch(只用了五分鐘),現在準確率重新回到了 68.4%,這雖然不及原模型(69.4%),但已經很接近了。

一點樣本圖像就讓準確率回到了 65% 以上,這太簡單了,請記住:我們用於重訓練的樣本大小只有整個數據集的 0.4%。我認為,如果這樣一點圖像就可以讓分數大體回復,那麼整個數據集的訓練應該可以讓準確率完全回歸原水平。

註解:使用相同的樣本進行長時間訓練可不是什麼好主意。當你用同樣的子集訓練 10 個 epoch 之後,神經網路就會嚴重過擬合。所以在使用了 10 個 epoch 之後,你得換一個新的訓練子集。

現在,第一卷積層減少了 37.5% 的權重,這聽起來不錯,但是這只是小小的一層而已。它只有三個輸入通道與 32 個輸出通道(削減後為 24 個)。總共節省的參數是:3×3×3×12=324,效果太不明顯了,不過這是一個好的開始。

最後的 PW 卷積層

在壓縮第一層後,我們可以考慮壓縮分類層之前的最後一個卷積層。

在 MobileNet 的 Keras 版中,分類層也正好是一個卷積層,但是我們不能從這一卷積層中移除任何通道。因為這個網路是在 ImageNet 中訓練的,該數據集有 1000 個種類,因此分類層也必須有 1000 個輸出通道。如果我們刪除了任何通道,那麼模型就不能再對這些類別做預測。

conv_pw_13 層有 1024 個輸出通道,雖然並沒有理論依據,但我們可以先嘗試移除 256 個。conv_pw_13 層有 104.8576 萬個參數,它在整個網路中都是最大的層,因此我們可以對它執行多一點的壓縮。

這一層的 L1 範數如下所示:

從上圖來看,移除 256 個通道可能有點多,留待觀察。

再一次,我們移除了網路層的輸出通道,然後是批規範化層,然後調整下一層,因為它們也有一些輸入通道。

移除 256 個通道,不止為 conv_pw_13 節省了 1024×1×1×256 = 262,144 個參數,也為分類層移除了 256,000 個參數。

在壓縮完 conv_pw_13 層之後,驗證得分掉到了 60.7%(top1)和 82.9%(top5)。也沒多糟糕,特別是考慮到還沒再訓練。

在樣本上再訓練 10 個 epochs 之後,準確率提升到了 63.6%。在新的訓練樣本上再多訓練 10 個 epochs 之後,提升到了 65.0%(top1)和 86.1%(top5)。對 10 分鐘的訓練來說,不錯了。

得到 0.65 的得分我已經很開心了,能夠繼續修剪其他的層。雖然還沒得到最初的得分,但已經證明網路成功地補償了修剪的連接。

更多層 & 真實再訓練

接下來,我使用同樣的方法修剪 conv_pw_10(移除了 512 個濾波器中的 32 個)和 conv_pw_12(移除了 1024 個濾波器中的 256 個)。

在每個新的網路層上,我發現要得到之前的標準準確率越來越難。在 conv_pw_10 之後準確率是 64.2%,conv_pw_12 之後只有 63.4%。

每次我使用不同的訓練樣本,只是為了確保結果仍舊具有代表性,模型不會過擬合。

在 pw_10 與 pw_12 之後,我做了 conv_pw_11。其實在選擇壓縮層上我沒有總體規劃,是隨機選擇的。

在 conv_pw_11 上,我修剪了 512 個濾波器中的 96 個。在其他層上,我最多修剪掉濾波器個數的 25%,部分是基於 L1-norms 所獲得的信息進行修剪,但主要是因為它是很好的約整數。但是,移除 128 個濾波器導致準確率下降太多,再訓練也無法提升到 60% 以上。只剪除 96 個濾波器有更好的結果,但再訓練之後得分也只是 61.5%。

在驗證得分如此令人失望的情況下,無疑是我過於激動,移除了太多的濾波器,導致神經網路不再能夠學習 ImageNet。

所有的再訓練都是在 5000 張圖像的小樣本上進行的,所以修剪過的網路只是在所有訓練集上的一部分再訓練的。時間限制了網路只能在全部訓練集上運行幾輪。

在 1 個 epoch 之後,準確率達到 66.4 (top 1)、0.87(top 5)。我並未使用數據增強,只使用了原始的訓練圖像。

既然已經開始,我決定多訓練幾個 epoch,看看會有什麼不同結果。在第二個 epoch 之後,網路得分是 67.2(top1)、87.7(top 5)。

之後,我又在完整訓練集上訓練了更多的 epochs,且加入了數據增強,用了更小的學習率。不幸的是,這對結果改善毫無益處。

於是,我停止了實驗,時間不夠。在多訓練幾輪,我保證有很大可能把網路再次壓縮。而且,還有 9 個 pw 卷積層我們還沒處理,我保證這些層也能修剪掉一些濾波器。

結論

Original network size: 4,253,864 parameters

Compressed network size: 3,210,232 parameters

Compressed to: 75.5% of original size

Top-1 accuracy over 50000 images = 67.2%

Top-5 accuracy over 50000 images = 87.7%

以上結果未能達到我的預期目標:網路確實壓縮了 25%,但準確率有點差,雖然準確率沒有損失 25%。

因為我顧及的是整個流程,所以該工作流中有些地方可以改進。在選擇需要移除的濾波器上,或者壓縮網路層的順序上,我做的也不科學。但對該項目而言,足夠了,我只是想要獲取可能壓縮神經網路的思路。

可以明顯看到,我未能做到對網路的最優修剪。使用 L1-norms 可能不是確定濾波器重要度的最佳方式。也可能一次性只移除一些濾波器,而非砍掉網路層輸出通道的 1/4,這樣會更好一些。我很開心使用的樣本在再訓練中表現非常好,不需要幾個小時進行再訓練,這意味著我能快速的進行新實驗。

這件事是否值得做呢?我覺得是。假設你有一個神經網路在手機端運行需要 25FPS,意味著每幀需要 0.04 秒的處理時間。如果網路壓縮了 25%,假設這意味著處理速度快了 25%,也就是每幀的處理時間為 0.03 秒,節約了很大的時間。在運行流暢與運行不暢的 APP 上,可能會有細微差別。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 機器之心 的精彩文章:

受夠了碎片信息和大眾搜索?機器之心新上線的AI商用垂直搜索
2 億條視頻,Google Brain 如何讓 YouTube 煥發生機
三問 Christopher Manning:超越模型存在的語言之美
谷歌全新神經網路架構Transformer:基於自注意力機制,擅長自然語言理解
超少量數據訓練神經網路:IEEE論文提出徑向變換實現圖像增強

TAG:機器之心 |