因爲受到計算機性能的影響,雖然LeNet在圖像分類中取得了較好的成績,可是並無引發不少的關注。 知道2012年,Alex等人提出的AlexNet網絡在ImageNet大賽上以遠超第二名的成績奪冠,卷積神經網絡乃至深度學習從新引發了普遍的關注。python
AlexNet是在LeNet的基礎上加深了網絡的結構,學習更豐富更高維的圖像特徵。AlexNet的特色:git
在最初的感知機模型中,輸入和輸出的關係以下:
\[ y = \sum_i{w_ix_i} + b \]
只是單純的線性關係,這樣的網絡結構有很大的侷限性:即便用不少這樣結構的網絡層疊加,其輸出和輸入仍然是線性關係,沒法處理有非線性關係的輸入輸出。所以,對每一個神經元的輸出作個非線性的轉換也就是,將上面就加權求和\(\sum_i{w_ix_i} + b\)的結果輸入到一個非線性函數,也就是激活函數中。 這樣,因爲激活函數的引入,多個網絡層的疊加就再也不是單純的線性變換,而是具備更強的表現能力。
github
在最初,\(sigmoid\)和\(tanh\)函數最經常使用的激活函數。網絡
在網絡層數較少時,\(sigmoid\)函數的特性可以很好的知足激活函數的做用:它把一個實數壓縮至0到1之間,當輸入的數字很是大的時候,結果會接近1;當輸入很是大的負數時,則會獲得接近0的結果。這種特性,可以很好的模擬神經元在受刺激後,是否被激活向後傳遞信息(輸出爲0,幾乎不被激活;輸出爲1,徹底被激活)。
\(sigmoid\)一個很大的問題就是梯度飽和。 觀察\(sigmoid\)函數的曲線,當輸入的數字較大(或較小)時,其函數值趨於不變,其導數變的很是的小。這樣,在層數不少的的網絡結構中,進行反向傳播時,因爲不少個很小的\(sigmoid\)導數累成,致使其結果趨於0,權值更新較慢。ide
這裏有個問題,前面提到,激活函數要用非線性的,是爲了使網絡結構有更強的表達的能力。那這裏使用ReLU本質上倒是個線性的分段函數,是怎麼進行非線性變換的。函數
這裏把神經網絡看着一個巨大的變換矩陣\(M\),其輸入爲全部訓練樣本組成的矩陣\(A\),輸出爲矩陣\(B\)。
\[ B = M \cdot A \]
這裏的\(M\)是一個線性變換的話,則全部的訓練樣本\(A\)進行了線性變換輸出爲\(B\)。性能
那麼對於ReLU來講,因爲其是分段的,0的部分能夠看着神經元沒有激活,不一樣的神經元激活或者不激活,其神經玩過組成的變換矩陣是不同的。
設有兩個訓練樣本 \(a_1,a_2\),其訓練時神經網絡組成的變換矩陣爲\(M_1,M_2\)。 因爲\(M_1\)變換對應的神經網絡中激活神經元和\(M_2\)是不同的,這樣\(M_1,M_2\)其實是兩個不一樣的線性變換。也就是說,每一個訓練樣本使用的線性變換矩陣\(M_i\)是不同的,在整個訓練樣本空間來講,其經歷的是非線性變換。學習
簡單來講,不一樣訓練樣本中的一樣的特徵,在通過神經網絡學習時,流經的神經元是不同的(激活函數值爲0的神經元不會被激活)。這樣,最終的輸出其實是輸入樣本的非線性變換。測試
單個訓練樣本是線性變換,可是每一個訓練樣本的線性變換是不同的,這樣整個訓練樣本集來講,就是非線性的變換。ui
神經網絡因爲訓練的參數多,表能能力強,因此須要比較多的數據量,否則很容易過擬合。當訓練數據有限時,能夠經過一些變換從已有的訓練數據集中生成一些新的數據,以快速地擴充訓練數據。對於圖像數據集來講,能夠對圖像進行一些形變操做:
AlexNet中對數據作了如下操做:
在LeNet中池化是不重疊的,即池化的窗口的大小和步長是相等的,以下
在AlexNet中使用的池化(Pooling)倒是可重疊的,也就是說,在池化的時候,每次移動的步長小於池化的窗口長度。AlexNet池化的大小爲3×3的正方形,每次池化移動步長爲2,這樣就會出現重疊。重疊池化能夠避免過擬合,這個策略貢獻了0.3%的Top-5錯誤率。與非重疊方案\(s = 2,z = 2\)相比,輸出的維度是相等的,而且能在必定程度上抑制過擬合。
ReLU具備讓人滿意的特性,它不須要經過輸入歸一化來防止飽和。若是至少一些訓練樣本對ReLU產生了正輸入,那麼那個神經元上將發生學習。然而,咱們仍然發現接下來的局部響應歸一化有助於泛化。\(a_{x,y}^i\)表示神經元激活,經過在\((x, y)\)位置應用核\(i\),而後應用ReLU非線性來計算,響應歸一化激活\(b^i_{x,y}\)經過下式給定:
\[b^i_{x,y} = a_{x,y}^i / \big( k + \alpha \sum _{j = max(0, i-n / 2)} ^{min(N-1, i+n / 2)} (a_{x,y}^j)^2 \big)^\beta\]
其中,\(N\)是卷積核的個數,也就是生成的FeatureMap的個數;\(k,\alpha,\beta,n\)是超參數,論文中使用的值是\(k = 2 , n =5 ,\alpha = 10^{-4},\beta = 0.75\)
輸出\(b^i_{x,y}\)和輸入\(a_{x,y}^j\)的上標表示的是當前值所在的通道,也便是疊加的方向是沿着通道進行。將要歸一化的值\(a_{x,y}^i\)所在附近通道相同位置的值的平方累加起來\(\sum _{j = max(0, i-n / 2)} ^{min(N-1, i+n / 2)} (a_{x,y}^j)^2\)
這個是比較經常使用的抑制過擬合的方法了。
引入Dropout主要是爲了防止過擬合。在神經網絡中Dropout經過修改神經網絡自己結構來實現,對於某一層的神經元,經過定義的機率將神經元置爲0,這個神經元就不參與前向和後向傳播,就如同在網絡中被刪除了同樣,同時保持輸入層與輸出層神經元的個數不變,而後按照神經網絡的學習方法進行參數更新。在下一次迭代中,又從新隨機刪除一些神經元(置爲0),直至訓練結束。
Dropout應該算是AlexNet中一個很大的創新,如今神經網絡中的必備結構之一。Dropout也能夠當作是一種模型組合,每次生成的網絡結構都不同,經過組合多個模型的方式可以有效地減小過擬合,Dropout只須要兩倍的訓練時間便可實現模型組合(相似取平均)的效果,很是高效。
以下圖:
上圖中的輸入是\(224\times224\),不過通過計算\((224 - 11) / 4 = 54.75\)並非論文中的\(55 \times 55\),而使用\(227 \times 227\)做爲輸入,則\((227 - 11) / 4 = 55\)
網絡包含8個帶權重的層;前5層是卷積層,剩下的3層是全鏈接層。最後一層全鏈接層的輸出是1000維softmax的輸入,softmax會產生1000類標籤的分佈網絡包含8個帶權重的層;前5層是卷積層,剩下的3層是全鏈接層。最後一層全鏈接層的輸出是1000維softmax的輸入,softmax會產生1000類標籤的分佈。
卷積層的參數 = 卷積核的數量 * 卷積核 + 偏置
卷積層 C2,C4,C5中的卷積核只和位於同一GPU的上一層的FeatureMap相連。從上面能夠看出,參數大多數集中在全鏈接層,在卷積層因爲權值共享,權值參數較少。
因爲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上找到。