AlexNet

from: https://www.cnblogs.com/wangguchangqing/p/10333370.html

由於受到計算機性能的影響,雖然LeNet在圖像分類中取得了較好的成績,但是並沒有引起很多的關注。 知道2012年,Alex等人提出的AlexNet網絡在ImageNet大賽上以遠超第二名的成績奪冠,卷積神經網絡乃至深度學習重新引起了廣泛的關注。

AlexNet特點

AlexNet是在LeNet的基礎上加深了網絡的結構,學習更豐富更高維的圖像特徵。AlexNet的特點:

  • 更深的網絡結構
  • 使用層疊的卷積層,即卷積層+卷積層+池化層來提取圖像的特徵
  • 使用Dropout抑制過擬合
  • 使用數據增強Data Augmentation抑制過擬合
  • 使用Relu替換之前的sigmoid的作爲激活函數
  • 多GPU訓練

ReLu作爲激活函數

在最初的感知機模型中,輸入和輸出的關係如下:

 

y=∑iwixi+by=∑iwixi+b


只是單純的線性關係,這樣的網絡結構有很大的侷限性:即使用很多這樣結構的網絡層疊加,其輸出和輸入仍然是線性關係,無法處理有非線性關係的輸入輸出。因此,對每個神經元的輸出做個非線性的轉換也就是,將上面就加權求和∑iwixi+b∑iwixi+b的結果輸入到一個非線性函數,也就是激活函數中。 這樣,由於激活函數的引入,多個網絡層的疊加就不再是單純的線性變換,而是具有更強的表現能力。

在最初,sigmoidsigmoid和tanhtanh函數最常用的激活函數。

  1. sigmoidsigmoid

在網絡層數較少時,sigmoidsigmoid函數的特性能夠很好的滿足激活函數的作用:它把一個實數壓縮至0到1之間,當輸入的數字非常大的時候,結果會接近1;當輸入非常大的負數時,則會得到接近0的結果。這種特性,能夠很好的模擬神經元在受刺激後,是否被激活向後傳遞信息(輸出爲0,幾乎不被激活;輸出爲1,完全被激活)。
sigmoidsigmoid一個很大的問題就是梯度飽和。 觀察sigmoidsigmoid函數的曲線,當輸入的數字較大(或較小)時,其函數值趨於不變,其導數變的非常的小。這樣,在層數很多的的網絡結構中,進行反向傳播時,由於很多個很小的sigmoidsigmoid導數累成,導致其結果趨於0,權值更新較慢。

  1. ReLu

    針對sigmoidsigmoid梯度飽和導致訓練收斂慢的問題,在AlexNet中引入了ReLU。ReLU是一個分段線性函數,小於等於0則輸出爲0;大於0的則恆等輸出。相比於sigmoidsigmoid,ReLU有以下有點:
    • 計算開銷下。sigmoidsigmoid的正向傳播有指數運算,倒數運算,而ReLu是線性輸出;反向傳播中,sigmoidsigmoid有指數運算,而ReLU有輸出的部分,導數始終爲1.
    • 梯度飽和問題
    • 稀疏性。Relu會使一部分神經元的輸出爲0,這樣就造成了網絡的稀疏性,並且減少了參數的相互依存關係,緩解了過擬合問題的發生。

這裏有個問題,前面提到,激活函數要用非線性的,是爲了使網絡結構有更強的表達的能力。那這裏使用ReLU本質上卻是個線性的分段函數,是怎麼進行非線性變換的。

這裏把神經網絡看着一個巨大的變換矩陣MM,其輸入爲所有訓練樣本組成的矩陣AA,輸出爲矩陣BB。

 

B=M⋅AB=M⋅A


這裏的MM是一個線性變換的話,則所有的訓練樣本AA進行了線性變換輸出爲BB。

那麼對於ReLU來說,由於其是分段的,0的部分可以看着神經元沒有激活,不同的神經元激活或者不激活,其神經玩過組成的變換矩陣是不一樣的。
設有兩個訓練樣本 a1,a2a1,a2,其訓練時神經網絡組成的變換矩陣爲M1,M2M1,M2。 由於M1M1變換對應的神經網絡中激活神經元和M2M2是不一樣的,這樣M1,M2M1,M2實際上是兩個不同的線性變換。也就是說,每個訓練樣本使用的線性變換矩陣MiMi是不一樣的,在整個訓練樣本空間來說,其經歷的是非線性變換。

