最近復現了幾個經典的卷積網絡,拿自己的數據集試了了試,不得不承認的是卷積神經網絡在圖像處理方面確實有着得天獨厚的優勢,寫這篇博客講述早期最經典的卷積神經網絡Alex_Net以及附上一些自己的理解
2012年,Hinton的學生在ILSVRC 2012中以顯著的優勢贏得了比賽,top-5的錯誤率降低至了16.4%,相比於第二名的26.2%有了巨大的提升,這可以說是神經網絡在低谷期以後的第一次發聲,確立了深度學習在計算機視覺領域的統治地位。AlexNet可以算是LeNet的一種更深更寬的版本,首次應用了ReLU、Dropout和LRN等trick,同時也使用了GPU進行加速運算。
AlexNet包含了6億3000萬個鏈接,6000萬個參數和65萬個神經元,擁有5個卷積層,其中3個卷積層後面有最大池化層,最後還有3個全鏈接層。本文的內容參考AlexNet的論文
ImageNet classification with deep convolutional neural networks
同時在文末用tensorflow代碼對其進行復現
一、AlexNet的新技術
AlexNet主要用到了一下幾個比較新的技術點
· 使用ReLU作爲CNN的激活函數,並驗證了其效果在較深的網絡中超過了傳統的sigmoid,論文中有下圖
上圖中實線表示使用ReLU激活函數時loss的下降曲線,虛線表示的是sigmoid,我們可以發現使用ReLU激活函數時,其下降明顯變快了
· 訓練時使用了dropout隨機忽略一部分神經元,這已經被證明是避免過擬合的有效手段
· 在CNN中使用重疊的最大池化,在此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊效果。並且,AlexNet中提出讓步長比池化核的尺寸小,這樣池化層之間會有重疊和覆蓋,提升了特徵的豐富性
·提出了Local Response Normalization(LRN)層,對局部神經元的活動創建競爭機制,使得其中響應比較大的值變得更大,並抑制其他反饋較小的神經元,增強了模型的泛化能力
·利用GPU強大的並行計算能力,加速卷積網絡的訓練
·數據增強,隨機的從256x256的原始圖像中截取224x224大小的區域,爲訓練數據增加了2058倍的數量,使用數據增強可以大大減輕過擬合,提升泛化性能。進行預測時,則提取圖片的四個角加中間五個位置,並進行左右翻轉,一共獲得10張圖片,對他們進行預測並對10次結果求均值
·ALexNet論文中還提到了會對圖像的RGB數據進行PCA處理,並對主成分做一個標準差爲0.1的高斯擾動,增加一些噪聲,這個Trick可以讓錯誤率再下降1%
二、AlexNet模型結構
整個AlexNet有8個需要訓練參數的層(不包括池化層和LRN層),前5層爲卷積層,後三層爲全鏈接層
從上圖可以看出AlexNet最後一層是有1000類輸出的SoftMax層用作分類,輸入的圖片尺寸爲224x224,第一個卷積層使用了較大的卷積核11x11,步長爲2。這之後的卷積核爲5x5或者3x3,且步長爲1,即會掃描全圖所有像素;而最大池化層保持爲3x3,並且步長爲2
我們可以發現在前面幾個卷積層,雖然計算量很大,但參數量較小,都在1M左右甚至更小,只佔網絡總參數量很小的一部分,這就是卷積很有用的一個地方,可以通過較小的參數量提取有效的特徵
三、AlexNet的Tensorflow實現
首先給出完整代碼鏈接
https://github.com/LiangjunFeng/Alex_Net/blob/master/Alex_net.py
這裏主要講述AlexNet模型部分的代碼,數據預處理部分請見上述鏈接,數據集較大,想要完整數據集的可以留言給我,郵件發送
import tensorflow as tf
def print_activations(t):
print(t.op.name,' ',t.get_shape().as_list()) #輸出每層網絡結構的函數
config = tf.ConfigProto()
config.gpu_options.allocator_type = 'BFC'
my_graph = tf.Graph()
sess = tf.InteractiveSession(graph=my_graph,config=config) #創建tensorflow圖與會話
with my_graph.as_default():
x = tf.placeholder(tf.float32,[None,image_size*image_size*3])
x = tf.reshape(x,[-1,image_size,image_size,3])
y = tf.placeholder(tf.float32,[None,190]) #爲輸入數據和標籤創立佔符
#conv1,第一個卷積層
c1_kernel = tf.get_variable('weights1',
shape=[11,11,3,64],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d()) #卷積核
c1_conv = tf.nn.conv2d(x,c1_kernel,[1,4,4,1],padding = 'SAME')
c1_biases = tf.Variable(tf.constant(0.0,shape=[64],dtype = tf.float32),trainable = True,name='biases')
c1_bias = tf.nn.bias_add(c1_conv,c1_biases) #加入偏置
conv1 = tf.nn.relu(c1_bias,name = 'conv1') #ReLU激活函數
print_activations(conv1) #輸出網絡結構
#pool1 最大池化層
pool1 = tf.nn.max_pool(conv1,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID',name='pool1')
print_activations(pool1)
#conv2 第二個卷積層
c2_kernel = tf.get_variable('weights2',
shape=[5,5,64,192],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d())
c2_conv = tf.nn.conv2d(pool1,c2_kernel,[1,1,1,1],padding='SAME')
c2_biases = tf.Variable(tf.constant(0.0,shape=[192],dtype=tf.float32),trainable=True,name='biases')
c2_bias = tf.nn.bias_add(c2_conv,c2_biases)
conv2 = tf.nn.relu(c2_bias,name='conv2')
print_activations(conv2)
#pool2 第二個最大池化層
pool2 = tf.nn.max_pool(conv2,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID',name='pool2')
print_activations(pool2)
#conv3 第三個卷積層
c3_kernel = tf.get_variable('weights3',
shape=[3,3,192,384],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d())
c3_conv = tf.nn.conv2d(pool2,c3_kernel,[1,1,1,1],padding='SAME')
c3_biases = tf.Variable(tf.constant(0.0,shape=[384],dtype=tf.float32),trainable = True,name='biases')
c3_bias = tf.nn.bias_add(c3_conv,c3_biases)
conv3 = tf.nn.relu(c3_bias,name='conv3')
print_activations(conv3)
# conv4 第四個卷積層
c4_kernel = tf.get_variable('weights4',
shape=[3, 3, 384, 256],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d())
c4_conv = tf.nn.conv2d(conv3, c4_kernel, [1, 1, 1, 1], padding='SAME')
c4_biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32),trainable=True, name='biases')
c4_bias = tf.nn.bias_add(c4_conv, c4_biases)
conv4 = tf.nn.relu(c4_bias, name='conv4')
print_activations(conv4)
# conv5 第五個卷積層
c5_kernel = tf.get_variable('weights5',
shape=[3, 3, 256, 256],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d())
c5_conv = tf.nn.conv2d(conv4, c5_kernel, [1, 1, 1, 1], padding='SAME')
c5_biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32),trainable=True, name='biases')
c5_bias = tf.nn.bias_add(c5_conv, c5_biases)
conv5 = tf.nn.relu(c5_bias, name='conv5')
print_activations(conv5)
# pool5 最大池化層
pool5 = tf.nn.max_pool(conv4,ksize=[1, 3, 3, 1],strides=[1, 2, 2, 1],padding='VALID',name='pool5')
print_activations(pool5)
# flat1 第一個全鏈接層
f1_weight = tf.get_variable('weights6',
shape=[6*6*256,4096],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
f1_biases = tf.Variable(tf.constant(0.1,shape=[4096]))
pool5_flat = tf.reshape(pool5,[-1,6*6*256])
flat1 = tf.nn.relu(tf.matmul(pool5_flat,f1_weight)+f1_biases,name = 'flat1')
print_activations(flat1)
# flat2 第二個全鏈接層
f2_weight = tf.get_variable('weights7',
shape=[4096,4096],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
f2_biases = tf.Variable(tf.constant(0.1,shape=[4096]))
flat2 = tf.nn.relu(tf.matmul(flat1,f2_weight)+f2_biases,name = 'flat2')
print_activations(flat2)
# output 輸出層
op_weight = tf.get_variable('weights8',
shape=[4096,190],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer()) #使用的數據集共190個類別,所以190個輸出節點
op_biases = tf.Variable(tf.constant(0.1,shape=[190]))
y_conv = tf.nn.softmax(tf.matmul(flat2,op_weight)+op_biases,name = 'output')
print_activations(y_conv)
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(tf.clip_by_value(y_conv, 1e-10, 1.0)),reduction_indices=[1])) #定義損失函數
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) #確定優化算法
correct_prediction = tf.equal(tf.arg_max(y_conv,1),tf.arg_max(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32)) #精度定義
sess.run(tf.global_variables_initializer())
for i in range(num_batches):
rand_index = np.random.choice(35221,size=(batch_size))
train_step.run(feed_dict={x:traindata[rand_index],y:trainlabel[rand_index]})
if i%100 == 0: #每100個batch輸出訓練精度與loss
rand_index = np.random.choice(35221,size=(3000))
train_accuracy = accuracy.eval(feed_dict={x:traindata[rand_index],y:trainlabel[rand_index]})
print('step %d, training accuracy %g'%(i,train_accuracy))
print('step %d, training accuracy %g'%(cross_entropy.eval(feed_dict={x:traindata[rand_index],y:trainlabel[rand_index]})))
print("test accuracy %g"%accuracy.eval(feed_dict={x:testdata,y:testlabel}))#輸出測試精度