使用LeNet-5實現mnist手寫數字分類識別 TensorFlow
阿新 • • 發佈:2018-12-11
TensorFlow的學習材料很多,但很少有講得特別詳細,讓小白一看就懂的。我自己總結了cnn實現mnist分類識別的方法,希望能給TensorFlow初學者一些幫助,實測在python3下可以執行。
# -*- coding: utf-8 -*- # 使用LeNet-5實現mnist手寫數字分類識別 import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import os os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" os.environ["CUDA_VISIBLE_DEVICES"] = "3" # 獲取mnist資料 data_path = os.path.join('.', 'mnist') mnist = input_data.read_data_sets(data_path, one_hot=True) # 一定要加 one_hot # 註冊預設session 後面操作無需指定session 不同sesson之間的資料是獨立的 sess = tf.InteractiveSession() # 建立一個session物件,之後的運算都會跑在這個session裡 ## 引數初始化 # 構造引數W函式 給一些偏差0.1防止死亡節點,標準差為0.1 def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) # 權重在初始化時應該加入少量的噪聲來打破對稱性以及避免0梯度 truncated_normal函式產生正態分佈 return tf.Variable(initial) # 構造偏差b函式 ,給偏置加了一個正值0.1來避免死亡節點 def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial) ## 定義卷積層和池化層函式 # x是輸入,W為卷積引數 如[5,5,1,30] 前兩個表示卷積核的尺寸 # 第三個表示通道channel 第四個表示提取多少類特徵 # strides 表示卷積模板移動的步長,中間兩個引數都是1代表不遺漏的劃過圖片每一個點 # padding 表示邊界處理方式這裡的SAME代表給邊界加上padding讓輸出和輸入保持相同尺寸 def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') # ksize 使用2x2最大池化即將一個2x2畫素塊變為1x1 最大池化保持畫素最高的點 # stride也橫豎兩個方向為2歩長,如果步長為1 得到尺寸不變的圖片 def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') ## 定義張量流輸入格式 # reshape變換張量shape 2維張量變4維 [None, 784] to [-1,28,28,1] 784=28*28 # [-1, 28, 28, 1] -1表示樣本數量不固定 28 28為尺寸 1為通道 x = tf.placeholder(tf.float32, [None, 784]) # placeholder 佔位符 此函式可以理解為形參,用於定義過程,在執行的時候再賦具體的值 [None, 784]表示列是784,行不定 y_ = tf.placeholder(tf.float32, [None, 10]) # 來自MNIST的訓練集,每一個圖片所對應的真實值 x_image = tf.reshape(x, [-1, 28, 28, 1]) # 第2、第3維對應圖片的寬、高,最後一維代表圖片的顏色通道數(因為是灰度圖所以這裡的通道數為1,如果是rgb彩色圖,則為3) ## 構建模型 # 第一次卷積池化 卷積層用ReLU啟用函式 # 權重這個值很重要,因為我們深度學習的過程,就是發現特徵,經過一系列訓練,從而得出每一個特徵對結果影響的權重,我們訓練,就是為了得到這個最佳權重值 W_conv1 = weight_variable([5, 5, 1, 32]) # 前兩個維度是patch的大小,接著是輸入的通道數目,最後是輸出的通道數目 b_conv1 = bias_variable([32]) # 對於每一個輸出通道都有一個對應的偏置量 這裡定義32維常量為0.1 h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # 把x_image和權值向量進行卷積,加上偏置項,然後應用ReLU啟用函式 32*28*28 h_pool1 = max_pool_2x2(h_conv1) # 最後進行max pooling 32*14*14 # 第二次卷積池化 卷積層用ReLU啟用函式 W_conv2 = weight_variable([5, 5, 32, 64]) # 每個5x5的patch會得到64個特徵 b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # 64*14*14 h_pool2 = max_pool_2x2(h_conv2) # 64*7*7 # 全連線層使用ReLU啟用函式 reshape改變張量結構 變成一維 W_fc1 = weight_variable([7 * 7 * 64, 1024]) # 圖片尺寸減小到7x7,我們加入一個有1024個神經元的全連線層,用於處理整個圖片 b_fc1 = bias_variable([1024]) h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) # tf.matmul 矩陣乘法,表示全連線,而不是conv2d # 為了減輕過擬合使用一個Dropout層,隨機丟掉一些神經元不參與運算 keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) # softmax層 第二個全連線層 分為十類資料 softmax後輸出概率最大的數字 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) # tf.nn.softmax 而不是 tf.nn.relu, y_conv是概率 ## 儲存模型 # 建立saver的時候可以指明要儲存的tensor,如果不指明,就會全部存下來 # saves a model every 2 hours and maximum 4 latest models are saved. #saver = tf.train.Saver(max_to_keep=4) saver = tf.train.Saver() # 儲存模型的路徑 ckpt_file_path = "./models/mnist" # models是資料夾,mnist是檔案命名使用的 path = os.path.dirname(os.path.abspath(ckpt_file_path)) if os.path.isdir(path) is False: os.makedirs(path) # loss函式 模型預測的類別概率輸出與真實類別的one hot形式進行cross entropy損失函式的計算。 cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv), reduction_indices=[1])) # 交叉熵 reduction_indices引數,表示函式的處理維度 # 優化演算法Adam函式 train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) #這裡用Adam優化器優化 也可以使用隨機梯度下降 #cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv),reduction_indices=[1]) #交叉熵 #train_step = tf.train.GradientDescentOptimizer(0.5*1e-4).minimize(cross_entropy) # 梯度下降法 # accuracy函式 tf.equal(A, B)是對比這兩個矩陣或者向量的相等的元素,如果是相等的那就返回True,反之返回False correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1)) # tf.argmax()返回最大數值的下標, 第二個引數 0按列找,1按行找 accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) # 準確率 tf.cast是型別轉換函式,tf.float32是轉換目標型別,返回Tensor tf.global_variables_initializer().run() # 使用全域性引數初始化器 並呼叫run方法 來進行引數初始化 # 訓練1000次 每次大小為50的mini-batch 每100次訓練檢視訓練結果 用以實時監測模型效能 1000次是iteration,其實只有1個epoch??? for i in range(1000): batch = mnist.train.next_batch(50) if i % 100 == 0: # 每100次驗證一下準確率 # feed_dict:一個字典,用來表示tensor被feed的值(聯絡placeholder一起看) train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0}) # 評估模型,得出訓練的準確率 print("step %d, train_accuracy %g" % (i+1, train_accuracy)) # %g 指數(e) 或浮點數(根據顯示長度) if i % 200 == 0: tf.train.Saver().save(sess, ckpt_file_path, write_meta_graph=True) # 儲存模型 # batch[0] [1] 分別指資料維度 和標記維度 將資料傳入定義好的優化器進行訓練 train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5}) # train_step是定義好的優化器 print("test accuracy %g" % accuracy.eval(feed_dict={ # 評估模型,得出測試的準確率 x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0 }))