tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)
續(xù)集請(qǐng)點(diǎn)擊我:tensorflow學(xué)習(xí)筆記——使用TensorFlow操作MNIST數(shù)據(jù)(2)
本節(jié)開始學(xué)習(xí)使用tensorflow教程,當(dāng)然從最簡單的MNIST開始。這怎么說呢,就好比編程入門有Hello World,機(jī)器學(xué)習(xí)入門有MNIST。在此節(jié),我將訓(xùn)練一個(gè)機(jī)器學(xué)習(xí)模型用于預(yù)測圖片里面的數(shù)字。
開始先普及一下基礎(chǔ)知識(shí),我們所說的圖片是通過像素來定義的,即每個(gè)像素點(diǎn)的顏色不同,其對(duì)應(yīng)的顏色值不同,例如黑白圖片的顏色值為0到255,手寫體字符,白色的地方為0,黑色為1,如下圖。
MNIST 是一個(gè)非常有名的手寫體數(shù)字識(shí)別數(shù)據(jù)集,在很多資料中,這個(gè)數(shù)據(jù)集都會(huì)被用做深度學(xué)習(xí)的入門樣例。而Tensorflow的封裝讓MNIST數(shù)據(jù)集變得更加方便。MNIST是NIST數(shù)據(jù)集的一個(gè)子集,它包含了60000張圖片作為訓(xùn)練數(shù)據(jù),10000張圖片作為測試數(shù)據(jù)。在MNIST數(shù)據(jù)集中的每一張圖片都代表了0~9中的一個(gè)數(shù)字。圖片的大小都為28*28,且數(shù)字都會(huì)出現(xiàn)在圖片的正中間,如下圖:
在上圖中右側(cè)顯示了一張數(shù)字1的圖片,而右側(cè)顯示了這個(gè)圖片所對(duì)應(yīng)的像素矩陣,MNIST數(shù)據(jù)集提供了四個(gè)下載文件,在tensorflow中可以將這四個(gè)文件直接下載放到一個(gè)目錄中并加載,如下代碼input_data.read_data_sets所示,如果指定目錄中沒有數(shù)據(jù),那么tensorflow會(huì)自動(dòng)去網(wǎng)絡(luò)上進(jìn)行下載。下面代碼介紹了如何使用tensorflow操作MNIST數(shù)據(jù)集。
MNIST數(shù)據(jù)集的官網(wǎng)是Yann LeCun's website。在這里,我們提供了一份python源代碼用于自動(dòng)下載和安裝這個(gè)數(shù)據(jù)集。你可以下載這份代碼,然后用下面的代碼導(dǎo)入到你的項(xiàng)目里面,也可以直接復(fù)制粘貼到你的代碼文件里面。
注意:MNIST數(shù)據(jù)集包括訓(xùn)練集的圖片和標(biāo)記數(shù)據(jù),以及測試集的圖片和標(biāo)記數(shù)據(jù),在測試集包含的10000個(gè)樣例中,前5000個(gè)樣例取自原始的NIST訓(xùn)練集,后5000個(gè)取自原始的NIST測試集,因此前5000個(gè)預(yù)測起來更容易些。
import tensorflow.examples.tutorials.mnist.input_data as input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
當(dāng)one-hot變?yōu)闉門RUE,則只要對(duì)應(yīng)的位置的值為1,其余都是0。
這里,我直接下載了數(shù)據(jù)集,并放在了我的代碼里面。測試如下:
# _*_coding:utf-8_*_
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
print("OK")
# 打印“Training data size: 55000”
print("Training data size: ", mnist.train.num_examples)
# 打印“Validating data size: 5000”
print("Validating data size: ", mnist.validation.num_examples)
# 打印“Testing data size: 10000”
print("Testing data size: ", mnist.test.num_examples)
# 打印“Example training data: [0. 0. 0. ... 0.380 0.376 ... 0.]”
print("Example training data: ", mnist.train.images[0])
# 打印“Example training data label: [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]”
print("Example training data label: ", mnist.train.labels[0])
batch_size = 100
# 從train的集合中選取batch_size個(gè)訓(xùn)練數(shù)據(jù)
xs, ys = mnist.train.next_batch(batch_size)
# 輸出“X shape:(100,784)”
print("X shape: ", xs.shape)
# 輸出"Y shape:(100,10)"
print("Y shape: ", ys.shape)
結(jié)果如下:
Extracting MNIST_data rain-images-idx3-ubyte.gz Extracting MNIST_data rain-labels-idx1-ubyte.gz Extracting MNIST_data 10k-images-idx3-ubyte.gz Extracting MNIST_data 10k-labels-idx1-ubyte.gz OK Training data size: 55000 Validating data size: 5000 Testing data size: 10000 X shape: (100, 784) Y shape: (100, 10)
從上面的代碼可以看出,通過input_data.read_data_sets函數(shù)生成的類會(huì)自動(dòng)將MNIST數(shù)據(jù)集劃分成train,validation和test三個(gè)數(shù)據(jù)集,其中train這個(gè)集合內(nèi)含有55000張圖片,validation集合內(nèi)含有5000張圖片,這兩個(gè)集合組成了MNIST本身提供的訓(xùn)練數(shù)據(jù)集。test集合內(nèi)有10000張圖片,這些圖片都來自與MNIST提供的測試數(shù)據(jù)集。處理后的每一張圖片是一個(gè)長度為784的一維數(shù)組,這個(gè)數(shù)組中的元素對(duì)應(yīng)了圖片像素矩陣中的每一個(gè)數(shù)字(28*28=784)。因?yàn)樯窠?jīng)網(wǎng)絡(luò)的輸入是一個(gè)特征向量,所以在此把一張二維圖像的像素矩陣放到一個(gè)一維數(shù)組中可以方便tensorflow將圖片的像素矩陣提供給神經(jīng)網(wǎng)絡(luò)的輸入層。像素矩陣中元素的取值范圍為[0, 1],它代表了顏色的深淺。其中0表示白色背景,1表示黑色前景。
Softmax回歸的再復(fù)習(xí)
我們知道MNIST的每一張圖片都表示一個(gè)數(shù)字,從0到9。我們希望得到給定圖片代表每個(gè)數(shù)字的概率。比如說,我們的模型可能推測一張包含9的圖片代表數(shù)字9 的概率為80%但是判斷它是8 的概率是5%(因?yàn)?和9都有上半部分的小圓),然后給予它代表其他數(shù)字的概率更小的值。
這是一個(gè)使用softmax regression模型的經(jīng)典案例。softmax模型可以用來給不同的對(duì)象分配概率。即使在之后,我們訓(xùn)練更加精細(xì)的模型的時(shí)候,最后一步也是需要softmax來分配概率。
Softmax回歸可以解決兩種以上的分類,該模型是Logistic回歸模型在分類問題上的推廣。對(duì)于要識(shí)別的0~9這10類數(shù)字,首選Softmax回歸。MNIST的Softmax回歸源代碼位于Tensorflow-1.1.0/tensorflow.examples/tutorials/mnist/mnist_softmax.py
softmax回歸第一步
為了得到一張給定圖片屬于某個(gè)特定數(shù)字類的證據(jù)(evidence),我們對(duì)圖片像素值進(jìn)行加權(quán)求和。如果這個(gè)像素具有很強(qiáng)的證據(jù)說明這種圖片不屬于該類,那么相應(yīng)的權(quán)值為負(fù)數(shù),相反如果這個(gè)像素?fù)碛泻軓?qiáng)的證據(jù)說明這張圖片不屬于該類,那么相應(yīng)的權(quán)值為負(fù)數(shù),相反如果這個(gè)像素?fù)碛杏辛Φ淖C據(jù)支持這張圖片屬于這個(gè)類,那么權(quán)值是正數(shù)。
下面的圖片顯示了一個(gè)模型學(xué)習(xí)到的圖片上每個(gè)像素對(duì)于特定數(shù)字類的權(quán)值,紅色代表負(fù)數(shù)權(quán)值,藍(lán)色代表正數(shù)權(quán)值。
我們也需要加入一個(gè)額外的偏置量(bias),因?yàn)檩斎胪鶗?huì)帶有一些無關(guān)的干擾量。因?yàn)閷?duì)于給定的輸入圖片 x 它代表的是數(shù)字 i 的證據(jù)可以表示為:
其中,Wi代表權(quán)重,bi代表數(shù)字 i 類的偏置量, j 代表給定圖片 x 的像素索引用于像素求和。然后用 softmax 函數(shù)可以把這些證據(jù)轉(zhuǎn)換成概率 y:
這里的softmax可以看成是一個(gè)激勵(lì)(activation)函數(shù)或者鏈接(link)函數(shù),把我們定義的線性函數(shù)的輸出轉(zhuǎn)換成我們想要的格式,也就是關(guān)于10個(gè)數(shù)字類的概率分布。因此,給定一張圖片,它對(duì)于每一個(gè)數(shù)字的吻合度可以被softmax函數(shù)轉(zhuǎn)換成一個(gè)概率值。softmax函數(shù)可以定義為:
展開等式右邊的子式,可以得到:
但是更多的時(shí)候把softmax模型函數(shù)定義為前一種形式:把輸入值當(dāng)做冪指函數(shù)求值,再正則化這些結(jié)果值。這個(gè)冪運(yùn)算表示,更大的證據(jù)對(duì)應(yīng)更大的假設(shè)模型(hypothesis)里面的乘數(shù)權(quán)重值。反之,擁有更少的證據(jù)意味著在假設(shè)模型里面擁有更小的乘數(shù)系數(shù)。假設(shè)模型里面的權(quán)值不可以是0值或者負(fù)值。Softmax然后會(huì)正則化這些權(quán)重值,使他們的總和等于1,以此構(gòu)造一個(gè)有效的概率分布。
對(duì)于softmax回歸模型可以用下面的圖解釋,對(duì)于輸入的 xs 加權(quán)求和,再分別加上一個(gè)偏置量,最后再輸入到 softmax 函數(shù)中:
如果把它寫成一個(gè)等式,我們可以得到:
我們也可以用向量表示這個(gè)計(jì)算過程:用矩陣乘法和向量相加。這有助于提高計(jì)算效率:
更進(jìn)一步,可以寫成更加緊湊的方式:
接下來就是使用Tensorflow實(shí)現(xiàn)一個(gè) Softmax Regression。其實(shí)在Python中,當(dāng)還沒有TensorFlow時(shí),通常使用Numpy做密集的運(yùn)算操作。因?yàn)镹umpy是使用C和一部分 fortran語言編寫的,并且調(diào)用了 openblas,mkl 等矩陣運(yùn)算庫,因此效率很好。其中每一個(gè)運(yùn)算操作的結(jié)果都要返回到Python中,但不同語言之間傳輸數(shù)據(jù)可能會(huì)帶來更大的延遲。TensorFlow同樣將密集的復(fù)雜運(yùn)算搬到Python外面執(zhí)行,不過做的更徹底。
首先載入TensorFlow庫,并創(chuàng)建一個(gè)新的 InteractiveSession ,使用這個(gè)命令會(huì)將這個(gè)session 注冊(cè)為默認(rèn)的session,之后的運(yùn)算也默認(rèn)跑在這個(gè)session里,不同session之間的數(shù)據(jù)和運(yùn)算應(yīng)該都是相互獨(dú)立的。接下來創(chuàng)建一個(gè) placeholder,即輸入數(shù)據(jù)的地方。Placeholder 的第一個(gè)參數(shù)是數(shù)據(jù)類型,第二個(gè)參數(shù) [None, 784] 代表 tensor 的 shape,也就是數(shù)據(jù)的尺寸,這里的None代表不限條數(shù)的輸入, 784代表每條數(shù)據(jù)的是一個(gè)784維的向量。
sess = tf.InteractiveSession() x = tf.placeholder(tf.float32, [None, 784])
接下來要給Softmax Regression模型中的 weights和 biases 創(chuàng)建 Variable 對(duì)象,Variable是用來存儲(chǔ)模型參數(shù)的,不同于存儲(chǔ)數(shù)據(jù)的 tensor 一旦使用掉就會(huì)消失,Variable 在模型訓(xùn)練迭代中是持久的,它可以長期存在并且在每輪迭代中被更新。我們把 weights 和 biases 全部初始化為0,因?yàn)槟P陀?xùn)練時(shí)會(huì)自動(dòng)學(xué)習(xí)適合的值,所以對(duì)這個(gè)簡單模型來說初始值不太重要,不過對(duì)于復(fù)雜的卷積網(wǎng)絡(luò),循環(huán)網(wǎng)絡(luò)或者比較深的全連接網(wǎng)絡(luò),初始化的方法就比較重要,甚至可以說至關(guān)重要。注意這里的W的shape是 [784, 10] 中 , 784是特征的維數(shù),而后面的10 代表有10類,因?yàn)長abel在 one-hot編碼后是10維的向量。
W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10]))
接下來要實(shí)現(xiàn) Softmax Regression 算法,我們將上面提到的 y = softmax(Wx+b),改為 TensorFlow的語言就是下面的代碼:
y = tf.nn.softmax(tf.matmul(x, W) + b)
Softmax是 tf.nn 下面的一個(gè)函數(shù),而 tf.nn 則包含了大量的神經(jīng)網(wǎng)絡(luò)的組件, tf.matmul是TensorFlow中矩陣乘法函數(shù)。我們就定義了簡單的 Softmax Regression,語法和直接寫數(shù)學(xué)公式很像。然而Tensorflow最厲害的地方還不是定義公式,而是將forward和backward的內(nèi)容都自動(dòng)實(shí)現(xiàn)。所以我們接下來定義好loss,訓(xùn)練時(shí)將會(huì)自動(dòng)求導(dǎo)并進(jìn)行梯度下降,完成對(duì) Softmax Regression模型參數(shù)的自動(dòng)學(xué)習(xí)。
回歸模型訓(xùn)練的步驟
構(gòu)建回歸模型,我們需要輸入原始真實(shí)值(group truth),計(jì)算采用softmax函數(shù)擬合后的預(yù)測值,并定義損失函數(shù)和優(yōu)化器。
為了訓(xùn)練模型,我們首先需要定義一個(gè)指標(biāo)來評(píng)估這個(gè)模型是好的。其實(shí),在機(jī)器學(xué)習(xí)中,我們通常定義指標(biāo)來表示一個(gè)模型是壞的,這個(gè)指標(biāo)稱為成本(cost)或者損失(loss),然后近鄰最小化這個(gè)指標(biāo)。但是這兩種方式是相同的。
一個(gè)非常常見的的成本函數(shù)是“交叉熵(cross-entropy)”。交叉熵產(chǎn)生于信息論里面的信息壓縮編碼技術(shù),但是后來他演變?yōu)閺牟┺恼摰綑C(jī)器學(xué)習(xí)等其他領(lǐng)域里的重要技術(shù)手段,它的定義如下:
Y是我們預(yù)測的概率分布, y' 是實(shí)際的分布(我們輸入的 one-hot vector)。比較粗糙的理解是,交叉熵是用來衡量我們的預(yù)測用于描述真相的低效性。
為了計(jì)算交叉熵,我們首先需要添加一個(gè)新的占位符用于輸入正確值:
y_ = tf.placeholder("float", [None,10])
然后我們可以用交叉熵的公式計(jì)算交叉熵:
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
或者
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y),
reduction_indices=[1]))
?。ㄟ@里我們 y_ * tf.log(y)是前面公式的交叉熵公式,而 tf.reduce_sum就是求和,而tf.reduce_mean則是用來對(duì)每個(gè) batch 的數(shù)據(jù)結(jié)果求均值。)
首先,用 tf.log 計(jì)算 y 的每個(gè)元素的對(duì)數(shù)。接下來,我們把 y_ 的每一個(gè)元素 和 tf.log(y) 的對(duì)應(yīng)元素相乘。最后用 tf.reduce_sum 計(jì)算張量的所有元素總和。(注意:這里的交叉熵不僅僅用來衡量單一的一對(duì)預(yù)測和真實(shí)值,而是所有的圖片的交叉熵的總和。對(duì)于所有的數(shù)據(jù)點(diǎn)的預(yù)測表現(xiàn)比單一數(shù)據(jù)點(diǎn)的表現(xiàn)能更好的描述我們的模型的性能)。
當(dāng)我們知道我們需要我們的模型做什么的時(shí)候,用TensorFlow來訓(xùn)練它是非常容易的。因?yàn)門ensorFlow擁有一張描述我們各個(gè)計(jì)算單元的圖,它可以自動(dòng)的使用反向傳播算法(backpropagation algorithm)來有效的確定變量是如何影響想要最小化的那個(gè)成本值。然后TensorFlow會(huì)用選擇的優(yōu)化算法來不斷地修改變量以降低成本。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
在這里,我們要求TensorFlow用梯度下降算法(gradient descent alogrithm)以0.01的學(xué)習(xí)速率最小化交叉熵。梯度下降算法(gradient descent algorithm)是一個(gè)簡單的學(xué)習(xí)過程,TensorFlow只需將每個(gè)變量一點(diǎn)點(diǎn)地往使成本不斷降低的方向移動(dòng)。當(dāng)然TensorFlow也提供了其他許多優(yōu)化算法:只要簡單地調(diào)整一行代碼就可以使用其他的算法。
TensorFlow在這里實(shí)際上所做的是,它會(huì)在后臺(tái)給描述你的計(jì)算的那張圖里面增加一系列新的計(jì)算操作單元用于實(shí)現(xiàn)反向傳播算法和梯度下降算法。然后,它返回給你的只是一個(gè)單一的操作,當(dāng)運(yùn)行這個(gè)操作時(shí),它用梯度下降算法訓(xùn)練你的模型,微調(diào)你的變量,不斷減少成本。
現(xiàn)在,我們已經(jīng)設(shè)置好了我們的模型,在運(yùn)行計(jì)算之前,我們需要添加一個(gè)操作來初始化我們創(chuàng)建的變量并初始化,但是我們可以在一個(gè)Session里面啟動(dòng)我們的模型:
with tf.Session() as sess:
tf.global_variables_initializer().run()
然后開始訓(xùn)練模型,這里我們讓模型訓(xùn)練訓(xùn)練1000次:
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
或者
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
train_step.run({x: batch_xs, y:batch_ys})
該訓(xùn)練的每個(gè)步驟中,我們都會(huì)隨機(jī)抓取訓(xùn)練數(shù)據(jù)中的100個(gè)批處理數(shù)據(jù)點(diǎn),然后我們用這些數(shù)據(jù)點(diǎn)作為參數(shù)替換之前的占位符來運(yùn)行 train_step。而且我們可以通過 feed_dict 將 x 和 y_ 張量 占位符用訓(xùn)練數(shù)據(jù)替代。(注意:在計(jì)算圖中,我們可以用 feed_dict 來替代任何張量,并不僅限于替換占位符)
使用一小部分的隨機(jī)數(shù)據(jù)來進(jìn)行訓(xùn)練被稱為隨機(jī)訓(xùn)練(stochastic training)- 在這里更確切的說是隨機(jī)梯度下降訓(xùn)練。在理想情況下,我們希望用我們所有的數(shù)據(jù)來進(jìn)行每一步的訓(xùn)練,因?yàn)檫@能給我們更好的訓(xùn)練結(jié)果,但顯然這需要很大的計(jì)算開銷。所以,每一次訓(xùn)練我們可以使用不同的數(shù)據(jù)子集,這樣做既可以減少計(jì)算開銷,又可以最大化地學(xué)習(xí)到數(shù)據(jù)集的總體特性。
現(xiàn)在我們已經(jīng)完成了訓(xùn)練,接下來就是對(duì)模型的準(zhǔn)確率進(jìn)行驗(yàn)證,下面代碼中的 tf.argmax是從一個(gè) tensor中尋找最大值的序號(hào), tf.argmax(y, 1)就是求各個(gè)預(yù)測的數(shù)字中概率最大的一個(gè),而 tf.argmax(y_, 1)則是找到樣本的真實(shí)數(shù)字類別。而 tf.equal方法則是用來判斷預(yù)測數(shù)字類別是否就是正確的類別,最后返回計(jì)算分類是否正確的操作 correct_predition。
訓(xùn)練模型的步驟
已經(jīng)設(shè)置好了模型,在訓(xùn)練之前,需要先初始化我們創(chuàng)建的變量,以及在會(huì)話中啟動(dòng)模型。
# 這里使用InteractiveSession() 來創(chuàng)建交互式上下文的TensorFlow會(huì)話 # 與常規(guī)會(huì)話不同的時(shí),交互式會(huì)話成為默認(rèn)會(huì)話方法 # 如tf.Tensor.eval 和 tf.Operation.run 都可以使用該會(huì)話來運(yùn)行操作(OP sess = tf.InteractiveSession() tf.global_variables_initializer().run()
我們讓模型循環(huán)訓(xùn)練1000次,在每次循環(huán)中我們都隨機(jī)抓取訓(xùn)練數(shù)據(jù)中100個(gè)數(shù)據(jù)點(diǎn),來替代之前的占位符。
# Train
for _ in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x:batch_xs, y_:batch_ys})
這種訓(xùn)練方式成為隨機(jī)訓(xùn)練(stochastic training),使用SGD方法進(jìn)行梯度下降,也就是每次從訓(xùn)練數(shù)據(jù)集中抓取一小部分?jǐn)?shù)據(jù)進(jìn)行梯度下降訓(xùn)練。即與每次對(duì)所有訓(xùn)練數(shù)據(jù)進(jìn)行計(jì)算的BGD相比,SGD即能夠?qū)W習(xí)到數(shù)據(jù)集的總體特征,又能夠加速訓(xùn)練過程。
保存檢查點(diǎn)(checkpoint)的步驟
為了得到可以用來后續(xù)恢復(fù)模型以進(jìn)一步訓(xùn)練或評(píng)估的檢查點(diǎn)文件(checkpoint file),我們實(shí)例化一個(gè) tf.train.Saver。
saver = tf.train.Saver()
在訓(xùn)練循環(huán)中,將定期調(diào)用 saver.save() 方法,向訓(xùn)練文件夾中寫入包含了當(dāng)前所有可訓(xùn)練變量值的檢查點(diǎn)文件。
saver.save(sess, FLAGS.train_dir, global_step=step)
這樣,我們以后就可以使用 saver.restore() 方法,重載模型的參數(shù),繼續(xù)訓(xùn)練。
saver.restore(sess, FLAGS.train_dir)
這里不多講,具體請(qǐng)參考博客:TensorFlow學(xué)習(xí)筆記:模型持久化的原理。。
評(píng)估模型的步驟
那么我們的模型性能如何呢?
首先讓我們找出那些預(yù)測正確的標(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ù)測到的標(biāo)簽值,而 tf.argmax(y_, 1) 代表正確的標(biāo)簽,我們可以用 tf.equal 來檢測我們的預(yù)測是否真實(shí)標(biāo)簽匹配(索引位置一樣表示匹配)。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
這里返回一個(gè)布爾數(shù)組,為了計(jì)算我們分類的準(zhǔn)確率,我們使用cast將布爾值轉(zhuǎn)換為浮點(diǎn)數(shù)來代表對(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ì)算出在測試數(shù)據(jù)上的準(zhǔn)確率,大概是91%。
print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
完整代碼如下:
下面我們總結(jié)一下實(shí)現(xiàn)了上面簡單機(jī)器學(xué)習(xí)算法 Softmax Regression,這可以算作是一個(gè)沒有隱含層的最淺的神經(jīng)網(wǎng)絡(luò),我們來總結(jié)一下整個(gè)流程,我們做的事情分為下面四部分:
1,定義算法公式,也就是神經(jīng)網(wǎng)絡(luò)forward時(shí)的計(jì)算
2,定義loss,選定優(yōu)化器,并指定優(yōu)化器優(yōu)化loss
3,迭代地對(duì)數(shù)據(jù)進(jìn)行訓(xùn)練
4,在測試集或驗(yàn)證集上對(duì)準(zhǔn)確率進(jìn)行評(píng)測
這幾個(gè)步驟是我們使用Tensorflow進(jìn)行算法設(shè)計(jì),訓(xùn)練的核心步驟,也將會(huì)貫穿所有神經(jīng)網(wǎng)絡(luò)。
代碼如下:
#_*_coding:utf-8_*_
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 首先我們直接加載TensorFlow封裝好的MNIST數(shù)據(jù) 28*28=784
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
print(mnist.train.images.shape, mnist.train.labels.shape)
print(mnist.test.images.shape, mnist.test.labels.shape)
print(mnist.validation.images.shape, mnist.validation.labels.shape)
'''
(55000, 784) (55000, 10)
(10000, 784) (10000, 10)
(5000, 784) (5000, 10)
'''
sess = tf.InteractiveSession()
x = tf.placeholder(tf.float32, [None, 784])
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x, W) + b)
y_ = tf.placeholder(tf.float32, [None, 10])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y),
reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
tf.global_variables_initializer().run()
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
train_step.run({x: batch_xs, y_: batch_ys})
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
傳統(tǒng)神經(jīng)網(wǎng)絡(luò)訓(xùn)練MNIST數(shù)據(jù)
1,訓(xùn)練步驟
輸入層:拉伸的手寫文字圖像,維度為 [-1, 28*28]
全連接層:500個(gè)節(jié)點(diǎn),維度為[-1, 500]
輸出層:分類輸出,維度為 [-1, 10]
2,訓(xùn)練代碼及其結(jié)果
傳統(tǒng)的神經(jīng)網(wǎng)絡(luò)訓(xùn)練代碼:
# _*_coding:utf-8_*_
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
BATCH_SIZE = 100 # 訓(xùn)練批次
INPUT_NODE = 784 # 784=28*28 輸入節(jié)點(diǎn)
OUTPUT_NODE = 10 # 輸出節(jié)點(diǎn)
LAYER1_NODE = 784 # 層級(jí)節(jié)點(diǎn)
TRAIN_STEP = 10000 # 訓(xùn)練步數(shù)
LEARNING_RATE = 0.01
L2NORM_RATE = 0.001
def train(mnist):
# define input placeholder
# None表示此張量的第一個(gè)維度可以是任何長度的
input_x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='input_x')
input_y = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='input_y')
# define weights and biases
w1 = tf.Variable(tf.truncated_normal(shape=[INPUT_NODE, LAYER1_NODE], stddev=0.1))
b1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
w2 = tf.Variable(tf.truncated_normal(shape=[LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
b2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))
layer1 = tf.nn.relu(tf.nn.xw_plus_b(input_x, w1, b1))
y_hat = tf.nn.xw_plus_b(layer1, w2, b2)
print("1 step ok!")
# define loss
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=y_hat, labels=input_y)
cross_entropy_mean = tf.reduce_mean(cross_entropy)
regularization = tf.nn.l2_loss(w1) + tf.nn.l2_loss(w2) + tf.nn.l2_loss(b1) + tf.nn.l2_loss(b2)
loss = cross_entropy_mean + L2NORM_RATE * regularization
print("2 step ok")
# define accuracy
correct_predictions = tf.equal(tf.argmax(y_hat, 1), tf.argmax(input_y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_predictions, tf.float32))
print("3 step ok")
# train operation
global_step = tf.Variable(0, trainable=False)
train_op = tf.train.AdamOptimizer(LEARNING_RATE).minimize(loss, global_step=global_step)
print("4 step ok ")
with tf.Session() as sess:
tf.global_variables_initializer().run() # 初始化創(chuàng)建的變量
print("5 step OK")
# 開始訓(xùn)練模型
for i in range(TRAIN_STEP):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
feed_dict = {
input_x: xs,
input_y: ys,
}
_, step, train_loss, train_acc = sess.run([train_op, global_step, loss, accuracy], feed_dict=feed_dict)
if (i % 100 == 0):
print("After %d steps, in train data, loss is %g, accuracy is %g." % (step, train_loss, train_acc))
test_feed = {input_x: mnist.test.images, input_y: mnist.test.labels}
test_acc = sess.run(accuracy, feed_dict=test_feed)
print("After %d steps, in test data, accuracy is %g." % (TRAIN_STEP, test_acc))
if __name__ == '__main__':
mnist = input_data.read_data_sets("data", one_hot=True)
print("0 step ok!")
train(mnist)
結(jié)果如下:
Extracting data rain-images-idx3-ubyte.gz Extracting data rain-labels-idx1-ubyte.gz Extracting data 10k-images-idx3-ubyte.gz Extracting data 10k-labels-idx1-ubyte.gz 0 step ok! 1 step ok! 2 step ok 3 step ok 4 step ok 5 step OK After 1 steps, in train data, loss is 5.44352, accuracy is 0.03. After 101 steps, in train data, loss is 0.665012, accuracy is 0.95. ... ... After 9901 steps, in train data, loss is 0.304703, accuracy is 0.96. After 10000 steps, in test data, accuracy is 0.9612.
3,部分代碼解析:
占位符
我們通過為輸入圖像和目標(biāo)輸出類別創(chuàng)建節(jié)點(diǎn),來開始構(gòu)建計(jì)算圖:
x = tf.placeholder("float", [None, 784])
x 不是一個(gè)特定的值,而是一個(gè)占位符 placeholder,我們?cè)赥ensorFlow運(yùn)行計(jì)算時(shí)輸入這個(gè)值我們希望能夠輸入任意數(shù)量的MNIST圖像,每張圖展平為784維(28*28)的向量。我們用二維的浮點(diǎn)數(shù)張量來表示這些圖,這個(gè)張量的形狀是 [None, 784],這里的None表示此張量的第一個(gè)維度可以是任意長度的。
雖然palceholder 的shape參數(shù)是可選的,但是有了它,TensorFlow能夠自動(dòng)捕捉因數(shù)據(jù)維度不一致導(dǎo)致的錯(cuò)誤。
變量
一個(gè)變量代表著TensorFlow計(jì)算圖中的一個(gè)值,能夠在計(jì)算過程中使用,甚至進(jìn)行修改。在機(jī)器學(xué)習(xí)的應(yīng)用過程中,模型參數(shù)一般用Variable來表示。
w1 = tf.Variable(tf.truncated_normal(shape=[INPUT_NODE, LAYER1_NODE], stddev=0.1)) b1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
我們的模型也需要權(quán)重值和偏置量,當(dāng)然我們可以將其當(dāng)做是另外的輸入(使用占位符),這里使用Variable。一個(gè)Variable代表一個(gè)可修改的張量,它可以用于計(jì)算輸入值,也可以在計(jì)算中被修改。對(duì)于各種機(jī)器學(xué)習(xí)英語,一般都會(huì)有模型參數(shù),可以用Variable來表示。
變量需要通過session初始化后,才能在session中使用,這一初始化的步驟為,為初始化指定具體值,并將其分配給每個(gè)變量,可以一次性為所有變量完成此操作。
sess.run(tf.initialize_all_variables())
卷積神經(jīng)網(wǎng)絡(luò)-CNN訓(xùn)練MNIST數(shù)據(jù)
在MNIST上只有96%的正確率是在是糟糕,所以這里我們用一個(gè)稍微復(fù)雜的模型:卷積神經(jīng)網(wǎng)絡(luò)來改善效果,這里大概會(huì)達(dá)到100%的準(zhǔn)確率。
同樣,構(gòu)建的流程也是先加載數(shù)據(jù),再構(gòu)架網(wǎng)絡(luò)模型,最后訓(xùn)練和評(píng)估模型。
1,訓(xùn)練步驟
輸入層:手寫文字圖像,維度為[-1, 28, 28, 1]
卷積層1:filter的shape為5*5*32,strides為1,padding為“SAME”。卷積后維度為 [-1, 28, 28, 32]
池化層2:max-pooling,ksize為2*2, 步長為2,池化后維度為 [-1, 14, 14, 32]
卷積層3:filter的shape為5*5*64,strides為1,padding為“SAME”。卷積后維度為 [-1, 14, 14, 64]
池化層4:max-pooling,ksize為2*2, 步長為2。池化后維度為 [-1, 7, 7, 64]
池化后結(jié)果展開:將池化層4 feature map展開, [-1, 7, 7, 64] ==> [-1, 3136]
全連接層5:輸入維度 [-1, 3136],輸出維度 [-1, 512]
全連接層6:輸入維度 [-1, 512],輸出維度[-1, 10]。經(jīng)過softmax后,得到分類結(jié)果。
2,構(gòu)建模型
構(gòu)建一個(gè)CNN模型,需要以下幾步。
(1)定義輸入數(shù)據(jù)并預(yù)處理數(shù)據(jù)。這里,我們首先讀取數(shù)據(jù)MNIST,并分別得到訓(xùn)練集的圖片和標(biāo)記的矩陣,以及測試集的圖片和標(biāo)記的矩陣。代碼如下:
# _*_coding:utf-8_*_
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
trX, trY = mnist.train.images, mnist.train.labels
teX, teY = mnist.test.images, mnist.test.labels
其中,mnist是TensorFlow的TensorFlow.contrib.learn中的Datasets,其值如下:
trX, trY, teX, teY 是數(shù)據(jù)的矩陣表現(xiàn),其值類似于:
接著,需要處理輸入的數(shù)據(jù),把上面 trX 和 teX 的形狀變?yōu)?[-1, 28, 28, 1], -1表示不考慮輸入圖片的數(shù)量,28*28表示圖片的長和寬的像素?cái)?shù),1是通道(channel)數(shù)量,因?yàn)镸NIST的圖片是黑白的,所以通道為1,如果是RGB彩色圖像,則通道為3 。
trX = trX.reshape(-1, 28, 28, 1) # 28*28*1 input image
teX = teX.reshape(-1, 28, 28, 1) # 28*28*1 input image
X = tf.placeholder('float', [None, 28, 28, 1])
Y = tf.placeholder('float', [None, 10])
?。?)初始化權(quán)重與定義網(wǎng)絡(luò)結(jié)構(gòu)。這里,我們將要構(gòu)建一個(gè)擁有3個(gè)卷積和3個(gè)池化層,隨后一個(gè)全連接層,再接一個(gè)輸出層的卷積神經(jīng)網(wǎng)絡(luò)。
首先定義初始化權(quán)重的函數(shù):
def init_weights(shape):
return tf.Variable(tf.random_normal(shape, stddev=0.01))
# 初始化權(quán)重方法如下,我們?cè)O(shè)置卷積核的大小為3*3
w = init_weights([3, 3, 1, 32]) # patch大小為3*3,輸入維度為1,輸出維度為32
w2 = init_weights([3, 3, 32, 64]) # patch大小為3*3,輸入維度為32,輸出維度為64
w3 = init_weights([3, 3, 64, 128]) # patch大小為3*3,輸入維度為64,輸出維度為128
# 全連接層,輸入維度為128*4*4,是上一層的輸出數(shù)據(jù)又三維的轉(zhuǎn)變成一維,輸出維度為625
w4 = init_weights([128 * 4 * 4, 625])
# 輸出層,輸入維度為625 輸出維度為10, 代表10類
w_o = init_weights([625, 10])
隨后,定義一個(gè)模型函數(shù),代碼如下:
# 神經(jīng)網(wǎng)絡(luò)模型的構(gòu)建函數(shù),傳入以下參數(shù)
# X:輸入數(shù)據(jù)
# w:每一層的權(quán)重
# p_keep_conv , p_keep_hidden, dropout 要保留的神經(jīng)元比例
def model(X, w, w2, w3, w4, w_0, p_keep_conv, p_keep_hidden):
# 第一組卷積層以及池化層,最后dropout一些神經(jīng)元
l1a = tf.nn.relu(tf.nn.conv2d(X, w, strides=[1, 1, 1, 1], padding='SAME'))
# l1a shape=(?, 28, 28, 32)
l1 = tf.nn.max_pool(l1a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# l1 shape=(?, 14, 14, 32)
l1 = tf.nn.dropout(l1, p_keep_conv)
# 第二組卷積層以及池化層,最后dropout一些神經(jīng)元
l2a = tf.nn.relu(tf.nn.conv2d(l1, w2, strides=[1, 1, 1, 1], padding='SAME'))
# l2a shape=(?, 14, 14, 64)
l2 = tf.nn.max_pool(l2a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# l2 shape=(?, 7, 7, 64)
l2 = tf.nn.dropout(l2, p_keep_conv)
# 第三組卷積層以及池化層,最后dropout一些神經(jīng)元
l3a = tf.nn.relu(tf.nn.conv2d(l2, w3, strides=[1, 1, 1, 1], padding='SAME'))
# l3a shape=(?, 7, 7, 128)
l3 = tf.nn.max_pool(l3a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# l3 shape=(?, 4, 4, 128)
l3 = tf.reshape(l3, [-1, w4.get_shape().as_list()[0]]) # reshape to (?, 2048)
l3 = tf.nn.dropout(l3, p_keep_conv)
# 全連接層,最后dropout一些神經(jīng)元
l4 = tf.nn.relu(tf.matmul(l3, w4))
l4 = tf.nn.dropout(l4, p_keep_hidden)
# 輸出層
pyx = tf.matmul(l4, w_o)
return pyx # 返回預(yù)測值
我們定義dropout的占位符——keep_conv,它表示在一層中有多少比例的神經(jīng)元被保留下來,生成網(wǎng)絡(luò)模型,得到預(yù)測值,如下:
p_keep_cobv = tf.placeholder('float')
p_keep_hidden = tf.placeholder('float')
# 得到預(yù)測值
py_x = model(X, w, w2, w3, w4, w_o, p_keep_cobv, p_keep_hidden)
接下來,定義頓時(shí)函數(shù),這里我們?nèi)匀徊捎?tf.nn.softmax_cross_entropy_with_logits 來作比較預(yù)測值和真實(shí)值的差異,并做均值處理,定義訓(xùn)練的操作(train_op),采用實(shí)現(xiàn) RMSProp算法的優(yōu)化器 tf.train.RMSPropOptimizer,學(xué)習(xí)率為0.001,衰減值為0.9,使損失最小;定義預(yù)測的操作(predict_op)。具體如下:
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels=Y)) train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost) predict_op = tf.argmax(py_x, 1)
?。?)定義訓(xùn)練時(shí)的批次大小和評(píng)估時(shí)的批次大小,如下:
batch_size = 128
test_size = 256
# 在一個(gè)會(huì)話中啟動(dòng)圖,開始訓(xùn)練和評(píng)估
# Launch the graph in a session
with tf.Session() as sess:
# you need to initilaize all variables
tf.global_variables_initializer().run()
for i in range(100):
training_batch = zip(range(0, len(trX), batch_size),
range(batch_size, len(trX) + 1, batch_size))
for start, end in training_batch:
sess.run(train_op, feed_dict={
X: trX[start:end], Y: trY[start:end],
p_keep_conv: 0.8,
p_keep_hidden: 0.5
})
test_indices = test_indices[0: test_size]
print(i, np.mean(np.argmax(teY[test_indices], axis=1) ==
sess.run(predict_op, feed_dict={X: teX[test_indices],
p_keep_conv: 1.0,
p_keep_hidden: 1.0})))
完整代碼如下:
# _*_coding:utf-8_*_
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
trX, trY = mnist.train.images, mnist.train.labels
teX, teY = mnist.test.images, mnist.test.labels
trX = trX.reshape(-1, 28, 28, 1) # 28*28*1 input image
teX = teX.reshape(-1, 28, 28, 1) # 28*28*1 input image
X = tf.placeholder('float', [None, 28, 28, 1])
Y = tf.placeholder('float', [None, 10])
def init_weights(shape):
return tf.Variable(tf.random_normal(shape, stddev=0.01))
# 初始化權(quán)重方法如下,我們?cè)O(shè)置卷積核的大小為3*3
w = init_weights([3, 3, 1, 32]) # patch大小為3*3,輸入維度為1,輸出維度為32
w2 = init_weights([3, 3, 32, 64]) # patch大小為3*3,輸入維度為32,輸出維度為64
w3 = init_weights([3, 3, 64, 128]) # patch大小為3*3,輸入維度為64,輸出維度為128
# 全連接層,輸入維度為128*4*4,是上一層的輸出數(shù)據(jù)又三維的轉(zhuǎn)變成一維,輸出維度為625
w4 = init_weights([128 * 4 * 4, 625])
# 輸出層,輸入維度為625 輸出維度為10, 代表10類
w_o = init_weights([625, 10])
# 神經(jīng)網(wǎng)絡(luò)模型的構(gòu)建函數(shù),傳入以下參數(shù)
# X:輸入數(shù)據(jù)
# w:每一層的權(quán)重
# p_keep_conv , p_keep_hidden, dropout 要保留的神經(jīng)元比例
def model(X, w, w2, w3, w4, w_0, p_keep_conv, p_keep_hidden):
# 第一組卷積層以及池化層,最后dropout一些神經(jīng)元
l1a = tf.nn.relu(tf.nn.conv2d(X, w, strides=[1, 1, 1, 1], padding='SAME'))
# l1a shape=(?, 28, 28, 32)
l1 = tf.nn.max_pool(l1a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# l1 shape=(?, 14, 14, 32)
l1 = tf.nn.dropout(l1, p_keep_conv)
# 第二組卷積層以及池化層,最后dropout一些神經(jīng)元
l2a = tf.nn.relu(tf.nn.conv2d(l1, w2, strides=[1, 1, 1, 1], padding='SAME'))
# l2a shape=(?, 14, 14, 64)
l2 = tf.nn.max_pool(l2a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# l2 shape=(?, 7, 7, 64)
l2 = tf.nn.dropout(l2, p_keep_conv)
# 第三組卷積層以及池化層,最后dropout一些神經(jīng)元
l3a = tf.nn.relu(tf.nn.conv2d(l2, w3, strides=[1, 1, 1, 1], padding='SAME'))
# l3a shape=(?, 7, 7, 128)
l3 = tf.nn.max_pool(l3a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# l3 shape=(?, 4, 4, 128)
l3 = tf.reshape(l3, [-1, w4.get_shape().as_list()[0]]) # reshape to (?, 2048)
l3 = tf.nn.dropout(l3, p_keep_conv)
# 全連接層,最后dropout一些神經(jīng)元
l4 = tf.nn.relu(tf.matmul(l3, w4))
l4 = tf.nn.dropout(l4, p_keep_hidden)
# 輸出層
pyx = tf.matmul(l4, w_o)
return pyx # 返回預(yù)測值
p_keep_conv = tf.placeholder('float')
p_keep_hidden = tf.placeholder('float')
# 得到預(yù)測值
py_x = model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels=Y))
train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
predict_op = tf.argmax(py_x, 1)
batch_size = 128
test_size = 256
# 在一個(gè)會(huì)話中啟動(dòng)圖,開始訓(xùn)練和評(píng)估
# Launch the graph in a session
with tf.Session() as sess:
# you need to initilaize all variables
tf.global_variables_initializer().run()
for i in range(100):
training_batch = zip(range(0, len(trX), batch_size),
range(batch_size, len(trX) + 1, batch_size))
for start, end in training_batch:
sess.run(train_op, feed_dict={
X: trX[start:end], Y: trY[start:end],
p_keep_conv: 0.8,
p_keep_hidden: 0.5
})
test_indices = test_indices[0: test_size]
print(i, np.mean(np.argmax(teY[test_indices], axis=1) ==
sess.run(predict_op, feed_dict={X: teX[test_indices],
p_keep_conv: 1.0,
p_keep_hidden: 1.0})))
3,部分代碼解析:
初始化權(quán)重
為了不在建立模型的時(shí)候反復(fù)做初始化的操作,我們定義兩個(gè)函數(shù)用于初始化。
# 定義W初始化函數(shù)
def get_weights(shape):
form = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(form)
# 定義b初始化函數(shù)
def get_biases(shape):
form = tf.constant(0.1, shape=shape)
return tf.Variable(form)
為了創(chuàng)建此模型,我們需要?jiǎng)?chuàng)建大量的權(quán)重和偏置項(xiàng)。這個(gè)模型中的權(quán)重在初始化時(shí)應(yīng)該加入少量的噪聲來打破對(duì)稱性以及避免0梯度。由于我們使用的是ReLU神經(jīng)元,因此比較好的做法是用一個(gè)較小的正數(shù)來初始化偏置項(xiàng),以避免神經(jīng)元節(jié)點(diǎn)輸出恒為0 的問題(dead neurons)。為了不在建立模型的時(shí)候反復(fù)做初始化操作,我們定義兩個(gè)函數(shù)用于初始化。
卷積和池化
TensorFlow在卷積和池化上有很強(qiáng)的靈活性,那么如何處理邊界?步長應(yīng)該設(shè)置多大?這里我們卷積使用的是1步長(stride size),0邊距(padding size)。保證輸出和輸出的大小相同,我們的池化是用簡單的2*2大小的模板做 max pooling 。
第一層卷積
現(xiàn)在我們開始實(shí)現(xiàn)第一層卷積,它是由一個(gè)卷積接一個(gè) max pooling 完成。卷積在每個(gè)5*5的patch中算出 32個(gè)特征。卷積的權(quán)重張量形狀是 [5, 5, 1, 32],前兩個(gè)維度是patch的大小,接著是輸出通道的數(shù)目,最后是輸出的通道數(shù)目。為了使用這一層卷積,我們將x 變成一個(gè)4d向量,其第二,第三對(duì)應(yīng)圖片的寬,高。最后一維代表輸出的通道數(shù)目。
最后我們將x 和權(quán)重向量進(jìn)行卷積,加上偏置項(xiàng),然后應(yīng)用ReLU激活函數(shù),代碼如下:
# 第一層:卷積層conv1
'''
input: [-1, 28, 28, 1]
filter: [5, 5, 32]
output: [-1, 28, 28, 32]
'''
with tf.name_scope("conv1"):
w = get_weights([FILTER1_SIZE, FILTER1_SIZE, 1, FILTER1_NUM])
b = get_biases([FILTER1_NUM])
conv1_op = tf.nn.conv2d(
input=input_x,
filter=w,
strides=[1, 1, 1, 1],
padding="SAME",
name='conv1_op'
)
conv1 = tf.nn.relu(tf.nn.bias_add(conv1_op, b), name='relu')
第二層池化
前面也說過,我們的池化用簡單傳統(tǒng)的2*2大小模板做 max pooling .
# 第二層:池化層pooling2
'''
input: [-1, 28, 28, 32]
output: [-1. 14, 14, 32]
'''
with tf.name_scope('pooling2'):
pooling2 = tf.nn.max_pool(
value=conv1,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='SAME',
name='pooling1'
)
第三層卷積
為了構(gòu)建一個(gè)更深的網(wǎng)絡(luò),我們會(huì)把幾個(gè)類似的層堆疊起來,第二層中,每個(gè)5*5的patch 會(huì)得到64個(gè)特征,代碼如下:
# 第三次:卷積層conv3
'''
input" [-1, 14, 14, 32]
filter: [5, 5, 64]
output: [-1, 14, 14, 64]
'''
with tf.name_scope('conv3'):
w = get_weights([FILTER3_SIZE, FILTER3_SIZE, FILTER1_NUM, FILTER3_NUM])
b = get_biases([FILTER3_NUM])
conv3_op = tf.nn.conv2d(
input=pooling2,
filter=w,
strides=[1, 1, 1, 1],
padding="SAME",
name='conv3_op'
)
conv3 = tf.nn.relu(tf.nn.bias_add(conv3_op, b), name='relu')
第四層池化
然后進(jìn)行池化,隨機(jī)減少特征:
# 第四層:池化層pooling4
'''
input: [-1, 14, 14, 64]
output: [-1, 7, 7, 64]
'''
with tf.name_scope("pooling4"):
pooling4 = tf.nn.max_pool(
value=conv3,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding="SAME",
name="pooling4"
)
全連接層
現(xiàn)在圖片的尺寸減少到7*7,我們加入一個(gè)3136個(gè)神經(jīng)元的全連接層,用于處理整個(gè)圖片。我們先把池化層輸出的張量reshape成一些向量,也就是展開它。然后輸入其權(quán)重公式,偏置項(xiàng)公式,然后求其L2_Loss函數(shù),最后代碼如下:
# 池化結(jié)果展開
'''
input: [-1, 7,7,64]
output: [-1, 3136]
'''
pooling4_flat = tf.reshape(pooling4, [-1, FLAT_SIZE])
print("5 step ok!")
# 第五層:全連接層 FC5
'''
input: [-1, 3136] (56*56=3136)
output: [-1, 512]
'''
with tf.name_scope('fc5'):
w = get_weights([FLAT_SIZE, FC5_SIZE])
b = get_biases([FC5_SIZE])
fc5 = tf.nn.relu(tf.nn.xw_plus_b(pooling4_flat, w, b, name='fc5'), name='relu')
fc5_drop = tf.nn.dropout(fc5, droput_keep_prob)
l2_loss += tf.nn.l2_loss(w) + tf.nn.l2_loss(b)
print("6 step OK")
# 第六層:全連接層(輸出)
'''
input: [-1, 512]
output: [-1, 10]
'''
with tf.name_scope('fc6'):
w = get_weights([FC5_SIZE, OUTPUT_SIZE])
b = get_biases([OUTPUT_SIZE])
y_hat = tf.nn.xw_plus_b(fc5_drop, w, b, name='y_hat')
l2_loss += tf.nn.l2_loss(w) + tf.nn.l2_loss(b)
print("7 step ok!")
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=y_hat, labels=input_y)
cross_entropy_mean = tf.reduce_mean(cross_entropy)
loss = cross_entropy_mean + L2NORM_RATE * l2_loss
簡易前饋神經(jīng)網(wǎng)絡(luò)訓(xùn)練MNIST數(shù)據(jù)
下面利用TensorFlow使用MNIST數(shù)據(jù)集訓(xùn)練并評(píng)估一個(gè)用于識(shí)別手寫數(shù)字的簡易前饋神經(jīng)網(wǎng)絡(luò)(feed-forward neural network)。
1,代碼如下:
其中說明一下下面兩個(gè)文件的目的:
1,full_connected_mnist.py 構(gòu)建一個(gè)完全連接(fully connected)的MNIST模型所需的代碼
2,full_connected_feed.py 利用下載的數(shù)據(jù)集訓(xùn)練構(gòu)建好的MNIST模型的主要代碼
full_connected_mnist.py代碼如下:
# _*_coding:utf-8_*_
"""Builds the MNIST network.
# 為模型構(gòu)建實(shí)現(xiàn)推理 損失 訓(xùn)練的模型
Implements the inference/loss/training pattern for model building.
# 推理 根據(jù)運(yùn)行網(wǎng)絡(luò)的需要 構(gòu)建模型向前做預(yù)測
1. inference() - Builds the model as far as required for running the network forward to make predictions.
# 將生成loss所需的層添加到推理模型中
2. loss() - Adds to the inference model the layers required to generate loss.
# 訓(xùn)練 將生成和所需的ops添加到損失模型中并應(yīng)用梯度
3. training() - Adds to the loss model the Ops required to generate and apply gradients.
This file is used by the various "fully_connected_*.py" files and not meant to
be run.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import math
import tensorflow as tf
# The MNIST dataset has 10 classes representing the digits 0 through 9
NUM_CLASSES = 10
# The MNIST images are always 28*28 pixels
IMAGES_SIZE = 28
IMAGES_PIXELS = IMAGES_SIZE * IMAGES_SIZE
def inference(images, hidden1_units, hidden2_units):
"""Build the MNIST model up to where it may be used for inference.
Args:
images: Images placeholder, from inputs().
hidden1_units: Size of the first hidden layer.
hidden2_units: Size of the second hidden layer.
Returns:
softmax_linear: Output tensor with the computed logits.
"""
# hidden1
with tf.name_scope('hidden1'):
weights = tf.Variable(
tf.truncated_normal([IMAGES_PIXELS, hidden1_units],
stddev=1.0 / math.sqrt(float(IMAGES_PIXELS))),
name='weights'
)
biases = tf.nn.relu(tf.matmul([hidden1_units]),
name='weights')
hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases)
# hidden2
with tf.name_scope('hidden2'):
weights = tf.Variable(
tf.truncated_normal([hidden1_units, hidden2_units],
stddev=1.0 / math.sqrt(float(hidden1_units))),
name='weights'
)
biases = tf.Variable(tf.zeros([hidden2_units]),
name='biases')
hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)
# Linear
with tf.name_scope('softmax_linear'):
weights = tf.Variable(
tf.truncated_normal([hidden2_units, NUM_CLASSES],
stddev=1.0 / math.sqrt(float(hidden2_units))),
name='weights'
)
biases = tf.nn.relu(tf.zeros([NUM_CLASSES]),
name='biases')
logits = tf.matmul(hidden2, weights) + biases
return logits
def loss(logits, labels):
"""Calculates the loss from the logits and the labels.
Args:
logits: Logits tensor, float - [batch_size, NUM_CLASSES].
labels: Labels tensor, int32 - [batch_size].
Returns:
loss: Loss tensor of type float.
"""
labels = tf.to_int64(labels)
return tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
def training(loss, learning_rate):
"""Sets up the training Ops.
Creates a summarizer to track the loss over time in TensorBoard.
Creates an optimizer and applies the gradients to all trainable variables.
The Op returned by this function is what must be passed to the
`sess.run()` call to cause the model to train.
Args:
loss: Loss tensor, from loss().
learning_rate: The learning rate to use for gradient descent.
Returns:
train_op: The Op for training.
"""
# add a scalar summary for the snapshot loss
tf.summary.scalar('loss', loss)
# create the gradient descent optimizer with the given learning rate
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
# create a variable to track the global step
global_step = tf.Variable(0, name='global_step', trainable=False)
# Use the optimizer to apply the gradients that minimize the loss
# (and also increment the global step counter) as a single training step.
train_op = optimizer.minimize(loss, global_step=global_step)
return train_op
def evaluation(logits, labels):
"""Evaluate the quality of the logits at predicting the label.
Args:
logits: Logits tensor, float - [batch_size, NUM_CLASSES].
labels: Labels tensor, int32 - [batch_size], with values in the
range [0, NUM_CLASSES).
Returns:
A scalar int32 tensor with the number of examples (out of batch_size)
that were predicted correctly.
"""
# For a classifier model, we can use the in_top_k Op.
# It returns a bool tensor with shape [batch_size] that is true for
# the examples where the label is in the top k (here k=1)
# of all logits for that example.
correct = tf.nn.in_top_k(logits, labels, 1)
# Return the number of true entries.
return tf.reduce_sum(tf.cast(correct, tf.int32))
full_connected_feed.py代碼如下:
#_*_coding:utf-8_*_
'''Trains and Evaluates the MNIST network using a feed dictionary'''
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
# pulint:disable=missing-docstring
import argparse
import os
import sys
import time
from six.moves import xrange # pylint: disable=redefined-builtin
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
from tensorflow.examples.tutorials.mnist import mnist
# Basic model parameters as external flags
FLAGS = None
def placeholder_inputs(batch__size):
'''
生成一個(gè)占位符變量來表述輸入張量
這些占位符被模型構(gòu)建的其余部分用作輸入代碼,并將從下面 .run() 循環(huán)中下載數(shù)據(jù)提供
:param batch__size:
:return: 返回一個(gè) 圖像的占位符,一個(gè)標(biāo)簽占位符
'''
# Note that the shapes of the placeholders match the shapes of the full
# image and label tensors, except the first dimension is now batch_size
# rather than the full size of the train or test data sets.
images_placeholder = tf.placeholder(tf.float32, shape=(batch__size, mnist.IMAGE_PIXELS))
labels_placeholder = tf.placeholder(tf.int32, shape=(batch__size))
return images_placeholder, labels_placeholder
def fill_feed_dict(data_set, images_pl, labels_pl):
"""Fills the feed_dict for training the given step.
A feed_dict takes the form of:
feed_dict = {
<placeholder>: <tensor of values to be passed for placeholder>,
....
}
Args:
data_set: The set of images and labels, from input_data.read_data_sets()
images_pl: The images placeholder, from placeholder_inputs().
labels_pl: The labels placeholder, from placeholder_inputs().
Returns:
feed_dict: The feed dictionary mapping from placeholders to values.
"""
# Create the feed_dict for the placeholders filled with the next
# `batch size` examples.
images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size, FLAGS.fake_data)
feed_dict = {
images_pl: images_feed,
labels_pl: labels_feed,
}
return feed_dict
def do_evaluation(sess, eval_correct, images_placeholder, labels_placeholder, data_set):
'''
對(duì)陣個(gè)數(shù)據(jù)進(jìn)行評(píng)估
:param sess: The session in which the model has been trained.
:param eval_correct: The Tensor that returns the number of correct predictions.
:param images_placeholder: The images placeholder.
:param labels_placeholder: The labels placeholder.
:param data_set: The set of images and labels to evaluate, from
input_data.read_data_sets().
:return:
'''
# and run one epoch of eval
true_count = 0 # counts the number of correct predictions
steps_per_epoch = data_set.num_examples // FLAGS.batch_size
num_examples = steps_per_epoch * FLAGS.batch_size
for step in range(steps_per_epoch):
feed_dict = fill_feed_dict(data_set, images_placeholder, labels_placeholder)
true_count += sess.run(eval_correct, feed_dict=feed_dict)
precision = float(true_count) / num_examples
print('Num examples: %d Num correct: %d Precision @ 1: %0.04f' %
(num_examples, true_count, precision))
def run_training():
'''Train MNIST for a number of steps'''
# get the sets of images and labels for training validation and test on MNIST
data_sets = input_data.read_data_sets(FLAGS.input_data_dir, FLAGS.fake_data)
# Tell Tensorflow that the model will be built into the default graph
with tf.Graph().as_default():
#Generate placeholders for the images and labels
images_placeholder, labels_placeholder = placeholder_inputs(FLAGS.batch_size)
# build a graph that computes predictions from the inference model
logits = mnist.inference(images_placeholder, FLAGS.hidden1, FLAGS.hidden2)
# add to graph the ops for loss calculation
loss = mnist.loss(logits, labels_placeholder)
# add to the graph the ops that calculate and apply gradients
train_op = mnist.training(loss, FLAGS.learning_rate)
# add the Op to compare the logits to the labels during evaluation
eval_correct = mnist.evaluation(logits, labels_placeholder)
# bulid the summary Tensor based on the TF collection of Summaries
summary = tf.summary.merge_all()
# add the variable initializer Op
init = tf.global_variables_initializer()
# create a saver fro writing training checkpoint
saver = tf.train.Saver()
# create a session for running Ops on the Graph
sess = tf.compat.v1.Session()
# Instantiate a SummaryWriter to output summaries and the graph
summary_writer = tf.summary.FileWriter(FLAGS.log_dir, sess.graph)
# and then after everything is built
# run the Op to initialize the variables
sess.run(init)
# start the training loop
for step in range(FLAGS.max_steps):
start_time = time.time()
# Fill a feed dictionary with the actual set of images and labels
# for this particular training step.
feed_dict = fill_feed_dict(data_sets.train,
images_placeholder,
labels_placeholder)
# Run one step of the model. The return values are the activations
# from the `train_op` (which is discarded) and the `loss` Op. To
# inspect the values of your Ops or variables, you may include them
# in the list passed to sess.run() and the value tensors will be
# returned in the tuple from the call.
_, loss_value = sess.run([train_op, loss],
feed_dict=feed_dict)
duration = time.time() - start_time
# write the summaries and print an overview fairly often
if step % 100 == 0:
# Print status to stdout.
print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))
# Update the events file.
summary_str = sess.run(summary, feed_dict=feed_dict)
summary_writer.add_summary(summary_str, step)
summary_writer.flush()
# Save a checkpoint and evaluate the model periodically.
if (step + 1) % 1000 == 0 or (step + 1) == FLAGS.max_steps:
checkpoint_file = os.path.join(FLAGS.log_dir, 'model.ckpt')
saver.save(sess, checkpoint_file, global_step=step)
# Evaluate against the training set.
print('Training Data Eval:')
do_evaluation(sess,
eval_correct,
images_placeholder,
labels_placeholder,
data_sets.train)
# Evaluate against the validation set.
print('Validation Data Eval:')
do_evaluation(sess,
eval_correct,
images_placeholder,
labels_placeholder,
data_sets.validation)
# Evaluate against the test set.
print('Test Data Eval:')
do_evaluation(sess,
eval_correct,
images_placeholder,
labels_placeholder,
data_sets.test)
def main():
if tf.gfile.Exists(FLAGS.log_dir):
tf.gfile.DeleteRecursively(FLAGS.log_dir)
tf.gfile.MakeDirs(FLAGS.log_dir)
run_training()
def main(_):
if tf.gfile.Exists(FLAGS.log_dir):
tf.gfile.DeleteRecursively(FLAGS.log_dir)
tf.gfile.MakeDirs(FLAGS.log_dir)
run_training()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'--learning_rate',
type=float,
default=0.01,
help='Initial learning rate.'
)
parser.add_argument(
'--max_steps',
type=int,
default=2000,
help='Number of steps to run trainer.'
)
parser.add_argument(
'--hidden1',
type=int,
default=128,
help='Number of units in hidden layer 1.'
)
parser.add_argument(
'--hidden2',
type=int,
default=32,
help='Number of units in hidden layer 2.'
)
parser.add_argument(
'--batch_size',
type=int,
default=100,
help='Batch size. Must divide evenly into the dataset sizes.'
)
parser.add_argument(
'--input_data_dir',
type=str,
default=os.path.join(os.getenv('TEST_TMPDIR', ''),
'input_data'),
help='Directory to put the input data.'
)
parser.add_argument(
'--log_dir',
type=str,
default=os.path.join(os.getenv('TEST_TMPDIR', ''),
'fully_connected_feed'),
help='Directory to put the log data.'
)
parser.add_argument(
'--fake_data',
default=False,
help='If true, uses fake data for unit testing.',
action='store_true'
)
FLAGS, unparsed = parser.parse_known_args()
tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
注意事項(xiàng):
此處參數(shù)注意修改!!
結(jié)果展示:
Extracting data rain-images-idx3-ubyte.gz Extracting data rain-labels-idx1-ubyte.gz Extracting data 10k-images-idx3-ubyte.gz Extracting data 10k-labels-idx1-ubyte.gz Step 0: loss = 2.30 (0.114 sec) Step 100: loss = 2.11 (0.002 sec) Step 200: loss = 1.92 (0.002 sec) Step 300: loss = 1.49 (0.002 sec) Step 400: loss = 1.22 (0.002 sec) Step 500: loss = 0.95 (0.003 sec) Step 600: loss = 0.73 (0.003 sec) Step 700: loss = 0.58 (0.004 sec) Step 800: loss = 0.58 (0.002 sec) Step 900: loss = 0.50 (0.002 sec) Training Data Eval: Num examples: 55000 Num correct: 47310 Precision @ 1: 0.8602 Validation Data Eval: Num examples: 5000 Num correct: 4365 Precision @ 1: 0.8730 Test Data Eval: Num examples: 10000 Num correct: 8628 Precision @ 1: 0.8628 Step 1000: loss = 0.51 (0.017 sec) Step 1100: loss = 0.46 (0.104 sec) Step 1200: loss = 0.50 (0.002 sec) Step 1300: loss = 0.40 (0.003 sec) Step 1400: loss = 0.57 (0.003 sec) Step 1500: loss = 0.40 (0.003 sec) Step 1600: loss = 0.42 (0.003 sec) Step 1700: loss = 0.44 (0.003 sec) Step 1800: loss = 0.41 (0.002 sec) Step 1900: loss = 0.37 (0.000 sec) Training Data Eval: Num examples: 55000 Num correct: 49375 Precision @ 1: 0.8977 Validation Data Eval: Num examples: 5000 Num correct: 4529 Precision @ 1: 0.9058 Test Data Eval: Num examples: 10000 Num correct: 8985 Precision @ 1: 0.8985
準(zhǔn)確率比起卷積神經(jīng)網(wǎng)絡(luò)還是差點(diǎn),但是效果還是不錯(cuò)的。這里我們主要是學(xué)習(xí)這個(gè)過程,了解深度學(xué)習(xí)訓(xùn)練模型的過程,所以還好。
2,部分代碼解析
下載
在run_training() 方法的一開始, input_data.read_data_sets() 函數(shù)會(huì)確保我們的本地訓(xùn)練文件夾中,是否已經(jīng)下載了正確的數(shù)據(jù),然后將這些數(shù)據(jù)解壓并返回一個(gè)含有DataSet實(shí)例的字典。
data_sets = input_data.read_data_sets(FLAGS.train_dir, FLAGS.fake_data)
注意:fake_data標(biāo)記是用于單元測試的。
數(shù)據(jù)展示如下:
輸入與占位符(inputs and placeholders)
placeholder_inputs() 函數(shù)將生成兩個(gè) tf.placeholder 操作,定義傳入圖表中的 shape 參數(shù), shape參數(shù)中包括 batch_size 值,后續(xù)還會(huì)將實(shí)際的訓(xùn)練用例傳入圖表。
images_placeholder = tf.placeholder(tf.float32, shape=(batch_size,
IMAGE_PIXELS))
labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size))
在訓(xùn)練循環(huán)(training loop)的后續(xù)步驟中,傳入的整個(gè)圖像和標(biāo)簽數(shù)據(jù)集會(huì)被切片,以符合每一個(gè)操作所設(shè)置的batch_size值,占位符操作將會(huì)填補(bǔ)以符合這個(gè)batch_size值。然后使用feed_dict參數(shù),將數(shù)據(jù)傳入sess.run()函數(shù)。
構(gòu)建圖表(Build the Graph)
在為數(shù)據(jù)創(chuàng)建占位符之后,就可以運(yùn)行full_connected_mnist.py文件,經(jīng)過三階段的模式函數(shù)操作: inference() loss() training() 圖表就構(gòu)建完成了。
inference():盡可能的構(gòu)建好圖表,滿足促使神經(jīng)網(wǎng)絡(luò)向前反饋并作出預(yù)測的要求。
loss():往inference 圖表中添加生成損失(loss)所需要的操作(ops)
training() :往損失函數(shù)中添加計(jì)算并應(yīng)用梯度(gradients)所需要的操作
推理(Inference)
inference()函數(shù)會(huì)盡可能的構(gòu)建圖表,做到返回包含了預(yù)測結(jié)果(output prediction)的Tensor。
它接受圖像占位符為輸入,在此基礎(chǔ)上借助ReLU(Rectified Linear Units)激活函數(shù),構(gòu)建一對(duì)完全連接層(layers),以及一個(gè)有著十個(gè)節(jié)點(diǎn)(node),指明了輸出 logits 模型的線性層。
每一層都創(chuàng)建于一個(gè)唯一的 tf.name_scope之下,創(chuàng)建于該作用域之下的所有元素都將帶有其前綴。
with tf.name_scope('hidden1') as scope:
在定義的作用域中,每一層所使用的權(quán)重和偏差都在 tf.Variable實(shí)例中生成,并且包含了各自期望的shape。
weights = tf.Variable(
tf.truncated_normal([IMAGE_PIXELS, hidden1_units],
stddev=1.0 / math.sqrt(float(IMAGE_PIXELS))),
name='weights')
biases = tf.Variable(tf.zeros([hidden1_units]),
name='biases')
例如,當(dāng)這些層是在hidden1 作用域下生成時(shí),賦予權(quán)重變量的獨(dú)特名稱將會(huì)是“hidden1/weights”。
每個(gè)變量在構(gòu)建時(shí),都會(huì)獲得初始化操作(initializer ops)。
在這種最常見的情況下,通過tf.truncated_normal函數(shù)初始化權(quán)重變量,給賦予的shape則是一個(gè)二維tensor,其中第一個(gè)維度代表該層中權(quán)重變量所連接(connect from)的單元數(shù)量,第二個(gè)維度代表該層中權(quán)重變量所連接到的(connect to)單元數(shù)量。對(duì)于名叫hidden1的第一層,相應(yīng)的維度則是[IMAGE_PIXELS, hidden1_units],因?yàn)闄?quán)重變量將圖像輸入連接到了hidden1層。tf.truncated_normal初始函數(shù)將根據(jù)所得到的均值和標(biāo)準(zhǔn)差,生成一個(gè)隨機(jī)分布。
然后,通過tf.zeros函數(shù)初始化偏差變量(biases),確保所有偏差的起始值都是0,而它們的shape則是其在該層中所接到的(connect to)單元數(shù)量。
圖表的三個(gè)主要操作,分別是兩個(gè)tf.nn.relu操作,它們中嵌入了隱藏層所需的tf.matmul;以及l(fā)ogits模型所需的另外一個(gè)tf.matmul。三者依次生成,各自的tf.Variable實(shí)例則與輸入占位符或下一層的輸出tensor所連接。
hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases) hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases) logits = tf.matmul(hidden2, weights) + biases
最后,程序會(huì)返回包含了輸出結(jié)果的logits Tensor。
損失(loss)
loss()函數(shù)通過添加所需要的損失操作,進(jìn)一步構(gòu)建圖表。
首先,labels_placeholder中的值,將被編碼為一個(gè)含有1-hot values 的Tensor。例如,如果類標(biāo)識(shí)符為“3”,那么該值就會(huì)被轉(zhuǎn)換為:[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
batch_size = tf.size(labels)
labels = tf.expand_dims(labels, 1)
indices = tf.expand_dims(tf.range(0, batch_size, 1), 1)
concated = tf.concat(1, [indices, labels])
onehot_labels = tf.sparse_to_dense(
concated, tf.pack([batch_size, NUM_CLASSES]), 1.0, 0.0)
之后,又添加一個(gè) tf.nn.softmax_cross_entropy_with_logits操作,用來比較 inference() 函數(shù)與 1-hot 標(biāo)簽所輸出的 logits Tensor。
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits,
onehot_labels,
name='xentropy')
然后,使用tf.reduce_mean() 函數(shù),計(jì)算 batch 維度(第一維度)下交叉熵(cross entropy)的平均值,將該值作為總損失。
loss = tf.reduce_mean(cross_entropy, name='xentropy_mean')
最后程序會(huì)返回包含了損失值的Tensor。
訓(xùn)練training()
training() 函數(shù)添加了通過梯度下降(gradient descent)將損失最小化所需要的操作。
首先,該函數(shù)從loss() 函數(shù)中獲得損失Tensor,將其交給 tf.scalar_summary,后者在于SummaryWriter配合使用時(shí),可以向事件文件(events file)中生成匯總值(summary values)。每次寫入?yún)R總值時(shí),它都會(huì)釋放損失Tensor的當(dāng)前值(snapshot value)。
tf.scalar_summary(loss.op.name, loss)
接下來,我們實(shí)例化一個(gè)tf.train.GradientDescentOptimizer,負(fù)責(zé)按照所要求的學(xué)習(xí)效率(learning rate)應(yīng)用梯度下降法(gradients)。
optimizer = tf.train.GradientDescentOptimizer(FLAGS.learning_rate)
之后,我們生成一個(gè)變量用于保存全局訓(xùn)練步驟(global training step)的數(shù)值,并使用minimize()函數(shù)更新系統(tǒng)中的三角權(quán)重(triangle weights)、增加全局步驟的操作。根據(jù)慣例,這個(gè)操作被稱為train_op,是TensorFlow會(huì)話(session)誘發(fā)一個(gè)完整訓(xùn)練步驟所必須運(yùn)行的操作(見下文)。
global_step = tf.Variable(0, name='global_step', trainable=False) train_op = optimizer.minimize(loss, global_step=global_step)
最后,程序返回包含了訓(xùn)練操作(training op)輸出結(jié)果的Tensor。
圖表
在 run_training() 這個(gè)函數(shù)的一開始,是一個(gè)python語言的with命令,這個(gè)命令表明所有已經(jīng)構(gòu)建的操作都要與默認(rèn)的 tf.Graph 全局實(shí)例關(guān)聯(lián)起來。
with tf.Graph().as_default():
tf.Graph實(shí)例是一系列可以作為整體執(zhí)行的操作。TensorFlow的大部分場景只需要依賴默認(rèn)圖表一個(gè)實(shí)例即可。
會(huì)話
完成全部的構(gòu)建準(zhǔn)備,生成全部所需的操作之后,我們就可以創(chuàng)建一個(gè)tf.Session,用于運(yùn)行圖表。
sess = tf.Session()
當(dāng)然,我們也可以利用with 代碼塊生成Session,限制作用域:
with tf.Session() as sess:
Session函數(shù)中沒有傳入?yún)?shù),表明該代碼將會(huì)依附于(如果還沒有創(chuàng)建會(huì)話,則會(huì)創(chuàng)建新的會(huì)話)默認(rèn)的本地會(huì)話。
生成會(huì)話之后,所有tf.Variable實(shí)例都會(huì)立即通過調(diào)用各自初始化操作中的sess.run()函數(shù)進(jìn)行初始化。
init = tf.initialize_all_variables() sess.run(init)
sess.run()方法將會(huì)運(yùn)行圖表中與作為參數(shù)傳入的操作相對(duì)應(yīng)的完整子集。在初次調(diào)用時(shí),init操作只包含了變量初始化程序tf.group。圖表的其他部分不會(huì)在這里,而是在下面的訓(xùn)練循環(huán)運(yùn)行。
訓(xùn)練循環(huán)
完成會(huì)話中變量的初始化之后,就可以開始訓(xùn)練了。
訓(xùn)練的每一步都是通過用戶代碼控制,而能實(shí)現(xiàn)有效訓(xùn)練的最簡單循環(huán)就是:
for step in range(max_steps):
sess.run(train_op)
但是我們這里需要更復(fù)雜一點(diǎn),因?yàn)槲覀儽仨毎演斎氲臄?shù)據(jù)根據(jù)每一步的情況進(jìn)行切分,以匹配之前生成的占位符。
向圖表提供反饋
執(zhí)行每一步時(shí),我們的代碼會(huì)生成一個(gè)反饋?zhàn)值洌╢eed dictionary),其中包含對(duì)應(yīng)步驟中訓(xùn)練所要使用的例子,這些例子的哈希鍵就是其所代表的占位符操作。
fill_feed_dict函數(shù)會(huì)查詢給定的DataSet,索要下一批次batch_size的圖像和標(biāo)簽,與占位符相匹配的Tensor則會(huì)包含下一批次的圖像和標(biāo)簽。
images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size)
然后,以占位符為哈希鍵,創(chuàng)建一個(gè)python字典對(duì)象,鍵值則是其代表的反饋Tensor。
feed_dict = {
images_placeholder: images_feed,
labels_placeholder: labels_feed,
}
這個(gè)字典隨后作為feed_dict參數(shù),傳入sess.run()函數(shù)中,為這一步的訓(xùn)練提供輸入樣例。
檢查狀態(tài)
在運(yùn)行sess.run函數(shù)時(shí),要在代碼中明確其需要獲取的兩個(gè)值:[train_op, loss]。
for step in range(FLAGS.max_steps):
feed_dict = fill_feed_dict(data_sets.train,
images_placeholder,
labels_placeholder)
_, loss_value = sess.run([train_op, loss],
feed_dict=feed_dict)
因?yàn)橐@取這兩個(gè)值,sess.run()會(huì)返回一個(gè)有兩個(gè)元素的元組。其中每一個(gè)Tensor對(duì)象,對(duì)應(yīng)了返回的元組中的numpy數(shù)組,而這些數(shù)組中包含了當(dāng)前這步訓(xùn)練中對(duì)應(yīng)Tensor的值。由于train_op并不會(huì)產(chǎn)生輸出,其在返回的元祖中的對(duì)應(yīng)元素就是None,所以會(huì)被拋棄。但是,如果模型在訓(xùn)練中出現(xiàn)偏差,lossTensor的值可能會(huì)變成NaN,所以我們要獲取它的值,并記錄下來。
假設(shè)訓(xùn)練一切正常,沒有出現(xiàn)NaN,訓(xùn)練循環(huán)會(huì)每隔100個(gè)訓(xùn)練步驟,就打印一行簡單的狀態(tài)文本,告知用戶當(dāng)前的訓(xùn)練狀態(tài)。
if step % 100 == 0:
print 'Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration)
狀態(tài)可視化
為了釋放TensorBoard所使用的事件文件(events file),所有的即時(shí)數(shù)據(jù)(在這里只有一個(gè))都要在圖表構(gòu)建階段合并至一個(gè)操作(op)中。
summary_op = tf.merge_all_summaries()
在創(chuàng)建好會(huì)話(session)之后,可以實(shí)例化一個(gè)tf.train.SummaryWriter,用于寫入包含了圖表本身和即時(shí)數(shù)據(jù)具體值的事件文件。
summary_writer = tf.train.SummaryWriter(FLAGS.train_dir,
graph_def=sess.graph_def)
最后,每次運(yùn)行summary_op時(shí),都會(huì)往事件文件中寫入最新的即時(shí)數(shù)據(jù),函數(shù)的輸出會(huì)傳入事件文件讀寫器(writer)的add_summary()函數(shù)。
summary_str = sess.run(summary_op, feed_dict=feed_dict) summary_writer.add_summary(summary_str, step)
執(zhí)行sess會(huì)話的下面代碼報(bào)錯(cuò):
sess = tf.compat.v1.Session() AttributeError: module 'tensorflow.python.util.compat' has no attribute 'v1'
解決方法:
sess = tf.Session()
參考文獻(xiàn):https://blog.csdn.net/GeForce_GTX1080Ti/article/details/80119194
TensorFlow官網(wǎng)(http://www.tensorfly.cn/tfdoc/tutorials/mnist_beginners.html)
http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_tf.html
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/fully_connected_feed.py
此處是做以筆記,記錄學(xué)習(xí)過程,若有侵權(quán),請(qǐng)聯(lián)系我
總結(jié)
以上是生活随笔為你收集整理的tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS3与页面布局学习总结——多种页面布
- 下一篇: 做鼻子用筋膜起到什么作用