W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
在本章中,我們將知道構(gòu)建一個 TensorFlow 模型的基本步驟,并將通過這些步驟為 MNIST 構(gòu)建一個深度卷積神經(jīng)網(wǎng)絡.
TensorFlow 是一個非常強大的用來做大規(guī)模數(shù)值計算的庫.其所擅長的任務之一就是實現(xiàn)以及訓練深度神經(jīng)網(wǎng)絡.
這個教程假設你已經(jīng)熟悉神經(jīng)網(wǎng)絡和MNIST數(shù)據(jù)集.
在創(chuàng)建模型之前,我們會先加載 MNIST 數(shù)據(jù)集,然后啟動一個 TensorFlow 的 session.
為了方便起見,我們已經(jīng)準備了一個腳本來自動下載和導入MNIST數(shù)據(jù)集.它會自動創(chuàng)建一個'MNIST_data'
的目錄來存儲數(shù)據(jù).
import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
這里,mnist
是一個輕量級的類.它以 Numpy 數(shù)組的形式存儲著訓練、校驗和測試數(shù)據(jù)集.同時提供了一個函數(shù),用于在迭代中獲得 minibatch,后面我們將會用到.
Tensorflow 依賴于一個高效的 C++ 后端來進行計算.與后端的這個連接叫做 session.一般而言,使用 TensorFlow 程序的流程是先創(chuàng)建一個圖,然后在 session 中啟動它.
這里,我們使用更加方便的InteractiveSession
類.通過它,你可以更加靈活地構(gòu)建你的代碼.它能讓你在運行圖的時候,插入一些計算圖,這些計算圖是由某些操作(operations)構(gòu)成的.這對于工作在交互式環(huán)境中的人們來說非常便利,比如使用 IPython.如果你沒有使用InteractiveSession
,那么你需要在啟動 session 之前構(gòu)建整個計算圖,然后啟動該計算圖.
import tensorflow as tf
sess = tf.InteractiveSession()
為了在 Python 中進行高效的數(shù)值計算,我們通常會使用像 NumPy 一類的庫,將一些諸如矩陣乘法的耗時操作在 Python 環(huán)境的外部來計算,這些計算通常會通過其它語言并用更為高效的代碼來實現(xiàn).
但遺憾的是,每一個操作切換回Python環(huán)境時仍需要不小的開銷.如果你想在 GPU 或者分布式環(huán)境中計算時,這一開銷更加可怖,這一開銷主要可能是用來進行數(shù)據(jù)遷移.
TensorFlow 也是在 Python 外部完成其主要工作,但是進行了改進以避免這種開銷.其并沒有采用在 Python 外部獨立運行某個耗時操作的方式,而是先讓我們描述一個交互操作圖,然后完全將其運行在 Python 外部.這與 Theano 或 Torch 的做法類似.
因此 Python 代碼的目的是用來構(gòu)建這個可以在外部運行的計算圖,以及安排計算圖的哪一部分應該被運行.詳情請查看基本用法中的計算圖表一節(jié).
在這一節(jié)中我們將建立一個擁有一個線性層的 softmax 回歸模型.在下一節(jié),我們會將其擴展為一個擁有多層卷積網(wǎng)絡的 softmax 回歸模型.
我們通過為輸入圖像和目標輸出類別創(chuàng)建節(jié)點,來開始構(gòu)建計算圖.
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
這里的x
和y
并不是特定的值,相反,他們都只是一個占位符
,可以在 TensorFlow 運行某一計算時根據(jù)該占位符輸入具體的值.
輸入圖片x
是一個二維的浮點數(shù)張量.這里,分配給它的shape
為[None, 784]
,其中784
是一張展平的 MNIST 圖片的維度.None
表示其值大小不定,在這里作為第一個維度值,用以指代batch的大小,意即x
的數(shù)量不定.輸出類別值y_
也是一個二維張量,其中每一行為一個10維的 one-hot 向量,用于代表對應某一 MNIST 圖片的類別.
雖然placeholder
的shape
參數(shù)是可選的,但有了它,TensorFlow 能夠自動捕捉因數(shù)據(jù)維度不一致導致的錯誤.
我們現(xiàn)在為模型定義權(quán)重W
和偏置b
.可以將它們當作額外的輸入量,但是 TensorFlow 有一個更好的處理方式:變量
.
一個變量
代表著 TensorFlow 計算圖中的一個值,能夠在計算過程中使用,甚至進行修改.
在機器學習的應用過程中,模型參數(shù)一般用Variable
來表示.
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
我們在調(diào)用tf.Variable
的時候傳入初始值.在這個例子里,我們把W
和b
都初始化為零向量.W
是一個784x10的矩陣(因為我們有784個特征和10個輸出值).b
是一個10維的向量(因為我們有10個分類).
變量
需要通過 seesion 初始化后,才能在session中使用.這一初始化步驟為,為初始值指定具體值(本例當中是全為零),并將其分配給每個變量
,可以一次性為所有變量
完成此操作.
sess.run(tf.initialize_all_variables())
現(xiàn)在我們可以實現(xiàn)我們的回歸模型了.這只需要一行!我們把向量化后的圖片x
和權(quán)重矩陣W
相乘,加上偏置b
,然后計算每個分類的 softmax 概率值.
y = tf.nn.softmax(tf.matmul(x,W) + b)
可以很容易的為訓練過程指定最小化誤差用的損失函數(shù),我們的損失函數(shù)是目標類別和預測類別之間的交叉熵.
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
注意,tf.reduce_sum
把minibatch里的每張圖片的交叉熵值都加起來了.我們計算的交叉熵是指整個minibatch的.
我們已經(jīng)定義好模型和訓練用的損失函數(shù),那么用 TensorFlow 進行訓練就很簡單了.因為 TensorFlow 知道整個計算圖,它可以使用自動微分法找到對于各個變量的損失的梯度值.TensorFlow 有大量內(nèi)置的優(yōu)化算法 這個例子中,我們用最速下降法讓交叉熵下降,步長為0.01.
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
這一行代碼實際上是用來往計算圖上添加一個新操作,其中包括計算梯度,計算每個參數(shù)的步長變化,并且計算出新的參數(shù)值.
返回的train_step
操作對象,在運行時會使用梯度下降來更新參數(shù).因此,整個模型的訓練可以通過反復地運行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個訓練樣本,然后執(zhí)行一次train_step
,并通過feed_dict
將x
和 y_
張量占位符
用訓練訓練數(shù)據(jù)替代.
注意,在計算圖中,你可以用feed_dict
來替代任何張量,并不僅限于替換占位符
.
那么我們的模型性能如何呢?
首先讓我們找出那些預測正確的標簽.tf.argmax
是一個非常有用的函數(shù),它能給出某個 tensor 對象在某一維上的其數(shù)據(jù)最大值所在的索引值.由于標簽向量是由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))
這里返回一個布爾數(shù)組.為了計算我們分類的準確率,我們將布爾值轉(zhuǎn)換為浮點數(shù)來代表對、錯,然后取平均值.例如:[True, False, True, True]
變?yōu)?code>[1,0,1,1],計算出平均值為0.75
.
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
最后,我們可以計算出在測試數(shù)據(jù)上的準確率,大概是91%.
print accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})
在 MNIST 上只有91%正確率,實在太糟糕.在這個小節(jié)里,我們用一個稍微復雜的模型:卷積神經(jīng)網(wǎng)絡來改善效果.這會達到大概99.2%的準確率.雖然不是最高,但是還是比較讓人滿意.
為了創(chuàng)建這個模型,我們需要創(chuàng)建大量的權(quán)重和偏置項.這個模型中的權(quán)重在初始化時應該加入少量的噪聲來打破對稱性以及避免0梯度.由于我們使用的是 ReLU 神經(jīng)元,因此比較好的做法是用一個較小的正數(shù)來初始化偏置項,以避免神經(jīng)元節(jié)點輸出恒為0的問題(dead neurons).為了不在建立模型的時候反復做初始化操作,我們定義兩個函數(shù)用于初始化.
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)的模板,保證輸出和輸入是同一個大小.我們的池化用簡單傳統(tǒng)的2x2大小的模板做 max pooling.為了代碼更簡潔,我們把這部分抽象成一個函數(shù).
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')
現(xiàn)在我們可以開始實現(xiàn)第一層了.它由一個卷積接一個 max pooling 完成.卷積在每個5x5的 patch 中算出32個特征.卷積的權(quán)重張量形狀是[5, 5, 1, 32]
,前兩個維度是 patch 的大小,接著是輸入的通道數(shù)目,最后是輸出的通道數(shù)目. 而對于每一個輸出通道都有一個對應的偏置量.
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
為了用這一層,我們把x
變成一個4d向量,其第2、第3維對應圖片的寬、高,最后一維代表圖片的顏色通道數(shù)(因為是灰度圖所以這里的通道數(shù)為1,如果是 rgb 彩色圖,則為3).
x_image = tf.reshape(x, [-1,28,28,1])
我們把x_image
和權(quán)值向量進行卷積,加上偏置項,然后應用ReLU激活函數(shù),最后進行 max pooling.
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
為了構(gòu)建一個更深的網(wǎng)絡,我們會把幾個類似的層堆疊起來.第二層中,每個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)
現(xiàn)在,圖片尺寸減小到7x7,我們加入一個有1024個神經(jīng)元的全連接層,用于處理整個圖片.我們把池化層輸出的張量 reshape 成一些向量,乘上權(quán)重矩陣,加上偏置,然后對其使用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
來代表一個神經(jīng)元的輸出在 dropout 中保持不變的概率.這樣我們可以在訓練過程中啟用 dropout,在測試過程中關閉 dropout. TensorFlow 的tf.nn.dropout
操作除了可以屏蔽神經(jīng)元的輸出外,還會自動處理神經(jīng)元輸出值的 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 神經(jīng)網(wǎng)絡模型幾乎相同的一套代碼,只是我們會用更加復雜的 ADAM 優(yōu)化器來做梯度最速下降,在feed_dict
中加入額外的參數(shù)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快捷地搭建、訓練和評估一個復雜一點兒的深度學習模型已經(jīng)有了一定的掌握.
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: