CNN經典模型——AlexNet

AlexNet網絡結構剖析

2012年AlexNet在ImageNet大賽上一舉奪冠,展示了CNN在圖像識別上的驚人表現,掀起了CNN研究的熱潮,深度學習自此引爆,在AlexNet之前,深度學習已經沉寂了很久。該網絡是以論文的第一作者 Alex Krizhevsky 的名字命名的,另外兩位合著者是 Ilya Sutskever 和 Geoffery Hinton。
該網絡結構如下(爲了講清楚AlexNet結構,找了不同的示意圖):

需要注意的是,論文發表時的GPU運算能力達不到該網絡的訓練需求,所以使用的是兩個GPU並行,中間爲了提高模型效果使用了不少交互相關的Trick。我們下面只關注網絡結構,不關注兩個GPU處理之間的交互。

下面對該結構做詳細介紹,數據預處理首先將所有圖像都處理爲256×256×3的圖像,隨後AlexNet通過隨機採樣的方式從中選取一張227×227×3的圖片作爲輸入(原論文AlexNet結構圖示寫的是224×224×3,AlexNet2是吳恩達老師講義中的圖示,經計算確實應該是227)。
注:以下卷積操作由於卷積核通道數和上一層次輸出FeatureMap個數相同,故沒有寫進Kernel中,Kernel的最後一個參數是卷積核個數。

  • 第一個卷積層Kernel=(11,11,96),Padding=0,Stride= 4,因此輸出大小爲\frac{(227-11+2×0)}{4}+1=55,將輸出經過Relu激活層後,進行Local Response Normalization(LRN局部響應歸一化),之後經過一個3×3的Maxpooling,Stride=2,Padding=0,於是輸出大小爲\frac{(55-3+2×0)}{2}+1=27
  • 第二個卷積層,Kernel=(5,5,256),Padding=2,Stride=1,於是輸出爲\frac{(27-5+2×2)}{1}+1=27,接着Relu,LRN,之後經過一個3×3的Maxpooling,Stride=2,Padding=0,於是輸出大小爲\frac{(27-3+2×0)}{2}+1=13
  • 第三個卷積層,Kernel=(3,3,384),Padding=1,Stride=1,輸出爲\frac{(13-3+2×1)}{1}+1=13,接着經過Relu層;
  • 第四個卷積層和第三層操作一樣;
  • 第五個卷積層,Kernel=(3,3,256),Padding=1,Stride=1,輸出爲\frac{(13-3+2×1)}{1}+1=13,Relu,之後經過一個3×3的Maxpooling,Stride=2,Padding=0,於是輸出大小爲\frac{(13-3+2×0)}{2}+1=6;-
  • 全連接層1,這一層實際上是4096個(6,6,256)的Kernel,只是正好可以看做拉直後的全連接,接着依次經過Relu,Dropout;
  • 全連接層2,4096,依次經過Relu,Dropout。
  • 全連接層3,輸出1000個結果,Softmax給出分類。

在原論文的雙GPU並行計算結構下,卷積層 Conv2,Conv4,Conv5中的卷積核只和位於同一GPU的上一層的FeatureMap相連,於是需要訓練的參數數量是:

卷積層的參數 = 卷積核的數量 * 卷積核 + 偏置
Conv1: 96個11×11×3的卷積核,96×11×11×3+96=34848
Conv2: 2組,每組128個5×5×48的卷積核,(128×5×5×48+128)×2=307456
Conv3: 384個3×3×256的卷積核,3×3×256×384+384=885120
Conv4: 2組,每組192個3×3×192的卷積核,(3×3×192×192+192)×2=663936
Conv5: 2組,每組128個3×3×192的卷積核,(3×3×192×128+128)×2=442624
FuulC6: 4096個6×6×256的卷積核,6×6×256×4096+4096=37752832
FuulC7: 4096∗4096+4096=16781312
output(FuulC8): 4096∗1000=4096000

而實際如果Conv2,Conv4,Conv5也合在一塊計算的話,這3個層次中相應的參數數量會翻倍。
不過,從上面也可以看出,參數大多數集中在全連接層,在卷積層由於局部連接和權值共享,權值參數相對較少。

AlexNet的特點及其解釋

AlexNet首次在CNN中成功應用了ReLU和Dropout等Trick,同時也使用了GPU進行運算加速。

  • 激活函數ReLu:
    在最初,Sigmoid和Tanh函數最常用的激活函數。在網絡層數較少時,Sigmoid函數的特性能夠很好地向後傳遞信息。但它有一個很大的問題就是梯度飽和(梯度消失)。
    \sigma(x)=\frac{1}{1+e^{-x}}

所謂梯度飽和就是當輸入的數字很大(或很小)時,其導數值接近於0。這樣在深層次網絡結構中進行反向傳播時,由於鏈式法則,很多個很小的sigmoid導數相乘,導致其結果趨於0,權值更新將非常緩慢。
而ReLU是一個分段線性函數:
ReLu=max(x,0)

相比於Sigmoid不僅運算更快,且導數是恆定值,一般不會發生嚴重的梯度飽和。另外Relu會使一部分神經元的輸出爲0,這樣就形成了網絡的稀疏性,減少了參數的相互依賴,從而一定程度抑制了過擬合。

  • Dropout
    對Dropout可以有以下幾個層面的解釋。
    組合解釋:每次的Dropout都相當於訓練了一個子網絡,最終結果是這些子網絡的組合;
    動機解釋:Dropout的使用降低了參數之間的依賴性,減弱了神經元之間相互依賴協作的工作模式,從而提高了神經元的獨立學習能力,增加了模型泛化能力;
    數據解釋:對於Dropout後的訓練結果總能找到與之對應的樣本,這相當於數據增強。

AlexNet邏輯的Keras實現

由於是用個人的PC跑程序,相當於只有一個運算中心,所以不可能實現論文最原始的網絡了,我們只實現AlexNet的核心邏輯,忽略其關於雙GPU的交互。另外,原論文中使用了LRN,LRN的基本思路是,比如 一張13×13×256的FeatureMap,LRN 要做的就是選取一個位置,從這個位置穿過整個通道,能得到 256 個數字,並進行歸一化。進行LRN的動機是,對於這張 13×13 的圖像中的每個位置來說,我們可能並不需要太多的高激活神經元。不過後來的實驗發現LRN會讓前向傳播和反向傳播的速度降低,但最終對模型效果提升卻不明顯,所以只有AlexNet用LRN,其之後的模型都放棄了。這裏實現的網絡中雖然加入了LRN,但爲了訓練速度可以自行刪除LRN過程:

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

原數據集太大,可使用小一點的數據集圖片進行測試,重點只需放在關注AlexNet網絡結構上。

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