TensorFlow官方文件樣例——三層卷積神經網路訓練MNIST資料
阿新 • • 發佈:2018-11-01
上篇部落格根據TensorFlow官方文件樣例實現了一個簡單的單層神經網路模型,在訓練10000次左右可以達到92.7%左右的準確率。但如果將神經網路的深度拓展,那麼很容易就能夠達到更高的準確率。官方中文文件中就提供了這樣的樣例,它的網路結構如下:
- 輸入層(28 * 28 * 1)
- 卷積層1(28 * 28 * 32)
- 池化層1(14 * 14 * 32)
- 卷積層2(14 * 14 * 64)
- 池化層2(7 * 7 * 64)
- 全連線層(1 * 1024)
- softmax層(10)
使用卷積層給神經網路帶來的好處是能夠提取到影象的邊緣特徵,但是由於圖片本身尺寸一般較大,通過卷積層獲得到的特徵數量也會很大,而通過池化層(一般為最大池化層)進行池化,能夠將特徵中最為明顯的部分提取出來,在儘量不損失特徵的情況下,達到縮小特徵數量的目的。
關於這些層的意義與原理,在吳恩達的deeplearning.ai課程中有非常詳盡的描述,這裡給上網易雲課堂的連結。大家可以自行去學習。
本次的程式碼使用了上篇部落格提到的TensorFlow的內建函式 tf.nn.softmax_cross_entropy_with_logits() 。
本次的程式碼使用了dropout函式,是一個應對神經網路中過擬合非常有效的方式。
本次程式碼使用時,因為本人沒有在優化器函式 train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) 中自定義使用學習速率引數,導致優化器不能自適應訓練資料所需的學習速率,將會使損失函式無法收斂。新增學習速率引數如 1e-4 後,損失函式可以正常收斂,模型正常進行訓練。
程式碼如下:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import tensorflow as tf import time # 模型結構 # 輸入層(28 * 28 * 1) # 卷積層1(28 * 28 * 32) # pooling層1(14 * 14 * 32) # 卷積層2(14 * 14 * 64) # pooling層2(7 * 7 * 64) # 全連線層(1 * 1024) # softmax層(10) sess = tf.InteractiveSession() # 從TensorFlow的樣例中獲取下載mnist的檔案,並解壓輸入 import tensorflow.examples.tutorials.mnist.input_data as input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) NUM_STEPS = 10000 MINIBATCH_SIZE = 64 x = tf.placeholder(tf.float32, [None, 784]) # 初始化輸入影象矩陣x W = tf.Variable(tf.zeros([784, 10])) # 初始化權重矩陣w b = tf.Variable(tf.zeros([10])) # 初始化偏置矩陣b y_labels = tf.placeholder("float", [None, 10]) # 初始化標籤矩陣y_labels def weight_variable(shape): """權重初始化函式,接收矩陣行列值,返回權重矩陣""" initial = tf.truncated_normal(shape, stddev=0.1) # 產生正太分佈資料 return tf.Variable(initial) def bias_variable(shape): """權重初始化函式,接收矩陣行列值,返回權重矩陣""" initial = tf.constant(0.1, shape=shape) # 產生常量 return tf.Variable(initial) def conv2d(x, W): """二維影象卷積函式""" return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') # 步長為1,尺寸不變,邊距為'SAME',尺寸不變 def max_pool_2x2(x): """2x2的最大池化層""" return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') # 2x2池化,步長為2,寬高縮小一倍 # 計算開始時間 start = time.clock() """第一層卷積,在每個5x5的patch中算出32個特徵,得到[28,28,32],第一層池化,將4個畫素池化為1個,使寬高縮減一半成為[14,14,32]""" """將x轉化為四維向量,分別為圖片數量,寬,高,通道# (取-1,使每次從x中取出[28,28,1]後,就會取下一個同樣的矩陣,因此可以取出一個個[28,28,1])""" x_image = tf.reshape(x, [-1, 28, 28, 1]) W_conv1 = weight_variable([5, 5, 1, 32]) # 權重矩陣,前兩個維度為patch的大小,以及輸入通道數目,輸出通道數目 b_conv1 = bias_variable([32]) # 偏置矩陣,每個輸出通道對應一個輸出矩陣 h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # 第一層卷積,啟用函式為relu h_pool1 = max_pool_2x2(h_conv1) # 第一層卷積後的池化層 """第二層卷積,在每個5x5的patch中由32個特徵計算出64個特徵,得到[14,14,64],第一層池化,將4個畫素池化為1個,使寬高縮減一半成為[7,7,64]""" W_conv2 = weight_variable([5, 5, 32, 64]) # 權重矩陣,前兩個維度為patch的大小,以及輸入通道數目,輸出通道數目 b_conv2 = bias_variable([64]) # 偏置矩陣,每個輸出通道對應一個輸出矩陣 h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # 第二層卷積,啟用函式為relu h_pool2 = max_pool_2x2(h_conv2) # 第一層卷積後的池化層 """密集連線層(全連線層),在每個5x5的patch中算出32個特徵""" W_fc1 = weight_variable([7 * 7 * 64, 1024]) # 權重矩陣,維度為輸入特徵數目,輸出通道數目 b_fc1 = bias_variable([1024]) # 偏置矩陣,每個輸出通道對應一個輸出特徵 h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) # 全連線層,把輸入reshape迴向量 h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) # 全連線層,啟用函式為relu """dropout層,隨機丟棄部分權重值,以達到預防過擬合的效果""" keep_prob = tf.placeholder("float") # dropout層,保留引數率,可使訓練時啟動dropout層,測試時關閉dropout層 h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) # dropout層 """softmax層,根據1024個特徵計算輸出標籤""" W_fc2 = weight_variable([1024, 10]) # 權重矩陣,維度為輸入特徵數目,標籤數 b_fc2 = bias_variable([10]) # 偏置矩陣,每個輸出通道對應一個標籤 y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2) # softmax層,計算輸出標籤 # cross_entropy = -tf.reduce_sum(y_labels*tf.log(y_conv)) # 使用TensorFlow內建的交叉熵函式避免出現權重消失問題 cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y_labels, logits=y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) # 呼叫Adma演算法,最小化損失函式 correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_labels, 1)) # 計算預測準確的數量 accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) # 計算準確率 sess.run(tf.global_variables_initializer()) for i in range(NUM_STEPS): # 隨機獲取批資料進行訓練 batch = mnist.train.next_batch(MINIBATCH_SIZE) train_step.run(feed_dict={x: batch[0], y_labels: batch[1], keep_prob: 0.5}) # 以0.5的dropout保留率進行訓練 if i % 100 == 0: # 每當訓練次數達到100的整數倍時,輸出當前批次訓練集資料的準確率 train_accuracy = accuracy.eval(feed_dict={ x: batch[0], y_labels: batch[1], keep_prob: 1.0}) print("訓練次數為{}次時,當前批次訓練集準確率為:{:.4}%".format(i, 100 * train_accuracy)) if i % int(NUM_STEPS/10) == 0 and i != 0: # 每當訓練次數達到1000的整數倍時,輸出當前模型在驗證集以及測試集上的準確率 validation_accuracy = [] for _ in range(int(mnist.validation.num_examples / MINIBATCH_SIZE)): # 由於記憶體問題,將驗證集分批獲取並計算準確率 batch = mnist.validation.next_batch(MINIBATCH_SIZE) validation_accuracy.append(accuracy.eval(feed_dict={x: batch[0], y_labels: batch[1], keep_prob: 1.0})) # 以1.0的dropout保留率進行訓練 test_accuracy = [] for _ in range(int(mnist.test.num_examples / MINIBATCH_SIZE)): # 由於記憶體問題,將測試集分批獲取並計算準確率 batch = mnist.test.next_batch(MINIBATCH_SIZE) test_accuracy.append(accuracy.eval(feed_dict={x: batch[0], y_labels: batch[1], keep_prob: 1.0})) # 以1.0的dropout保留率進行訓練 print("訓練已完成{:.4}%, 當前訓練次數為{}次,驗證集準確率為:{:.4}%, 測試集準確率為: {:.4}%".format(i / int(NUM_STEPS/100), i, 100 * sum(validation_accuracy) / (mnist.validation.num_examples / MINIBATCH_SIZE), 100 * sum(test_accuracy) / (mnist.test.num_examples / MINIBATCH_SIZE))) test_accuracy = [] for _ in range(int(mnist.test.num_examples / MINIBATCH_SIZE)): # 由於記憶體問題,將測試集分批獲取並計算準確率 batch = mnist.test.next_batch(MINIBATCH_SIZE) test_accuracy.append(accuracy.eval(feed_dict={x: batch[0], y_labels: batch[1], keep_prob: 1.0})) # 以1.0的dropout保留率進行訓練 print("訓練完成,測試集準確率為: {:.4}%".format(100 * sum(test_accuracy) / (mnist.test.num_examples / MINIBATCH_SIZE))) # 計算程式結束時間 end = time.clock() print("訓練完成,本次共訓練{}輪,用時{}秒".format(NUM_STEPS, end-start))
以上為TensorFlow的官方文件入門樣例,對MNIST手寫數字資料集使用三層卷積神經網路的我的一個總結。