TensorFlow是一個很是強大的用來作大規模數值計算的庫。其所擅長的任務之一就是實現以及訓練深度神經網絡。html
在本教程中,咱們將學到構建一個TensorFlow模型的基本步驟,並將經過這些步驟爲MNIST構建一個深度卷積神經網絡。python
這個教程假設你已經熟悉神經網絡和MNIST數據集。若是你還沒有了解,請查看新手指南.git
在建立模型以前,咱們會先加載MNIST數據集,而後啓動一個TensorFlow的session。github
爲了方便起見,咱們已經準備了一個腳本來自動下載和導入MNIST數據集。它會自動建立一個'MNIST_data'
的目錄來存儲數據。算法
import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
這裏,mnist
是一個輕量級的類。它以Numpy數組的形式存儲着訓練、校驗和測試數據集。同時提供了一個函數,用於在迭代中得到minibatch,後面咱們將會用到。後端
Tensorflow依賴於一個高效的C++後端來進行計算。與後端的這個鏈接叫作session。通常而言,使用TensorFlow程序的流程是先建立一個圖,而後在session中啓動它。api
這裏,咱們使用更加方便的InteractiveSession
類。經過它,你能夠更加靈活地構建你的代碼。它能讓你在運行圖的時候,插入一些計算圖,這些計算圖是由某些操做(operations)構成的。這對於工做在交互式環境中的人們來講很是便利,好比使用IPython。若是你沒有使用InteractiveSession
,那麼你須要在啓動session以前構建整個計算圖,而後啓動該計算圖。數組
import tensorflow as tf sess = tf.InteractiveSession()
爲了在Python中進行高效的數值計算,咱們一般會使用像NumPy一類的庫,將一些諸如矩陣乘法的耗時操做在Python環境的外部來計算,這些計算一般會經過其它語言並用更爲高效的代碼來實現。網絡
但遺憾的是,每個操做切換回Python環境時仍須要不小的開銷。若是你想在GPU或者分佈式環境中計算時,這一開銷更加可怖,這一開銷主要多是用來進行數據遷移。session
TensorFlow也是在Python外部完成其主要工做,可是進行了改進以免這種開銷。其並無採用在Python外部獨立運行某個耗時操做的方式,而是先讓咱們描述一個交互操做圖,而後徹底將其運行在Python外部。這與Theano或Torch的作法相似。
所以Python代碼的目的是用來構建這個能夠在外部運行的計算圖,以及安排計算圖的哪一部分應該被運行。詳情請查看基本用法中的計算圖表一節。
在這一節中咱們將創建一個擁有一個線性層的softmax迴歸模型。在下一節,咱們會將其擴展爲一個擁有多層卷積網絡的softmax迴歸模型。
咱們經過爲輸入圖像和目標輸出類別建立節點,來開始構建計算圖。
x = tf.placeholder("float", shape=[None, 784]) 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
來表示。
W = tf.Variable(tf.zeros([784,10])) b = tf.Variable(tf.zeros([10]))
咱們在調用tf.Variable
的時候傳入初始值。在這個例子裏,咱們把W
和b
都初始化爲零向量。W
是一個784x10的矩陣(由於咱們有784個特徵和10個輸出值)。b
是一個10維的向量(由於咱們有10個分類)。
Before Variable
s can be used within a session, they must be initialized using that session. This step takes the initial values (in this case tensors full of zeros) that have already been specified, and assigns them to each Variable
. This can be done for all Variables
at once.
變量
須要經過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
來完成。
for i in range(1000): batch = mnist.train.next_batch(50) 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)。爲了避免在創建模型的時候反覆作初始化操做,咱們定義兩個函數用於初始化。
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)
TensorFlow在卷積和池化上有很強的靈活性。咱們怎麼處理邊界?步長應該設多大?在這個實例裏,咱們會一直使用vanilla版本。咱們的卷積使用1步長(stride size),0邊距(padding size)的模板,保證輸出和輸入是同一個大小。咱們的池化用簡單傳統的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')
如今咱們能夠開始實現第一層了。它由一個卷積接一個max pooling完成。卷積在每一個5x5的patch中算出32個特徵。卷積的權重張量形狀是[5, 5, 1, 32]
,前兩個維度是patch的大小,接着是輸入的通道數目,最後是輸出的通道數目。 而對於每個輸出通道都有一個對應的偏置量。
W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32])
爲了用這一層,咱們把x
變成一個4d向量,其第二、第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。
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個神經元的全鏈接層,用於處理整個圖片。咱們把池化層輸出的張量reshape成一些向量,乘上權重矩陣,加上偏置,而後對其使用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。咱們用一個placeholder
來表明一個神經元的輸出在dropout中保持不變的機率。這樣咱們能夠在訓練過程當中啓用dropout,在測試過程當中關閉dropout。 TensorFlow的tf.nn.dropout
操做除了能夠屏蔽神經元的輸出外,還會自動處理神經元輸出值的scale。因此用dropout的時候能夠不用考慮scale。
keep_prob = tf.placeholder("float") h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
最後,咱們添加一個softmax層,就像前面的單層softmax regression同樣。
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)
這個模型的效果如何呢?
爲了進行訓練和評估,咱們使用與以前簡單的單層SoftMax神經網絡模型幾乎相同的一套代碼,只是咱們會用更加複雜的ADAM優化器來作梯度最速降低,在feed_dict
中加入額外的參數keep_prob
來控制dropout比例。而後每100次迭代輸出一第二天志。
cross_entropy = -tf.reduce_sum(y_*tf.log(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, "float")) sess.run(tf.initialize_all_variables()) 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%。
目前爲止,咱們已經學會了用TensorFlow快捷地搭建、訓練和評估一個複雜一點兒的深度學習模型。
原文地址:Deep MNIST for Experts 翻譯:chenweican 校對:HongyangWang