1. 程式人生 > >卷積神經網路做mnist資料集識別

卷積神經網路做mnist資料集識別

TensorFlow是一個非常強大的用來做大規模數值計算的庫。其所擅長的任務之一就是實現以及訓練深度神經網路。

在本教程中,我們將學到構建一個TensorFlow模型的基本步驟,並將通過這些步驟為MNIST構建一個深度卷積神經網路。

這個教程假設你已經熟悉神經網路和MNIST資料集。如果你尚未了解,請檢視新手指南.

安裝

在建立模型之前,我們會先載入MNIST資料集,然後啟動一個TensorFlow的session。

載入MNIST資料

為了方便起見,我們已經準備了一個指令碼來自動下載和匯入MNIST資料集。它會自動建立一個'MNIST_data'的目錄來儲存資料。

  1. import input_data

  2. mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

這裡,mnist是一個輕量級的類。它以Numpy陣列的形式儲存著訓練、校驗和測試資料集。同時提供了一個函式,用於在迭代中獲得minibatch,後面我們將會用到。

執行TensorFlow的InteractiveSession

Tensorflow依賴於一個高效的C++後端來進行計算。與後端的這個連線叫做session。一般而言,使用TensorFlow程式的流程是先建立一個圖,然後在session中啟動它。

這裡,我們使用更加方便的InteractiveSession類。通過它,你可以更加靈活地構建你的程式碼。它能讓你在執行圖的時候,插入一些計算圖,這些計算圖是由某些操作(operations)構成的。這對於工作在互動式環境中的人們來說非常便利,比如使用IPython。如果你沒有使用InteractiveSession,那麼你需要在啟動session之前構建整個計算圖,然後啟動該計算圖。

  1. import tensorflow as tf

  2. sess = tf.InteractiveSession()

計算圖  

為了在Python中進行高效的數值計算,我們通常會使用像NumPy一類的庫,將一些諸如矩陣乘法的耗時操作在Python環境的外部來計算,這些計算通常會通過其它語言並用更為高效的程式碼來實現。

但遺憾的是,每一個操作切換回Python環境時仍需要不小的開銷。如果你想在GPU或者分散式環境中計算時,這一開銷更加可怖,這一開銷主要可能是用來進行資料遷移。

TensorFlow也是在Python外部完成其主要工作,但是進行了改進以避免這種開銷。其並沒有採用在Python外部獨立執行某個耗時操作的方式,而是先讓我們描述一個互動操作圖,然後完全將其執行在Python外部。這與Theano或Torch的做法類似。

因此Python程式碼的目的是用來構建這個可以在外部執行的計算圖,以及安排計算圖的哪一部分應該被執行。詳情請檢視基本用法中的計算圖表一節。

構建Softmax 迴歸模型

在這一節中我們將建立一個擁有一個線性層的softmax迴歸模型。在下一節,我們會將其擴充套件為一個擁有多層卷積網路的softmax迴歸模型。

佔位符

我們通過為輸入影象和目標輸出類別建立節點,來開始構建計算圖。

  1. x = tf.placeholder("float", shape=[None, 784])

  2. y_ = tf.placeholder("float", shape=[None, 10])

這裡的x和y並不是特定的值,相反,他們都只是一個佔位符,可以在TensorFlow執行某一計算時根據該佔位符輸入具體的值。

輸入圖片x是一個2維的浮點數張量。這裡,分配給它的shape為[None, 784],其中784是一張展平的MNIST圖片的維度。None表示其值大小不定,在這裡作為第一個維度值,用以指代batch的大小,意即x的數量不定。輸出類別值y_也是一個2維張量,其中每一行為一個10維的one-hot向量,用於代表對應某一MNIST圖片的類別。

雖然placeholder的shape引數是可選的,但有了它,TensorFlow能夠自動捕捉因資料維度不一致導致的錯誤。

變數

我們現在為模型定義權重W和偏置b。可以將它們當作額外的輸入量,但是TensorFlow有一個更好的處理方式:變數。一個變數代表著TensorFlow計算圖中的一個值,能夠在計算過程中使用,甚至進行修改。在機器學習的應用過程中,模型引數一般用Variable來表示。

  1. W = tf.Variable(tf.zeros([784,10]))

  2. b = tf.Variable(tf.zeros([10]))

