Tensorflow自學(xué)之前的bigpicture
前言
目前,深度學(xué)習(xí)在計(jì)算機(jī)科學(xué)各領(lǐng)域的應(yīng)用越來越多,各種新技術(shù)層出不窮,比如圖像識(shí)別、圖形定位與檢測、語音識(shí)別這一系列的技術(shù)。這個(gè)領(lǐng)域,對于初入技術(shù)行業(yè)的同學(xué)來講,吸引力十分足夠:特別是在整個(gè)領(lǐng)域工資居高不下的時(shí)候。
說到深度學(xué)習(xí),就不能不提一下開源框架tensorflow。不僅是因?yàn)檫@個(gè)框架最火、使用率最高,也是因?yàn)檫@個(gè)框架是非常適合初學(xué)者接觸、學(xué)習(xí)的。
我們會(huì)講到一些深度學(xué)習(xí)的基礎(chǔ)概念,包括計(jì)算圖,graph 與 session,基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),Variable,placeholder 與 feed_dict 以及使用它們時(shí)需要注意的點(diǎn)。最后我們會(huì)根據(jù)教程里提到的內(nèi)容,通過一個(gè)實(shí)戰(zhàn)案例讓大家對整個(gè)tensorflow有一個(gè)正確、可用的理解。
1
tensorflow是什么?
tensorflow 是 google 開源的機(jī)器學(xué)習(xí)工具,在2015年11月其實(shí)現(xiàn)正式開源,開源協(xié)議Apache 2.0。
下圖是 query 詞頻時(shí)序圖,從中可以看出 tensorflow 的火爆程度。

2
why tensorflow?
Tensorflow有很多優(yōu)點(diǎn),包括具有Python接口、平臺(tái)兼容性極佳、部署環(huán)境要求沒有其他框架那么嚴(yán)格。同時(shí),它自己還有可視化工具,可以方便的進(jìn)行實(shí)驗(yàn)管理。
對于新人而言,它的最大優(yōu)點(diǎn)可能是擁有一個(gè)包含了極多開發(fā)者的社區(qū):這讓你遇到的問題大部分都可以解決而不用自己鉆研。
同時(shí),你還可以借鑒諸多優(yōu)秀項(xiàng)目——因?yàn)橐呀?jīng)有很多優(yōu)秀項(xiàng)目已經(jīng)在開發(fā)了。
3
易用的tensorflow工具
如果不想去研究 tensorflow 繁雜的API,僅想快速的實(shí)現(xiàn)些什么,可以使用其他高層工具。比如 tf.contrib.learn,tf.contrib.slim,Keras 等,它們都提供了高層封裝。
4
tensorflow安裝
目前 tensorflow 的安裝已經(jīng)十分方便,有興趣可以參考官方文檔。
tensorflow基礎(chǔ)
實(shí)際上編寫tensorflow可以總結(jié)為兩步.
(1)組裝一個(gè)graph;
(2)使用session去執(zhí)行g(shù)raph中的operation。
因此我們從 graph 與 session 說起。
1
graph與session
(1)計(jì)算圖
Tensorflow 是基于計(jì)算圖的框架,因此理解 graph 與 session 顯得尤為重要。不過在講解 graph 與 session 之前首先介紹下什么是計(jì)算圖。假設(shè)我們有這樣一個(gè)需要計(jì)算的表達(dá)式。該表達(dá)式包括了兩個(gè)加法與一個(gè)乘法,為了更好講述引入中間變量c與d。由此該表達(dá)式可以表示為:

當(dāng)需要計(jì)算e時(shí)就需要計(jì)算c與d,而計(jì)算c就需要計(jì)算a與b,計(jì)算d需要計(jì)算b。這樣就形成了依賴關(guān)系。這種有向無環(huán)圖就叫做計(jì)算圖,因?yàn)閷τ趫D中的每一個(gè)節(jié)點(diǎn)其微分都很容易得出,因此應(yīng)用鏈?zhǔn)椒▌t求得一個(gè)復(fù)雜的表達(dá)式的導(dǎo)數(shù)就成為可能,所以它會(huì)應(yīng)用在類似tensorflow這種需要應(yīng)用反向傳播算法的框架中。
(2)概念說明
下面是 graph , session , operation , tensor 四個(gè)概念的簡介。
Tensor:類型化的多維數(shù)組,圖的邊;
Operation:執(zhí)行計(jì)算的單元,圖的節(jié)點(diǎn);
Graph:一張有邊與點(diǎn)的圖,其表示了需要進(jìn)行計(jì)算的任務(wù);
Session:稱之為會(huì)話的上下文,用于執(zhí)行圖。
Graph作為一張圖,僅展示所有單元及數(shù)組的流向,并不會(huì)對其進(jìn)行計(jì)算,上下文則是執(zhí)行單元,根據(jù)Graph的流程定義為各個(gè)計(jì)算分配資源,計(jì)算節(jié)點(diǎn),從而得出計(jì)算結(jié)果。Operation作為圖計(jì)算的點(diǎn),可以使任何形式的數(shù)學(xué)運(yùn)算,包括各類算法,通過零個(gè)或以上的輸入,來得到零個(gè)或以上的輸出。Tensor就是它的輸出和輸出,可以做出多種邊是。幾乎所有的tensor在進(jìn)入下一個(gè)節(jié)點(diǎn)后都不在保存,除非是Variables指向的。
(3)舉例
下面首先定義一個(gè)圖(其實(shí)沒有必要,tensorflow會(huì)默認(rèn)定義一個(gè)),并做一些計(jì)算。

