深度學習之殘差神經網路(ResNet)
寫在前面
自2012年AlexNet在ImageNet圖片分類比賽中大獲成功以來,關於深度神經網路的研究又一次如火如荼般進行,在2015年ResNet奪魁到達頂峰。此後的無數卷積神經網路,似乎都有了ResNet的影子,例如DenseNet等等。那麼ResNet究竟有何神奇之處?本文將基於何愷明等人的論文
Deep Residual Learning for Image Recognition
和其他相關資料,探討ResNet的細節所在。
為什麼ResNet會誕生
深度神經網路由於其具有很多層數(卷積層+全連線層)而冠以“深度”之名,基於神經網路的機器學習演算法因此也被稱作“深度學習”(由此可見AI從業者的包裝能力之強)。然而隨著算力的不斷增強、資料集的不斷擴張,一系列問題隨之而來,例如:
· 層數越多,訓練效果一定越好嗎?
· 如何最佳化過深的神經網路?
· 如何避免梯度消失和梯度爆炸?
對於第一個問題,不假思索來看,在相同的最佳化條件下,更深神經網路的訓練效果更強是十分顯然的,然而由於引數過多、模型複雜度更高,深層神經網路會出現十分嚴重的過擬合問題,即訓練集與測試集準確度之間的gap過大,由此引發了一系列的正則化方法,在此先按下不表。
然而,何愷明在實際實驗時發現,不僅是測試精度,而且在訓練精度上,更深的(56層)神經網路也遠不如淺一些的(20層)神經網路(如下圖)。
更深的神經網路訓練和測試精度均不如淺層網路
由此引出了問題:
Is learning better networks as easy as stacking more layers?
對於以上問題而言,除了十分棘手的梯度消失/梯度爆炸,以及過擬合的問題之外(以上議題將會在未來的更新中涉及),一個十分嚴峻的挑戰在於負最佳化問題(degradation)。也就是說,56層的神經網路相比於20層,新增加的36層是對神經網路的“惡化”,它們非但沒有起到自己應有的作用,反而扭曲了網路空間,升高了training error。
由此一個想法自然而然的產生:如果這36層神經網路是恆等對映(identity mapping),那麼56層的神經網路不就和20層的一樣好了嗎?
更進一步呢?如果這36層神經網路相比於恆等對映再好上那麼一點點(更接近最優函式),那麼不就起到了正最佳化的作用了嗎?ResNet的insight由此誕生。
ResNet的數學表示
基於以上的考慮,我們在所擬合的函式中加入恆等函式。
假設某一層內,最優函式記為
,那麼我們所擬合的目標函式
定義為
,函式
被稱為“殘差函式”。
由此可見,我們所需要的函式由兩部分組成:恆等函式和殘差函式。恆等函式的存在,避免了“負最佳化”問題,而殘差函式則起到了“錦上添花”的作用。
這一做法基於這樣的假設:最優函式與線性函式有較高的相似性,極端來看,若最優函式就是線性函式,那麼我們的最佳化就會變得極為容易,因為F(x)的引數在初始化時接近於0,因此不會在訓練過程中獲取較大的梯度,因而不會影響恆等函式的表現。
因此,ResNet的基本架構就由如下圖的殘差塊所組成。
殘差塊
殘差塊中的恆等函式部分,也被稱為shortcut connection(or skip link)。
對應到神經網路中,殘差塊的數學表示式可以寫成:
。
其中,
代表殘差塊的輸出,
代表啟用函式,
代表殘差函式,
代表輸入,
代表殘差塊內的所有權重。
當然,在全連線層中,如果
項的維度與
不同,則可以用一個變換矩陣
與
相乘(即對
做線性對映);如果在卷積層中二者形狀不同,則可以使用1*1卷積核(kernel)和zero-padding使得二者的維度與通道數相等。
殘差塊的程式碼實現(PyTorch)
注:部分內容參考了李沐等所著《Dive into Deep Learning》
ResNet沿用VGG完整的3*3卷積層設計,並使用了Batch Normalization層和ReLU啟用函式,此外引入了額外的1*1卷積層將輸入變換為需要的形狀,與殘差函式結果直接相加。
殘差塊的實現如下:
import
torch
from
torch
import
nn
from
torch。nn
import
functional
as
F
class
Residual
(
nn
。
Module
):
def
__init__
(
self
,
input_channels
,
num_channels
,
use_conv
=
False
,
strides
=
1
):
super
()
。
__init__
()
self
。
conv1
=
nn
。
Conv2d
(
input_channels
,
num_channels
,
kernel_size
=
3
,
padding
=
1
,
stride
=
strides
)
self
。
conv2
=
nn
。
Conv2d
(
num_channels
,
num_channels
,
kernel_size
=
3
,
padding
=
1
)
if
use_conv
:
self
。
conv3
=
nn
。
Conv2d
(
input_channels
,
num_channels
,
kernel_size
=
3
,
padding
=
1
)
else
:
self
。
conv3
=
None
self
。
bn1
=
nn
。
BatchNorm2d
(
num_channels
)
self
。
bn2
=
nn
。
BatchNorm2d
(
num_channels
)
def
forward
(
self
,
X
):
Y
=
F
。
relu
(
self
。
bn1
(
self
。
conv1
(
X
)))
Y
=
self
。
bn2
(
self
。
conv2
(
Y
))
if
self
。
conv3
:
X
=
self
。
conv3
(
X
)
Y
+=
X
return
F
。
relu
(
Y
)
如下圖所示,此程式碼生成兩種型別的網路,分別對應形狀一致和不同的情況,透過use_conv來判斷是否改變輸入的形狀。
殘差塊架構
透過以上的程式碼,我們構造出了殘差塊單元。由於其繼承了nn。Module,因此可以直接將其作為nn。Sequential()的引數進行ResNet的架構。這會在之後的內容中加以呈現。
為什麼需要Shortcut Connection
1、最佳化梯度回傳
在梯度更新的步驟,我們一般使用反向傳播法(backpropagation)來計算損失函式對於權重的梯度,並加以更新。然而在深層的神經網路中,由於各種原因(比如啟用函式的飽和性(saturated)、權重的分佈不佳等等),梯度在反向傳播的過程中,訊號會逐漸消失,這會導致頂層權重更新速度快,而底層權重幾乎不更新的情況。這是災難性的,因為底層所提取到的特徵往往更為重要,例如邊緣、紋理等等更為普遍的性質,從而使得訓練效果不佳。
除此之外,在一般的神經網路中,如果某一層停止學習,則訊號就會在此消失,而無法傳到更底層,從而造成“梯度消失”現象。
然而,在shortcut connection的存在下,梯度回傳時,即使幾層還沒有開始學習(殘差函式的梯度接近零),由於恆等函式的導數恆為1,整個函式的梯度依然接近於1,根據鏈式法則,先前的梯度依然可以反向傳播,網路也可以開始取得進展(見下圖)。藉助shortcut connection,訊號可以輕鬆地在整個網路中傳播,從而避免了“梯度消失”。
殘差網路中,梯度傳播更加順暢
2、殘差函式訓練更容易
讓我們複習一下:在訓練神經網路時,目標是使其成為目標函式H(x)的模型。如果將輸入x(即恆等函式)新增到網路的輸出,則網路則被迫建模殘差函式F(x),這稱為
殘差學習
。
如前所述,ResNet基於這樣一種假設:最優函式與線性函式有一定的相似性。初始化常規神經網路時,其權重引數接近零,因此網路僅輸出其輸入的副本。換言之,它首先對恆等函式建模。所以如果目標函式和恆等函式相當接近(通常是這種情況),那麼訓練速度會大大加快。這也是殘差學習相對更加容易的原因。
此外,利用殘差塊可以訓練出一個有效的深層神經網路:輸入可以透過層間的殘餘連線更快地向前傳播。
*3、巢狀函式類的實現
ResNet的提出,最初是為了解決“負最佳化”問題,也就是說讓更深層的網路表現的至少和淺層網路一樣好。這就涉及函式類的概念。
注:以下內容來源於
Dive into Deep Learning by Mu Li, et al。
假設我們有一類特定的神經網路架構
,它包括學習率及其他超引數。對於所有
,存在一些引數集(包括權重和偏置),這些引數可以透過在合適的訓練集上進行訓練所獲得。現在假設
是我們真正想要找到的函式,如果
,那麼我們可以輕而易舉地獲得它,但通常我們不會那麼幸運。相反,我們將嘗試找到一個函式
,這是我們在
中的最佳選擇。例如,給定一個具有
特性和
標籤的資料集,我們可以嘗試透過解決以下最佳化問題來找到它:
。
那麼,怎樣得到更近似真正
的函式呢?唯一合理的可能性是,我們需要設計一個更強大的架構
。換句話說,我們預計
比
“更近似”。然而,如果
,則無法保證新的體系“更近似”。事實上,
可能更糟:如圖所示,對於非巢狀函式(non-nested function)類,較複雜的函式類並不總是向“真”函式
靠攏(複雜度由
向
遞增)。在下圖的左邊,雖然
比
更接近
,但
卻離得更遠了。相反對於下圖右側的巢狀函式(nested function)類
,我們可以避免上述問題。
巢狀函式類和非巢狀函式類
因此,只有當較複雜的函式類包含較小的函式類時,我們才能確保提高它們的效能。對於深度神經網路,如果我們能將新新增的層訓練成恆等對映,新模型將和原模型同樣有效。同時,由於新模型可能得出更優的解來擬合訓練資料集,因此新增層似乎更容易降低訓練誤差。
如上所述,透過加入shortcut connection,我們構造出了一系列“巢狀函式類”,使得層數越多,訓練效果越好。
也許有人疑問,難道殘差函式不會對恆等函式“負最佳化”嗎?假設最優函式就是恆等函式,那麼在最初的訓練中,由於殘差函式部分的權重初始化接近於零,因此它們並不會獲得較大的梯度(相較於1),在更新時也只會發生極其有限的改變,對於恆等函式的影響幾乎可以忽略不計。因此,即使在最極端的情況下(最優函式等於恆等函式),殘差函式也並不會產生“肉眼可見”的degradation。
殘差神經網路(ResNet)的架構及程式碼實現
注:部分內容參考了
Dive into Deep Learning by Mu Li, et al。
ResNet的前兩層與GoogLeNet中一樣:在輸出通道數為64,步幅為2的7*7卷積層後,接步幅為2的3*3的最大池化層。不同之處在於ResNet每個卷積層後增加了Batch Norm層。程式碼如下(需要之前的所有程式碼):
b1
=
nn
。
Sequential
(
nn
。
Conv2d
(
1
,
64
,
kernel_size
=
7
,
stride
=
2
,
padding
=
3
),
nn
。
BatchNorm2d
(
64
),
nn
。
ReLU
(),
nn
。
MaxPool2d
(
kernel_size
=
3
,
stride
=
2
,
padding
=
1
))
之後,ResNet使用了4個由殘差塊組成的
模組
,每個模組使用若干個同樣輸出通道數的殘差塊。第一個模組的通道數同輸入通道數一致。由於之前已經使用了步幅為2的最大池化層,所以無需減小高和寬。之後的每個模組在第一個殘差塊裡將上一個模組的通道數翻倍,並將高寬減半。
下面是程式碼實現,注意,我們對第一個模組做了特別處理。
def
resnet_block
(
input_channels
,
num_channels
,
num_residuals
,
first_block
=
False
):
blk
=
[]
for
i
in
range
(
num_residuals
):
if
i
==
0
and
not
first_block
:
blk
。
append
(
Residual
(
input_channels
,
num_channels
,
use_conv
=
True
,
strides
=
2
))
else
:
blk
。
append
(
Residual
(
num_channels
,
num_channels
))
return
blk
接著在ResNet加入所有殘差塊,這裡每個模組使用2個殘差塊。
b2
=
nn
。
Sequential
(
*
resnet_block
(
64
,
64
,
2
,
first_block
=
True
))
b3
=
nn
。
Sequential
(
*
resnet_block
(
64
,
128
,
2
))
b4
=
nn
。
Sequential
(
*
resnet_block
(
128
,
256
,
2
))
b5
=
nn
。
Sequential
(
*
resnet_block
(
256
,
512
,
2
))
最後,與GoogLeNet一樣,在ResNet中加入全域性平均池化層,以及全連線層輸出。
net
=
nn
。
Sequential
(
b1
,
b2
,
b3
,
b4
,
b5
,
nn
。
AdaptiveAvgPool2d
((
1
,
1
)),
nn
。
Flatten
(),
nn
。
Linear
(
512
,
10
))
每個模組有4個卷積層(不包括恆等對映的1*1卷積層)。加上第一個7*7卷積層和最後一個全連線層,共有18層。因此這種模型被稱為ResNet-18,其架構圖如下:
此外,在何愷明等的論文中,給出了ResNet-34的架構圖:
ResNet的意義和引申
何愷明等人使用ResNet贏得了ILSVRC 2015挑戰賽,其錯誤率低至3。57%,已經超越了人的識別能力(大約5%)。這是一個劃時代的成就。
除此之外,ResNet的出現還啟發了之後的諸多神經網路架構,例如DenseNet,受到ResNet的shortcut connection和泰勒展開式的啟發,構建了類似多項式的稠密連線方式,是ResNet的邏輯擴充套件。其模式圖如下:
除此之外,ResNet還有更多的改進,例如deeper bottleneck architecture(如下圖),透過在3*3卷積層前後引入1*1卷積層分別來減少和增加維度,讓3*3卷積層成為“瓶頸”,減少引數和浮點運算,使得模型更加“輕量”。
這樣的改進,直接使得ResNet層數突破100大關,相繼產生了ResNet-101和ResNet-152這樣極其深邃的神經網路。經實驗驗證,它們的精確度均超過了ResNet-34。
實驗表明,單一ResNet-152的錯誤率可達4。49%。而透過整合6個不同深度的ResNet(含2個ResNet-152),何愷明等人以3。57%的錯誤率在比賽中拔得頭籌。而如此深的網路能夠正常訓練,也得益於shortcut connection提供的數值穩定性。
References:
· Deep Residual Learning for Image Recognition by Kaiming He, et al。
(arXiv: 1512。03385v1 [cs。CV] 10 Dec 2015)
· Dive into Deep Learning by Mu Li, et al。
·
Hands-on Machine Learning with Scikit-Learn & Tensorflow
by Aurélien Géron
寫在最後
本文從多個角度出發,簡單地介紹了ResNet的“前世今生”,旨在學術交流,如有錯誤,敬請批評指正!
本文部分圖片為轉載,如有侵權,請聯絡替換。
上一篇:《詩經》中有哪些關於星象的記載?
下一篇:初三學習不進反退怎麼辦?