1. 經(jīng)典回顧:ResNet的原理和實(shí)現(xiàn)

        共 6130字,需瀏覽 13分鐘

         ·

        2021-01-18 17:06


        編輯:張?? 歡


        PART

        01 ResNet簡(jiǎn)介


        引言


        深度殘差網(wǎng)絡(luò)(Deep residual network, ResNet)的提出是CNN圖像史上的一件里程碑事件,讓我們先看一下ResNetILSVRCCOCO 2015上的戰(zhàn)績(jī):


        1 ResNetILSVRCCOCO 2015上的戰(zhàn)績(jī)

        ResNet取得了5項(xiàng)第一,并又一次刷新了CNN模型在ImageNet上的歷史:

        2 ImageNet分類Top-5誤差

        ResNet的作者何凱明(http://kaiminghe.com/)也因此摘得CVPR2016最佳論文獎(jiǎng),當(dāng)然何博士的成就遠(yuǎn)不止于此,感興趣的可以去搜一下他后來的輝煌戰(zhàn)績(jī)。那么ResNet為什么會(huì)有如此優(yōu)異的表現(xiàn)呢?其實(shí)ResNet是解決了深度CNN模型難訓(xùn)練的問題,從圖2中可以看到14年的VGG19層,而15年的ResNet多達(dá)152層,這在網(wǎng)絡(luò)深度完全不是一個(gè)量級(jí)上,所以如果是第一眼看這個(gè)圖的話,肯定會(huì)覺得ResNet是靠深度取勝。事實(shí)當(dāng)然是這樣,但是ResNet還有架構(gòu)上的trick,這才使得網(wǎng)絡(luò)的深度發(fā)揮出作用,這個(gè)trick就是殘差學(xué)習(xí)(Residual learning)。下面詳細(xì)講述ResNet的理論及實(shí)現(xiàn)。


        PART

        02 深度網(wǎng)絡(luò)的退化問題



        從經(jīng)驗(yàn)來看,網(wǎng)絡(luò)的深度對(duì)模型的性能至關(guān)重要,當(dāng)增加網(wǎng)絡(luò)層數(shù)后,網(wǎng)絡(luò)可以進(jìn)行更加復(fù)雜的特征模式的提取,所以當(dāng)模型更深時(shí)理論上可以取得更好的結(jié)果,從圖2中也可以看出網(wǎng)絡(luò)越深而效果越好的一個(gè)實(shí)踐證據(jù)。但是更深的網(wǎng)絡(luò)其性能一定會(huì)更好嗎?實(shí)驗(yàn)發(fā)現(xiàn)深度網(wǎng)絡(luò)出現(xiàn)了退化問題(Degradation problem):網(wǎng)絡(luò)深度增加時(shí),網(wǎng)絡(luò)準(zhǔn)確度出現(xiàn)飽和,甚至出現(xiàn)下降。這個(gè)現(xiàn)象可以在圖3中直觀看出來:56層的網(wǎng)絡(luò)比20層網(wǎng)絡(luò)效果還要差。這不會(huì)是過擬合問題,因?yàn)?/span>56層網(wǎng)絡(luò)的訓(xùn)練誤差同樣高。我們知道深層網(wǎng)絡(luò)存在著梯度消失或者爆炸的問題,這使得深度學(xué)習(xí)模型很難訓(xùn)練。但是現(xiàn)在已經(jīng)存在一些技術(shù)手段如BatchNorm來緩解這個(gè)問題。因此,出現(xiàn)深度網(wǎng)絡(luò)的退化問題是非常令人詫異的。

        ?


        圖3 20層與56層網(wǎng)絡(luò)在CIFAR-10上的誤差



        PART

        03 殘差學(xué)習(xí)



        深度網(wǎng)絡(luò)的退化問題至少說明深度網(wǎng)絡(luò)不容易訓(xùn)練。但是我們考慮這樣一個(gè)事實(shí):現(xiàn)在你有一個(gè)淺層網(wǎng)絡(luò),你想通過向上堆積新層來建立深層網(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ǎng)絡(luò)很難去找到一個(gè)好的參數(shù)。

        這個(gè)有趣的假設(shè)讓何博士靈感爆發(fā),他提出了殘差學(xué)習(xí)來解決退化問題。對(duì)于一個(gè)堆積層結(jié)構(gòu)(幾層堆積而成)當(dāng)輸入為時(shí)其學(xué)習(xí)到的特征記為,現(xiàn)在我們希望其可以學(xué)習(xí)到殘差,這樣其實(shí)原始的學(xué)習(xí)特征是。之所以這樣是因?yàn)闅埐顚W(xué)習(xí)相比原始特征直接學(xué)習(xí)更容易。當(dāng)殘差為0時(shí),此時(shí)堆積層僅僅做了恒等映射,至少網(wǎng)絡(luò)性能不會(huì)下降,實(shí)際上殘差不會(huì)為0,這也會(huì)使得堆積層在輸入特征基礎(chǔ)上學(xué)習(xí)到新的特征,從而擁有更好的性能。殘差學(xué)習(xí)的結(jié)構(gòu)如圖4所示。這有點(diǎn)類似與電路中的“短路”,所以是一種短路連接(shortcutconnection)。

        4 殘差學(xué)習(xí)單元

        為什么殘差學(xué)習(xí)相對(duì)更容易,從直觀上看殘差學(xué)習(xí)需要學(xué)習(xí)的內(nèi)容少,因?yàn)闅埐钜话銜?huì)比較小,學(xué)習(xí)難度小點(diǎn)。不過我們可以從數(shù)學(xué)的角度來分析這個(gè)問題,首先殘差單元可以表示為:


        其中分別表示的是第個(gè)殘差單元的輸入和輸出,注意每個(gè)殘差單元一般包含多層結(jié)構(gòu)。?是殘差函數(shù),表示學(xué)習(xí)到的殘差,而?表示恒等映射,?ReLU激活函數(shù)。基于上式,我們求得從淺層?到深層的學(xué)習(xí)特征

        利用鏈?zhǔn)揭?guī)則,可以求得反向過程的梯度:


        式子的第一個(gè)因子表示的損失函數(shù)到達(dá)的梯度,小括號(hào)中的1表明短路機(jī)制可以無損地傳播梯度,而另外一項(xiàng)殘差梯度則需要經(jīng)過帶有weights的層,梯度不是直接傳遞過來的。殘差梯度不會(huì)那么巧全為-1,而且就算其比較小,有1的存在也不會(huì)導(dǎo)致梯度消失。所以殘差學(xué)習(xí)會(huì)更容易。要注意上面的推導(dǎo)并不是嚴(yán)格的證明。




        PART

        04 Resnet的網(wǎng)絡(luò)結(jié)構(gòu)































        ResNet網(wǎng)絡(luò)是參考了VGG19網(wǎng)絡(luò),在其基礎(chǔ)上進(jìn)行了修改,并通過短路機(jī)制加入了殘差單元,如圖5所示。變化主要體現(xiàn)在ResNet直接使用stride=2的卷積做下采樣,并且用global average pool層替換了全連接層。ResNet的一個(gè)重要設(shè)計(jì)原則是:當(dāng)feature map大小降低一半時(shí),featuremap的數(shù)量增加一倍,這保持了網(wǎng)絡(luò)層的復(fù)雜度。從圖5中可以看到,ResNet相比普通網(wǎng)絡(luò)每?jī)蓪娱g增加了短路機(jī)制,這就形成了殘差學(xué)習(xí),其中虛線表示featuremap數(shù)量發(fā)生了改變。圖5展示的34-layerResNet,還可以構(gòu)建更深的網(wǎng)絡(luò)如表1所示。從表中可以看到,對(duì)于18-layer34-layerResNet,其進(jìn)行的兩層間的殘差學(xué)習(xí),當(dāng)網(wǎng)絡(luò)更深時(shí),其進(jìn)行的是三層間的殘差學(xué)習(xí),三層卷積核分別是1x1,3x31x1,一個(gè)值得注意的是隱含層的feature map數(shù)量是比較小的,并且是輸出feature map數(shù)量的1/4。

        5 ResNet網(wǎng)絡(luò)結(jié)構(gòu)圖

        ?

        ?

        1 不同深度的ResNet

        下面我們?cè)俜治鲆幌職埐顔卧?/span>ResNet使用兩種殘差單元,如圖6所示。左圖對(duì)應(yīng)的是淺層網(wǎng)絡(luò),而右圖對(duì)應(yīng)的是深層網(wǎng)絡(luò)。對(duì)于短路連接,當(dāng)輸入和輸出維度一致時(shí),可以直接將輸入加到輸出上。但是當(dāng)維度不一致時(shí)(對(duì)應(yīng)的是維度增加一倍),這就不能直接相加。有兩種策略:(1)采用zero-padding增加維度,此時(shí)一般要先做一個(gè)downsamp,可以采用strde=2pooling,這樣不會(huì)增加參數(shù);(2)采用新的映射(projection shortcut),一般采用1x1的卷積,這樣會(huì)增加參數(shù),也會(huì)增加計(jì)算量。短路連接除了直接使用恒等映射,當(dāng)然都可以采用projection shortcut

        6 不同的殘差單元

        作者對(duì)比18-layer34-layer的網(wǎng)絡(luò)效果,如圖7所示??梢钥吹狡胀ǖ木W(wǎng)絡(luò)出現(xiàn)退化現(xiàn)象,但是ResNet很好的解決了退化問題。

        7 18-layer34-layer的網(wǎng)絡(luò)效果

        最后展示一下ResNet網(wǎng)絡(luò)與其他網(wǎng)絡(luò)在ImageNet上的對(duì)比結(jié)果,如表2所示。可以看到ResNet-152其誤差降到了4.49%,當(dāng)采用集成模型后,誤差可以降到3.57%

        ?

        ?

        2 ResNet與其他網(wǎng)絡(luò)的對(duì)比結(jié)果

        說一點(diǎn)關(guān)于殘差單元題外話,上面我們說到了短路連接的幾種處理方式,其實(shí)作者在文獻(xiàn)[2]中又對(duì)不同的殘差單元做了細(xì)致的分析與實(shí)驗(yàn),這里我們直接拋出最優(yōu)的殘差結(jié)構(gòu),如圖8所示。改進(jìn)前后一個(gè)明顯的變化是采用pre-activation,BNReLU都提前了。而且作者推薦短路連接采用恒等變換,這樣保證短路連接不會(huì)有阻礙。感興趣的可以去讀讀這篇文章。

        8 改進(jìn)后的殘差單元及效果





































































        PART

        05 ResNet的TensorFlow實(shí)現(xiàn)



        這里給出ResNet50TensorFlow實(shí)現(xiàn),模型的實(shí)現(xiàn)參考了Caffe版本的實(shí)現(xiàn)(https://github.com/KaimingHe/deep-residual-networks),核心代碼如下:


        class ResNet50(object):
        ?def __init__(self, inputs,num_classes=1000, is_training=True,scope="resnet50"):
        ?self.inputs
        =inputs
        ?self.is_training =is_training
        ?self.num_classes =num_classes
        ?with tf.variable_scope(scope):
        ? ? # construct themodel
        ? net =conv2d(inputs, 64, 7, 2, scope="conv1")#->
        ?[batch,112, 112, 64]
        ? net = tf.nn.relu(batch_norm(net,is_training=self.is_training, scope="bn1"))
        ? net = max_pool(net, 3, 2, scope="maxpool1")#->
        [batch, 56, 56, 64]
        ? net = self._block(net, 256, 3, init_stride=1, is_training=self.is_training,scope="block2")# ->
        [batch, 56,56, 256]
        ? net = self._block(net, 512, 4, is_training=self.is_training, scope="block3")#->
        [batch, 28, 28, 512]
        ? net = self._block(net, 1024, 6, is_training=self.is_training,scope="block4")#->
        [batch, 14, 14, 1024]
        ? net = self._block(net, 2048, 3, is_training=self.is_training, scope="block5")#->
        [batch, 7, 7, 2048]
        ? net =avg_pool(net, 7, scope="avgpool5")#->
        [batch, 1,1, 2048]
        ? net =tf.squeeze(net, [1, 2],
        ? ? ? ? ? ?name="SpatialSqueeze")#->
        [batch,2048]
        ? self.logits = fc(net, self.num_classes,"fc6")#->
        [batch,num_classes]
        ? self.predictions =tf.nn.softmax(self.logits)
        ? def _block(self, x, n_out, n,init_stride=2, is_training=True, scope="block"):
        ? ?with tf.variable_scope(scope):
        ? ?h_out = n_out // 4
        ? ?out = self._bottleneck(x,h_out, n_out, ? ? ?stride=init_stride,is_training=is_training, scope="bottlencek1")
        ? ?for i in range(1, n):
        ? ? ?out = self._bottleneck(out,h_out, n_out, is_training=is_training,scope=("bottlencek%s"% (i + 1)))
        ? ? ?return out
        ? ?def _bottleneck(self, x, h_out, n_out,stride=None, is_training=True, scope="bottleneck"):
        ? ?"""A residual bottleneck unit"""
        ? ? n_in =x.get_shape()[-1]
        ? ? if stride is None:
        ? ? ?stride = 1 if n_in == n_out else 2
        ? ? with tf.variable_scope(scope):
        ? ? ? h = conv2d(x, h_out, 1, stride=stride, scope="conv_1")
        ? ? ? h = batch_norm(h, is_training=is_training, scope="bn_1")
        ? ? ? h = tf.nn.relu(h)
        ? ? ? h = conv2d(h, h_out, 3, stride=1, scope="conv_2")
        ? ? ? h = batch_norm(h, is_training=is_training, scope="bn_2")
        ? ? ? h = tf.nn.relu(h)
        ? ? ? h = conv2d(h, n_out, 1, stride=1, scope="conv_3")
        ? ? ? h = batch_norm(h, is_training=is_training, scope="bn_3")
        ? ? if n_in != n_out:
        ? ? ? shortcut = conv2d(x,n_out, 1, stride=stride, scope="conv_4")
        ? ? ? shortcut =batch_norm(shortcut, is_training=is_training, scope="bn_4")
        ? ? ?else:
        ? ? ? ?shortcut = x
        ? ? ? ?return tf.nn.relu(shortcut+ h)


        ?

        完整實(shí)現(xiàn)可以參見GitHub(https://github.com/xiaohu2015/DeepLearning_tutorials/)。

























        PART

        06 結(jié)束


        總結(jié)


        ResNet通過殘差學(xué)習(xí)解決了深度網(wǎng)絡(luò)的退化問題,讓我們可以訓(xùn)練出更深的網(wǎng)絡(luò),這稱得上是深度網(wǎng)絡(luò)的一個(gè)歷史大突破吧。也許不久會(huì)有更好的方式來訓(xùn)練更深的網(wǎng)絡(luò),讓我們一起期待吧!

        參考資料




        參考資料


        1. Deep ResidualLearning for Image Recognition: https://arxiv.org/abs/1512.03385.

        2. Identity Mappings in Deep Residual Networks: https://arxiv.org/abs/1603.05027.

        3. 去膜拜一下大神: http://kaiminghe.com/.




















        1.全面直觀認(rèn)識(shí)深度神經(jīng)網(wǎng)絡(luò)

        2.機(jī)器學(xué)習(xí)實(shí)戰(zhàn)——LBP特征提取

        3.RNN入門與實(shí)踐

        4.Logistic回歸實(shí)戰(zhàn)篇之預(yù)測(cè)病馬死亡率(三)





        機(jī)器學(xué)習(xí)算法全棧工程師


        ? ? ? ? ? ? ? ? ? ? ? ? ? ? 一個(gè)用心的公眾號(hào)

        長(zhǎng)按,識(shí)別,加關(guān)注

        進(jìn)群,學(xué)習(xí),得幫助

        你的關(guān)注,我們的熱度,

        我們一定給你學(xué)習(xí)最大的幫助









        公眾號(hào)商務(wù)合作請(qǐng)聯(lián)系 ????



        瀏覽 149
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 在线艹B 天天狠狠干 | 人人艹大香蕉 | 加勒比av | aaa视频 | 国产黄色三级片网站 |