Tensorflow解決MNIST手寫體數字識別
阿新 • • 發佈:2019-01-27
這裡給出的程式碼是來自《Tensorflow實戰Google深度學習框架》,以供參考和學習。
首先這個示例應用了幾個基本的方法:
- 使用隨機梯度下降(batch)
- 使用Relu啟用函式去線性化
- 使用正則化避免過擬合
- 使用帶指數衰減的學習率
- 使用滑動平均模型來使模型更健壯
- 使用交叉熵損失函式來刻畫預測值和真實值之間的差距的損失函式
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
"""
設定輸入和輸出節點的個數,配置神經網路的引數
"""
# MNIST資料集相關的常數
INPUT_NODE = 784 # 輸入節點個數,因為28x28=784
OUTPUT_NODE = 10 # 輸出節點個數,因為(0-9)10個數字
# 配置神經網路的引數:這裡是3層的網路層,一層的隱藏層
LAYER1_NODE = 500 # 這裡使用隱藏層數只有一個的網路結構,而節點有500個
BATCH_SIZE = 100 # 每次batch打包的樣本個數,個數越小訓練過程越接近隨機梯度下降,數字越大,訓練越接近梯度下降
# 模型相關的引數
LEARNING_RATE_BASE = 0.8 # 基礎的學習率
LEARNING_RATE_DECAY = 0.99 # 學習率的衰減率
REGULARAZTION_RATE = 0.0001 # 描述模型複雜度的正則化在損失函式中的係數
TRAINING_STEPS = 5000 # 訓練輪數
MOVING_AVERAGE_DECAY = 0.99 # 滑動平均衰減率
"""
定義輔助函式來計算前向傳播結果,使用ReLU做為啟用函式
"""
# 輔助函式給定神經網路的輸入和所有引數,計算前向傳播結果。在這裡是一個三層的全連線神經網路,RELU函式可以去線性化,同時也可以傳入用於計算平均值的類,
# 這樣方便在測試時使用滑動平均模型
def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
# 不使用滑動平均類時,直接使用引數當前的取值
if avg_class == None:
# 計算隱藏層的前向傳播結果,這裡使用RELU啟用函式
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)
return tf.matmul(layer1, weights2) + biases2
else:
# 使用滑動平均類:首先使用avg_class.average函式來計算得出變數的滑動平均值,然後再計算相應的前向傳播結果
layer1 = tf.nn.relu(tf.matmul(input_tensor, avg_class.average(weights1)) + avg_class.average(biases1))
return tf.matmul(layer1, avg_class.average(weights2)) + avg_class.average(biases2)
"""
定義訓練過程
"""
def train(mnist):
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='y-input')
# 生成隱藏層的引數。
weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1)) # 這裡是784個輸入節點,500個隱層接點,也就是784x500的矩陣
biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE])) # 偏置是根據隱層的節點數而定的
# 生成輸出層的引數。
weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1)) # 同上
biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE])) # 輸出層的節點的引數
# 計算不含滑動平均類的前向傳播結果,因為這裡的avg_class=NONE,所以函式不會使用引數的滑動平均值
y = inference(x, None, weights1, biases1, weights2, biases2)
# 定義儲存訓練輪數的變數,這個變數不需要計算滑動平均值,所以這裡指定這個變數為不可訓練的變數(trainable=false),在tensorflow訓練神經網路中
# 一般會將代表訓練輪數的變數指定為不可訓練的引數
global_step = tf.Variable(0, trainable=False)
# 給定滑動平均衰減率和訓練輪數的變數,初始化滑動平均類。這裡知道給定訓練輪數的變數可以加快訓練早期變數的更新速度。
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
# 在所有代表神經網路引數的變數上使用滑動平均,而其他的輔助變數就不需要了。tf.trainable_variables()返回的就是圖上集合GraphKes.TRAINABLE_VARIABLES
# 中的元素,這個集合的元素就是所有沒有指定trainable=false的引數
variables_averages_op = variable_averages.apply(tf.trainable_variables())
# 計算使用了滑動平均之後的前向傳播結果。但滑動平均不會改變變數本身的值,而是會維護一個影子變數來記錄其滑動平均值。所以需要使用滑動平均值時
# 就需要明確呼叫average函式
average_y = inference(x, variable_averages, weights1, biases1, weights2, biases2)
# 計算交叉熵及其平均值:其中交叉熵作為刻畫預測值和真實值之間差距的損失函式。這裡使用了tensorflow提供的tf.nn.sparse_softmax_cross_entropy_with_logits
# 來計算交叉熵。當分類問題只有一個正確答案時,可以使用該函式加速計算。第一個引數是神經網路不包括softmax層的前向傳播結果,第二個是給定的訓練資料的正確答案。
# 因為標準lable是一個長度為10的一位陣列,而函式argmax得到的是相應標籤對應的類別編號
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cross_entropy_mean = tf.reduce_mean(cross_entropy) # 計算在當前batch中 所有樣例的交叉熵平均值
# 計算L2正則化損失函式
regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
# 計算模型的正則化損失函式。一般只計算神經網路邊上權重的正則化損失,而不使用偏置項
regularaztion = regularizer(weights1) + regularizer(weights2)
# 總損失等於交叉熵損失和正則化損失的和
loss = cross_entropy_mean + regularaztion
# 設定指數衰減的學習率。
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE, # 基礎的學習率,隨著迭代的進行,更新變數時使用的學習率在這個基礎上遞減
global_step, # 當前迭代的輪數,初始值為0
mnist.train.num_examples / BATCH_SIZE, # 跑完所有的訓練資料需要的迭代次數
LEARNING_RATE_DECAY, # 學習率衰減速度
staircase=True) # 決定衰減學習率的曲線圖是何種形式,這裡是階梯衰減
# 優化損失函式:這裡使用tf.train.GradientDescentOptimizer優化演算法來優化損失函式,注意這裡的損失函式包括了交叉熵函式和L2正則化損失
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
# 反向傳播更新引數和更新每一個引數的滑動平均值
# 在訓練神經網路時,每過一遍資料既需要通過反向傳播更新神經網路的引數,又需要更新每一個引數的滑動平均值,為了一次完成多個操作,
# tensorflow提供了 tf.control_dependencies和tf.group兩種機制。
with tf.control_dependencies([train_step, variables_averages_op]): # 等同於train_op = tf.group(train_step, variables_averages_op)
train_op = tf.no_op(name='train')
# 計算正確率:
# 檢查使用了滑動平均模型的神經網路前向傳播結果是否正確:
# tf.argmax(average_y, 1)計算每一個樣例的預測答案。其中average_y是一個batch*10的二維陣列,每一行表示一個樣例的前向傳播結果。
# 第二個引數1表示選取最大值的操作僅在第一個維度中進行(也就是說只在每一行中選取最大值的下標)。於是得到的結果是一個長度為batch的一維陣列,
# 這個一維陣列中的值就表示了每一個樣例對應的數字識別結果。tf.equal判斷兩個張量的每一維是否相等,如果相等則返回TRUE,否則返回False
correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1)) # 簡單來說就是判斷預測結果和真實結果是否相同
# 這裡首先將布林值轉換為實數型,然後再計算平均值。這個平均值就是模型在這一維資料上的正確率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 初始化會話,並開始訓練過程。
with tf.Session() as sess:
tf.global_variables_initializer().run()
# 準備驗證資料。一般在神經網路的訓練過程中會通過驗證資料來大致判斷停止的條件和評判訓練的效果
validate_feed = {x: mnist.validation.images, y_: mnist.validation.labels}
# 準備測試資料。在真實的應用中,這部分資料在訓練的時候是不可見的,這個資料只是作為模型優劣的最後評判標準
test_feed = {x: mnist.test.images, y_: mnist.test.labels}
# 迭代的訓練神經網路。
for i in range(TRAINING_STEPS+1):
if i % 1000 == 0: # 每1000輪輸出一次在驗證資料集上的測試結果
# 計算滑動平均模型在驗證資料上的結果。這裡由於MNIST資料集比較小,所以一次可以處理所有的驗證資料。但如果是太大的資料集不化分為小的
# batch會導致計算時間過長甚至發生記憶體溢位
validate_acc = sess.run(accuracy, feed_dict=validate_feed)
print("After %d training step(s), validation accuracy using average model is %g " % (i, validate_acc))
# mnist.train.next_batch可以讀取一小部分作為訓練batch
xs,ys=mnist.train.next_batch(BATCH_SIZE)
# 把資料餵給定義好的模型
sess.run(train_op,feed_dict={x:xs,y_:ys})
# 在訓練結束之後,在測試資料集上檢測神經網路模型的最終正確率
test_acc=sess.run(accuracy,feed_dict=test_feed)
print('...................................................')
print(("After %d training step(s), test accuracy using average model is %g" %(TRAINING_STEPS, test_acc)))
summary_writer = tf.summary.FileWriter("log_mnist", sess.graph)
summary_writer.close()
"""
主程式入口,這裡設定模型訓練次數為5000次
"""
def main(argv=None):
# 宣告處理MNIST資料集的類,這個類在初始化時會自動下載(當然要聯網),但這裡我使用已經下載好的資料集
# 因為首先會檢查指定目錄下有沒有下載好,沒有的話tensorflow會自動下載
MNIST_data_folder="Mnist/"
# input_data.read_data_sets會將資料分成訓練集、驗證集、測試集
mnist = input_data.read_data_sets(MNIST_data_folder, one_hot=True)
print('...................................................')
print ("Training data size: ", mnist.train.num_examples)# 列印訓練資料集大小
print ("Validating data size: ", mnist.validation.num_examples) # 列印驗證資料集的大小
print ("Testing data size: ", mnist.test.num_examples) # 列印測試資料集的大小
train(mnist)
if __name__=='__main__':
main()
執行結果:
Extracting Mnist/train-images-idx3-ubyte.gz
Extracting Mnist/train-labels-idx1-ubyte.gz
Extracting Mnist/t10k-images-idx3-ubyte.gz
Extracting Mnist/t10k-labels-idx1-ubyte.gz
...................................................
Training data size: 55000
Validating data size: 5000
Testing data size: 10000
After 0 training step(s), validation accuracy using average model is 0.075
After 1000 training step(s), validation accuracy using average model is 0.9758
After 2000 training step(s), validation accuracy using average model is 0.9818
After 3000 training step(s), validation accuracy using average model is 0.9834
After 4000 training step(s), validation accuracy using average model is 0.9828
After 5000 training step(s), validation accuracy using average model is 0.9838
...................................................
After 5000 training step(s), test accuracy using average model is 0.9835
程式碼中有每一行的詳細解釋,從執行結果可以看出tensorflow隨著訓練的進行模型在驗證集上的表現越來越好,最後是在測試集上的執行效果。