@
一、前言
深度殘差網絡(Deep residual network, ResNet)的提出是CNN圖像史上的一件里程碑事件,讓我們先看一下ResNet在ILSVRC和COCO 2015上的戰績:
ResNet取得了5項第一,並又一次刷新了CNN模型在ImageNet上的歷史,
ImageNet分類Top-5誤差:
那么ResNet為什么會有如此優異的表現呢?其實ResNet是解決了深度CNN模型難訓練的問題,從圖2中可以看到14年的VGG才19層,而15年的ResNet多達152層,這在網絡深度完全不是一個量級上,所以如果是第一眼看這個圖的話,肯定會覺得ResNet是靠深度取勝。事實當然是這樣,但是ResNet還有架構上的技巧,這才使得網絡的深度發揮出作用,這個技巧就是殘差學習(Residual learning)。
-
論文名稱:Deep Residual Learning for Image Recognition
-
論文作者:Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun
-
論文地址:[1512.03385] Deep Residual Learning for Image Recognition
二、深度網絡的退化問題
從經驗來看,網絡的深度對模型的性能至關重要,當增加網絡層數后,網絡可以進行更加復雜的特征模式的提取,所以當模型更深時理論上可以取得更好的結果,
在深度學習中,網絡層數增多一般會伴着下面幾個問題
- 計算資源的消耗
- 模型容易過擬合
- 梯度消失/梯度爆炸問題的產生
- 問題1可以通過GPU集群來解決,對於一個企業資源並不是很大的問題;
- 問題2的過擬合通過采集海量數據,並配合Dropout正則化等方法也可以有效避免;
- 問題3通過Batch Normalization也可以避免。
貌似我們只要無腦的增加網絡的層數,我們就能從此獲益,但實驗數據給了我們當頭一棒。實驗發現深度網絡出現了退化問題(Degradation problem):網絡深度增加時,網絡准確度出現飽和,甚至出現下降。這個現象可以在下圖中直觀看出來:
56層的網絡比20層網絡效果還要差。這不會是過擬合問題,因為56層網絡的訓練誤差同樣高。我們知道深層網絡存在着梯度消失或者爆炸的問題,這使得深度學習模型很難訓練。但是現在已經存在一些技術手段如BatchNorm來緩解這個問題。因此,出現深度網絡的退化問題是非常令人詫異的。
當網絡退化時,淺層網絡能夠達到比深層網絡更好的訓練效果,這時如果我們把低層的特征傳到高層,那么效果應該至少不比淺層的網絡效果差,或者說如果一個VGG-100網絡在第98層使用的是和VGG-16第14層一模一樣的特征,那么VGG-100的效果應該會和VGG-16的效果相同。但是實驗結果表明,VGG-100網絡的訓練和測試誤差比VGG-16網絡的更大。這說明A網絡在學習恆等映射的時候出了問題,也就是傳統的網絡("plain" networks)很難去學習恆等映射。
也就是說,我們不得不承認肯定是目前的訓練方法有問題,才使得深層網絡很難去找到一個好的參數。
所以,我們可以在VGG-100的98層和14層之間添加一條直接映射(Identity Mapping)來達到此效果。
從信息論的角度講,由於DPI(數據處理不等式)的存在,在前向傳輸的過程中,隨着層數的加深,Feature Map包含的圖像信息會逐層減少,而ResNet的直接映射的加入,保證了 l+1層的網絡一定比 l 層包含更多的圖像信息。
基於這種使用直接映射來連接網絡不同層直接的思想,殘差網絡應運而生。
三、殘差學習
3.1 殘差網絡原理
對於一個堆積層結構(幾層堆積而成)當輸入為x時其學習到的特征記為H (x),現在我們希望其可以學習到殘差F(x)= H(x) - x,這樣其實原始的學習特征是H(x)= F(x) + x。之所以這樣是因為殘差學習相比原始特征直接學習更容易。當殘差為0時,此時堆積層僅僅做了恆等映射,至少網絡性能不會下降,實際上殘差不會為0,這也會使得堆積層在輸入特征基礎上學習到新的特征,從而擁有更好的性能。殘差學習的結構如下圖所示。這有點類似與電路中的“短路”,所以是一種短路連接(shortcut connection)。
- 改變前目標: 訓練F(x) 逼近 H(x)
- 改變后目標:訓練 F(x)逼近H(x) - x
上圖中,左邊的original block需要調整其內部參數,使得輸入的x經過卷積操作后最終輸出的F(x)等於x,即實現了恆等映射F(x)=x,等號左邊是block的輸出,右邊是block的輸入。但是這種結構的卷積網絡很難調整其參數完美地實現F(x)=x。再看右邊的Res block。因為shortcut的引入,整個block的輸出變成了F(x)+x,block的輸入還是x。此時網絡需要調整其內部參數使得F(x)+x=x,也就是直接令其內部的所有參數為0,使得F(x)=0,F(x)+x=x就變成了0+x = x,等號左邊是block的輸出,右邊是block的輸入。輸出等於輸入,即完美地完成了恆等映射。
3.2 ResNet結構為什么可以解決深度網絡退化問題?
因為ResNet更加容易擬合恆等映射,原因如下:
ResNet的結構使得網絡具有與學習恆等映射的能力,同時也具有學習其他映射的能力。因此ResNet的結構要優於傳統的卷積網絡(plain networks)結構。
3.3 殘差單元
由於ResNet要求 F(x)與 x 的維度大小要一致才能夠相加,因此在 F(x) 與 x 維度不相同時就需要對 x 的維度做調整。
ResNet使用兩種殘差單元,如下圖所示。左圖對應的是淺層網絡,而右圖對應的是深層網絡。對於短路連接,當輸入和輸出維度一致時,可以直接將輸入加到輸出上。但是當維度不一致時(對應的是維度增加一倍),這就不能直接相加。有兩種策略:
(A方式)采用zero-padding增加維度,此時一般要先做一個downsamp,可以采用strde=2的pooling,這樣不會增加參數;
(B方式)采用新的映射(projection shortcut),一般采用1x1的卷積,這樣會增加參數,也會增加計算量。
A方式采用0填充.,完全不存在任何的殘差學習能力。
B方式的模型復雜度偏高
因此論文中采用折中的C方式
C方式在F(x)的維度與 x的維度相同時,直接用 F(x) 加上 x,在維度不同時,才采用1x1的卷積層對 x 的維度進行調整。
def identity_block(input_tensor, kernel_size, filters, stage, block):
"""The identity block is the block that has no conv layer at shortcut. A方式
# Arguments
input_tensor: input tensor
kernel_size: defualt 3, the kernel size of middle conv layer at main path
filters: list of integers, the filters of 3 conv layer at main path
stage: integer, current stage label, used for generating layer names
block: 'a','b'..., current block label, used for generating layer names
# Returns
Output tensor for the block.
"""
filters1, filters2, filters3 = filters
if K.image_data_format() == 'channels_last':
bn_axis = 3
else:
bn_axis = 1
conv_name_base = 'res' + str(stage) + block + '_branch'
bn_name_base = 'bn' + str(stage) + block + '_branch'
x = Conv2D(filters1, (1, 1), name=conv_name_base + '2a')(input_tensor)
x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2a')(x)
x = Activation('relu')(x)
x = Conv2D(filters2, kernel_size,
padding='same', name=conv_name_base + '2b')(x)
x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2b')(x)
x = Activation('relu')(x)
x = Conv2D(filters3, (1, 1), name=conv_name_base + '2c')(x)
x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2c')(x)
x = layers.add([x, input_tensor])
x = Activation('relu')(x)
return x
def conv_block(input_tensor, kernel_size, filters, stage, block, strides=(2, 2)):
"""conv_block is the block that has a conv layer at shortcut
B方式
# Arguments
input_tensor: input tensor
kernel_size: defualt 3, the kernel size of middle conv layer at main path
filters: list of integers, the filterss of 3 conv layer at main path
stage: integer, current stage label, used for generating layer names
block: 'a','b'..., current block label, used for generating layer names
# Returns
Output tensor for the block.
Note that from stage 3, the first conv layer at main path is with strides=(2,2)
And the shortcut should have strides=(2,2) as well
"""
filters1, filters2, filters3 = filters
if K.image_data_format() == 'channels_last':
bn_axis = 3
else:
bn_axis = 1
conv_name_base = 'res' + str(stage) + block + '_branch'
bn_name_base = 'bn' + str(stage) + block + '_branch'
x = Conv2D(filters1, (1, 1), strides=strides,
name=conv_name_base + '2a')(input_tensor)
x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2a')(x)
x = Activation('relu')(x)
x = Conv2D(filters2, kernel_size, padding='same',
name=conv_name_base + '2b')(x)
x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2b')(x)
x = Activation('relu')(x)
x = Conv2D(filters3, (1, 1), name=conv_name_base + '2c')(x)
x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2c')(x)
shortcut = Conv2D(filters3, (1, 1), strides=strides,
name=conv_name_base + '1')(input_tensor)
shortcut = BatchNormalization(axis=bn_axis, name=bn_name_base + '1')(shortcut)
x = layers.add([x, shortcut])
x = Activation('relu')(x)
return x
3.4 ResNet的網絡結構
ResNet網絡是參考了VGG19網絡,在其基礎上進行了修改,並通過短路機制加入了殘差單元,如下圖所示。變化主要體現在ResNet直接使用stride=2的卷積做下采樣,並且用global average pool層替換了全連接層。ResNet的一個重要設計原則是:當feature map大小降低一半時,feature map的數量增加一倍,這保持了網絡層的復雜度。從下圖中可以看到,ResNet相比普通網絡每兩層間增加了短路機制,這就形成了殘差學習,其中虛線表示feature map數量發生了改變。下圖展示的34-layer的ResNet,還可以構建更深的網絡如表1所示。從表中可以看到,對於18-layer和34-layer的ResNet,其進行的兩層間的殘差學習,當網絡更深時,其進行的是三層間的殘差學習,三層卷積核分別是1x1,3x3和1x1,一個值得注意的是隱含層的feature map數量是比較小的,並且是輸出feature map數量的1/4。
四、實驗結果
作者搭建了不同深度的ResNet模型進行了實驗,結果如上圖所示。ResNet在ImageNet上的錯誤率與網絡的深度是線性關系,網絡越深,錯誤率越低。說明在一定ResNet的網路結構解決之前所說的網絡退化的問題。
這是作者在CIFAR-10上的實驗結果,這里使用的shortcut是A方式,可以看出來在相同深度下ResNet的參數量遠遠小於其他網絡,這也使得我們在訓練和預測模型的時候計算比較快。作者還嘗試訓練了1202層的ResNet,但是最終結果並沒有110層的效果好,作者分析可能是因為過擬合的原因。作者還將ResNet用於Faster-RCNN中,獲得了2015年COCO的detection的冠軍。
ResNet-50 完整代碼
https://download.csdn.net/download/qq_34213260/12457533
參考資料:
https://zhuanlan.zhihu.com/p/31852747
https://zhuanlan.zhihu.com/p/32085715
https://zhuanlan.zhihu.com/p/42706477