1. 程式人生 > >【TensorFlow實戰】用TensorFlow實現簡單的卷積神經網路

【TensorFlow實戰】用TensorFlow實現簡單的卷積神經網路

#本次將練習實現一個簡單的卷積神經網路,使用的資料集依然是MNIST,
#預期可以達到99.2%左右的準確性
#使用兩個卷積層加上一個全連線蹭構建一個簡單但是非常具有代表性的卷積神經網路
#載入MNIST資料集,並且建立預設的Interactive Session
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
#載入本地資料
mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)
sess = tf.InteractiveSession()
#接下來要實現的這個卷積神經網路會有很多的權重和偏置需要建立,
#因此我們先定義好初始化函式以便於重複使用
#我們需要給權重製造一些隨機的噪聲來打破完全對稱,比如截斷的正態分佈噪聲
#標準差設為0.1
#同時因為我們使用ReLU
#也給偏置增加一些小的正值(0.1),用來避免死亡節點

#ReLU  用於隱層神經元輸出
#  F(x) = max(0,x)
#當輸入值小於0,就等於0,當大於0,就等於輸入值

#權重
def weight_variable(shape):
    #tf.truncated_normal(shape, mean, stddev) :shape表示生成張量的
    # 維度,mean是均值,stddev是標準差。
    # 這個函式產生正太分佈
    # ,均值和標準差自己設定。這是一個截斷的產生正太分佈的函式,
    # 就是說產生正太分佈的值如果與均值的差值大於兩倍的標準差,
    # 那就重新生成。和一般的正太分佈的產生隨機資料比起來,
    # 這個函式產生的隨機數
    # 與均值的差距不會超過兩倍的標準差,但是一般的別的函式是可能的。
    initial = tf.truncated_normal(shape,stddev=0.1)
    return tf.Variable(initial)

#偏置
def bias_variable(shape):
    #tf.constant 常量
    initial = tf.constant(0.1,shape=shape)
    return tf.Variable(initial)

#卷積層、池化層也是接下來要重複使用的,因此也為他們分別定義建立函式。
#這裡的tf.nn.conv2d是TensorFlow中的2維卷積函式,引數中x是輸入,
#W是卷積的引數,比如[5,5,1,32]:全兩個數字代表卷積核的尺寸,第三個數字代表有多少個channel
#因為我們只有灰度單色,所以是1,如果是彩色的RGB圖片,這裡應該是3
#最後一個數字代表卷積核的數量,也就是這個卷積層會提取多少嘞的特徵,
#strides 代表卷積模板移動的步長,都是1代表會不遺漏地劃過圖片的每一個點。
#padding代表邊界的處理方式,這裡的SAME代表個邊界加上Padding讓卷積的輸出和輸入保持同樣的尺寸

def conv2d(x,W):
    return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')


#tf.nn.max_pool是TensorFlow中的最大池化函式,我們這裡使用2X2的最大池劃,
#將一個2x2的畫素塊降為1x1的畫素。最大池化會保留原始畫素塊中灰度值最高的那個畫素
#就是保留最顯著的特徵。
#因為希望整體上縮小圖片尺寸,因此池化層的strides也社會橫豎兩個方向以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')

#在正式設計卷積神經網路的結構之前,先定義輸入的placeholder,x是特徵
#y_是真實的label

#因為卷積神經網路會利用到空間結構資訊,因此需要將1D的輸入向量轉為2D的
#圖片結構,即從1x784的形式轉為原始的28x28的結構。
#同時因為只有一個顏色通道,所以最終尺寸為[-1,28,28,1],前面的-1代表樣本數量不固定
#最後的1代表顏色通道數量。
#這裡我們使用的tensor變形函式是tf.reshape
x = tf.placeholder(tf.float32,[None,784])
y_ = tf.placeholder(tf.float32,[None,10])
x_image = tf.reshape(x,[-1,28,28,1])

#接下來定義我們的第一個卷積層,我們先使用前面寫好的函式進行引數初始化
#包括weights和bias,這裡的[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_conv = tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)
h_pool1 = max_pool_2x2(h_conv)


#現在定義第二個卷積層,這個卷積層基本和第一個卷積層一樣,唯一的不同是
#卷積核數量變成了64,也就是說這一層的卷積會提取64中特徵
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層,Dropout的用法第四章已經講過
#是通過一個placeholder傳入keep_prob比率來控制的
#在訓練時,我們隨機丟棄一部分節點的資料來減輕過擬合,
#預測時則保留全部資料來追求最好的預測效能
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,並給予一個較小的學習速率1e-4
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),
                                              reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

#再繼續定義評測準確率的操作
correcct_prediction = tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correcct_prediction,tf.float32))

#下面開始訓練過程,首先依然是初始化所有引數
#設定訓練時Dropout的keep_prob比率為0.5
#然後使用大小為50的mini_batch,共進行20000次訓練迭代
#參與訓練的樣本數量總共為100萬,
#其中為100次訓練,我們會對準確率進行一次評測
#評測時keep_prob設為1
#用以實時監測模型的效能

tf.global_variables_initializer().run()
for i in range(2000):
    batch  =  mnist.train.next_batch(50)
    if i%10 == 0:
        #feed_dict 用來傳值
        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:0.5})

#全部訓練完成之後,我們在最終的測試集上進行全面的測試,得到整體的分類準確率
print("text accuray%g"%accuracy.eval(feed_dict={
    x:mnist.test.images,y_:mnist.test.lables,keep_prob:1.0
}))