簡單來說,不同訓練樣本中的同樣的特徵,在經過神經網絡學習時,流經的神經元是不一樣的(激活函數值爲0的神經元不會被激活)。這樣,最終的輸出實際上是輸入樣本的非線性變換。

單個訓練樣本是線性變換,但是每個訓練樣本的線性變換是不一樣的,這樣整個訓練樣本集來說,就是非線性的變換。

數據增強

神經網絡由於訓練的參數多,表能能力強,所以需要比較多的數據量,不然很容易過擬合。當訓練數據有限時,可以通過一些變換從已有的訓練數據集中生成一些新的數據,以快速地擴充訓練數據。對於圖像數據集來說,可以對圖像進行一些形變操作:

  • 翻轉
  • 隨機裁剪
  • 平移,顏色光照的變換
  • ...

AlexNet中對數據做了以下操作:

  1. 隨機裁剪,對256×256256×256的圖片進行隨機裁剪到227×227227×227,然後進行水平翻轉。
  2. 測試的時候,對左上、右上、左下、右下、中間分別做了5次裁剪,然後翻轉,共10個裁剪,之後對結果求平均。
  3. 對RGB空間做PCA(主成分分析),然後對主成分做一個(0, 0.1)的高斯擾動,也就是對顏色、光照作變換,結果使錯誤率又下降了1%。

層疊池化

在LeNet中池化是不重疊的,即池化的窗口的大小和步長是相等的,如下

在AlexNet中使用的池化(Pooling)卻是可重疊的,也就是說,在池化的時候,每次移動的步長小於池化的窗口長度。AlexNet池化的大小爲3×3的正方形,每次池化移動步長爲2,這樣就會出現重疊。重疊池化可以避免過擬合,這個策略貢獻了0.3%的Top-5錯誤率。與非重疊方案s=2,z=2s=2,z=2相比,輸出的維度是相等的,並且能在一定程度上抑制過擬合。

局部相應歸一化

ReLU具有讓人滿意的特性,它不需要通過輸入歸一化來防止飽和。如果至少一些訓練樣本對ReLU產生了正輸入,那麼那個神經元上將發生學習。然而,我們仍然發現接下來的局部響應歸一化有助於泛化。aix,yax,yi表示神經元激活,通過在(x,y)(x,y)位置應用核ii,然後應用ReLU非線性來計算,響應歸一化激活bix,ybx,yi通過下式給定:

 

bix,y=aix,y/(k+α∑j=max(0,i−n/2)min(N−1,i+n/2)(ajx,y)2)βbx,yi=ax,yi/(k+α∑j=max(0,i−n/2)min(N−1,i+n/2)(ax,yj)2)β

其中,NN是卷積核的個數,也就是生成的FeatureMap的個數;k,α,β,nk,α,β,n是超參數,論文中使用的值是k=2,n=5,α=10−4,β=0.75k=2,n=5,α=10−4,β=0.75

輸出bix,ybx,yi和輸入ajx,yax,yj的上標表示的是當前值所在的通道,也即是疊加的方向是沿着通道進行。將要歸一化的值aix,yax,yi所在附近通道相同位置的值的平方累加起來∑min(N−1,i+n/2)j=max(0,i−n/2)(ajx,y)2∑j=max(0,i−n/2)min(N−1,i+n/2)(ax,yj)2

Dropout

這個是比較常用的抑制過擬合的方法了。
引入Dropout主要是爲了防止過擬合。在神經網絡中Dropout通過修改神經網絡本身結構來實現,對於某一層的神經元,通過定義的概率將神經元置爲0,這個神經元就不參與前向和後向傳播,就如同在網絡中被刪除了一樣,同時保持輸入層與輸出層神經元的個數不變,然後按照神經網絡的學習方法進行參數更新。在下一次迭代中,又重新隨機刪除一些神經元(置爲0),直至訓練結束。
Dropout應該算是AlexNet中一個很大的創新,現在神經網絡中的必備結構之一。Dropout也可以看成是一種模型組合,每次生成的網絡結構都不一樣,通過組合多個模型的方式能夠有效地減少過擬合,Dropout只需要兩倍的訓練時間即可實現模型組合(類似取平均)的效果,非常高效。
如下圖:

