深度學習入門——利用卷積神經網路實現MNIST手寫數字識別
MNIST(Modified National Institute of Standards and Technology)資料庫是一個大型手寫數字資料庫,通常用於訓練各種影象處理系統。該資料庫還廣泛用於機器學習領域的培訓和測試。它是通過重新打亂來自NIST原始資料集的樣本而建立的。創作者認為,因為NIST的訓練資料集來自美國人口普查局的員工,而測試資料集來自美國高中學生,這不是非常適合於機器學習實驗。此外,來自NIST的黑白影象被歸一化以適合28x28畫素的邊界框和抗鋸齒,並引入了灰度級。
MNIST資料庫包含60,000個訓練影象和10,000個測試影象。訓練集的一半和測試集的一半來自NIST的訓練資料集,而訓練集的另一半和測試集的另一半來自NIST的測試資料集。 有許多關於試圖實現最低錯誤率的科學論文 ; 一篇論文使用卷積神經網路的分層系統,設法在MNIST資料庫上獲得0.23%的錯誤率。
同時,他也被Kaggle設立為入門級機器學習競賽Digit Recognizer(Learn computer vision fundamentals with the famous MNIST data),可謂是計算機視覺中的”hello world“資料集。
載入MNIST資料
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
使用單層神經網路
佔位符
我們通過為輸入影象和目標輸出類建立節點來開始構建計算圖。
x = tf.placeholder(tf.float32, shape=[None, 784])
y_ = tf.placeholder(tf.float32, shape=[None, 10])
輸入影象x
將由浮點數的二維張量組成。這裡我們給它賦予一個[None,784]
的大小,其中784
是一個影象的28×28個畫素,None
表示第一維度可以是任意大小。目標輸出y_
也將由二維張量組成,其中每一行是一個唯一的10維向量,指示對應的MNIST影象是哪個數字(0到9)。
變數
我們現在定義權重W
,併為我們的模型賦予偏差b
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
我們將呼叫中的每個引數的初始值傳遞給tf.Variable
。 在這種情況下,我們將W
和b
初始化為全0的張量。 W是784x10矩陣(因為我們有784個輸入特徵和10個輸出),b
是10維向量(因為我們有10個數字)。
在會話中使用變數之前,必須使用該會話進行初始化變數。 這一步將已經指定的初始值(在這裡,張量全零),分配給每個變數。 這可以一次完成所有變數的賦值:
sess.run(tf.global_variables_initializer())
理論輸出為
y = tf.matmul(x,W) + b
在以後的訓練中要儘量減少損失函式的值。 定義損失函式是目標和應用於模型預測的softmax啟用函式之間的交叉熵。
cross_entropy = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))
請注意,tf.nn.softmax_cross_entropy_with_logits
在應用softmax啟用函式的交叉熵代價函式,並在所有類中進行求和,tf.reduce_mean表示
取這些和的平均值。
訓練模型
現在我們已經定義了模型和訓練損失函式,之後使用梯度下降法,設定步長為0.5來最小化交叉熵。
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
執行train_step
時返回的引數會用於下一步優化。 因此,訓練模型可以通過重複執行train_step
來完成。
for _ in range(1000):
batch = mnist.train.next_batch(100)
train_step.run(feed_dict={x: batch[0], y_: batch[1]})
每次訓練迭代中載入100個訓練樣例。 然後我們執行train_step
操作,使用feed_dict
將訓練樣例中的佔位符張量x
和y_
替換。
準確度
可以使用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, tf.float32))
最後這種方法準確率大約為92%。
print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
全部程式碼
#出自《21個專案玩轉深度學習》
# coding:utf-8
# 匯入tensorflow。
# 這句import tensorflow as tf是匯入TensorFlow約定俗成的做法,請大家記住。
import tensorflow as tf
# 匯入MNIST教學的模組
from tensorflow.examples.tutorials.mnist import input_data
# 與之前一樣,讀入MNIST資料
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
# 建立x,x是一個佔位符(placeholder),代表待識別的圖片
x = tf.placeholder(tf.float32, [None, 784])
# W是Softmax模型的引數,將一個784維的輸入轉換為一個10維的輸出
# 在TensorFlow中,變數的引數用tf.Variable表示
W = tf.Variable(tf.zeros([784, 10]))
# b是又一個Softmax模型的引數,我們一般叫做“偏置項”(bias)。
b = tf.Variable(tf.zeros([10]))
# y=softmax(Wx + b),y表示模型的輸出
y = tf.nn.softmax(tf.matmul(x, W) + b)
# y_是實際的影象標籤,同樣以佔位符表示。
y_ = tf.placeholder(tf.float32, [None, 10])
# 至此,我們得到了兩個重要的Tensor:y和y_。
# y是模型的輸出,y_是實際的影象標籤,不要忘了y_是獨熱表示的
# 下面我們就會根據y和y_構造損失
# 根據y, y_構造交叉熵損失
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y)))
# 有了損失,我們就可以用隨機梯度下降針對模型的引數(W和b)進行優化
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
# 建立一個Session。只有在Session中才能執行優化步驟train_step。
sess = tf.InteractiveSession()
# 執行之前必須要初始化所有變數,分配記憶體。
tf.global_variables_initializer().run()
print('start training...')
# 進行1000步梯度下降
for _ in range(1000):
# 在mnist.train中取100個訓練資料
# batch_xs是形狀為(100, 784)的影象資料,batch_ys是形如(100, 10)的實際標籤
# batch_xs, batch_ys對應著兩個佔位符x和y_
batch_xs, batch_ys = mnist.train.next_batch(100)
# 在Session中執行train_step,執行時要傳入佔位符的值
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
# 正確的預測結果
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
# 計算預測準確率,它們都是Tensor
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 在Session中執行Tensor可以得到Tensor的值
# 這裡是獲取最終模型的正確率
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) # 0.9185
構建一個多層卷積神經網路
在MNIST上獲得92%的準確性並不是很高。下面使用卷積神經網路。 這將達到約99.2%的準確性 。
權重初始化
要建立這個模型需要建立很多權重和偏差。 一般應該用少量的噪聲初始化權重,以防止對稱性破壞,並防止0梯度。 由於使用的是ReLU神經元,為了避免“死神經元”,初始化這些神經元是一個很好的做法。
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
卷積和池化
卷積設定步幅大小為1,並在周圍填充零,以便輸出與輸入大小相同。 池化為2x2的max pooling。 為程式碼更清晰將這些操作抽象為函式。
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')
第一卷積層
卷積將為每個5x5 patch計算32個特徵。 它的權重張量是[5,5,1,32]
的形狀。 前兩個維度是patch大小,下一個是輸入通道的數量,最後一個是輸出通道的數量。 每個輸出通道還會有帶有一個偏差向量的分量。
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
為了應用該層首先將x
重塑為4維張量,第二維和第三維對應於影象的寬度和高度,並且最後一個維度對應於色彩通道的數量。
x_image = tf.reshape(x, [-1, 28, 28, 1])
然後將x_image
與權重張量進行卷積,加上偏差,應用ReLU函式,最後使用max pooling。 max_pool_2x2
方法將影象大小減小到14x14。
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
第二卷積層
第二層將為每個5x5 patch有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)
密集連線層
現在影象尺寸已經減小到7x7,下面新增一個1024個神經元的全連線圖層,以允許在整個影象上進行處理。 我們將pooling層中的張量重塑為一批向量,乘以權重矩陣,新增一個偏差,並應用一個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。 建立一個佔位符,用於在dropout期間保持神經元輸出的概率。 這可以讓在訓練過程中關閉dropout,並在測試過程中將其關閉。 TensorFlow的tf.nn.dropout
可以自動處理縮放神經元輸出和掩蔽它們,所以dropout只是在沒有任何附加縮放的情況下工作。
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
輸出層
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
訓練和評估模型
不同之處在於:
- 我們將用更復雜的ADAM優化器替代最陡的梯度下降優化器。
- 我們將在
feed_dict
中包含附加引數keep_prob
來控制丟失率。 - 我們將在訓練過程中每100次迭代新增一次記錄。
使用tf.Session
而不是tf.InteractiveSession可以
更好地分離了建立圖(模型說明)的過程和評估圖(模型擬合)的過程。 它通常使更清晰的程式碼。
tf.Session
是在一個塊內建立的,所以一旦塊退出,它就會被自動銷燬。
cross_entropy = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
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: 0.5})
print('test accuracy %g' % accuracy.eval(feed_dict={
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
執行此程式碼後的最終測試集精度應該約為99.2%。
卷積神經網路處理的全部程式碼
# coding: utf-8
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
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')
if __name__ == '__main__':
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
x_image = tf.reshape(x, [-1, 28, 28, 1])
# 第一層卷積層
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)
# 第二層卷積層
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)
# 全連線層,輸出為1024維的向量
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是一個佔位符,訓練時為0.5,測試時為1
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# 把1024維的向量轉換成10維,對應10種數字
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
cross_entropy = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
# 同樣定義train_step
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# 定義測試的準確率
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 建立Session和變數初始化
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
# 訓練20000步
for i in range(20000):
batch = mnist.train.next_batch(50)
# 每100步報告一次在驗證集上的準確度
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: 0.5})
# 訓練結束後報告在測試集上的準確度
print("test accuracy %g" % accuracy.eval(feed_dict={
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
參考
21個專案玩轉深度學習