深度殘差網(wǎng)絡(luò)(Deep Residual Network夸溶,ResNet)的提出是CNN圖像史上的里程碑事件顽频。ResNet在ILSVRC和COCO 2015上的取得了五項(xiàng)第一毙沾,ResNet的作者何凱明也因此摘得CVPR2016最佳論文獎(jiǎng)(鏈接:https://arxiv.org/pdf/1512.03385.pdf)
ResNet主要解決深度CNN網(wǎng)絡(luò)難訓(xùn)練的問(wèn)題窑睁,從圖一可以看出归形,14年的VGG才19層托慨,而15年的ResNet多達(dá)152層,雖然ResNet確實(shí)好像是靠深度取勝暇榴,但是ResNet還有架構(gòu)上的trick厚棵,這才使得網(wǎng)絡(luò)的深度發(fā)揮出作用,這個(gè)trick就是殘差學(xué)習(xí)(Residual learning)蔼紧。下面詳細(xì)講述ResNet的理論及實(shí)現(xiàn)婆硬。1 深度網(wǎng)絡(luò)的退化問(wèn)題
從經(jīng)驗(yàn)來(lái)看,網(wǎng)絡(luò)的深度對(duì)模型的性能至關(guān)重要奸例,當(dāng)增加網(wǎng)絡(luò)層數(shù)后彬犯,網(wǎng)絡(luò)可以進(jìn)行更加復(fù)雜的特征模式的提取,所以當(dāng)模型更深時(shí)理論上可以取得更好的結(jié)果查吊,從圖一也可以看出網(wǎng)絡(luò)越深而效果越好的一個(gè)實(shí)踐證據(jù)谐区。但是更深的網(wǎng)絡(luò)性能一定會(huì)更好嗎?實(shí)驗(yàn)發(fā)現(xiàn)深度網(wǎng)絡(luò)出現(xiàn)了退化問(wèn)題(Degradation problem):網(wǎng)絡(luò)深度增加時(shí)菩貌,網(wǎng)絡(luò)準(zhǔn)確度出現(xiàn)飽和卢佣,甚至出現(xiàn)下降。這個(gè)現(xiàn)象可以從圖二中直觀看出來(lái):56層的網(wǎng)絡(luò)比20層網(wǎng)絡(luò)的效果還要差箭阶。這不會(huì)是過(guò)擬合問(wèn)題虚茶,因?yàn)?6層網(wǎng)絡(luò)的訓(xùn)練誤差同樣高。我們知道深層網(wǎng)絡(luò)存在著梯度爆炸或者梯度消失的問(wèn)題仇参,這使得深度學(xué)習(xí)模型很難訓(xùn)練嘹叫。但是現(xiàn)在已經(jīng)存在一些技術(shù)手段如BatchNorm來(lái)解決這個(gè)問(wèn)題,因此出現(xiàn)深度網(wǎng)絡(luò)的退化問(wèn)題是非常令人詫異的诈乒。2 殘差學(xué)習(xí)
2.1 殘差塊
深度網(wǎng)絡(luò)的退化問(wèn)題至少說(shuō)明深度網(wǎng)絡(luò)不容易訓(xùn)練罩扇。但是我們考慮這樣一個(gè)事實(shí):現(xiàn)在你有一個(gè)淺層網(wǎng)絡(luò),你想通過(guò)向上堆積新層來(lái)建立深層網(wǎng)絡(luò),一個(gè)極端情況是這些增加的層什么也不學(xué)習(xí)喂饥,僅僅復(fù)制淺層網(wǎng)絡(luò)的特征消约,即這樣新層是恒等映射(Identity mapping)。在這種情況下员帮,深層網(wǎng)絡(luò)應(yīng)該至少和淺層網(wǎng)絡(luò)性能一樣或粮,也不應(yīng)該出現(xiàn)退化現(xiàn)象。好吧捞高,你不得不承認(rèn)肯定是目前的訓(xùn)練方法有問(wèn)題氯材,才使得深層網(wǎng)絡(luò)很難去找到一個(gè)好的參數(shù)。
這個(gè)有趣的假設(shè)讓何博士靈感爆發(fā)硝岗,他提出了殘差學(xué)習(xí)來(lái)解決退化問(wèn)題氢哮。對(duì)于一個(gè)堆積層結(jié)構(gòu)(幾層堆積而成)當(dāng)輸入為x時(shí)其學(xué)習(xí)到的特征記為H(x),現(xiàn)在我們希望其可以學(xué)習(xí)到殘差F(x) = H(x) - x型檀,這樣其實(shí)原始的學(xué)習(xí)特征是F(x) + x冗尤。之所以這樣是因?yàn)闅埐顚W(xué)習(xí)相比原始特征直接學(xué)習(xí)更容易。當(dāng)殘差為0時(shí)贱除,此時(shí)堆積層在輸入特征基礎(chǔ)上學(xué)習(xí)到新的特征生闲,從而擁有更好的性能媳溺。殘差學(xué)習(xí)的結(jié)構(gòu)如圖三所示月幌,這有點(diǎn)類似于電路中的“短路”,所以是一種短路連接(shortcut connection)悬蔽。
從數(shù)學(xué)的角度來(lái)分析這個(gè)問(wèn)題澎埠,殘差單元可以表示為
2.2 殘差網(wǎng)絡(luò)
殘差網(wǎng)絡(luò)的搭建分為兩步
1:使用VGG公式搭建Plain VGG
2:在Plain VGG的卷積網(wǎng)絡(luò)之前插入Identity Mapping祥国,注意需要升維或者降維的時(shí)候加入1*1卷積
在實(shí)現(xiàn)過(guò)程中,一般是直接stack殘差塊的方式晾腔。
def resnet_v1(x):
x = Conv2D(kernel_size=(3,3), filters=16, strides=1, padding='same', activation='relu')(x)
x = res_block_v1(x, 16, 16)
x = res_block_v1(x, 16, 32)
x = Flatten()(x)
outputs = Dense(10, activation='softmax', kernel_initializer='he_normal')(x)
return outputs
2.3 為什么叫殘差網(wǎng)絡(luò)
在統(tǒng)計(jì)學(xué)中舌稀,殘差和誤差是兩個(gè)很容易混淆的概念啊犬。誤差是衡量觀測(cè)值(模型的輸入)和真實(shí)值之間的差距,殘差是指預(yù)測(cè)值(模型的輸出)和觀測(cè)值之間的差距壁查。對(duì)于殘差網(wǎng)絡(luò)的命名原因椒惨,作者給出的解釋是,網(wǎng)絡(luò)的一層通吵弊铮可以看做 y=H(x) 康谆,而殘差網(wǎng)絡(luò)的一個(gè)殘差塊可以表示為 H(x) = F(x) + x,也就是 F(x) = H(x) -x嫉到,在單位映射中沃暗,y = x 便是觀測(cè)值,而 H(x) 是預(yù)測(cè)值何恶,所以 F(x) 便對(duì)應(yīng)著殘差孽锥,因此叫做殘差網(wǎng)絡(luò)。
3 殘差網(wǎng)絡(luò)的背后原理
殘差塊一個(gè)更通用的表示方式是
利用BP中使用的鏈?zhǔn)揭?guī)則介粘,可以求得反向過(guò)程的梯度:
1,在整個(gè)訓(xùn)練過(guò)程中晚树,對(duì)F(xi,Wi)對(duì)xl的偏導(dǎo)(即括號(hào)里的那個(gè)公式)不可能一直為-1姻采,也就是說(shuō)在殘差網(wǎng)絡(luò)中不會(huì)出現(xiàn)梯度消失的問(wèn)題
2,損失函數(shù)對(duì)xL求得偏導(dǎo)(即上式第一個(gè)等式后的第一個(gè)公式)表示L層的梯度可以直接傳遞到任何一個(gè)比它淺的l層爵憎。
通過(guò)分析殘差網(wǎng)絡(luò)的正向和反向的兩個(gè)過(guò)程慨亲,我們發(fā)現(xiàn),當(dāng)殘差塊滿足上面兩個(gè)假設(shè)時(shí)纲堵,信息可以非常暢通地在高層和低層之間相互傳導(dǎo)巡雨,說(shuō)明這兩個(gè)假設(shè)是讓殘差網(wǎng)絡(luò)可以訓(xùn)練深度模型的充分條件。
那么這兩個(gè)假設(shè)是必要條件嗎席函?
3.1 直接映射是最好的選擇
對(duì)于假設(shè)1铐望,我們采用反證法,假設(shè)h(xl) = axl,那么這時(shí)候正蛙,殘差塊表示為(圖中的系數(shù)就是a):
1,當(dāng)系數(shù)a>1時(shí)锻全,很有可能發(fā)生梯度爆炸狂塘;
2,當(dāng)系數(shù)a<1時(shí)鳄厌,梯度變成0荞胡,會(huì)阻礙殘差網(wǎng)絡(luò)信息的反向傳遞,從而影響殘差網(wǎng)絡(luò)的訓(xùn)練了嚎。
所以系數(shù)a必須為1泪漂。同理,其他常見的激活函數(shù)都會(huì)產(chǎn)生和上面的例子類似的阻礙信息反向傳播的問(wèn)題歪泳。
對(duì)于其他不影響梯度的h()萝勤,例如LSTM中的門機(jī)制,或者Dropout以及用于降維的1*1卷積也許會(huì)有效果呐伞,
作者采用了實(shí)驗(yàn)的方法進(jìn)行驗(yàn)證敌卓,實(shí)驗(yàn)結(jié)果表明,在所有的變異模型中荸哟,依舊是直接映射的效果最好假哎。
3.2 激活函數(shù)的位置
上面提出的殘差塊可以詳細(xì)展開如下圖a,即在卷積之后使用BN做歸一化鞍历,然后在和直接映射單位加之后使用ReLU作為激活函數(shù)。
該網(wǎng)絡(luò)一般就在resnet_v2刑枝,keras實(shí)現(xiàn)如下:
def res_block_v2(x, input_filter, output_filter):
res_x = BatchNormalization()(x)
res_x = Activation('relu')(res_x)
res_x = Conv2D(kernel_size=(3,3), filters=output_filter, strides=1, padding='same')(res_x)
res_x = BatchNormalization()(res_x)
res_x = Activation('relu')(res_x)
res_x = Conv2D(kernel_size=(3,3), filters=output_filter, strides=1, padding='same')(res_x)
if input_filter == output_filter:
identity = x
else: #需要升維或者降維
identity = Conv2D(kernel_size=(1,1), filters=output_filter, strides=1, padding='same')(x)
output= keras.layers.add([identity, res_x])
return output
def resnet_v2(x):
x = Conv2D(kernel_size=(3,3), filters=16 , strides=1, padding='same', activation='relu')(x)
x = res_block_v2(x, 16, 16)
x = res_block_v2(x, 16, 32)
x = BatchNormalization()(x)
y = Flatten()(x)
outputs = Dense(10, activation='softmax', kernel_initializer='he_normal')(y)
return outputs