TensorFlow卷積神經網絡AlexNet實現示例詳解
2012年,Hinton的學生Alex Krizhevsky提出瞭深度卷積神經網絡模型AlexNet,它可以算是LeNet的一種更深更寬的版本。AlexNet以顯著的優勢贏得瞭競爭激烈的ILSVRC 2012比賽,top-5的錯誤率降低至瞭16.4%,遠遠領先第二名的26.2%的成績。AlexNet的出現意義非常重大,它證明瞭CNN在復雜模型下的有效性,而且使用GPU使得訓練在可接受的時間范圍內得到結果,讓CNN和GPU都大火瞭一把。AlexNet可以說是神經網絡在低谷期後的第一次發聲,確立瞭深度學習(深度卷積網絡)在計算機視覺的統治地位,同時也推動瞭深度學習在語音識別、自然語言處理、強化學習等領域的拓展。
模型結構
整個AlexNet前5層為卷積層,後3個為全連接層,其中最後一層是1000類輸出的Softmax分類輸出層。LRN層在第1個及第2個卷積層之後,Max pooling層在兩個LRN層之後和最後一個卷積層之後。ReLU激活函數跟在5個卷積層和2個全連接層後面(最後輸出層沒有)。
因為AlexNet訓練時使用瞭兩塊GPU,因此這個結構圖中不少組件都被拆為瞭兩部分。現在我們GPU的顯存足夠大可以放下全部模型參數,因此隻考慮一塊GPU的情況。
AlexNet中包含的新技術點
成功使用ReLU作為CNN的激活函數,並驗證瞭在較深的網絡中其效果超過瞭Sigmoid,成功解決瞭Sigmoid在網絡較深時的梯度消失問題。雖然ReLU在很久之前就被提出來瞭,但AlexNet的出現才將其發揚光大。
網絡中加入瞭Dropout層,訓練時使用Dropout隨機殺死(忽略)一部分神經元,以避免模型過擬合。(AlexNet通過實踐證實瞭Dropout的效果,關於Dropout有單獨的論文論述。)
池化層使用Max pooling,此前CNN普遍使用平均池化,最大池化避免瞭平均池化的模糊化效果。且提出讓步長小於池化核的尺寸,這樣池化層的輸出之間會有重疊和覆蓋,提升瞭特征的豐富性。
提出瞭LRN層(Local Response Normalization),對局部神經元的活動創建競爭機制,使得其中響應比較大的值變的相對更大,並抑制其它反饋較小的神經元,增強瞭模型的泛化能力。
使用CUDA加速深度神經網絡的訓練,利用GPU強大的並行計算能力,處理神經網絡訓練時大量的矩陣運算。(當時Alex使用的是兩塊GTX 580,單個GPU隻有3GB顯存,限制瞭可訓練的網絡的最大規模。因此他將AlexNet分佈在兩個GPU上,每個GPU的顯存中儲存一半的神經元的參數。現在GTX 1080Ti都出來瞭,硬件發展的還是比較快的,這也是深度學習能飛速發展的重要原因之一吧)
數據增強。隨機地從256*256的原始圖像中截取224*224大小的區域(以及水平翻轉的鏡像),相當於增加瞭((256-224)^2)*2=2048倍的數據量。如果沒有數據增強,僅靠原始數據的數據量,參數眾多的CNN會陷入過擬合中,進行數據增強後可以大大減輕過擬合,提升模型的泛化能力。進行預測時,則是取圖片的四個角加中間共5個位置,並進行左右翻轉,一共獲得10個圖片,對它們進行預測並對求10次結果求均值。同時,AlexNet論文中提到瞭會對圖像的RGB數據進行PCA處理,並對主成分做一個標準差為0.1的高斯擾動,增加瞭一些噪聲,這個Trick可以讓錯誤率再下降1%。
AlexNet每層的超參數
其中,
Input:圖片尺寸224*224
Conv1:卷積核11*11,步長4,96個filter(卷積核尺寸較大)
ReLU
LRN1
Max pooling1:3*3,步長2
Conv2:卷積核5*5,步長1,256個filter
ReLU
LRN2
Max pooling2:3*3,步長2
Conv3:卷積核3*3,步長1,384個filter
ReLU
Conv4:卷積核3*3,步長1,384個filter
ReLU
Conv5:卷積核3*3,步長1,256個filter
ReLU
Max pooling3:3*3,步長2
FC1:4096
ReLU
FC2:4096
ReLU
FC3(Output):1000
對AlexNet使用MNIST數據集
代碼如下:
from datetime import datetime import time import tensorflow as tf import input_data mnist = input_data.read_data_sets('data/', one_hot=True) print("MNIST READY") # 定義網絡參數 n_input = 784 # 輸入的維度 n_output = 10 # 標簽的維度 learning_rate = 0.001 dropout = 0.75 # 定義函數print_activations來顯示網絡每一層結構,展示每一個卷積層或池化層輸出tensor的尺寸 def print_activations(t): print(t.op.name, '', t.get_shape().as_list()) # 定義卷積操作 def conv2d(input, w, b): return tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(input, w, strides=[1, 1, 1, 1], padding='SAME'), b)) # 參數分別指定瞭卷積核的尺寸、多少個channel、filter的個數即產生特征圖的個數 # 步長為1,即掃描全圖像素,[1, 1, 1, 1]分別代表batch_size、h、w、c的stride # 定義池化操作 def max_pool(input): return tf.nn.max_pool(input, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') # padding有兩種選擇:'SAME'(窗口滑動時,像素不夠會自動補0)或'VALID'(不夠就跳過)兩種選擇 # 定義全連接操作 def fc(input, w, b): return tf.nn.relu(tf.add(tf.matmul(input, w), b)) # w*x+b,再通過非線性激活函數relu # 定義網絡結構 def alex_net(_input, _weights, _biases, _keep_prob): _input_r = tf.reshape(_input, [-1, 28, 28, 1]) # 對圖像做一個預處理,轉換為tf支持的格式,即[n, h, w, c],-1是確定好其它3維後,讓tf去推斷剩下的1維 with tf.name_scope('conv1'): _conv1 = conv2d(_input_r, _weights['wc1'], _biases['bc1']) print_activations(_conv1) # 將這一層最後輸出的tensor conv1的結構打印出來 # # 這裡參數基本都是AlexNet論文中的推薦值,但目前其他經典卷積神經網絡模型基本都放棄瞭LRN(主要是效果不明顯), # # 並且使用LRN也會讓前饋、反饋的速度大大下降(整體速度降到1/3) # with tf.name_scope('_lrn1'): # _lrn1 = tf.nn.lrn(_conv1, 4, bias=1.0, alpha=0.001/9, beta=0.75) with tf.name_scope('pool1'): _pool1 = max_pool(_conv1) print_activations(_pool1) with tf.name_scope('conv2'): _conv2 = conv2d(_pool1, _weights['wc2'], _biases['bc2']) print_activations(_conv2) # with tf.name_scope('_lrn2'): # _lrn2 = tf.nn.lrn(_conv2, 4, bias=1.0, alpha=0.001/9, beta=0.75) with tf.name_scope('pool2'): _pool2 = max_pool(_conv2) print_activations(_pool2) with tf.name_scope('conv3'): _conv3 = conv2d(_pool2, _weights['wc3'], _biases['bc3']) print_activations(_conv3) with tf.name_scope('conv4'): _conv4 = conv2d(_conv3, _weights['wc4'], _biases['bc4']) print_activations(_conv4) with tf.name_scope('conv5'): _conv5 = conv2d(_conv4, _weights['wc5'], _biases['bc5']) print_activations(_conv5) with tf.name_scope('pool3'): _pool3 = max_pool(_conv5) print_activations(_pool3) _densel = tf.reshape(_pool3, [-1, _weights['wd1'].get_shape().as_list()[0]]) # 定義全連接層的輸入,把pool2的輸出做一個reshape,變為向量的形式 # pool_shape = _pool3.get_shape().as_list() # nodes = pool_shape[1] * pool_shape[2] * pool_shape[3] with tf.name_scope('fc1'): _fc1 = fc(_densel, _weights['wd1'], _biases['bd1']) _fc1_drop = tf.nn.dropout(_fc1, _keep_prob) # 為瞭減輕過擬合,使用Dropout層 print_activations(_fc1_drop) with tf.name_scope('fc2'): _fc2 = fc(_fc1_drop, _weights['wd2'], _biases['bd2']) _fc2_drop = tf.nn.dropout(_fc2, _keep_prob) print_activations(_fc2_drop) with tf.name_scope('out'): _out = tf.add(tf.matmul(_fc2_drop, _weights['wd3']), _biases['bd3']) print_activations(_out) return _out print("CNN READY") x = tf.placeholder(tf.float32, [None, n_input]) # 用placeholder先占地方,樣本個數不確定為None y = tf.placeholder(tf.float32, [None, n_output]) # 用placeholder先占地方,樣本個數不確定為None keep_prob = tf.placeholder(tf.float32) # 存儲所有的網絡參數 weights = { # 使用截斷的正態分佈(標準差0.1)初始化卷積核的參數kernel,卷積核大小為3*3,channel為1,個數64 'wc1': tf.Variable(tf.truncated_normal([3, 3, 1, 64], dtype=tf.float32, stddev=0.1), name='weights1'), 'wc2': tf.Variable(tf.truncated_normal([3, 3, 64, 128], dtype=tf.float32, stddev=0.1), name='weights2'), 'wc3': tf.Variable(tf.truncated_normal([3, 3, 128, 256], dtype=tf.float32, stddev=0.1), name='weights3'), 'wc4': tf.Variable(tf.truncated_normal([3, 3, 256, 256], dtype=tf.float32, stddev=0.1), name='weights4'), 'wc5': tf.Variable(tf.truncated_normal([3, 3, 256, 128], dtype=tf.float32, stddev=0.1), name='weights5'), 'wd1': tf.Variable(tf.truncated_normal([4*4*128, 1024], dtype=tf.float32, stddev=0.1), name='weights_fc1'), 'wd2': tf.Variable(tf.random_normal([1024, 1024], dtype=tf.float32, stddev=0.1), name='weights_fc2'), 'wd3': tf.Variable(tf.random_normal([1024, n_output], dtype=tf.float32, stddev=0.1), name='weights_output') } biases = { 'bc1': tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32), trainable=True, name='biases1'), 'bc2': tf.Variable(tf.constant(0.0, shape=[128], dtype=tf.float32), trainable=True, name='biases2'), 'bc3': tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32), trainable=True, name='biases3'), 'bc4': tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32), trainable=True, name='biases4'), 'bc5': tf.Variable(tf.constant(0.0, shape=[128], dtype=tf.float32), trainable=True, name='biases5'), 'bd1': tf.Variable(tf.constant(0.0, shape=[1024], dtype=tf.float32), trainable=True, name='biases_fc1'), 'bd2': tf.Variable(tf.constant(0.0, shape=[1024], dtype=tf.float32), trainable=True, name='biases_fc2'), 'bd3': tf.Variable(tf.constant(0.0, shape=[n_output], dtype=tf.float32), trainable=True, name='biases_output') } pred = alex_net(x, weights, biases, keep_prob) # 前向傳播的預測值 cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y)) # 交叉熵損失函數,參數分別為預測值_pred和實際label值y,reduce_mean為求平均loss optm = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # 梯度下降優化器 corr = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) # tf.equal()對比預測值的索引和實際label的索引是否一樣,一樣返回True,不一樣返回False accuracy = tf.reduce_mean(tf.cast(corr, tf.float32)) # 將pred即True或False轉換為1或0,並對所有的判斷結果求均值 # 初始化所有參數 init = tf.global_variables_initializer() print("FUNCTIONS READY") # 上面神經網絡結構定義好之後,下面定義一些超參數 training_epochs = 1000 # 所有樣本迭代1000次 batch_size = 1 # 每進行一次迭代選擇50個樣本 display_step = 10 sess = tf.Session() # 定義一個Session sess.run(init) # 在sess裡run一下初始化操作 for epoch in range(training_epochs): avg_cost = 0. total_batch = int(mnist.train.num_examples/batch_size) start_time = time.time() for i in range(total_batch): batch_xs, batch_ys = mnist.train.next_batch(batch_size) # 逐個batch的去取數據 # 獲取批數據 sess.run(optm, feed_dict={x: batch_xs, y: batch_ys, keep_prob:dropout}) avg_cost += sess.run(cost, feed_dict={x: batch_xs, y: batch_ys, keep_prob:1.0})/total_batch if epoch % display_step == 0: train_accuracy = sess.run(accuracy, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.0}) test_accuracy = sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels, keep_prob:1.0}) print("Epoch: %03d/%03d cost: %.9f TRAIN ACCURACY: %.3f TEST ACCURACY: %.3f" % (epoch, training_epochs, avg_cost, train_accuracy, test_accuracy)) # 計算每輪迭代的平均耗時mn和標準差sd,並顯示 duration = time.time() - start_time print('%s: step %d, duration = %.3f' % (datetime.now(), epoch, duration)) print("DONE")
本文參考《TensorFlow實戰》黃文堅 唐源 著
以上就是TensorFlow卷積神經網絡AlexNet實現示例詳解的詳細內容,更多關於TensorFlow卷積神經網絡AlexNet的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- TensorFlow卷積神經網絡MNIST數據集實現示例
- 基於Python的人臉檢測與分類過程詳解
- Python 實現一個全連接的神經網絡
- TensorFlow神經網絡創建多層感知機MNIST數據集
- Python編程pytorch深度卷積神經網絡AlexNet詳解