1. 程式人生 > 其它 >神經網路太臃腫?教你如何將神經網路減小四分之一

神經網路太臃腫?教你如何將神經網路減小四分之一

想要讓深度神經網路更快,更節能一般有兩種方法。一種方法是提出更好的神經網路設計。例如,MobileNet比VGG16小32倍,快10倍,但結果相同。另一種方法是,通過去除神經元之間不影響結果的連線,壓縮現有的神經網路。本文將討論如何實現第二種方法。

我們將讓MobileNet-224縮小25%。換句話說,我們要在幾乎不損失精度的情況下,將把它的引數從400萬個減少到300萬個。

如何能做到這點

由於MobileNet比VGG16小32倍,但具有相同的精度,所以它必須比VGG更有效地捕捉知識。

事實上,VGG為了完成工作,用到的連線比它實際需要用到的連線要多很多。論文(https://arxiv.org/abs/1510.00149)顯示,通過修剪不重要的連線,VGG16的大小可以減少49倍,並且不影響結果。

那麼,MobileNet是否有它不需要的連線?我們能不能把它變得更小?

當你壓縮一個神經網路時,要找到網路大小與準確性的平衡點。一般來說,網路越小,執行速度越快(耗電也少),但預測的結果就越糟糕。MobileNet的分數比SqueezeNet好,但同時也是它的3.4倍。

理想情況下,我們希望找到能準確地表示我們想要學習的東西的儘可能小的神經網路。這是機器學習中的一個懸而未決的問題,除非有一個很好的理論能解決這個問題,否則我們將不得不從一個很大的網路開始,然後慢慢縮小它。

在這個專案中,我使用了帶有Keras 2.0.7的預訓練版MobileNet,並TensorFlow 1.0.3上執行。在ImageNet ILSVRC 2012驗證集上評估模型,得分如下:

Top-1 accuracy over50000 images= 68.4%
Top-5 accuracy over50000 images= 88.3%

這意味著它一次猜出正確答案可能性為68.4%,而正確的答案在前五個最佳猜測中的概率為88.3%。我們希望壓縮模型得到與它相當的準確度。

如何壓縮卷積神經網路

像大多數現代神經網路一樣,MobileNet有許多卷積層。壓縮卷積層的一種方法是將該層的權重從小到大排序,並丟棄具有最小權重的連線。上面提到的論文中也是使用這個方法,使VGG變小49倍。這聽起來很不錯,但它會造成稀疏連線。

由於GPU並不擅長處理稀疏矩陣,這種情況下縮小網路可能會讓你花費更多時間。在這種情況下,更小並不一定意味著更快。

那麼怎樣做到小而快呢?我們不再向那樣刪除連線,而是刪除完整的卷積過濾器。這樣連線還很密集,GPU也更輕鬆。

回想一下,卷積層產生具有一定數量輸出通道的影象。每個輸出通道都包含一個卷積過濾器的結果。這樣的過濾器在所有的輸入通道上取加權和,並把這個加權和寫入一個輸出通道。

我們將找到最不重要的卷積過濾器,然後從層中移除它們的輸出通道:

例如,MobileNet中的conv_pw_12 層有1024個輸出通道。我們將丟棄其中的256個通道,這樣conv_pw_12的壓縮版本只有768個輸出通道。

注意:為了讓Metal流暢執行,我們應該每次都要去除四個輸出通道。因為Metal實際上是個製圖介面,它使用“texture”來描述影象資料,每個“texture”儲存四個連續通道的資料。如果我們只刪除一個輸出通道,Metal仍然需要為其他三個通道處理“texture”。

那麼我們刪除了哪些過濾器或輸出通道,不會影響最終結果呢。有很多不同的指標可以估量過濾器的相關性,這裡我們使用一個非常簡單的指標:過濾器權重的L1範數(過濾器權重的絕對值的總和)。

例如,這些是MobileNet第一個卷積層(32個過濾器)的L1範數,從低到高如下:

正如你所看到的,這個第一層中大約有10個過濾器的L1範數幾乎為零。但因為我們的目標是在Metal上使用這個網路,所以刪除10個過濾器是沒有意義的。所以我們必須刪除8或12個。

我首先嚐試刪除8個最小的過濾器,精度沒有損失。 我決定放棄前12層。稍後你可以看到,精度也還不錯。這樣我們可以在網路中的第一個卷積層中去除37.5%的過濾器,神經網路沒有變差。

這裡是MobileNet中所有卷積層的L1範數圖。你可以看到,許多層都有對網路沒有太多貢獻過濾器(很低的L1範數)。

你可以在下面的論文中閱讀關於這種方法的更多內容。

論文地址:https://arxiv.org/abs/1608.08710

從層中刪除過濾器意味著該層的輸出通道數變小。這會對網路中的下一層產生影響,因為該層現在接收的輸入通道也會變少。因此,我們也必須從該層刪除相應的輸入通道。當卷積被批量歸一化時,我們也必須從批量歸一化的引數中刪除這些通道。

MobileNet實際上有三種卷積層:

  • 3×3卷積(第一層)
  • 深度卷積
  • 1×1的卷積(也稱為逐點卷積)

我們只能從3×3和1×1的卷積中去除過濾器,而不能從深度卷積中去除。深度卷積必須具有與輸入通道相同數量的輸出通道。壓縮沒有太大的收穫,而且深度卷積反應非常快(因為它們的工作量少於常規卷積)。所以我們主要關注3×3和1×1卷積層。

再訓練

因為從層中刪除過濾器會使網路的準確性變差。就算他們大都不重要,你也會丟掉神經網路學到的東西,所以你需要重新訓練一下網路。它可以學習彌補你刪除的部分。

這就意味著還要呼叫model.fit()。一點點的試錯讓我的學習率達到了0.00001,這個數相當小,但是更大的變化會讓訓練失去了控制。學習率必須這麼低的原因是,網路大部分已經被訓練好了,我們只想做一些微小的改變來調整結果。

這個過程是:

1.從層中以4的倍數刪除過濾器(即輸出通道)

2.重新訓練網路幾次

3.在驗證集上評估網路是否恢復了以前的準確性

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

正如你所看到的,這個過程是相當繁瑣的,因為我們每次只壓縮一層,每次改變都需要重新訓練網路。搞清楚每層下降多少過濾器準確率沒有明顯下降。

使用樣本資料集

MobileNet在ILSVRC競賽資料集上訓練,也稱為ImageNet。這是一個龐大的資料集,包含超過120萬個訓練影象。

我最近建立了一個深度學習平臺(一個只有一個GTX 1080 Ti GPU的Linux機器)。在這臺計算機上,需要2個小時完成一個訓練週期。甚至評估網路在5萬張影象驗證集上的效果也需要3分鐘。

這個速度太慢了。所以,我決定使用樣本。我沒有使用完整的訓練集,而是從1000個類別中挑選了5個隨機影象(這樣樣本有一定代表性),總共可以提供5000個訓練影象。現在需要大約30秒來完成一個訓練週期。這比2小時更容易管理!

為了進行驗證,我從完整的驗證集中選取了1,000個影象的一個隨機子集。在這個子集上對網路進行評估只需要3秒鐘。

事實證明,使用樣本的實踐效果不錯。

壓縮第一個卷積層

像上面看到的一樣,第一個卷積層有10個過濾器的L1範數小到接近0。我們需要刪除4的倍數的過濾器,最後我刪除了12最小的L1範數的過濾器。

最初,我沒有從神經網路中刪除過濾器,只是將它們的連線權重設定為0。理論上,這樣做和刪除是一樣的。這使Top1的精確度從69.4%下降到68.7% 。精確度稍低了一些,但只有一點再訓練可以修正。這算是個良好的開端!

接下來,我建立了一個除了在這裡我刪除過濾器的以外,與原來的層相同的新模型,所以第一個卷積層有24個輸出通道,而不是原來的36個。但是現在驗證成績更差只有29.9%。那麼,發生了什麼?

理論上,將連線的權重設定為0應該與刪除連線具有相同的效果。但我搞砸了:我忘記也把下一層的相應輸入通道的權重設定為0.並且,由於下一層是深度卷積,所以我還必須設定相應的引數讓該層的批量歸一化為0。

這樣我們知道了,從一個層移除過濾器也會對接下來的幾個層產生重大影響。而對其他層的更改也會影響驗證分數。

那麼刪除第一個卷積層過濾器的37.5%到底可不可行?通過檢查模型,我發現這裡所有的“錯誤”來自在第二個批量歸一化層中的12個偏置值,因為除了那些偏置的值之外,其他的值都是零。

而這12個數字讓準確度從68.7%下降到29.9%。但是,在具有400萬引數的神經網路中,這12個數字根本不重要。這樣,我相信網路可以通過一些再培訓從29.9%中恢復過來。

我在樣本資料集(5,000張影象)重新訓練了神經網路,10個訓練週期,現在驗證得分回升到了68.4%。雖然比原來的準確度(69.4%)低些,但現在已經足夠接近了。

對於這個專案,如果對一個樣本進行再培訓使精度回到65%左右,我就很滿意了。因為,我們重新訓練的樣本僅為全套訓練集大小的0.4%。我認為,如果這樣一小部分訓練影象可以使精度幾乎回到原來的分數,那麼在最後加入完整資料集訓練幾輪就可以了。

注意:相同的訓練樣本不要使用太久,會導致過擬合,一般我訓練十次就更新訓練樣本。

現在,從第一個卷積層節省37.5%的權重。聽起來很多,但這是一個非常小的層次。它只有3個輸入通道和32個輸出通道(現在24個)。總節約:3×3×3×12 = 324個引數,相比總數算是九牛一毛。

壓縮最後的卷積層

在壓縮第一層之後,我認為在分類層之前嘗試壓縮最後一個卷積層是會很不錯。

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

conv_pw_13層有1024個輸出通道。我決定刪除其中的256個。conv_pw_13有1,048,576個引數。這是網路中最大的一層,所以我們可以在這裡壓縮效果最好。

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

從圖中我們可以看到,去掉256個通道有點多似乎有點多,但我們先不管它。同樣,我們從層中刪除輸出通道並批量歸一化他們,然後調整下一層,以使它們也具有更少的輸入通道。

刪除這256個通道不僅可以在conv_pw_13上節省1024×1×1×256 = 262,144個引數,還可以從分類層中刪除256,000個引數。

在壓縮conv_pw_13層之後,驗證分數下降到60.7%(top1)和82.9%(top5)。經過10次訓練後,精度達到了63.6%,在新的訓練樣本上再訓練十次,準確率達到了65.0%(top1)和86.1%(top5)。

這個得分不錯,可以繼續修剪其它層了。雖然還沒有恢復到原來的以前的成績,但它表明,網路已經成功補償了被刪除的連線。

壓縮更多的層和再訓練

接下來,我使用相同的方法修剪conv_pw_10(從512個過濾器中刪除了32個)和conv_pw_12(從1024箇中刪除了256個)。

隨著每一個新的層中,我注意到,再培訓變得越來越難以使精度回到以前的標準。conv_pw_10是64.2%,conv_pw_12只有63.4%。

每次我使用不同的訓練樣本,只是為了確保結果模型不會過擬合。在逐點層10和12之後,我做了conv_pw_11。在做壓縮的圖層的選擇時,我只是隨意地選擇的。

在conv_pw_11上,我刪除了512個過濾器中的96個。對於其它的層,我大都刪除了25%的過濾器,一般根據L1範數刪除,主要由於它很好約整數。然而在這裡,刪除128個過濾器導致精度下降太多,再訓練不能達到60%以上。“刪除96個過濾器的效果會好點,但再訓練後的得分也只有61.5%。

看到這些令人失望的驗證分數,我覺得肯定是我刪除了太多的過濾器,所以現在神經網路不再有能力學習ImageNet了。

到目前為止,所有的再訓練都是用5000個影象的樣本完成的,因此修剪後的網路只在整個訓練集的一小部分上被重新訓練。我決定是時候對網路進行完整的訓練。

經過1個訓練週期,精度回升至66.4(top1),0.87(top5)。我沒有使用資料增強,只用原始的訓練影象。在訓練第二次後,為67.2(top1),87.7(top5)。

我在完整的訓練集又上訓練了幾次,用到了資料增強,還調小了學習率,可惜沒有什麼效果。

由於時間關係,到此我停止了實驗。我確信如果我繼續做還能壓縮的更多。

結論

Original network size: 4,253,864 parameters
Compressed network size:3,210,232 parameters
Compressed to:          75.5% of original size

Top-1 accuracy over50000 images= 67.2%
Top-5 accuracy over50000 images= 87.7%

以上是我的結果:網路變小了25%,但準確度差了點(當然絕對不會差到25%)。

我們的流程還有改進空間,在選擇移除和壓縮的順序上我做的也不是很科學。但對於這個專案足夠了,我只是想知道大致思路。

顯然,我沒有對這個網路進行最佳修剪。使用L1範數也可能不是確定過濾器重要性的最好方法,也許一次只移除一些過濾器比一次刪除層的輸出通道的四分之一要好。

關於這件事是否值得我想說:如果神經網路小25% – 假設這意味著它也會快25% ,如果在手機上執行這可能非常關鍵,所以這是值得的。