任務
設計一個bp神經網絡是實現對MNIST手寫數字集的識別任務。
網路結構包含一個輸入層、一個隱層和一個輸出層。 其實總共只有兩個層級結構。
包、數據集載入
我們使用tensorflow來簡化我們的操作。
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
# 載入數據集
data = input_data.read_data_sets('MNIST_data/', one_hot=True)
層級結構設計
輸入層中輸入的張量設爲x,類型爲32位浮點型,x以佔位符形式定義,並且其維度爲784。並且每次迭代樣本個數並不確定,主要由迭代batch的大小決定。
x = tf.placeholder(dtype=tf.float32, shape=[None, 784])
隱層牽涉到權值參數與偏置項的設定。在未運行算法之前,需要對權重參數與偏置項進行初始化。
爲防止權重參數恆爲0,我們採用tensorflow的內置方法,將權重參數初始化爲擁有固定標準差的高斯分佈的張量,方法如下:
def weight_variable(shape):
#權重的初始化
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
對於偏置項,我們可以直接將其初始化爲某一固定常量,方法如下:
def bias_variable(shape):
#偏置項的初始化
initial = tf.constant(0.5, shape=shape)
return tf.Variable(initial)
爲了增強模型的複雜度,令其擁有較強的識別能力,定義一個隱層來對輸入的圖像數據(28*28*1)進行相應的非線性映射和梯度傳播。
爲了防止命名衝突也爲了代碼邏輯清晰,設立兩個命名空間來存儲對應變量,其中隱層1位於命名空間hidden_layer中,輸出層位於命名空間output_layer中。
hidden_layer、output_layer中分別牽涉到變量w1、b1、w2、b2,分別代表隱層的突觸權重、偏置項,輸出層的突觸權重、偏置項。激活函數設定爲relu函數,隱層末尾添加dropout來隨機的斷開連接。
在經過兩個層級結構的計算和映射之後,將得到一個預測結果。故輸出層是一個由softmax函數作爲輸出單元,最終輸出是一個含10個概率值的行向量。設輸出爲y,則y的得出將根據上一個隱層計算出的張量L1得出,實現代碼如下:
# 定義神經網絡結構
def bp_nn(x, keep_prob):
with tf.name_scope('hidden_layer'):
w1 = weight_varibal([784, 500])
b1 = bias_variabl([500])
h1 = tf.nn.relu(tf.matmul(x, w1) + b1)
L1 = tf.nn.dropout(h1, keep_prob=keep_prob)
with tf.name_scope('output_layer'):
w2 = weight_varibal([500, 10])
b2 = bias_variabl([10])
y = tf.nn.softmax(tf.matmul(L1, w2) + b2)
return y
實現過程
在定義好了層級結構之後,我們還需要定義超參數、標籤、梯度下降優化器、損失函數等。
代碼如下:
# dropout隨機斷開的概率
keep_prob = tf.placeholder(dtype=tf.float32)
# 標籤
y_ = tf.placeholder(dtype=tf.float32, shape=[None, 10])
# 最終預測值
y = bp_nn(x, keep_prob)
# 定義交叉熵損失函數
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))
# 梯度下降優化目標函數
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(cross_entropy)
# 這是我們算法運行過程中需要判斷的模型準確率
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
要知道tensorflow是基於運算圖的框架,所以我們在定義了一系列佔位符、操作、變量等tensor後,需要在會話裏跑起來,不斷地迭代!
迭代代碼如下:
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(10000):
x_batch, y_batch = data.train.next_batch(100)
sess.run(train_step, feed_dict={x: x_batch, y_: y_batch, keep_prob: 0.8,})
if i % 1000 == 0:
train_accuracy = sess.run(accuracy, feed_dict={x: x_batch, y_:y_batch, keep_prob:1.0})
print('精度' + str(train_accuracy))
print('-----------------------載入測試集----------------------')
acc = []
for i in range(1000):
batch = data.test.next_batch(100)
test_accuracy = sess.run(accuracy, feed_dict={x: batch[0], y_:batch[1], keep_prob:1.0})
acc.append(test_accuracy)
mean_accuracy = np.mean(acc)
print(mean_accuracy)
最終輸出如下:
精度0.25
精度0.95
精度0.99
精度1.0
精度0.99
精度0.98
精度0.98
精度1.0
精度0.99
精度0.99
-----------------------載入測試集----------------------
0.97859997
精度不斷上升,並在dropout的作用下,並不穩定。最終測試集上的精度達到0.98。
我們將損失函數的變化記錄在tensorboard中,變化如下圖所示,可以看出來loss呈現明顯下降趨勢。
改進
爲了進一步提升模型準確率,我將在下一篇文章中採用卷積神經網絡(CNN)來解決該問題。