本篇文章涉及到的文獻
- Residual Network(ResNet)
- Deep Residual Learning for Image Recognition[arXiv:1512.03385]
- Identity Mappings in Deep Residual Networks[arXiv:1603.05027]
- 2016_tutorial_deep_residual_networks
- ResNet-50 Architecture
- KaimingHe/deep-residual-networks
- CS231n 2017 lecture9
- BIGBALLON/cifar-10-cnn
妹紙:昨天試了一下VGG19,訓練時間挺久的,不過效果不錯。
花花:嗚嗚,前幾天我們都在講很基本的網絡架構,今天我們講稍微難一丟丟的。
妹紙:好啊,好啊!我猜是ResNet!
花花:你猜得真准(23333orz)。
最原始的 Residual Network
Residual Network,簡稱 ResNet(殘差網絡),是MSRA 何凱明 團隊設計的一種網絡架構,在2015年的ILSVRC 和 COCO 上拿到了多項冠軍,其發表的論文Deep Residual Learning for Image Recognition, 是 CVPR 2016 的最佳論文。
Residual Network的歷史從這里開始。
卷積神經網絡(Convolutional Neural Network)正不斷朝着“Deep”這個方向發展,最早期的LeNet只有5層,后來VGG把深度增加到19層,而我們即將要介紹的ResNet,更是超過了100層。
人們不禁要問:
Is learning better networks as easy as stacking more layers?
不,事實並非如此
只是簡單地增加網路的深度,不能得到很好的效果,甚至還會使誤差增大:
多個datasets的測試表明,僅僅只是簡單地堆疊卷積層,並不能讓網絡訓練得更好。
按理來說,如果網絡加深,training acc應該增大,而testing acc減小,但是上圖並不是這么回事,於是乎,Kaiming He提出了Deep Residual Learning的架構。
關於殘差模塊(residual block)
這里Kaiming聚聚首先引入了一個殘差模塊(residual block)的概念
上圖所示的Residual Block:
輸入為 ,需要擬合的結果(輸出)為
。
那么我們把輸出差分為 ,也就是
再令 ,意思是
也是由
擬合而來,
那么最后的輸出就變為 本來就是輸入,
所以我們就只需要擬合 就好了。
如上圖,原始的plain架構,我們用兩層卷積層來模擬函數 ,
而在residual block中,我們用兩層卷積層來模擬函數
舉個例子:
輸入 , 經過擬合后的輸出為
那么殘差就是
如果擬合的是恆等變換,即輸入 ,輸出還是
那么殘差就是
而如上圖所示,假設 從
經過兩層卷基層(conv)之后變為
,
平原網絡的變化率
而殘差模塊的變化率為
殘差的引入去掉了主體部分,從而突出了微小的變化。我想這是他們敢說
We hypothesize that it is easier to optimize the residual mapping than to optimize
the original, unreferenced mapping
的原因。
有了殘差模塊(residual block)這個概念,我們再來設計網絡架構,
架構很簡單,基於VGG19的架構,我們首先把網絡增加到34層,增加過后的網絡我們叫做plain network,再此基礎上,增加殘差模塊,得到我們的Residual Network
關於bottleneck
論文中有兩種residual block的設計,如下圖所示:
在訓練淺層網絡的時候,我們選用前面這種,而如果網絡較深(大於50層)時,會考慮使用后面這種(bottleneck),這兩個設計具有相似的時間復雜度。
同樣舉個例子:
對於ImageNet而言,進入到第一個residual block的input為
采用左側的兩個 的卷積層:
參數量為
化簡一下:
采用右側的bottleneck:
參數量為
化簡一下:
可以看到它們的參數量屬於同一個量級,
但是這里bottleneck占用了整個network的「三層」,而原本只有「兩層」,
所以這樣就節省了大量的參數,
在大於50層的resnet中,他們使用了bottleneck這種形式。
具體細節如圖所示:
如果你還有問題,參考這里 ResNet之Deeper Bottleneck Architectures
Identity mapping 改進
Kaiming He最初的paper,就是上面介紹的部分,但很快,他們又對ResNet提出了進一步的改進,這便是我們接下來要提到的paper:
Identity Mappings in Deep Residual Networks[arXiv:1603.05027]
我們來仔細分析一下Residual Block, 在這篇paper中也被叫為Residual Unit.
對於原始的 Residual Unit(block),我們有如下計算:
表示 第
個Residual Unit的輸入,
則代表 第
個Residual Unit的輸出
代表的某個變換,在這里是恆等變換,
代表residual function,
代表某種操作,在這里是ReLU
所以可以寫成如下形式:
也就是Residual Unit一開始的做法了。
那如果,我們 是恆等變換呢,即
,那么有:
對於任意一層,我們都能用這個公式來表示:
這便是這篇paper的改進,把原本的ReLU,放到Residual Unit的conv前面去,而不是放在addition之后。
可以看到,在上圖作者對cifar10進行的多組實驗中,使用full pre-activation這種Residul Unit效果最佳,個人認為這張表格還是挺重要的,我們簡單分析一下!
- (a)original:原始的結構
- (b)BN after addition:這是在做相反的實驗,本來我們的目的是把ReLU移到旁路上去,這里反而把BN拿出來,這進一步破壞了主路線上的恆等關系,阻礙了信號的傳遞,從結果也很容易看出,這種做法不ok
- (c)ReLU before addition:將
變為恆等變換,最容易想到的方法就是將ReLU直接移動到BN后面,但這會出現一個問題,一個
(殘差函數)的輸出應該可以是
,但是經過ReLU之后就會變為
,這種做法的結果,也比(a)要差。
直接提上來似乎不行,但是問題反過來想, 在addition之后做ReLU,不是相當於在下一次conv之前做ReLU嗎?
- (d)ReLU-only pre-activation:根據剛才的想法,我們把ReLU放到前面去,然而我們得到的結果和(a)差不多,原因是什么呢?因為這個ReLU層不與BN層連接使用,因此無法共享BN所帶來的好處。
- (e)full pre-activation:啊,那要不我們也把BN弄前面去,驚喜出現了,我們得到了相當可觀的結果,是的,這便是我們最后要使用的Unit結構!!!
代碼實現
終於到了可以寫代碼的時候了,
還是放在我的 Github,測試只是用了50層,
使用GTX980TI,訓練時間為 8 h 58 min
最后testing accuracy:94.10%
妹紙:哇,ResNet的residual block好帥氣啊,何凱明簡直是我男神!
花花:喔,他是所有人心中的男神!
妹紙:要訓練9個小時啊,我周末試一下啊
花花:你的是1080TI,訓練個毛9小時,我980TI才要9小時啊!!
妹紙:啊,反正是學長給配的
花花:啊,2333