背景
2009年,李飛飛和他的團隊發表了ImageNet的論文,還附帶了數據集。
2012年,多倫多大學的Geoffrey Hinton、Ilya Sutskever和Alex Krizhevsky提出了一種深度卷積神經網絡結構:AlexNet,奪得了ImageNet冠軍,成績比當時的第二名高出41%。
結構
經典的Lenet(發布於1999)結構如下:
AlexNet的結構模型如下:
AlexNet相比LeNet主要的改動在於:
(1) Data Augmentation數據增長,現在的網絡中已經大量使用了。最主要的是剪裁,光照變換和水平翻轉。
(2) Dropout
Dropout方法和數據增強一樣,都是防止過擬合的。Dropout應該算是AlexNet中一個很大的創新,以至於Hinton在后來很長一段時間里的Talk都拿Dropout說事,后來還出來了一些變種,比如DropConnect等。
(3) ReLU激活函數
用ReLU代替了傳統的Tanh或者Logistic。好處有:
ReLU本質上是分段線性模型,前向計算非常簡單,無需指數之類操作;
ReLU的偏導也很簡單,反向傳播梯度,無需指數或者除法之類操作;
ReLU不容易發生梯度發散問題,Tanh和Logistic激活函數在兩端的時候導數容易趨近於零,多級連乘后梯度更加約等於0;
ReLU關閉了右邊,從而會使得很多的隱層輸出為0,即網絡變得稀疏,起到了類似L1的正則化作用,可以在一定程度上緩解過擬合。
當然,ReLU也是有缺點的,比如左邊全部關了很容易導致某些隱藏節點永無翻身之日,所以后來又出現pReLU、random ReLU等改進,而且ReLU會很容易改變數據的分布,因此ReLU后加Batch Normalization也是常用的改進的方法。
(4) Local Response Normalization
Local Response Normalization要硬翻譯的話是局部響應歸一化,簡稱LRN,實際就是利用臨近的數據做歸一化。這個策略貢獻了1.2%的Top-5錯誤率。
(5) Overlapping Pooling
Overlapping的意思是有重疊,即Pooling的步長比Pooling Kernel的對應邊要小。這個策略貢獻了0.3%的Top-5錯誤率。
(6) 多GPU並行
實現
線性的網絡結構
卷積層:5層
全連接層:3層
深度:8層
神經元個數:650k
分類數目:1000類
參數個數:60M
C1:96*11*11*3(卷積核個數/寬/高/厚度) 34848個參數
C2:256*5*5*48(卷積核個數/寬/高/厚度) 307200個參數
C3:384*3*3*256(卷積核個數/寬/高/厚度) 884736個參數
C4:384*3*3*192(卷積核個數/寬/高/厚度) 663552個參數
C5:256*3*3*192(卷積核個數/寬/高/厚度) 442368個參數
R1:4096*6*6*256(卷積核個數/寬/高/厚度) 37748736個參數
R2:4096*4096 16777216個參數
R3:4096*1000 4096000個參數
總計:60924656個參數
C1:96*55*55 290400個神經元
C2:256*27*27 186624個神經元
C3:384*13*13 64896個神經元
C4:384*13*13 64896個神經元
C5:256*13*13 43264個神經元
R1:4096個神經元
R2:4096個神經元
R3:1000個神經元
總計:659272個
input_data = mx.symbol.Variable(name="data") # stage 1 conv1 = mx.symbol.Convolution(name='conv1', data=input_data, kernel=(11, 11), stride=(4, 4), num_filter=96) relu1 = mx.symbol.Activation(data=conv1, act_type="relu") lrn1 = mx.symbol.LRN(data=relu1, alpha=0.0001, beta=0.75, knorm=2, nsize=5) pool1 = mx.symbol.Pooling( data=lrn1, pool_type="max", kernel=(3, 3), stride=(2,2)) # stage 2 conv2 = mx.symbol.Convolution(name='conv2', data=pool1, kernel=(5, 5), pad=(2, 2), num_filter=256) relu2 = mx.symbol.Activation(data=conv2, act_type="relu") lrn2 = mx.symbol.LRN(data=relu2, alpha=0.0001, beta=0.75, knorm=2, nsize=5) pool2 = mx.symbol.Pooling(data=lrn2, kernel=(3, 3), stride=(2, 2), pool_type="max") # stage 3 conv3 = mx.symbol.Convolution(name='conv3', data=pool2, kernel=(3, 3), pad=(1, 1), num_filter=384) relu3 = mx.symbol.Activation(data=conv3, act_type="relu") conv4 = mx.symbol.Convolution(name='conv4', data=relu3, kernel=(3, 3), pad=(1, 1), num_filter=384) relu4 = mx.symbol.Activation(data=conv4, act_type="relu") conv5 = mx.symbol.Convolution(name='conv5', data=relu4, kernel=(3, 3), pad=(1, 1), num_filter=256) relu5 = mx.symbol.Activation(data=conv5, act_type="relu") pool3 = mx.symbol.Pooling(data=relu5, kernel=(3, 3), stride=(2, 2), pool_type="max") # stage 4 flatten = mx.symbol.Flatten(data=pool3) fc1 = mx.symbol.FullyConnected(name='fc1', data=flatten, num_hidden=4096) relu6 = mx.symbol.Activation(data=fc1, act_type="relu") dropout1 = mx.symbol.Dropout(data=relu6, p=0.5) # stage 5 fc2 = mx.symbol.FullyConnected(name='fc2', data=dropout1, num_hidden=4096) relu7 = mx.symbol.Activation(data=fc2, act_type="relu") dropout2 = mx.symbol.Dropout(data=relu7, p=0.5) # stage 6 fc3 = mx.symbol.FullyConnected(name='fc3', data=dropout2, num_hidden=num_classes) softmax = mx.symbol.SoftmaxOutput(data=fc3, name='softmax') return softmax