教你用TensorFlow實現手寫數字識別
弱者用淚水安慰自己,強者用汗水磨練自己。
這段時間因為專案中有一塊需要用到影象識別,最近就一直在煉丹,寶寶心裡苦,但是寶寶不說。。。
能點開這篇文章的朋友估計也已經對TensorFlow有了一定了解,至少知道這是個什麼東西,我也就不過多介紹了。
沒安裝TensorFlow的建議去下一個Anaconda,可以很方便的下載配置好各種科學計算的常用庫,對於Anaconda的配置和更新問題可以去搜一些文章去看,這裡就不多說了。
實現手寫數字識別幾乎是所有入手影象識別的入門程式了,TensorFlow庫裡面也有手寫數字識別的示例程式,在這個路徑下,你可以對應自己的電腦去找一下C:\ProgramData\Anaconda3\pkgs\tensorflow-base-1.12.0-gpu_py36h6e53903_0\Lib\site-packages\tensorflow\examples\tutorials\mnist。
首先載入MNIST資料集,並建立預設的Interactive Session
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist=input_data.read_data_sets("MNIST/",one_hot=True)
sess=tf.InteractiveSession()
接下來要實現的這個卷積神經網路會有很多的權重和偏置要建立,因此我們先定義好初始化函式以便重複使用。我們需要給權重製造一些隨機的噪聲來打破完全對稱,比如截斷的正太分佈噪聲,標準差為0.1。同時因為我們使用的ReLU啟用函式,也給偏置增加一些大於零的值來避免死亡節點。
def weight_variable(shape):
initial=tf.truncated_normal(shape,stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
inital=tf.constant(0.1,shape=shape)
return tf.Variable(inital)
卷積層、池化層也是接下來要重複使用的,因此也要為他們定義方法。這裡的tf.nn.conv2d是TensorFlow中的2維卷積函式,引數中X是輸入,W是卷積的引數,比如[5,5,1,32],前面兩個數字代表卷積核的尺寸,第三個數字代表通道數量,因為我們只是灰度圖,所以是1,如果是RGB彩圖那麼就應該為3,最後一個數字代表卷積核的數量,也就是這個卷積層要提取多少類的特徵。Strides代表卷積模板移動的步長,都是1代表會一個不落的劃過每個點,Padding代表邊界處理方式,這裡的SAME代表給邊界加上Padding讓卷積的輸出和輸入保持同樣的尺寸。
tf.nn.max_pool是TensorFLow中的最大池化函式,我們這裡使用2X2的最大池化,就是說把一個2X2的畫素塊降到1X1的畫素。最大池化會保留原始畫素塊中灰度值最高的哪一個畫素,即保留最顯著的特徵。因為希望整體上縮小圖片尺寸,因此池化層的strides也設為橫豎兩個方向以2為步長。如果步長還是1,那麼我們會得到一個尺寸不變的圖片。
def conv2d(x,W):
return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
在設計卷積神經網路之前,我們還需要定義輸入的placeholder,x是特徵,y_是真實的類別,因為卷積神經網咯會利用到哦控制元件結構資訊,因此需要將1D的輸入向量轉為2D的圖片結構,就是說從1X784變成28X28,,又因為只有一個顏色通道,所以最終的尺寸應該是[-1,28,28,1],前面的-1代表樣本數量不固定,最後的1代表顏色通道數量。
x=tf.placeholder(tf.float32,[None,784])
y_=tf.placeholder(tf.float32,[None,10])
x_image=tf.reshape(x,[-1,28,28,1])
定義第一個卷積層,先初始化引數,[5,5,1,32]代表卷積核的尺寸為5X5,1個顏色通道,32個不同的卷積核。然後用conv2d進行卷積操作,加上偏置,用ReLU啟用函式進行非線性處理。最後,使用最大池化函式max_pool_2x2對卷積的輸出結果進行池化操作。
w_conv1=weight_variable([5,5,1,32])
b_conv1=bias_variable([32])
h_conv1=tf.nn.relu(conv2d(x_image,w_conv1)+b_conv1)
h_pool1=max_pool_2x2(h_conv1)
定義第二層卷積,大體上和第一層一樣,把卷積核數量改為64就OK了
w_conv2=weight_variable([5,5,32,64])
b_conv2=bias_variable([64])
h_conv2=tf.nn.relu(conv2d(h_pool1,w_conv2)+b_conv2)
h_pool2=max_pool_2x2(h_conv2)
因為前面經歷了兩次步長為2X2的最大池化,所以邊長已經只有1/4了,圖片尺寸從28X28變到了7X7,而第二個卷積層的卷積核數量為64,其輸出的tensor尺寸也就是7X7X64。用tf.reshape對第二個卷積層的輸出tensor進行變形,轉為1D的向量,然後連線一個全連線層,隱藏節點為1024,使用ReLU啟用函式。
w_fc1=weight_variable([7*7*64,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)
機器學習中經常出現過擬合問題,為了減輕過擬合,我們可以新增一個Dropout層,該層會隨機丟棄一些節點之間的連線,這樣可減輕過擬合,提高模型泛化性。
keep_prob=tf.placeholder(tf.float32)
h_fc1_drop=tf.nn.dropout(h_fc1,keep_prob)
將Dropout層連線一個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)
定義損失函式cross_entropy,優化器用Adam,學習效率儘量往小裡設定。
cross_entropy=tf.reduce_mean(-tf.reduce_sum(y_*tf.log(y_conv),reduction_indices=[1]))
train_step=tf.train.AdagradOptimizer(1e-4).minimize(cross_entropy)
定義評測準確率
correct_prediction=tf.equal(tf.argmax(y_conv,1),tf.arg_max(y_,1))
accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
接下里就是訓練過程了。開始初始化所有引數,設定訓練時Dropout層的丟棄率為0.5.使用大小為50的mini-batch訓練20000次,每100次訓練輸出一次準確率。
tf.global_variables_initializer().run()
for i in range(20000):
batch=mnist.train.next_batch(50)
if i%100==0:
train_accuracy=accuracy.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0})
print("step %d,training accuracy %g"%(i,train_accuracy))
train_step.run(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0})
最後訓練完成後要輸出一次最後的測試結果
print("test accury %g"%accuracy.eval(feed_dict={x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0}))
程式到這就完事了,就開始跑吧,你大概會跑上20分鐘,你的視窗就會一直輸出準確率
step 4300,training accuracy 0.88
step 4400,training accuracy 0.92
step 4500,training accuracy 0.82
step 4600,training accuracy 0.84
step 4700,training accuracy 0.94