這段代碼,首先會(huì)載入tensorflow,定義一個(gè)graph類,并在這張圖上定義了foo與bar的兩個(gè)變量,最后對這個(gè)值求和,并初始化所有變量。其中,Variable是定義變量并賦予初值。讓我們看下result(最后1行代碼)。后面是輸出,可以看到并沒有輸出實(shí)際的結(jié)果,由此可見在定義圖的時(shí)候其實(shí)沒有進(jìn)行任何實(shí)際的計(jì)算。

下面定義一個(gè)session,并進(jìn)行真正的計(jì)算。

這段代碼中,定義了session,并在session中執(zhí)行了真正的初始化,并且求得result的值并打印出來??梢钥吹?,在session中產(chǎn)生了真正的計(jì)算,得出值為5。
下圖是該graph在tensorboard中的顯示。這張圖整體是一個(gè)graph,其中foo,bar,add這些節(jié)點(diǎn)都是operation,而foo和bar與add連接邊的就是tensor。當(dāng)session運(yùn)行result時(shí),實(shí)際就是求得add這個(gè)operation流出的tensor值,那么add的所有上游節(jié)點(diǎn)都會(huì)進(jìn)行計(jì)算,如果圖中有非add上游節(jié)點(diǎn)(本例中沒有)那么該節(jié)點(diǎn)將不會(huì)進(jìn)行計(jì)算,這也是圖計(jì)算的優(yōu)勢之一。

2
數(shù)據(jù)結(jié)構(gòu)
Tensorflow的數(shù)據(jù)結(jié)構(gòu)有著rank,shape,data types的概念,下面來分別講解。
(1)rank
Rank一般是指數(shù)據(jù)的維度,其與線性代數(shù)中的rank不是一個(gè)概念。其常用rank舉例如下。

(2)shape
Shape指tensor每個(gè)維度數(shù)據(jù)的個(gè)數(shù),可以用Python的list/tuple表示。下圖表示了rank,shape的關(guān)系。

(3)data type
Data type,是指單個(gè)數(shù)據(jù)的類型。常用DT_FLOAT,也就是32位的浮點(diǎn)數(shù)。下圖表示了所有的types。

3
Variables
(1)介紹
當(dāng)訓(xùn)練模型時(shí),需要使用Variables保存與更新參數(shù)。Variables會(huì)保存在內(nèi)存當(dāng)中,所有tensor一旦擁有Variables的指向就不會(huì)在session中丟失。其必須明確的初始化而且可以通過Saver保存到磁盤上。Variables可以通過Variables初始化。

其中,tf.random_normal是隨機(jī)生成一個(gè)正態(tài)分布的tensor,其shape是第一個(gè)參數(shù),stddev是其標(biāo)準(zhǔn)差。tf.zeros是生成一個(gè)全零的tensor。之后將這個(gè)tensor的值賦值給Variable。
(2)初始化
實(shí)際在其初始化過程中做了很多的操作,比如初始化空間,賦初值(等價(jià)于tf.assign),并把Variable添加到graph中等操作。注意在計(jì)算前需要初始化所有的Variable。一般會(huì)在定義graph時(shí)定義global_variables_initializer,其會(huì)在session運(yùn)算時(shí)初始化所有變量。直接調(diào)用global_variables_initializer會(huì)初始化所有的Variable,如果僅想初始化部分Variable可以調(diào)用tf.variables_initializer。