Alex網絡結構

上圖中的輸入是224×224224×224,不過經過計算(224−11)/4=54.75(224−11)/4=54.75並不是論文中的55×5555×55,而使用227×227227×227作爲輸入,則(227−11)/4=55(227−11)/4=55

網絡包含8個帶權重的層;前5層是卷積層,剩下的3層是全連接層。最後一層全連接層的輸出是1000維softmax的輸入,softmax會產生1000類標籤的分佈網絡包含8個帶權重的層;前5層是卷積層,剩下的3層是全連接層。最後一層全連接層的輸出是1000維softmax的輸入,softmax會產生1000類標籤的分佈。

  • 卷積層C1
    該層的處理流程是: 卷積-->ReLU-->池化-->歸一化。
    • 卷積,輸入是227×227227×227,使用96個11×11×311×11×3的卷積核,得到的FeatureMap爲55×55×9655×55×96。
    • ReLU,將卷積層輸出的FeatureMap輸入到ReLU函數中。
    • 池化,使用3×33×3步長爲2的池化單元(重疊池化,步長小於池化單元的寬度),輸出爲27×27×9627×27×96((55−3)/2+1=27(55−3)/2+1=27)
    • 局部響應歸一化,使用k=2,n=5,α=10−4,β=0.75k=2,n=5,α=10−4,β=0.75進行局部歸一化,輸出的仍然爲27×27×9627×27×96,輸出分爲兩組,每組的大小爲27×27×4827×27×48
  • 卷積層C2
    該層的處理流程是:卷積-->ReLU-->池化-->歸一化
    • 卷積,輸入是2組27×27×4827×27×48。使用2組,每組128個尺寸爲5×5×485×5×48的卷積核,並作了邊緣填充padding=2,卷積的步長爲1. 則輸出的FeatureMap爲2組,每組的大小爲27×27 times12827×27 times128. ((27+2∗2−5)/1+1=27(27+2∗2−5)/1+1=27)
    • ReLU,將卷積層輸出的FeatureMap輸入到ReLU函數中
    • 池化運算的尺寸爲3×33×3,步長爲2,池化後圖像的尺寸爲(27−3)/2+1=13(27−3)/2+1=13,輸出爲13×13×25613×13×256
    • 局部響應歸一化,使用k=2,n=5,α=10−4,β=0.75k=2,n=5,α=10−4,β=0.75進行局部歸一化,輸出的仍然爲13×13×25613×13×256,輸出分爲2組,每組的大小爲13×13×12813×13×128
  • 卷積層C3
    該層的處理流程是: 卷積-->ReLU
    • 卷積,輸入是13×13×25613×13×256,使用2組共384尺寸爲3×3×2563×3×256的卷積核,做了邊緣填充padding=1,卷積的步長爲1.則輸出的FeatureMap爲13×13 times38413×13 times384
    • ReLU,將卷積層輸出的FeatureMap輸入到ReLU函數中
  • 卷積層C4
    該層的處理流程是: 卷積-->ReLU
    該層和C3類似。
    • 卷積,輸入是13×13×38413×13×384,分爲兩組,每組爲13×13×19213×13×192.使用2組,每組192個尺寸爲3×3×1923×3×192的卷積核,做了邊緣填充padding=1,卷積的步長爲1.則輸出的FeatureMap爲13×13 times38413×13 times384,分爲兩組,每組爲13×13×19213×13×192
    • ReLU,將卷積層輸出的FeatureMap輸入到ReLU函數中
  • 卷積層C5
    該層處理流程爲:卷積-->ReLU-->池化
    • 卷積,輸入爲13×13×38413×13×384,分爲兩組,每組爲13×13×19213×13×192。使用2組,每組爲128尺寸爲3×3×1923×3×192的卷積核,做了邊緣填充padding=1,卷積的步長爲1.則輸出的FeatureMap爲13×13×25613×13×256
    • ReLU,將卷積層輸出的FeatureMap輸入到ReLU函數中
    • 池化,池化運算的尺寸爲3×3,步長爲2,池化後圖像的尺寸爲 (13−3)/2+1=6(13−3)/2+1=6,即池化後的輸出爲6×6×2566×6×256
  • 全連接層FC6
    該層的流程爲:(卷積)全連接 -->ReLU -->Dropout
    • 卷積->全連接: 輸入爲6×6×2566×6×256,該層有4096個卷積核,每個卷積核的大小爲6×6×2566×6×256。由於卷積核的尺寸剛好與待處理特徵圖(輸入)的尺寸相同,即卷積核中的每個係數只與特徵圖(輸入)尺寸的一個像素值相乘,一一對應,因此,該層被稱爲全連接層。由於卷積核與特徵圖的尺寸相同,卷積運算後只有一個值,因此,卷積後的像素層尺寸爲4096×1×14096×1×1,即有4096個神經元。
    • ReLU,這4096個運算結果通過ReLU激活函數生成4096個值
    • Dropout,抑制過擬合,隨機的斷開某些神經元的連接或者是不激活某些神經元
  • 全連接層FC7
    流程爲:全連接-->ReLU-->Dropout
    • 全連接,輸入爲4096的向量
    • ReLU,這4096個運算結果通過ReLU激活函數生成4096個值
    • Dropout,抑制過擬合,隨機的斷開某些神經元的連接或者是不激活某些神經元
  • 輸出層
    第七層輸出的4096個數據與第八層的1000個神經元進行全連接,經過訓練後輸出1000個float型的值,這就是預測結果。

