日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CNN发展历史【从LeNet到DenseNet】

發(fā)布時間:2025/3/17 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CNN发展历史【从LeNet到DenseNet】 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前面介紹了很多CNN的網(wǎng)絡(luò)結(jié)構(gòu),很多很雜,自己都有點分不清了,所以現(xiàn)在寫一個總結(jié)。

CNN從90年代的LeNet開始,21世紀初沉寂了10年,直到12年AlexNet開始又再煥發(fā)第二春,從ZF Net到VGG,GoogLeNet再到ResNet和最近的DenseNet,網(wǎng)絡(luò)越來越深,架構(gòu)越來越復(fù)雜,解決反向傳播時梯度消失的方法也越來越巧妙。新年有假期,就好好總結(jié)一波CNN的各種經(jīng)典架構(gòu)吧,領(lǐng)略一下CNN的發(fā)展歷程中各路大神之間的智慧碰撞之美。

上面那圖是ILSVRC歷年的Top-5錯誤率,我們會按照以上經(jīng)典網(wǎng)絡(luò)出現(xiàn)的時間順序?qū)λ麄冞M行介紹。

本文將會談到以下經(jīng)典的卷積神經(jīng)網(wǎng)絡(luò):

  • LeNet
  • AlexNet
  • ZF
  • VGG
  • GoogLeNet
  • ResNet
  • DenseNet
  • 開山之作:LeNet

    閃光點:定義了CNN的基本組件,是CNN的鼻祖。

    LeNet是卷積神經(jīng)網(wǎng)絡(luò)的祖師爺LeCun在1998年提出,用于解決手寫數(shù)字識別的視覺任務(wù)。自那時起,CNN的最基本的架構(gòu)就定下來了:卷積層、池化層、全連接層。如今各大深度學(xué)習(xí)框架中所使用的LeNet都是簡化改進過的LeNet-5(-5表示具有5個層),和原始的LeNet有些許不同,比如把激活函數(shù)改為了現(xiàn)在很常用的ReLu。

    LeNet-5跟現(xiàn)有的conv->pool->ReLU的套路不同,它使用的方式是conv1->pool->conv2->pool2再接全連接層,但是不變的是,卷積層后緊接池化層的模式依舊不變。

    以上圖為例,對經(jīng)典的LeNet-5做深入分析:

  • 首先輸入圖像是單通道的28*28大小的圖像,用矩陣表示就是[1,28,28]
  • 第一個卷積層conv1所用的卷積核尺寸為5*5,滑動步長為1,卷積核數(shù)目為20,那么經(jīng)過該層后圖像尺寸變?yōu)?4,28-5+1=24,輸出矩陣為[20,24,24]。
  • 第一個池化層pool核尺寸為2*2,步長2,這是沒有重疊的max pooling,池化操作后,圖像尺寸減半,變?yōu)?2×12,輸出矩陣為[20,12,12]。
  • 第二個卷積層conv2的卷積核尺寸為5*5,步長1,卷積核數(shù)目為50,卷積后圖像尺寸變?yōu)?,這是因為12-5+1=8,輸出矩陣為[50,8,8].
  • 第二個池化層pool2核尺寸為2*2,步長2,這是沒有重疊的max pooling,池化操作后,圖像尺寸減半,變?yōu)?×4,輸出矩陣為[50,4,4]。
  • pool2后面接全連接層fc1,神經(jīng)元數(shù)目為500,再接relu激活函數(shù)。
  • 再接fc2,神經(jīng)元個數(shù)為10,得到10維的特征向量,用于10個數(shù)字的分類訓(xùn)練,送入softmaxt分類,得到分類結(jié)果的概率output。
  • LeNet的Keras實現(xiàn):

    def LeNet():model = Sequential()model.add(Conv2D(32,(5,5),strides=(1,1),input_shape=(28,28,1),padding='valid',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(2,2)))model.add(Conv2D(64,(5,5),strides=(1,1),padding='valid',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(2,2)))model.add(Flatten())model.add(Dense(100,activation='relu'))model.add(Dense(10,activation='softmax'))return model

    王者歸來:AlexNet

    AlexNet在2012年ImageNet競賽中以超過第二名10.9個百分點的絕對優(yōu)勢一舉奪冠,從此深度學(xué)習(xí)和卷積神經(jīng)網(wǎng)絡(luò)名聲鵲起,深度學(xué)習(xí)的研究如雨后春筍般出現(xiàn),AlexNet的出現(xiàn)可謂是卷積神經(jīng)網(wǎng)絡(luò)的王者歸來。

    閃光點:

    • 更深的網(wǎng)絡(luò)
    • 數(shù)據(jù)增廣
    • ReLU
    • dropout
    • LRN

    以上圖AlexNet架構(gòu)為例,這個網(wǎng)絡(luò)前面5層是卷積層,后面三層是全連接層,最終softmax輸出是1000類,取其前兩層進行詳細說明。

  • AlexNet共包含5層卷積層和三層全連接層,層數(shù)比LeNet多了不少,但卷積神經(jīng)網(wǎng)絡(luò)總的流程并沒有變化,只是在深度上加了不少。

  • AlexNet針對的是1000類的分類問題,輸入圖片規(guī)定是256×256的三通道彩色圖片,為了增強模型的泛化能力,避免過擬合,作者使用了隨機裁剪的思路對原來256×256的圖像進行隨機裁剪,得到尺寸為3×224×224的圖像,輸入到網(wǎng)絡(luò)訓(xùn)練。

  • 因為使用多GPU訓(xùn)練,所以可以看到第一層卷積層后有兩個完全一樣的分支,以加速訓(xùn)練。

  • 針對一個分支分析:第一層卷積層conv1的卷積核尺寸為11×11,滑動步長為4,卷積核數(shù)目為48。卷積后得到的輸出矩陣為[48,55,55]。這里的55是個難以理解的數(shù)字,作者也沒有對此說明,如果按照正常計算的話(224-11)/4+1 != 55的,所以這里是做了padding再做卷積的,即先padiing圖像至227×227,再做卷積(227-11)/4+1 = 55。這些像素層經(jīng)過relu1單元的處理,生成激活像素層,尺寸仍為2組48×55×55的像素層數(shù)據(jù)
    。然后經(jīng)過歸一化處理,歸一化運算的尺度為5*5。第一卷積層運算結(jié)束后形成的像素層的規(guī)模為48×27×27。
  • 輸入矩陣是[48,55,55].接著是池化層,做max pooling操作,池化運算的尺度為3*3,運算的步長為2,則池化后圖像的尺寸為(55-3)/2+1=27。所以得到的輸出矩陣是[48,27,27]。后面層不再重復(fù)敘述。

  • AlexNet用到訓(xùn)練技巧:

    • 數(shù)據(jù)增廣技巧來增加模型泛化能力。
    • 用ReLU代替Sigmoid來加快SGD的收斂速度
    • Dropout:Dropout原理類似于淺層學(xué)習(xí)算法的中集成算法,該方法通過讓全連接層的神經(jīng)元(該模型在前兩個全連接層引入Dropout)以一定的概率失去活性(比如0.5)失活的神經(jīng)元不再參與前向和反向傳播,相當(dāng)于約有一半的神經(jīng)元不再起作用。在測試的時候,讓所有神經(jīng)元的輸出乘0.5。Dropout的引用,有效緩解了模型的過擬合。
    • Local Responce Normalization:局部響應(yīng)歸一層的基本思路是,假如這是網(wǎng)絡(luò)的一塊,比如是 13×13×256, LRN 要做的就是選取一個位置,比如說這樣一個位置,從這個位置穿過整個通道,能得到 256 個數(shù)字,并進行歸一化。進行局部響應(yīng)歸一化的動機是,對于這張 13×13 的圖像中的每個位置來說,我們可能并不需要太多的高激活神經(jīng)元。但是后來,很多研究者發(fā)現(xiàn) LRN 起不到太大作用,因為并不重要,而且我們現(xiàn)在并不用 LRN 來訓(xùn)練網(wǎng)絡(luò)。

    AlexNet的Keras實現(xiàn):

    def AlexNet():model = Sequential()model.add(Conv2D(96,(11,11),strides=(4,4),input_shape=(227,227,3),padding='valid',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2)))model.add(Conv2D(256,(5,5),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2)))model.add(Conv2D(384,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(384,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2)))model.add(Flatten())model.add(Dense(4096,activation='relu'))model.add(Dropout(0.5))model.add(Dense(4096,activation='relu'))model.add(Dropout(0.5))model.add(Dense(1000,activation='softmax'))return model

    穩(wěn)步前行:ZF-Net

    ZFNet是2013ImageNet分類任務(wù)的冠軍,其網(wǎng)絡(luò)結(jié)構(gòu)沒什么改進,只是調(diào)了調(diào)參,性能較Alex提升了不少。ZF-Net只是將AlexNet第一層卷積核由11變成7,步長由4變?yōu)?,第3,4,5卷積層轉(zhuǎn)變?yōu)?84,384,256。這一年的ImageNet還是比較平靜的一屆,其冠軍ZF-Net的名堂也沒其他屆的經(jīng)典網(wǎng)絡(luò)架構(gòu)響亮。

    ZF-Net的Keras實現(xiàn):

    def ZF_Net():model = Sequential() model.add(Conv2D(96,(7,7),strides=(2,2),input_shape=(224,224,3),padding='valid',activation='relu',kernel_initializer='uniform')) model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2))) model.add(Conv2D(256,(5,5),strides=(2,2),padding='same',activation='relu',kernel_initializer='uniform')) model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2))) model.add(Conv2D(384,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform')) model.add(Conv2D(384,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform')) model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform')) model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2))) model.add(Flatten()) model.add(Dense(4096,activation='relu')) model.add(Dropout(0.5)) model.add(Dense(4096,activation='relu')) model.add(Dropout(0.5)) model.add(Dense(1000,activation='softmax')) return model

    越走越深:VGG-Nets

    VGG-Nets是由牛津大學(xué)VGG(Visual Geometry Group)提出,是2014年ImageNet競賽定位任務(wù)的第一名和分類任務(wù)的第二名的中的基礎(chǔ)網(wǎng)絡(luò)。VGG可以看成是加深版本的AlexNet. 都是conv layer + FC layer,在當(dāng)時看來這是一個非常深的網(wǎng)絡(luò)了,因為層數(shù)高達十多層,我們從其論文名字就知道了(《Very Deep Convolutional Networks for Large-Scale Visual Recognition》),當(dāng)然以現(xiàn)在的目光看來VGG真的稱不上是一個very deep的網(wǎng)絡(luò)。

    上面一個表格是描述的是VGG-Net的網(wǎng)絡(luò)結(jié)構(gòu)以及誕生過程。為了解決初始化(權(quán)重初始化)等問題,VGG采用的是一種Pre-training的方式,這種方式在經(jīng)典的神經(jīng)網(wǎng)絡(luò)中經(jīng)常見得到,就是先訓(xùn)練一部分小網(wǎng)絡(luò),然后再確保這部分網(wǎng)絡(luò)穩(wěn)定之后,再在這基礎(chǔ)上逐漸加深。表1從左到右體現(xiàn)的就是這個過程,并且當(dāng)網(wǎng)絡(luò)處于D階段的時候,效果是最優(yōu)的,因此D階段的網(wǎng)絡(luò)也就是VGG-16了!E階段得到的網(wǎng)絡(luò)就是VGG-19了!VGG-16的16指的是conv+fc的總層數(shù)是16,是不包括max pool的層數(shù)!

    下面這個圖就是VGG-16的網(wǎng)絡(luò)結(jié)構(gòu)。

    由上圖看出,VGG-16的結(jié)構(gòu)非常整潔,深度較AlexNet深得多,里面包含多個conv->conv->max_pool這類的結(jié)構(gòu),VGG的卷積層都是same的卷積,即卷積過后的輸出圖像的尺寸與輸入是一致的,它的下采樣完全是由max pooling來實現(xiàn)。

    VGG網(wǎng)絡(luò)后接3個全連接層,filter的個數(shù)(卷積后的輸出通道數(shù))從64開始,然后沒接一個pooling后其成倍的增加,128、512,VGG的注意貢獻是使用小尺寸的filter,及有規(guī)則的卷積-池化操作。

    閃光點:

    • 卷積層使用更小的filter尺寸和間隔

    與AlexNet相比,可以看出VGG-Nets的卷積核尺寸還是很小的,比如AlexNet第一層的卷積層用到的卷積核尺寸就是11*11,這是一個很大卷積核了。而反觀VGG-Nets,用到的卷積核的尺寸無非都是1×1和3×3的小卷積核,可以替代大的filter尺寸。

    3×3卷積核的優(yōu)點:

    • 多個3×3的卷基層比一個大尺寸filter卷基層有更多的非線性,使得判決函數(shù)更加具有判決性
    • 多個3×3的卷積層比一個大尺寸的filter有更少的參數(shù),假設(shè)卷基層的輸入和輸出的特征圖大小相同為C,那么三個3×3的卷積層參數(shù)個數(shù)3×(3×3×C×C)=27CC;一個7×7的卷積層參數(shù)為49CC;所以可以把三個3×3的filter看成是一個7×7filter的分解(中間層有非線性的分解)

    1*1卷積核的優(yōu)點:

    • 作用是在不影響輸入輸出維數(shù)的情況下,對輸入進行線性形變,然后通過Relu進行非線性處理,增加網(wǎng)絡(luò)的非線性表達能力。

    VGG-16的Keras實現(xiàn):

    def VGG_16(): model = Sequential()model.add(Conv2D(64,(3,3),strides=(1,1),input_shape=(224,224,3),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(64,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(2,2)))model.add(Conv2D(128,(3,2),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(128,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(2,2)))model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(2,2)))model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(2,2)))model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))model.add(MaxPooling2D(pool_size=(2,2)))model.add(Flatten())model.add(Dense(4096,activation='relu'))model.add(Dropout(0.5))model.add(Dense(4096,activation='relu'))model.add(Dropout(0.5))model.add(Dense(1000,activation='softmax'))return model

    大浪推手:GoogLeNet

    GoogLeNet在2014的ImageNet分類任務(wù)上擊敗了VGG-Nets奪得冠軍,其實力肯定是非常深厚的,GoogLeNet跟AlexNet,VGG-Nets這種單純依靠加深網(wǎng)絡(luò)結(jié)構(gòu)進而改進網(wǎng)絡(luò)性能的思路不一樣,它另辟幽徑,在加深網(wǎng)絡(luò)的同時(22層),也在網(wǎng)絡(luò)結(jié)構(gòu)上做了創(chuàng)新,引入Inception結(jié)構(gòu)代替了單純的卷積+激活的傳統(tǒng)操作(這思路最早由Network in Network提出)。GoogLeNet進一步把對卷積神經(jīng)網(wǎng)絡(luò)的研究推上新的高度。

    閃光點:

    • 引入Inception結(jié)構(gòu)
    • 中間層的輔助LOSS單元
    • 后面的全連接層全部替換為簡單的全局平均pooling

    上圖結(jié)構(gòu)就是Inception,結(jié)構(gòu)里的卷積stride都是1,另外為了保持特征響應(yīng)圖大小一致,都用了零填充。最后每個卷積層后面都立刻接了個ReLU層。在輸出前有個叫concatenate的層,直譯的意思是“并置”,即把4組不同類型但大小相同的特征響應(yīng)圖一張張并排疊起來,形成新的特征響應(yīng)圖。Inception結(jié)構(gòu)里主要做了兩件事:1. 通過3×3的池化、以及1×1、3×3和5×5這三種不同尺度的卷積核,一共4種方式對輸入的特征響應(yīng)圖做了特征提取。2. 為了降低計算量。同時讓信息通過更少的連接傳遞以達到更加稀疏的特性,采用1×1卷積核來實現(xiàn)降維。

    這里想再詳細談?wù)?×1卷積核的作用,它究竟是怎么實現(xiàn)降維的。現(xiàn)在運算如下:下面圖1是3×3卷積核的卷積,圖2是1×1卷積核的卷積過程。對于單通道輸入,1×1的卷積確實不能起到降維作用,但對于多通道輸入,就不不同了。假設(shè)你有256個特征輸入,256個特征輸出,同時假設(shè)Inception層只執(zhí)行3×3的卷積。這意味著總共要進行 256×256×3×3的卷積(589000次乘積累加(MAC)運算)。這可能超出了我們的計算預(yù)算,比方說,在Google服務(wù)器上花0.5毫秒運行該層。作為替代,我們決定減少需要卷積的特征的數(shù)量,比如減少到64(256/4)個。在這種情況下,我們首先進行256到64的1×1卷積,然后在所有Inception的分支上進行64次卷積,接著再使用一個64到256的1×1卷積。

    • 256×64×1×1 = 16000
    • 64×64×3×3 = 36000
    • 64×256×1×1 = 16000

    現(xiàn)在的計算量大約是70000(即16000+36000+16000),相比之前的約600000,幾乎減少了10倍。這就通過小卷積核實現(xiàn)了降維。

    現(xiàn)在再考慮一個問題:為什么一定要用1×1卷積核,3×3不也可以嗎?考慮[50,200,200]的矩陣輸入,我們可以使用20個1×1的卷積核進行卷積,得到輸出[20,200,200]。有人問,我用20個3×3的卷積核不是也能得到[20,200,200]的矩陣輸出嗎,為什么就使用1×1的卷積核?我們計算一下卷積參數(shù)就知道了,對于1×1的參數(shù)總數(shù):20×200×200×(1×1),對于3×3的參數(shù)總數(shù):20×200×200×(3×3),可以看出,使用1×1的參數(shù)總數(shù)僅為3×3的總數(shù)的九分之一!所以我們使用的是1×1卷積核。


    GoogLeNet網(wǎng)絡(luò)結(jié)構(gòu)中有3個LOSS單元,這樣的網(wǎng)絡(luò)設(shè)計是為了幫助網(wǎng)絡(luò)的收斂。在中間層加入輔助計算的LOSS單元,目的是計算損失時讓低層的特征也有很好的區(qū)分能力,從而讓網(wǎng)絡(luò)更好地被訓(xùn)練。在論文中,這兩個輔助LOSS單元的計算被乘以0.3,然后和最后的LOSS相加作為最終的損失函數(shù)來訓(xùn)練網(wǎng)絡(luò)。

    GoogLeNet還有一個閃光點值得一提,那就是將后面的全連接層全部替換為簡單的全局平均pooling,在最后參數(shù)會變的更少。而在AlexNet中最后3層的全連接層參數(shù)差不多占總參數(shù)的90%,使用大網(wǎng)絡(luò)在寬度和深度允許GoogleNet移除全連接層,但并不會影響到結(jié)果的精度,在ImageNet中實現(xiàn)93.3%的精度,而且要比VGG還要快。

    GoogLeNet的Keras實現(xiàn):

    def Conv2d_BN(x, nb_filter,kernel_size, padding='same',strides=(1,1),name=None):if name is not None:bn_name = name + '_bn'conv_name = name + '_conv'else:bn_name = Noneconv_name = Nonex = Conv2D(nb_filter,kernel_size,padding=padding,strides=strides,activation='relu',name=conv_name)(x)x = BatchNormalization(axis=3,name=bn_name)(x)return xdef Inception(x,nb_filter):branch1x1 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)branch3x3 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)branch3x3 = Conv2d_BN(branch3x3,nb_filter,(3,3), padding='same',strides=(1,1),name=None)branch5x5 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)branch5x5 = Conv2d_BN(branch5x5,nb_filter,(1,1), padding='same',strides=(1,1),name=None)branchpool = MaxPooling2D(pool_size=(3,3),strides=(1,1),padding='same')(x)branchpool = Conv2d_BN(branchpool,nb_filter,(1,1),padding='same',strides=(1,1),name=None)x = concatenate([branch1x1,branch3x3,branch5x5,branchpool],axis=3)return xdef GoogLeNet():inpt = Input(shape=(224,224,3))#padding = 'same',填充為(步長-1)/2,還可以用ZeroPadding2D((3,3))x = Conv2d_BN(inpt,64,(7,7),strides=(2,2),padding='same')x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)x = Conv2d_BN(x,192,(3,3),strides=(1,1),padding='same')x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)x = Inception(x,64)#256x = Inception(x,120)#480x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)x = Inception(x,128)#512x = Inception(x,128)x = Inception(x,128)x = Inception(x,132)#528x = Inception(x,208)#832x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)x = Inception(x,208)x = Inception(x,256)#1024x = AveragePooling2D(pool_size=(7,7),strides=(7,7),padding='same')(x)x = Dropout(0.4)(x)x = Dense(1000,activation='relu')(x)x = Dense(1000,activation='softmax')(x)model = Model(inpt,x,name='inception')return model

    里程碑式創(chuàng)新:ResNet

    2015年何愷明推出的ResNet在ISLVRC和COCO上橫掃所有選手,獲得冠軍。ResNet在網(wǎng)絡(luò)結(jié)構(gòu)上做了大創(chuàng)新,而不再是簡單的堆積層數(shù),ResNet在卷積神經(jīng)網(wǎng)絡(luò)的新思路,絕對是深度學(xué)習(xí)發(fā)展歷程上里程碑式的事件。

    閃光點:

    • 層數(shù)非常深,已經(jīng)超過百層
    • 引入殘差單元來解決退化問題

    從前面可以看到,隨著網(wǎng)絡(luò)深度增加,網(wǎng)絡(luò)的準確度應(yīng)該同步增加,當(dāng)然要注意過擬合問題。但是網(wǎng)絡(luò)深度增加的一個問題在于這些增加的層是參數(shù)更新的信號,因為梯度是從后向前傳播的,增加網(wǎng)絡(luò)深度后,比較靠前的層梯度會很小。這意味著這些層基本上學(xué)習(xí)停滯了,這就是梯度消失問題。深度網(wǎng)絡(luò)的第二個問題在于訓(xùn)練,當(dāng)網(wǎng)絡(luò)更深時意味著參數(shù)空間更大,優(yōu)化問題變得更難,因此簡單地去增加網(wǎng)絡(luò)深度反而出現(xiàn)更高的訓(xùn)練誤差,深層網(wǎng)絡(luò)雖然收斂了,但網(wǎng)絡(luò)卻開始退化了,即增加網(wǎng)絡(luò)層數(shù)卻導(dǎo)致更大的誤差,比如下圖,一個56層的網(wǎng)絡(luò)的性能卻不如20層的性能好,這不是因為過擬合(訓(xùn)練集訓(xùn)練誤差依然很高),這就是煩人的退化問題。殘差網(wǎng)絡(luò)ResNet設(shè)計一種殘差模塊讓我們可以訓(xùn)練更深的網(wǎng)絡(luò)。

    這里詳細分析一下殘差單元來理解ResNet的精髓。

    從下圖可以看出,數(shù)據(jù)經(jīng)過了兩條路線,一條是常規(guī)路線,另一條則是捷徑(shortcut),直接實現(xiàn)單位映射的直接連接的路線,這有點類似與電路中的“短路”。通過實驗,這種帶有shortcut的結(jié)構(gòu)確實可以很好地應(yīng)對退化問題。我們把網(wǎng)絡(luò)中的一個模塊的輸入和輸出關(guān)系看作是y=H(x),那么直接通過梯度方法求H(x)就會遇到上面提到的退化問題,如果使用了這種帶shortcut的結(jié)構(gòu),那么可變參數(shù)部分的優(yōu)化目標就不再是H(x),若用F(x)來代表需要優(yōu)化的部分的話,則H(x)=F(x)+x,也就是F(x)=H(x)-x。因為在單位映射的假設(shè)中y=x就相當(dāng)于觀測值,所以F(x)就對應(yīng)著殘差,因而叫殘差網(wǎng)絡(luò)。為啥要這樣做,因為作者認為學(xué)習(xí)殘差F(X)比直接學(xué)習(xí)H(X)簡單!設(shè)想下,現(xiàn)在根據(jù)我們只需要去學(xué)習(xí)輸入和輸出的差值就可以了,絕對量變?yōu)橄鄬α?#xff08;H(x)-x 就是輸出相對于輸入變化了多少),優(yōu)化起來簡單很多。

    考慮到x的維度與F(X)維度可能不匹配情況,需進行維度匹配。這里論文中采用兩種方法解決這一問題(其實是三種,但通過實驗發(fā)現(xiàn)第三種方法會使performance急劇下降,故不采用):

    • zero_padding:對恒等層進行0填充的方式將維度補充完整。這種方法不會增加額外的參數(shù)
    • projection:在恒等層采用1x1的卷積核來增加維度。這種方法會增加額外的參數(shù)

    下圖展示了兩種形態(tài)的殘差模塊,左圖是常規(guī)殘差模塊,有兩個3×3卷積核卷積核組成,但是隨著網(wǎng)絡(luò)進一步加深,這種殘差結(jié)構(gòu)在實踐中并不是十分有效。針對這問題,右圖的“瓶頸殘差模塊”(bottleneck residual block)可以有更好的效果,它依次由1×1、3×3、1×1這三個卷積層堆積而成,這里的1×1的卷積能夠起降維或升維的作用,從而令3×3的卷積可以在相對較低維度的輸入上進行,以達到提高計算效率的目的。

    ResNet-50的Keras實現(xiàn):

    def Conv2d_BN(x, nb_filter,kernel_size, strides=(1,1), padding='same',name=None):if name is not None:bn_name = name + '_bn'conv_name = name + '_conv'else:bn_name = Noneconv_name = Nonex = Conv2D(nb_filter,kernel_size,padding=padding,strides=strides,activation='relu',name=conv_name)(x)x = BatchNormalization(axis=3,name=bn_name)(x)return xdef Conv_Block(inpt,nb_filter,kernel_size,strides=(1,1), with_conv_shortcut=False):x = Conv2d_BN(inpt,nb_filter=nb_filter[0],kernel_size=(1,1),strides=strides,padding='same')x = Conv2d_BN(x, nb_filter=nb_filter[1], kernel_size=(3,3), padding='same')x = Conv2d_BN(x, nb_filter=nb_filter[2], kernel_size=(1,1), padding='same')if with_conv_shortcut:shortcut = Conv2d_BN(inpt,nb_filter=nb_filter[2],strides=strides,kernel_size=kernel_size)x = add([x,shortcut])return xelse:x = add([x,inpt])return xdef ResNet50():inpt = Input(shape=(224,224,3))x = ZeroPadding2D((3,3))(inpt)x = Conv2d_BN(x,nb_filter=64,kernel_size=(7,7),strides=(2,2),padding='valid')x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)x = Conv_Block(x,nb_filter=[64,64,256],kernel_size=(3,3),strides=(1,1),with_conv_shortcut=True)x = Conv_Block(x,nb_filter=[64,64,256],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[64,64,256],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[128,128,512],kernel_size=(3,3),strides=(2,2),with_conv_shortcut=True)x = Conv_Block(x,nb_filter=[128,128,512],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[128,128,512],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[128,128,512],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[256,256,1024],kernel_size=(3,3),strides=(2,2),with_conv_shortcut=True)x = Conv_Block(x,nb_filter=[256,256,1024],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[256,256,1024],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[256,256,1024],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[256,256,1024],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[256,256,1024],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[512,512,2048],kernel_size=(3,3),strides=(2,2),with_conv_shortcut=True)x = Conv_Block(x,nb_filter=[512,512,2048],kernel_size=(3,3))x = Conv_Block(x,nb_filter=[512,512,2048],kernel_size=(3,3))x = AveragePooling2D(pool_size=(7,7))(x)x = Flatten()(x)x = Dense(1000,activation='softmax')(x)model = Model(inputs=inpt,outputs=x)return model

    繼往開來:DenseNet

    自Resnet提出以后,ResNet的變種網(wǎng)絡(luò)層出不窮,都各有其特點,網(wǎng)絡(luò)性能也有一定的提升。本文介紹的最后一個網(wǎng)絡(luò)是CVPR 2017最佳論文DenseNet,論文中提出的DenseNet(Dense Convolutional Network)主要還是和ResNet及Inception網(wǎng)絡(luò)做對比,思想上有借鑒,但卻是全新的結(jié)構(gòu),網(wǎng)絡(luò)結(jié)構(gòu)并不復(fù)雜,卻非常有效,在CIFAR指標上全面超越ResNet。可以說DenseNet吸收了ResNet最精華的部分,并在此上做了更加創(chuàng)新的工作,使得網(wǎng)絡(luò)性能進一步提升。

    閃光點:

    • 密集連接:緩解梯度消失問題,加強特征傳播,鼓勵特征復(fù)用,極大的減少了參數(shù)量

    DenseNet 是一種具有密集連接的卷積神經(jīng)網(wǎng)絡(luò)。在該網(wǎng)絡(luò)中,任何兩層之間都有直接的連接,也就是說,網(wǎng)絡(luò)每一層的輸入都是前面所有層輸出的并集,而該層所學(xué)習(xí)的特征圖也會被直接傳給其后面所有層作為輸入。下圖是 DenseNet 的一個dense block示意圖,一個block里面的結(jié)構(gòu)如下,與ResNet中的BottleNeck基本一致:BN-ReLU-Conv(1×1)-BN-ReLU-Conv(3×3) ,而一個DenseNet則由多個這種block組成。每個DenseBlock的之間層稱為transition layers,由BN?>Conv(1×1)?>averagePooling(2×2)組成

    密集連接不會帶來冗余嗎?不會!密集連接這個詞給人的第一感覺就是極大的增加了網(wǎng)絡(luò)的參數(shù)量和計算量。但實際上 DenseNet 比其他網(wǎng)絡(luò)效率更高,其關(guān)鍵就在于網(wǎng)絡(luò)每層計算量的減少以及特征的重復(fù)利用。DenseNet則是讓l層的輸入直接影響到之后的所有層,它的輸出為:xl=Hl([X0,X1,…,xl?1]),其中[x0,x1,...,xl?1]就是將之前的feature map以通道的維度進行合并。并且由于每一層都包含之前所有層的輸出信息,因此其只需要很少的特征圖就夠了,這也是為什么DneseNet的參數(shù)量較其他模型大大減少的原因。這種dense connection相當(dāng)于每一層都直接連接input和loss,因此就可以減輕梯度消失現(xiàn)象,這樣更深網(wǎng)絡(luò)不是問題

    需要明確一點,dense connectivity 僅僅是在一個dense block里的,不同dense block 之間是沒有dense connectivity的,比如下圖所示。

    天底下沒有免費的午餐,網(wǎng)絡(luò)自然也不例外。在同層深度下獲得更好的收斂率,自然是有額外代價的。其代價之一,就是其恐怖如斯的內(nèi)存占用。

    DenseNet-121的Keras實現(xiàn):

    def DenseNet121(nb_dense_block=4, growth_rate=32, nb_filter=64, reduction=0.0, dropout_rate=0.0, weight_decay=1e-4, classes=1000, weights_path=None):'''Instantiate the DenseNet 121 architecture,# Argumentsnb_dense_block: number of dense blocks to add to endgrowth_rate: number of filters to add per dense blocknb_filter: initial number of filtersreduction: reduction factor of transition blocks.dropout_rate: dropout rateweight_decay: weight decay factorclasses: optional number of classes to classify imagesweights_path: path to pre-trained weights# ReturnsA Keras model instance.'''eps = 1.1e-5# compute compression factorcompression = 1.0 - reduction# Handle Dimension Ordering for different backendsglobal concat_axisif K.image_dim_ordering() == 'tf':concat_axis = 3img_input = Input(shape=(224, 224, 3), name='data')else:concat_axis = 1img_input = Input(shape=(3, 224, 224), name='data')# From architecture for ImageNet (Table 1 in the paper)nb_filter = 64nb_layers = [6,12,24,16] # For DenseNet-121# Initial convolutionx = ZeroPadding2D((3, 3), name='conv1_zeropadding')(img_input)x = Convolution2D(nb_filter, 7, 7, subsample=(2, 2), name='conv1', bias=False)(x)x = BatchNormalization(epsilon=eps, axis=concat_axis, name='conv1_bn')(x)x = Scale(axis=concat_axis, name='conv1_scale')(x)x = Activation('relu', name='relu1')(x)x = ZeroPadding2D((1, 1), name='pool1_zeropadding')(x)x = MaxPooling2D((3, 3), strides=(2, 2), name='pool1')(x)# Add dense blocksfor block_idx in range(nb_dense_block - 1):stage = block_idx+2x, nb_filter = dense_block(x, stage, nb_layers[block_idx], nb_filter, growth_rate, dropout_rate=dropout_rate, weight_decay=weight_decay)# Add transition_blockx = transition_block(x, stage, nb_filter, compression=compression, dropout_rate=dropout_rate, weight_decay=weight_decay)nb_filter = int(nb_filter * compression)final_stage = stage + 1x, nb_filter = dense_block(x, final_stage, nb_layers[-1], nb_filter, growth_rate, dropout_rate=dropout_rate, weight_decay=weight_decay)x = BatchNormalization(epsilon=eps, axis=concat_axis, name='conv'+str(final_stage)+'_blk_bn')(x)x = Scale(axis=concat_axis, name='conv'+str(final_stage)+'_blk_scale')(x)x = Activation('relu', name='relu'+str(final_stage)+'_blk')(x)x = GlobalAveragePooling2D(name='pool'+str(final_stage))(x)x = Dense(classes, name='fc6')(x)x = Activation('softmax', name='prob')(x)model = Model(img_input, x, name='densenet')if weights_path is not None:model.load_weights(weights_path)return modeldef conv_block(x, stage, branch, nb_filter, dropout_rate=None, weight_decay=1e-4):'''Apply BatchNorm, Relu, bottleneck 1x1 Conv2D, 3x3 Conv2D, and option dropout# Argumentsx: input tensor stage: index for dense blockbranch: layer index within each dense blocknb_filter: number of filtersdropout_rate: dropout rateweight_decay: weight decay factor'''eps = 1.1e-5conv_name_base = 'conv' + str(stage) + '_' + str(branch)relu_name_base = 'relu' + str(stage) + '_' + str(branch)# 1x1 Convolution (Bottleneck layer)inter_channel = nb_filter * 4 x = BatchNormalization(epsilon=eps, axis=concat_axis, name=conv_name_base+'_x1_bn')(x)x = Scale(axis=concat_axis, name=conv_name_base+'_x1_scale')(x)x = Activation('relu', name=relu_name_base+'_x1')(x)x = Convolution2D(inter_channel, 1, 1, name=conv_name_base+'_x1', bias=False)(x)if dropout_rate:x = Dropout(dropout_rate)(x)# 3x3 Convolutionx = BatchNormalization(epsilon=eps, axis=concat_axis, name=conv_name_base+'_x2_bn')(x)x = Scale(axis=concat_axis, name=conv_name_base+'_x2_scale')(x)x = Activation('relu', name=relu_name_base+'_x2')(x)x = ZeroPadding2D((1, 1), name=conv_name_base+'_x2_zeropadding')(x)x = Convolution2D(nb_filter, 3, 3, name=conv_name_base+'_x2', bias=False)(x)if dropout_rate:x = Dropout(dropout_rate)(x)return xdef transition_block(x, stage, nb_filter, compression=1.0, dropout_rate=None, weight_decay=1E-4):''' Apply BatchNorm, 1x1 Convolution, averagePooling, optional compression, dropout # Argumentsx: input tensorstage: index for dense blocknb_filter: number of filterscompression: calculated as 1 - reduction. Reduces the number of feature maps in the transition block.dropout_rate: dropout rateweight_decay: weight decay factor'''eps = 1.1e-5conv_name_base = 'conv' + str(stage) + '_blk'relu_name_base = 'relu' + str(stage) + '_blk'pool_name_base = 'pool' + str(stage) x = BatchNormalization(epsilon=eps, axis=concat_axis, name=conv_name_base+'_bn')(x)x = Scale(axis=concat_axis, name=conv_name_base+'_scale')(x)x = Activation('relu', name=relu_name_base)(x)x = Convolution2D(int(nb_filter * compression), 1, 1, name=conv_name_base, bias=False)(x)if dropout_rate:x = Dropout(dropout_rate)(x)x = AveragePooling2D((2, 2), strides=(2, 2), name=pool_name_base)(x)return xdef dense_block(x, stage, nb_layers, nb_filter, growth_rate, dropout_rate=None, weight_decay=1e-4, grow_nb_filters=True):''' Build a dense_block where the output of each conv_block is fed to subsequent ones# Argumentsx: input tensorstage: index for dense blocknb_layers: the number of layers of conv_block to append to the model.nb_filter: number of filtersgrowth_rate: growth ratedropout_rate: dropout rateweight_decay: weight decay factorgrow_nb_filters: flag to decide to allow number of filters to grow'''eps = 1.1e-5concat_feat = xfor i in range(nb_layers):branch = i+1x = conv_block(concat_feat, stage, branch, growth_rate, dropout_rate, weight_decay)concat_feat = merge([concat_feat, x], mode='concat', concat_axis=concat_axis, name='concat_'+str(stage)+'_'+str(branch))if grow_nb_filters:nb_filter += growth_ratereturn concat_feat, nb_filter

    總結(jié)

    以上是生活随笔為你收集整理的CNN发展历史【从LeNet到DenseNet】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。