Variables可以通過eval顯示其值,也可以通過assign進(jìn)行賦值。Variables支持很多數(shù)學(xué)運(yùn)算,具體可以參照官方文檔。
(3)Variables與constant的區(qū)別
值得注意的是Variables與constant的區(qū)別。Constant一般是常量,可以被賦值給Variables,constant保存在graph中,如果graph重復(fù)載入那么constant也會(huì)重復(fù)載入,其非常浪費(fèi)資源,如非必要盡量不使用其保存大量數(shù)據(jù)。而Variables在每個(gè)session中都是單獨(dú)保存的,甚至可以單獨(dú)存在一個(gè)參數(shù)服務(wù)器上??梢酝ㄟ^代碼觀察到constant實(shí)際是保存在graph中,具體如下。

這里第二行是打印出圖的定義,其輸出如下:

(4)命名
另外一個(gè)值得注意的地方是盡量每一個(gè)變量都明確的命名,這樣易于管理命令空間,而且在導(dǎo)入模型的時(shí)候不會(huì)造成不同模型之間的命名沖突,這樣就可以在一張graph中容納很多個(gè)模型。
4
placeholders與feed_dict
當(dāng)我們定義一張graph時(shí),有時(shí)候并不知道需要計(jì)算的值,比如模型的輸入數(shù)據(jù),其只有在訓(xùn)練與預(yù)測時(shí)才會(huì)有值。這時(shí)就需要placeholder與feed_dict的幫助。
定義一個(gè)placeholder,可以使用tf.placeholder(dtype,shape=None,name=None)函數(shù)。

在上面的代碼中,會(huì)拋出錯(cuò)誤(InvalidArgumentError),因?yàn)橛?jì)算result需要foo的具體值,而在代碼中并沒有給出。這時(shí)候需要將實(shí)際值賦給foo。最后一行修改如下(其中最后的dict就是一個(gè)feed_dict,一般會(huì)使用Python讀入一些值后傳入,當(dāng)使用minbatch的情況下,每次輸入的值都不同):

mnist識(shí)別實(shí)例
介紹了一些tensorflow基礎(chǔ)后,我們用一個(gè)完整的例子將這些串起來。首先,需要下載數(shù)據(jù)集,mnist數(shù)據(jù)可以在Yann LeCun's website下載到,也可以通過如下兩行代碼得到。

該數(shù)據(jù)集中一共有55000個(gè)樣本,其中50000用于訓(xùn)練,5000用于驗(yàn)證。每個(gè)樣本分為X與y兩部分,其中X如下圖所示,是28*28的圖像,在使用時(shí)需要拉伸成784維的向量。



而本次演示所使用的模型為邏輯回歸,其可以表示為:

用圖形可以表示為下圖,具體原理這里不再闡述。
那么 let’s coding!
當(dāng)使用tensorflow進(jìn)行g(shù)raph構(gòu)建時(shí),大體可以分為五部分:
1.為輸入X與輸出y定義placeholder;
2.定義權(quán)重W;
3.定義模型結(jié)構(gòu);
4.定義損失函數(shù);
5.定義優(yōu)化算法。
首先導(dǎo)入需要的包,定義X與y的placeholder以及 W,b 的 Variables。其中None表示任意維度,一般是min-batch的 batch size。而 W 定義是 shape為784,10,rank為2的Variable,b是shape為10,rank為1的Variable。

之后是定義模型。x與W矩陣乘法后與b求和,經(jīng)過softmax得到y(tǒng)。

求邏輯回歸的損失函數(shù),這里使用了cross entropy,其公式可以表示為:

這里的 cross entropy 取了均值。定義了學(xué)習(xí)步長為0.5,使用了梯度下降算法(GradientDescentOptimizer)最小化損失函數(shù)。不要忘記初始化?Variables。

最后,我們的 graph 至此定義完畢,下面就可以進(jìn)行真正的計(jì)算,包括初始化變量,輸入數(shù)據(jù),并計(jì)算損失函數(shù)與利用優(yōu)化算法更新參數(shù)。

其中,迭代了1000次,每次輸入了100個(gè)樣本。mnist.train.next_batch 就是生成下一個(gè) batch 的數(shù)據(jù),這里知道它在干什么就可以。那么訓(xùn)練結(jié)果如何呢,需要進(jìn)行評估。這里使用單純的正確率,正確率是用取最大值索引是否相等的方式,因?yàn)檎_的 label 最大值為1,而預(yù)測的 label 最大值為最大概率。

至此,我們開發(fā)了一個(gè)簡單的手寫數(shù)字識(shí)別模型。
總結(jié)全文,我們首先介紹了 graph 與 session,并解釋了基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),講解了一些Variable需要注意的地方并介紹了 placeholders 與 feed_dict 。最終以一個(gè)手寫數(shù)字識(shí)別的實(shí)例將這些點(diǎn)串起來,希望可以給想要入門的你一丟丟的幫助。
掃描二維碼,添加馬哥個(gè)人微信,領(lǐng)取kindle大獎(jiǎng)!