AlexNet參數數量

卷積層的參數 = 卷積核的數量 * 卷積核 + 偏置

  • C1: 96個11×11×311×11×3的卷積核,96×11×11×3+96=3484896×11×11×3+96=34848
  • C2: 2組,每組128個5×5×485×5×48的卷積核,(128×5×5×48+128)×2=307456(128×5×5×48+128)×2=307456
  • C3: 384個3×3×2563×3×256的卷積核,3×3×256×384+384=8851203×3×256×384+384=885120
  • C4: 2組,每組192個3×3×1923×3×192的卷積核,(3×3×192×192+192)×2=663936(3×3×192×192+192)×2=663936
  • C5: 2組,每組128個3×3×1923×3×192的卷積核,(3×3×192×128+128)×2=442624(3×3×192×128+128)×2=442624
  • FC6: 4096個6×6×2566×6×256的卷積核,6×6×256×4096+4096=377528326×6×256×4096+4096=37752832
  • FC7: 4096∗4096+4096=167813124096∗4096+4096=16781312
  • output: 4096∗1000=40960004096∗1000=4096000

卷積層 C2,C4,C5中的卷積核只和位於同一GPU的上一層的FeatureMap相連。從上面可以看出,參數大多數集中在全連接層,在卷積層由於權值共享,權值參數較少。

Keras實現

由於AlexNet是使用兩塊顯卡進行訓練的,其網絡結構的實際是分組進行的。並且,在C2,C4,C5上其卷積核只和上一層的同一個GPU上的卷積核相連。 對於單顯卡來說,並不適用,本文基於Keras的實現,忽略其關於雙顯卡的的結構,並且將局部歸一化操作換成了BN。其網絡結構如下:

Keras代碼

class AlexNet:
    @staticmethod
    def build(width,height,depth,classes,reg=0.0002):
        model = Sequential()
        inputShape = (height,width,depth)
        chanDim = -1

        if K.image_data_format() == "channels_first":
            inputShape = (depth,height,width)
            chanDim = 1

        model.add(Conv2D(96,(11,11),strides=(4,4),input_shape=inputShape,padding="same",kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization(axis=chanDim))
        model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2)))
        model.add(Dropout(0.25))

        model.add(Conv2D(256,(5,5),padding="same",kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization(axis=chanDim))
        model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2)))
        model.add(Dropout(0.25))

        model.add(Conv2D(384,(3,3),padding="same",kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization(axis=chanDim))
        model.add(Conv2D(384,(3,3),padding="same",kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization(axis=chanDim))
        model.add(Conv2D(256,(3,3),padding="same",kernel_regularizer=l2(reg)))
        model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2)))
        model.add(Dropout(0.25))

        model.add(Flatten())
        model.add(Dense(4096,kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization())
        model.add(Dropout(0.25))

        model.add(Dense(4096,kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization())
        model.add(Dropout(0.25))

        model.add(Dense(classes,kernel_regularizer=l2(reg)))
        model.add(Activation("softmax"))

        return model

更多測測試代碼,可以從我的github上找到。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章