1. 程式人生 > >TensorFlow官方文件樣例——三層卷積神經網路訓練MNIST資料

TensorFlow官方文件樣例——三層卷積神經網路訓練MNIST資料

        上篇部落格根據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手寫數字資料集使用三層卷積神經網路的我的一個總結。