TensorFlow学习笔记(三)模型的基本步骤
在本教程中,我們將學(xué)到構(gòu)建一個(gè)TensorFlow模型的基本步驟,并將通過(guò)這些步驟為MNIST構(gòu)建一個(gè)深度卷積神經(jīng)網(wǎng)絡(luò)。
安裝
在創(chuàng)建模型之前,我們會(huì)先加載MNIST數(shù)據(jù)集,然后啟動(dòng)一個(gè)TensorFlow的session。
加載MNIST數(shù)據(jù)
為了方便起見(jiàn),我們已經(jīng)準(zhǔn)備了一個(gè)腳本來(lái)自動(dòng)下載和導(dǎo)入MNIST數(shù)據(jù)集。它會(huì)自動(dòng)創(chuàng)建一個(gè)'MNIST_data'的目錄來(lái)存儲(chǔ)數(shù)據(jù)。
import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True)這里,mnist是一個(gè)輕量級(jí)的類。它以Numpy數(shù)組的形式存儲(chǔ)著訓(xùn)練、校驗(yàn)和測(cè)試數(shù)據(jù)集。同時(shí)提供了一個(gè)函數(shù),用于在迭代中獲得minibatch,后面我們將會(huì)用到。
運(yùn)行TensorFlow的InteractiveSession
Tensorflow依賴于一個(gè)高效的C++后端來(lái)進(jìn)行計(jì)算。與后端的這個(gè)連接叫做session。一般而言,使用TensorFlow程序的流程是先創(chuàng)建一個(gè)圖,然后在session中啟動(dòng)它。
這里,我們使用更加方便的InteractiveSession類。通過(guò)它,你可以更加靈活地構(gòu)建你的代碼。它能讓你在運(yùn)行圖的時(shí)候,插入一些計(jì)算圖,這些計(jì)算圖是由某些操作(operations)構(gòu)成的。這對(duì)于工作在交互式環(huán)境中的人們來(lái)說(shuō)非常便利,比如使用IPython。如果你沒(méi)有使用InteractiveSession,那么你需要在啟動(dòng)session之前構(gòu)建整個(gè)計(jì)算圖,然后啟動(dòng)該計(jì)算圖。
import tensorflow as tf sess = tf.InteractiveSession()計(jì)算圖
為了在Python中進(jìn)行高效的數(shù)值計(jì)算,我們通常會(huì)使用像NumPy一類的庫(kù),將一些諸如矩陣乘法的耗時(shí)操作在Python環(huán)境的外部來(lái)計(jì)算,這些計(jì)算通常會(huì)通過(guò)其它語(yǔ)言并用更為高效的代碼來(lái)實(shí)現(xiàn)。
但遺憾的是,每一個(gè)操作切換回Python環(huán)境時(shí)仍需要不小的開(kāi)銷。如果你想在GPU或者分布式環(huán)境中計(jì)算時(shí),這一開(kāi)銷更加可怖,這一開(kāi)銷主要可能是用來(lái)進(jìn)行數(shù)據(jù)遷移。
TensorFlow也是在Python外部完成其主要工作,但是進(jìn)行了改進(jìn)以避免這種開(kāi)銷。其并沒(méi)有采用在Python外部獨(dú)立運(yùn)行某個(gè)耗時(shí)操作的方式,而是先讓我們描述一個(gè)交互操作圖,然后完全將其運(yùn)行在Python外部。這與Theano或Torch的做法類似。
因此Python代碼的目的是用來(lái)構(gòu)建這個(gè)可以在外部運(yùn)行的計(jì)算圖,以及安排計(jì)算圖的哪一部分應(yīng)該被運(yùn)行。詳情請(qǐng)查看基本用法中的計(jì)算圖表一節(jié)。
構(gòu)建Softmax 回歸模型
在這一節(jié)中我們將建立一個(gè)擁有一個(gè)線性層的softmax回歸模型。在下一節(jié),我們會(huì)將其擴(kuò)展為一個(gè)擁有多層卷積網(wǎng)絡(luò)的softmax回歸模型。
占位符
我們通過(guò)為輸入圖像和目標(biāo)輸出類別創(chuàng)建節(jié)點(diǎn),來(lái)開(kāi)始構(gòu)建計(jì)算圖。
x = tf.placeholder("float", shape=[None, 784]) y_ = tf.placeholder("float", shape=[None, 10])這里的x和y并不是特定的值,相反,他們都只是一個(gè)占位符,可以在TensorFlow運(yùn)行某一計(jì)算時(shí)根據(jù)該占位符輸入具體的值。
輸入圖片x是一個(gè)2維的浮點(diǎn)數(shù)張量。這里,分配給它的shape為[None, 784],其中784是一張展平的MNIST圖片的維度。None表示其值大小不定,在這里作為第一個(gè)維度值,用以指代batch的大小,意即x的數(shù)量不定。輸出類別值y_也是一個(gè)2維張量,其中每一行為一個(gè)10維的one-hot向量,用于代表對(duì)應(yīng)某一MNIST圖片的類別。
雖然placeholder的shape參數(shù)是可選的,但有了它,TensorFlow能夠自動(dòng)捕捉因數(shù)據(jù)維度不一致導(dǎo)致的錯(cuò)誤。
變量
我們現(xiàn)在為模型定義權(quán)重W和偏置b。可以將它們當(dāng)作額外的輸入量,但是TensorFlow有一個(gè)更好的處理方式:變量。一個(gè)變量代表著TensorFlow計(jì)算圖中的一個(gè)值,能夠在計(jì)算過(guò)程中使用,甚至進(jìn)行修改。在機(jī)器學(xué)習(xí)的應(yīng)用過(guò)程中,模型參數(shù)一般用Variable來(lái)表示。
W = tf.Variable(tf.zeros([784,10])) b = tf.Variable(tf.zeros([10]))我們?cè)谡{(diào)用tf.Variable的時(shí)候傳入初始值。在這個(gè)例子里,我們把W和b都初始化為零向量。W是一個(gè)784x10的矩陣(因?yàn)槲覀冇?84個(gè)特征和10個(gè)輸出值)。b是一個(gè)10維的向量(因?yàn)槲覀冇?0個(gè)分類)。
Before Variables can be used within a session, they must be initialized usingthat session.This step takes the initial values (in this case tensors full of zeros) thathave already been specified, and assigns them to eachVariable. This can bedone for all Variables at once.
變量需要通過(guò)seesion初始化后,才能在session中使用。這一初始化步驟為,為初始值指定具體值(本例當(dāng)中是全為零),并將其分配給每個(gè)變量,可以一次性為所有變量完成此操作。
sess.run(tf.initialize_all_variables())類別預(yù)測(cè)與損失函數(shù)
現(xiàn)在我們可以實(shí)現(xiàn)我們的回歸模型了。這只需要一行!我們把向量化后的圖片x和權(quán)重矩陣W相乘,加上偏置b,然后計(jì)算每個(gè)分類的softmax概率值。
y = tf.nn.softmax(tf.matmul(x,W) + b)可以很容易的為訓(xùn)練過(guò)程指定最小化誤差用的損失函數(shù),我們的損失函數(shù)是目標(biāo)類別和預(yù)測(cè)類別之間的交叉熵。
cross_entropy = -tf.reduce_sum(y_*tf.log(y))注意,tf.reduce_sum把minibatch里的每張圖片的交叉熵值都加起來(lái)了。我們計(jì)算的交叉熵是指整個(gè)minibatch的。
訓(xùn)練模型
我們已經(jīng)定義好模型和訓(xùn)練用的損失函數(shù),那么用TensorFlow進(jìn)行訓(xùn)練就很簡(jiǎn)單了。因?yàn)門ensorFlow知道整個(gè)計(jì)算圖,它可以使用自動(dòng)微分法找到對(duì)于各個(gè)變量的損失的梯度值。TensorFlow有大量?jī)?nèi)置的優(yōu)化算法 這個(gè)例子中,我們用最速下降法讓交叉熵下降,步長(zhǎng)為0.01.
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)這一行代碼實(shí)際上是用來(lái)往計(jì)算圖上添加一個(gè)新操作,其中包括計(jì)算梯度,計(jì)算每個(gè)參數(shù)的步長(zhǎng)變化,并且計(jì)算出新的參數(shù)值。
返回的train_step操作對(duì)象,在運(yùn)行時(shí)會(huì)使用梯度下降來(lái)更新參數(shù)。因此,整個(gè)模型的訓(xùn)練可以通過(guò)反復(fù)地運(yùn)行train_step來(lái)完成。
for i in range(1000):batch = mnist.train.next_batch(50)train_step.run(feed_dict={x: batch[0], y_: batch[1]})每一步迭代,我們都會(huì)加載50個(gè)訓(xùn)練樣本,然后執(zhí)行一次train_step,并通過(guò)feed_dict將x 和y_張量占位符用訓(xùn)練訓(xùn)練數(shù)據(jù)替代。
注意,在計(jì)算圖中,你可以用feed_dict來(lái)替代任何張量,并不僅限于替換占位符。
評(píng)估模型
那么我們的模型性能如何呢?
首先讓我們找出那些預(yù)測(cè)正確的標(biāo)簽。tf.argmax 是一個(gè)非常有用的函數(shù),它能給出某個(gè)tensor對(duì)象在某一維上的其數(shù)據(jù)最大值所在的索引值。由于標(biāo)簽向量是由0,1組成,因此最大值1所在的索引位置就是類別標(biāo)簽,比如tf.argmax(y,1)返回的是模型對(duì)于任一輸入x預(yù)測(cè)到的標(biāo)簽值,而tf.argmax(y_,1) 代表正確的標(biāo)簽,我們可以用 tf.equal 來(lái)檢測(cè)我們的預(yù)測(cè)是否真實(shí)標(biāo)簽匹配(索引位置一樣表示匹配)。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))這里返回一個(gè)布爾數(shù)組。為了計(jì)算我們分類的準(zhǔn)確率,我們將布爾值轉(zhuǎn)換為浮點(diǎn)數(shù)來(lái)代表對(duì)、錯(cuò),然后取平均值。例如:[True, False, True, True]變?yōu)閇1,0,1,1],計(jì)算出平均值為0.75。
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))最后,我們可以計(jì)算出在測(cè)試數(shù)據(jù)上的準(zhǔn)確率,大概是91%。
print accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})構(gòu)建一個(gè)多層卷積網(wǎng)絡(luò)
在MNIST上只有91%正確率,實(shí)在太糟糕。在這個(gè)小節(jié)里,我們用一個(gè)稍微復(fù)雜的模型:卷積神經(jīng)網(wǎng)絡(luò)來(lái)改善效果。這會(huì)達(dá)到大概99.2%的準(zhǔn)確率。雖然不是最高,但是還是比較讓人滿意。
權(quán)重初始化
為了創(chuàng)建這個(gè)模型,我們需要?jiǎng)?chuàng)建大量的權(quán)重和偏置項(xiàng)。這個(gè)模型中的權(quán)重在初始化時(shí)應(yīng)該加入少量的噪聲來(lái)打破對(duì)稱性以及避免0梯度。由于我們使用的是ReLU神經(jīng)元,因此比較好的做法是用一個(gè)較小的正數(shù)來(lái)初始化偏置項(xiàng),以避免神經(jīng)元節(jié)點(diǎn)輸出恒為0的問(wèn)題(dead neurons)。為了不在建立模型的時(shí)候反復(fù)做初始化操作,我們定義兩個(gè)函數(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在卷積和池化上有很強(qiáng)的靈活性。我們?cè)趺刺幚磉吔?#xff1f;步長(zhǎng)應(yīng)該設(shè)多大?在這個(gè)實(shí)例里,我們會(huì)一直使用vanilla版本。我們的卷積使用1步長(zhǎng)(stride size),0邊距(padding size)的模板,保證輸出和輸入是同一個(gè)大小。我們的池化用簡(jiǎn)單傳統(tǒng)的2x2大小的模板做max pooling。為了代碼更簡(jiǎn)潔,我們把這部分抽象成一個(gè)函數(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)在我們可以開(kāi)始實(shí)現(xiàn)第一層了。它由一個(gè)卷積接一個(gè)max pooling完成。卷積在每個(gè)5x5的patch中算出32個(gè)特征。卷積的權(quán)重張量形狀是[5, 5, 1, 32],前兩個(gè)維度是patch的大小,接著是輸入的通道數(shù)目,最后是輸出的通道數(shù)目。 而對(duì)于每一個(gè)輸出通道都有一個(gè)對(duì)應(yīng)的偏置量。
W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32])為了用這一層,我們把x變成一個(gè)4d向量,其第2、第3維對(duì)應(yīng)圖片的寬、高,最后一維代表圖片的顏色通道數(shù)(因?yàn)槭腔叶葓D所以這里的通道數(shù)為1,如果是rgb彩色圖,則為3)。
x_image = tf.reshape(x, [-1,28,28,1])We then convolve x_image with the weight tensor, add thebias, apply the ReLU function, and finally max pool.我們把x_image和權(quán)值向量進(jìn)行卷積,加上偏置項(xiàng),然后應(yīng)用ReLU激活函數(shù),最后進(jìn)行max pooling。
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1)第二層卷積
為了構(gòu)建一個(gè)更深的網(wǎng)絡(luò),我們會(huì)把幾個(gè)類似的層堆疊起來(lái)。第二層中,每個(gè)5x5的patch會(huì)得到64個(gè)特征。
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,我們加入一個(gè)有1024個(gè)神經(jīng)元的全連接層,用于處理整個(gè)圖片。我們把池化層輸出的張量reshape成一些向量,乘上權(quán)重矩陣,加上偏置,然后對(duì)其使用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
為了減少過(guò)擬合,我們?cè)谳敵鰧又凹尤雂ropout。我們用一個(gè)placeholder來(lái)代表一個(gè)神經(jīng)元的輸出在dropout中保持不變的概率。這樣我們可以在訓(xùn)練過(guò)程中啟用dropout,在測(cè)試過(guò)程中關(guān)閉dropout。TensorFlow的tf.nn.dropout操作除了可以屏蔽神經(jīng)元的輸出外,還會(huì)自動(dòng)處理神經(jīng)元輸出值的scale。所以用dropout的時(shí)候可以不用考慮scale。
keep_prob = tf.placeholder("float") h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)輸出層
最后,我們添加一個(gè)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)訓(xùn)練和評(píng)估模型
這個(gè)模型的效果如何呢?
為了進(jìn)行訓(xùn)練和評(píng)估,我們使用與之前簡(jiǎn)單的單層SoftMax神經(jīng)網(wǎng)絡(luò)模型幾乎相同的一套代碼,只是我們會(huì)用更加復(fù)雜的ADAM優(yōu)化器來(lái)做梯度最速下降,在feed_dict中加入額外的參數(shù)keep_prob來(lái)控制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})以上代碼,在最終測(cè)試集上的準(zhǔn)確率大概是99.2%。
目前為止,我們已經(jīng)學(xué)會(huì)了用TensorFlow快捷地搭建、訓(xùn)練和評(píng)估一個(gè)復(fù)雜一點(diǎn)兒的深度學(xué)習(xí)模型。
總結(jié)
以上是生活随笔為你收集整理的TensorFlow学习笔记(三)模型的基本步骤的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: scala不使用new关键字创建对象实例
- 下一篇: Akka并发编程——第五节:Actor模