我們在呼叫tf.Variable的時候傳入初始值。在這個例子裡,我們把W和b都初始化為零向量。W是一個784x10的矩陣(因為我們有784個特徵和10個輸出值)。b是一個10維的向量(因為我們有10個分類)。

變數需要通過seesion初始化後,才能在session中使用。這一初始化步驟為,為初始值指定具體值(本例當中是全為零),並將其分配給每個變數,可以一次性為所有變數完成此操作。

sess.run(tf.initialize_all_variables())

類別預測與損失函式

現在我們可以實現我們的迴歸模型了。這隻需要一行!我們把向量化後的圖片x和權重矩陣W相乘,加上偏置b,然後計算每個分類的softmax概率值。

y = tf.nn.softmax(tf.matmul(x,W) + b)

可以很容易的為訓練過程指定最小化誤差用的損失函式,我們的損失函式是目標類別和預測類別之間的交叉熵。

cross_entropy = -tf.reduce_sum(y_*tf.log(y))

注意,tf.reduce_sum把minibatch裡的每張圖片的交叉熵值都加起來了。我們計算的交叉熵是指整個minibatch的。訓練模型

我們已經定義好模型和訓練用的損失函式,那麼用TensorFlow進行訓練就很簡單了。因為TensorFlow知道整個計算圖,它可以使用自動微分法找到對於各個變數的損失的梯度值。TensorFlow有大量內建的優化演算法 這個例子中,我們用最速下降法讓交叉熵下降,步長為0.01.

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

這一行程式碼實際上是用來往計算圖上新增一個新操作,其中包括計算梯度,計算每個引數的步長變化,並且計算出新的引數值。

返回的train_step操作物件,在執行時會使用梯度下降來更新引數。因此,整個模型的訓練可以通過反覆地執行train_step來完成。

  1. for i in range(1000):

  2. batch = mnist.train.next_batch(50)

  3. train_step.run(feed_dict={x: batch[0], y_: batch[1]})

每一步迭代,我們都會載入50個訓練樣本,然後執行一次train_step,並通過feed_dict將x 和 y_張量佔位符用訓練訓練資料替代。

注意,在計算圖中,你可以用feed_dict來替代任何張量,並不僅限於替換佔位符。

評估模型

那麼我們的模型效能如何呢?

首先讓我們找出那些預測正確的標籤。tf.argmax 是一個非常有用的函式,它能給出某個tensor物件在某一維上的其資料最大值所在的索引值。由於標籤向量是由0,1組成,因此最大值1所在的索引位置就是類別標籤,比如tf.argmax(y,1)返回的是模型對於任一輸入x預測到的標籤值,而 tf.argmax(y_,1) 代表正確的標籤,我們可以用 tf.equal 來檢測我們的預測是否真實標籤匹配(索引位置一樣表示匹配)。

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))

這裡返回一個布林陣列。為了計算我們分類的準確率,我們將布林值轉換為浮點數來代表對、錯,然後取平均值。例如:[True, False, True, True]變為[1,0,1,1],計算出平均值為0.75。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

最後,我們可以計算出在測試資料上的準確率,大概是91%。

print accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})

構建一個多層卷積網路

在MNIST上只有91%正確率,實在太糟糕。在這個小節裡,我們用一個稍微複雜的模型:卷積神經網路來改善效果。這會達到大概99.2%的準確率。雖然不是最高,但是還是比較讓人滿意。

權重初始化 為了建立這個模型,我們需要建立大量的權重和偏置項。這個模型中的權重在初始化時應該加入少量的噪聲來打破對稱性以及避免0梯度。由於我們使用的是ReLU神經元,因此比較好的做法是用一個較小的正數來初始化偏置項,以避免神經元節點輸出恆為0的問題(dead neurons)。為了不在建立模型的時候反覆做初始化操作,我們定義兩個函式用於初始化。

  1. def weight_variable(shape):

  2. initial = tf.truncated_normal(shape, stddev=0.1)

  3. return tf.Variable(initial)

  4. def bias_variable(shape):

  5. initial = tf.constant(0.1, shape=shape)

  6. return tf.Variable(initial)

卷積和池化

TensorFlow在卷積和池化上有很強的靈活性。我們怎麼處理邊界?步長應該設多大?在這個例項裡,我們會一直使用vanilla版本。我們的卷積使用1步長(stride size),0邊距(padding size)的模板,保證輸出和輸入是同一個大小。我們的池化用簡單傳統的2x2大小的模板做max pooling。為了程式碼更簡潔,我們把這部分抽象成一個函式。

  1. def conv2d(x, W):

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

  3. def max_pool_2x2(x):

  4. return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],

  5. strides=[1, 2, 2, 1], padding='SAME')

第一層卷積 現在我們可以開始實現第一層了。它由一個卷積接一個max pooling完成。卷積在每個5x5的patch中算出32個特徵。卷積的權重張量形狀是[5, 5, 1, 32],前兩個維度是patch的大小,接著是輸入的通道數目,最後是輸出的通道數目。 而對於每一個輸出通道都有一個對應的偏置量。

  1. W_conv1 = weight_variable([5, 5, 1, 32])

  2. b_conv1 = bias_variable([32])

為了用這一層,我們把x變成一個4d向量,其第2、第3維對應圖片的寬、高,最後一維代表圖片的顏色通道數(因為是灰度圖所以這裡的通道數為1,如果是rgb彩色圖,則為3)。

x_image = tf.reshape(x, [-1,28,28,1])

We then convolve x_image with the weight tensor, add the bias, apply the ReLU function, and finally max pool. 我們把x_image和權值向量進行卷積,加上偏置項,然後應用ReLU啟用函式,最後進行max pooling。

  1. h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

  2. h_pool1 = max_pool_2x2(h_conv1)

第二層卷積 為了構建一個更深的網路,我們會把幾個類似的層堆疊起來。第二層中,每個5x5的patch會得到64個特徵。

  1. W_conv2 = weight_variable([5, 5, 32, 64])

  2. b_conv2 = bias_variable([64])

  3. h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

  4. h_pool2 = max_pool_2x2(h_conv2)

密集連線層 現在,圖片尺寸減小到7x7,我們加入一個有1024個神經元的全連線層,用於處理整個圖片。我們把池化層輸出的張量reshape成一些向量,乘上權重矩陣,加上偏置,然後對其使用ReLU。

  1. W_fc1 = weight_variable([7 * 7 * 64, 1024])

  2. b_fc1 = bias_variable([1024])

  3. h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])

  4. h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

Dropout

為了減少過擬合,我們在輸出層之前加入dropout。我們用一個placeholder來代表一個神經元的輸出在dropout中保持不變的概率。這樣我們可以在訓練過程中啟用dropout,在測試過程中關閉dropout。 TensorFlow的tf.nn.dropout操作除了可以遮蔽神經元的輸出外,還會自動處理神經元輸出值的scale。所以用dropout的時候可以不用考慮scale。

  1. keep_prob = tf.placeholder("float")

  2. h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

輸出層 最後,我們新增一個softmax層,就像前面的單層softmax regression一樣。

  1. W_fc2 = weight_variable([1024, 10])

  2. b_fc2 = bias_variable([10])

  3. y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

訓練和評估模型 這個模型的效果如何呢? 為了進行訓練和評估,我們使用與之前簡單的單層SoftMax神經網路模型幾乎相同的一套程式碼,只是我們會用更加複雜的ADAM優化器來做梯度最速下降,在feed_dict中加入額外的引數keep_prob來控制dropout比例。然後每100次迭代輸出一次日誌。

  1. cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))

  2. train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

  3. correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))

  4. accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

  5. sess.run(tf.initialize_all_variables())

  6. for i in range(20000):

  7. batch = mnist.train.next_batch(50)

  8. if i%100 == 0:

  9. train_accuracy = accuracy.eval(feed_dict={

  10. x:batch[0], y_: batch[1], keep_prob: 1.0})

  11. print "step %d, training accuracy %g"%(i, train_accuracy)

  12. train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

  13. print "test accuracy %g"%accuracy.eval(feed_dict={

  14. x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})

以上程式碼,在最終測試集上的準確率大概是99.2%。 目前為止,我們已經學會了用TensorFlow快捷地搭建、訓練和評估一個複雜一點兒的深度學習模型。