Peter Goldsborough寫(xiě)的文章,我個(gè)人覺(jué)得很不錯(cuò),就全文翻譯過(guò)來(lái),第一次發(fā)表在簡(jiǎn)書(shū)。原文在此,https:///pdf/1610.01178v1.pdf。
一,簡(jiǎn)介 現(xiàn)代人工智能系統(tǒng)和機(jī)器學(xué)習(xí)算法在各個(gè)領(lǐng)域都取得了革命性的進(jìn)步。我們注意到在計(jì)算機(jī)視覺(jué),語(yǔ)音識(shí)別和自然語(yǔ)言處理等技術(shù)領(lǐng)域的長(zhǎng)足進(jìn)步。更進(jìn)一步,這些技術(shù)進(jìn)步已經(jīng)惠及我們個(gè)人生活的方方面面。個(gè)人定制數(shù)字化助理,電商平臺(tái)的推薦系統(tǒng),金融詐騙檢測(cè),定制化網(wǎng)頁(yè)搜索和社交網(wǎng)絡(luò)訂閱以及新穎的地理位置經(jīng)濟(jì)學(xué)發(fā)現(xiàn)都得益于當(dāng)前機(jī)器學(xué)習(xí)算法。 近幾年,一種稱(chēng)之為深度學(xué)習(xí)的機(jī)器學(xué)習(xí)的分支已經(jīng)被證明特別有效。深度學(xué)習(xí)屬于表現(xiàn)學(xué)習(xí)算法類(lèi),它使用復(fù)雜神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),其中包含巨大數(shù)量的隱藏層。每個(gè)隱藏層包含對(duì)輸入數(shù)據(jù)進(jìn)行簡(jiǎn)單但卻是非線(xiàn)性轉(zhuǎn)換。給定足夠這樣的轉(zhuǎn)換模塊可以構(gòu)成非常復(fù)雜的功能模型去實(shí)現(xiàn)分類(lèi),回歸,翻譯和很多其他的事情【1】。 值得注意的是,深度學(xué)習(xí)的崛起也就是近幾年的事情,這都得益于越來(lái)越多可用的大數(shù)據(jù)集可以作為訓(xùn)練樣本;得益于有效使用GPU和大量并行硬件在大數(shù)據(jù)集上訓(xùn)練深度學(xué)習(xí)模型;也得益于一些新方法的發(fā)現(xiàn),比如修正線(xiàn)性單元(ReLU)激活函數(shù)或者退化(Dropout)作為一種正規(guī)化技術(shù)【1】【2】【3】【4】。 在深度學(xué)習(xí)算法和單獨(dú)算法模塊比如表示轉(zhuǎn)換,激活函數(shù),或者正規(guī)化方法在開(kāi)始還是數(shù)學(xué)公式的時(shí)候,接下來(lái)它們必須要轉(zhuǎn)化為某種計(jì)算機(jī)程序?qū)崿F(xiàn)才能用于應(yīng)用。因此,市場(chǎng)上存在很多這樣的開(kāi)源和商業(yè)軟件。在這些眾多的軟件中,Theano【5】,Torch【6】,scikit-learn【7】和許多其它的軟件會(huì)在本文第二部分有更詳細(xì)的評(píng)估。而Tensorflow是在2015年11月由谷歌發(fā)布的全新機(jī)器學(xué)習(xí)軟件庫(kù)【8】。根據(jù)開(kāi)始公布的情況來(lái)看,Tensorflow旨在擔(dān)當(dāng)在“大規(guī)?!碑悩?gòu)分布式系統(tǒng)上的“快速機(jī)器學(xué)習(xí)算法實(shí)現(xiàn)”【8】。 本文接下來(lái)將完整評(píng)估Tensorflow,并且將其放在當(dāng)前機(jī)器學(xué)習(xí)的大背景下來(lái)討論。本文詳細(xì)架構(gòu)如下,第二部分將介紹機(jī)器學(xué)習(xí)軟件發(fā)展歷史,不局限于Tensorflow相似項(xiàng)目。接下來(lái),第三部分將深入討論Tenorflow的計(jì)算范式。在第四部分,我們會(huì)解釋各類(lèi)編程語(yǔ)言的編程接口。第五部分會(huì)介紹Tensorflow的調(diào)錯(cuò)可視化工具。第六部分是Tensorflow和其它類(lèi)似軟件庫(kù)的定性和定量分析。在最后第八部分總結(jié),第七部分對(duì)于Tensorflow的學(xué)術(shù)界和工業(yè)界使用做了研究。 二,機(jī)器學(xué)習(xí)軟件庫(kù)歷史 本章旨在提供一個(gè)機(jī)器學(xué)習(xí)軟件庫(kù)歷史和關(guān)鍵里程碑的描述。開(kāi)始,我們回顧20年前一系列廣泛用于機(jī)器學(xué)習(xí)和數(shù)據(jù)分析的軟件庫(kù)。然后,我們聚焦到最近適合于深度學(xué)習(xí)的編程框架研究。圖一展示的是這一研究在時(shí)間軸上的展開(kāi)。本章不會(huì)比較比較Tensorflow和這些軟件庫(kù),因?yàn)檫@一部分將會(huì)在第六部分具體討論。 A. 通用機(jī)器學(xué)習(xí) 在接下來(lái)的文字,我們將會(huì)列舉和回顧一組通用機(jī)器學(xué)習(xí)軟件庫(kù)。所謂“通用”,意味著這些軟件庫(kù)用于機(jī)器學(xué)習(xí)和數(shù)據(jù)分析,并不局限于深度學(xué)習(xí)。所以,它們可能用于統(tǒng)計(jì)分析,聚類(lèi),降維分析,結(jié)構(gòu)化預(yù)測(cè),異常檢測(cè),淺神經(jīng)網(wǎng)絡(luò)(相對(duì)于深度神經(jīng)網(wǎng)絡(luò)而言)和其它。 讓我們從Tensorflow發(fā)布21年前的一個(gè)庫(kù)開(kāi)始說(shuō)起,MLC++【9】。MLC++是用C++開(kāi)發(fā)的算法庫(kù),包含了一系列數(shù)據(jù)挖掘,統(tǒng)計(jì)分析,模式識(shí)別技術(shù)的比較框架。它誕生于斯坦福大學(xué),現(xiàn)在被Silicon Graphics Inc公司(SGI)維護(hù)。據(jù)我們所知,這是迄今為止歲數(shù)最大的機(jī)器學(xué)習(xí)庫(kù)。 排在MLC++后面的,當(dāng)屬Bradski等人在2000年發(fā)布的OpenCV【10】。它主要解決計(jì)算機(jī)視覺(jué)和圖像識(shí)別問(wèn)題,包括一系列人臉識(shí)別,對(duì)象識(shí)別,3D模型提取等。它遵循BSD開(kāi)源,擁有C++,Python和MATLAB等多種編程接口。 另一種機(jī)器學(xué)習(xí)庫(kù)我們希望提及的是scikit-learn【7】。2008年由David Cournapeu作為谷歌編碼之夏項(xiàng)目的一部分開(kāi)發(fā)出來(lái)。它是使用Python編寫(xiě)的開(kāi)源庫(kù),基于NumPy,SciPy和matplotlib框架。它能夠用于廣泛的監(jiān)督學(xué)習(xí)和非監(jiān)督學(xué)習(xí)問(wèn)題。 Accord.NET和前述庫(kù)不一樣,它是2008年使用C#開(kāi)發(fā)的,除了一些機(jī)器學(xué)習(xí)算法,它還包括了用于語(yǔ)音識(shí)別和圖像識(shí)別的信號(hào)處理模塊?!?1】 Massive Online Analysis(MOA)是一個(gè)可以支持在線(xiàn)和離線(xiàn)大數(shù)據(jù)分析的開(kāi)源庫(kù)。MOA包括了一系列用于分類(lèi),回歸,推薦系統(tǒng)和其它訓(xùn)練能力。它于2010年發(fā)布,使用JAVA編程語(yǔ)言,由新西蘭的Waikato大學(xué)維護(hù)【12】。 Mahout是Apache軟件基金會(huì)支持的一個(gè)Java開(kāi)源庫(kù),基于Hadoop平臺(tái),支持可伸縮的機(jī)器學(xué)習(xí)應(yīng)用。它可以憑借Hadoop分布式文件系統(tǒng)(HDFS)對(duì)于大數(shù)據(jù)集的很好支持進(jìn)行分析。Mahout提供了分類(lèi),回歸和過(guò)濾算法。 Pattern在我們認(rèn)可的Python機(jī)器學(xué)習(xí)庫(kù)列表之中,得益于它提供豐富的網(wǎng)頁(yè)挖掘能力。它包含了不僅僅是機(jī)器學(xué)習(xí)算法(比如,聚類(lèi),分類(lèi)或者最近鄰近搜索),自然語(yǔ)言處理(n-gram搜索用于情感分析),還提供了網(wǎng)絡(luò)爬蟲(chóng),比如抓去Tweets或者Wiki百科的數(shù)據(jù)來(lái)幫助快速分析。它由Antwerp大學(xué)于2012年開(kāi)發(fā),開(kāi)源并維護(hù)。 最后一個(gè)需要介紹的是Spark MLib,也是一款開(kāi)源機(jī)器學(xué)習(xí)庫(kù),發(fā)布于2015年,看名字很容易知道,這是基于Spark開(kāi)發(fā)而成分布式分析系統(tǒng)。它支持處理大規(guī)模分布式數(shù)據(jù)集,并且在集群硬件環(huán)境下進(jìn)行機(jī)器訓(xùn)練。它包含了分類(lèi),回歸,聚類(lèi)和其它機(jī)器學(xué)習(xí)的算法【14】。 B. 深度學(xué)習(xí) 上述軟件庫(kù)能夠用于非常廣泛的機(jī)器學(xué)習(xí),統(tǒng)計(jì)分析應(yīng)用。接下來(lái)介紹的庫(kù)在訓(xùn)練深度模型方面特別有效。 首先介紹的是發(fā)布于2002年的,最古老的Torch【6】。Torch包含了一個(gè)純C++實(shí)現(xiàn)和編程接口?,F(xiàn)在,他的核心是C/CUDA實(shí)現(xiàn),對(duì)外暴露的語(yǔ)言是Lua腳本語(yǔ)言。Torch采用一個(gè)LuaJIT(實(shí)時(shí))編譯器把Lua的函數(shù)過(guò)程連接到C語(yǔ)言的具體實(shí)現(xiàn)。它包括了內(nèi)部別名,數(shù)值優(yōu)化過(guò)程,神經(jīng)網(wǎng)絡(luò)模型,同時(shí)能夠產(chǎn)生通用的N維數(shù)組(張量)對(duì)象。 Theano發(fā)布于2008年【5】,是另外值得一提的庫(kù)。我們注意到Theano在機(jī)器學(xué)習(xí)社區(qū)具有極高的人氣,但是它實(shí)際上并不是機(jī)器學(xué)習(xí)庫(kù)。它只是符號(hào)化數(shù)理編程框架,比如生成計(jì)算圖,然后編譯和優(yōu)化在CPU或者GPU上運(yùn)行,所以Theano更多時(shí)候只是“數(shù)理編譯器”。 Caffe是由Berkley視覺(jué)與學(xué)習(xí)中心(BVLC)維護(hù)的開(kāi)源深度學(xué)習(xí)庫(kù)。它于2014年發(fā)布,并遵循BSD開(kāi)源【15】。Caffe使用C++實(shí)現(xiàn),神經(jīng)網(wǎng)絡(luò)層是它的計(jì)算單元(這和Theano那些需要用戶(hù)自己定義詳細(xì)的數(shù)學(xué)公式去構(gòu)成神經(jīng)網(wǎng)絡(luò)層不同)。包含很多層的深度學(xué)習(xí)模型采用谷歌協(xié)議緩沖格式保存(GPBF)。模型能夠使用協(xié)議緩沖“語(yǔ)言”定義,然后通過(guò)和Python或者M(jìn)ATLAB已有的綁定,自動(dòng)生成神經(jīng)網(wǎng)絡(luò)。Caffe特別適合于訓(xùn)練卷積神經(jīng)網(wǎng)絡(luò)(CNNs,或者ConvNets)用于圖像識(shí)別領(lǐng)域。 上面介紹的機(jī)器學(xué)習(xí)庫(kù)都是使用Python,MTATLAB或者Lua定義深度學(xué)習(xí)模型,而Deeplearning4J(DL4J)則是使用Java去創(chuàng)建深度神經(jīng)網(wǎng)絡(luò)。DL4J能夠創(chuàng)建有限波資曼機(jī),卷積神經(jīng)網(wǎng)絡(luò)和遞歸神經(jīng)網(wǎng)絡(luò)。DL4J還能夠使用Hadoop或者Spark這樣的分布式計(jì)算平臺(tái)進(jìn)行工作。Adam Gibson2014年編寫(xiě)的DL4J遵循Apache 2.0開(kāi)源協(xié)議。 最后我們要介紹的是NVIDIA的深度學(xué)習(xí)SDK。這個(gè)SDK能夠充分利用GPU來(lái)加速計(jì)算。SDK首先提供了基于GPU的高性能深度算法(cuDNN),用于加速卷積,激活函數(shù)和張量轉(zhuǎn)換。其次,它提供了一個(gè)線(xiàn)性代數(shù)庫(kù),cuBLAS。最后,cuSPARSE包含了基于GPU的高效空間矩陣運(yùn)算。它可以單獨(dú)使用,也可以和其它庫(kù)聯(lián)合使用,比如Torch。 三, Tensorflow編程模型 在這一章,我們將深度討論Tensorflow的計(jì)算范式。開(kāi)始,我們會(huì)研究Tensorflow的基本架構(gòu)和計(jì)算原理,解釋機(jī)器學(xué)習(xí)算法如何在Tensorflow里面使用數(shù)據(jù)流圖語(yǔ)言表征。接下來(lái),我們研究Tensorflow的運(yùn)行模型,也就是Tensorflow如何把Tensorflow圖轉(zhuǎn)化為運(yùn)行狀態(tài)。然后,我們調(diào)查T(mén)ensorflow內(nèi)在的,針對(duì)軟硬件的不同算法優(yōu)化。最后,我們列舉一系列算法擴(kuò)展,以支撐用戶(hù)在Tensorflow對(duì)于計(jì)算模型,邏輯模型訓(xùn)練。 A. 計(jì)算圖架構(gòu) 在Tensorflow里面,機(jī)器學(xué)習(xí)算法被表證為計(jì)算圖。計(jì)算圖或者數(shù)據(jù)流圖是一種有向圖。有向圖的頂點(diǎn)或者節(jié)點(diǎn)代表運(yùn)算過(guò)程,有向圖的邊代表運(yùn)算過(guò)程之間的數(shù)據(jù)流。如下圖所示,先看左邊,輸出變量z是輸入x和y的二值運(yùn)算的結(jié)果,那么畫(huà)兩條分別從x和y向z的有向邊,并且在z上標(biāo)明運(yùn)算加號(hào)。一個(gè)更加完整和復(fù)雜的數(shù)據(jù)流圖在右邊。下面,對(duì)于數(shù)據(jù)流圖里面的元素(operation - 算子,tensor - 張量,variable - 變量和session - 過(guò)程)進(jìn)行更為詳盡的討論。 1)Operations(算子):使用圖來(lái)表示一個(gè)算法的主要好處不僅僅是直觀(guān)展示計(jì)算模型之間的依賴(lài)和關(guān)聯(lián),而且能夠更普適的定義運(yùn)算節(jié)點(diǎn)。在Tensorflow里,node(節(jié)點(diǎn))代表著算子,也就是一種運(yùn)算,更精確來(lái)說(shuō),代表了輸入的數(shù)據(jù)在有向圖上,如何流經(jīng)這個(gè)節(jié)點(diǎn)【8】。一個(gè)算子可以是0到多個(gè)輸入,也能產(chǎn)生0到多個(gè)輸出。因此,一個(gè)算子代表一個(gè)數(shù)學(xué)等式,一個(gè)變量,一個(gè)常量,一個(gè)有向控制流,一個(gè)文件I/O操作或者甚至是一個(gè)網(wǎng)絡(luò)通訊連接端口。在算子可以表達(dá)為一個(gè)常量或者變量不是像表達(dá)成一個(gè)函數(shù)那樣直觀(guān),但是一個(gè)常量可以被當(dāng)成一個(gè)沒(méi)有輸入,而且輸出恒定的運(yùn)算。相似的情況也適用于表征一個(gè)變量,也就是沒(méi)有輸入,輸出當(dāng)前狀態(tài)或者當(dāng)前變量的值。 任何算子必須要嚴(yán)格的定義和實(shí)現(xiàn)。在論文【8】的研究,任何一種實(shí)現(xiàn)被當(dāng)作一個(gè)算子的核函數(shù)。一種特定的核函數(shù)的具體編程都是針對(duì)某種硬件,比如CPU或者GPU的運(yùn)算。 2)Tensors(張量):在Tensorflow里面,代表數(shù)據(jù)流從一個(gè)算子流向另一個(gè)算子的邊稱(chēng)之為張量。一個(gè)張量是一個(gè)具有固定類(lèi)型同類(lèi)數(shù)據(jù)的多維集合。張量維度也稱(chēng)為rank(階)。張量的形狀(shape)描述張量大小的元祖(tuple)。比如,對(duì)于每一個(gè)維度的元素個(gè)數(shù)。從數(shù)學(xué)的觀(guān)點(diǎn)來(lái)看,張量是一個(gè)二維矩陣加一個(gè)表達(dá)張量階的一維向量或者標(biāo)量的生成物。 從計(jì)算圖的觀(guān)點(diǎn)來(lái)看,張量是表征通向輸出的算子的符號(hào)把手(symbolic handle)。在內(nèi)存里,張量本身并不包含和存儲(chǔ)數(shù)據(jù),但是它提供了張量代表的數(shù)據(jù)訪(fǎng)問(wèn)的接口。在Tensorflow里面創(chuàng)建一個(gè)算子的時(shí)候,比如x+y,返回一個(gè)張量對(duì)象。然后,這個(gè)張量可能作為其他計(jì)算的輸入,從而張量是連接源算子和目的算子的邊。基于這樣的認(rèn)知,數(shù)據(jù)在Tensorflow的圖中川流不息。 除了通常意義的張量,Tensorflow還支持一種稱(chēng)之為稀疏張量(SparseTensor)的數(shù)據(jù)結(jié)構(gòu)。這是一種空間有效字典類(lèi)的數(shù)據(jù)表證,也就是大量零值的稀疏張量。 3)Variables(變量):在通常情況,比如做隨機(jī)梯度下降的單次運(yùn)算的時(shí)候,機(jī)器學(xué)習(xí)模型的圖會(huì)從開(kāi)始到結(jié)束反復(fù)運(yùn)算多次。在兩次調(diào)用之間,圖中主要的張量并不會(huì)被保存。但是整體上對(duì)于圖的求值是需要保存狀態(tài)的,比如神經(jīng)網(wǎng)絡(luò)的權(quán)重和參數(shù)。因此,變量正是為了滿(mǎn)足這一需求而創(chuàng)建的算子,能夠被添加到計(jì)算圖中。 變量可以看成是在內(nèi)存中持久不變的張量副本。因此,變量的定義需要形狀和固定數(shù)據(jù)類(lèi)型兩個(gè)特征。Tensorflow提供了一系列賦值函數(shù)完成圖運(yùn)算。 在Tensorflow的圖中創(chuàng)建一個(gè)變量節(jié)點(diǎn)的時(shí)候,需要定義相應(yīng)的張量,這樣在圖運(yùn)行時(shí),變量可以隨之初始化。變量的形狀和數(shù)據(jù)類(lèi)型也來(lái)自于這個(gè)初始器。有趣的是,變量自己并不存儲(chǔ)這個(gè)初始的張量,相反構(gòu)造一個(gè)變量會(huì)增加三種不同的節(jié)點(diǎn): 1)變量節(jié)點(diǎn),保存持久狀態(tài)。 2)保存初始值的算子,通常是一個(gè)常量。 3)初始器算子,在圖求值的時(shí)候把初始值賦值給變量張量。 一個(gè)例子如下圖所示:三個(gè)節(jié)點(diǎn)代表變量定義。第一個(gè)變量v是一個(gè)變量的張量值在內(nèi)存中的持久副本。第二個(gè)變量i是給變量提供初始值(可以是任意張量)的節(jié)點(diǎn)。最后一個(gè)賦值節(jié)點(diǎn)把初始值賦給變量,在賦值節(jié)點(diǎn)產(chǎn)生一個(gè)新的張量具有初始值的變量v‘。這樣,v'可能作為另外算子的一個(gè)輸入。 4)Session(會(huì)話(huà)):在Tensorflow里面,算子的運(yùn)算和張量的估值會(huì)在一定的上下文中進(jìn)行,我們稱(chēng)之為Session(會(huì)話(huà))。會(huì)話(huà)的責(zé)任之一就是將分配和管理資源的工作封裝起來(lái),比如緩存變量。更進(jìn)一步來(lái)看,Tensorflow庫(kù)里面的Session接口提供了一個(gè)run函數(shù),作為整個(gè)圖計(jì)算的執(zhí)行入口。這個(gè)方法將輸入節(jié)點(diǎn)帶入整個(gè)圖計(jì)算的過(guò)程,并且根據(jù)圖定義返回相應(yīng)的結(jié)果。另外,一個(gè)可選的映射,即從任意節(jié)點(diǎn)到相應(yīng)替代值的映射,被稱(chēng)為feed nodes(反饋節(jié)點(diǎn)),可能也被run調(diào)用【8】。 在調(diào)用run的時(shí)候,Tensorflow將會(huì)出輸出節(jié)點(diǎn)開(kāi)始反向分析計(jì)算圖的節(jié)點(diǎn)依賴(lài),計(jì)算所有節(jié)點(diǎn)的傳遞閉包。這些節(jié)點(diǎn)可能被分配到一個(gè)或者多個(gè)物理計(jì)算單元(CPU,GPU等),這些計(jì)算單元可以在一臺(tái)或者多臺(tái)機(jī)器上。分配的規(guī)則由Tensorflow的placement algorithm(分配算法)定義。這個(gè)算法會(huì)在本文后面部分談到。此外,因?yàn)榇嬖诠?jié)點(diǎn)評(píng)估順序的特定顯式可能性,姑且稱(chēng)為控制依賴(lài)關(guān)系,執(zhí)行算法將確保這些依賴(lài)關(guān)系不變。 B. 執(zhí)行模型 如剛剛討論的那樣,對(duì)于執(zhí)行各種計(jì)算圖元素的組成,TensorFlow劃分其任務(wù)在四個(gè)不同的組中實(shí)現(xiàn):客戶(hù)端,主控端,一組工作進(jìn)程和一些設(shè)備。 當(dāng)客戶(hù)端通過(guò)會(huì)話(huà)的run調(diào)用請(qǐng)求評(píng)估一個(gè)TensorFlow圖,這個(gè)請(qǐng)求被發(fā)送到主控端,主控端將任務(wù)委托給一個(gè)或多個(gè)工作進(jìn)程處理和協(xié)調(diào)它們的執(zhí)行。 每個(gè)工作進(jìn)程隨后負(fù)責(zé)一個(gè)或多個(gè)設(shè)備真實(shí)的運(yùn)算和處理操作。 在這個(gè)模型中,有兩個(gè)擴(kuò)展度。 第一擴(kuò)展度是關(guān)于執(zhí)行圖運(yùn)算機(jī)器的數(shù)量。 事實(shí)上,第二擴(kuò)展度指的是在每臺(tái)機(jī)器上,可能會(huì)有更多設(shè)備,例如,五個(gè)獨(dú)立的GPU和/或三個(gè)CPU。 為此,存在兩個(gè)“版本”的TensorFlow,一個(gè)用于在單個(gè)機(jī)器上本地執(zhí)行(但可能有許多設(shè)備),一個(gè)支持分布式在許多機(jī)器和許多設(shè)備上實(shí)現(xiàn)。 上圖展示了執(zhí)行模型和響應(yīng)擴(kuò)展度。而且Tensorflow在開(kāi)始發(fā)布的時(shí)候,確實(shí)只公布了單機(jī)版本,而多機(jī)版本是在2016年的4月13日才姍姍來(lái)遲【16】。 1)Devices(設(shè)備):設(shè)備是在TensorFlow執(zhí)行模型中最小,最基本的實(shí)體。 上圖中的所有節(jié)點(diǎn),也就是每個(gè)算子的內(nèi)核,最終都必須是映射到要執(zhí)行的可用設(shè)備。 在實(shí)際執(zhí)行過(guò)程中,設(shè)備通常是CPU或GPU。 然而,TensorFlow能夠支持更多種類(lèi)的物理執(zhí)行單元。 例如,在2016年5月,谷歌宣布其定制的ASIC(專(zhuān)用集成電路)張量處理單元(TPU),是專(zhuān)門(mén)用于快速?gòu)埩坑?jì)算[17]。 因此,Tensorflow是可以容易地集成新出現(xiàn)的設(shè)備類(lèi)新型硬件。 為了整體評(píng)估在某個(gè)設(shè)備上的節(jié)點(diǎn),主控端生成相應(yīng)的工作進(jìn)程。 作為工作進(jìn)程可以在單個(gè)機(jī)器上管理一個(gè)或多個(gè)設(shè)備,設(shè)備是不僅通過(guò)名稱(chēng)標(biāo)識(shí),還標(biāo)識(shí)其所在工作進(jìn)程組的索引。 例如,特定組中的第一CPU可以由字符串“/ cpu:0”標(biāo)識(shí)。 2)placement algorithm(分配算法):為了決定哪一個(gè)節(jié)點(diǎn)分配給那一個(gè)設(shè)備,Tensorflow使用了一種特定分配算法。該算法模擬計(jì)算圖的運(yùn)算和遍歷從輸入張量到輸出張量的全部節(jié)點(diǎn)。在遍歷節(jié)點(diǎn)過(guò)程中,要決定哪一個(gè)可用設(shè)備D={d1,d2,...dn}運(yùn)行給定節(jié)點(diǎn)v,算法使用一個(gè)成本模型Cv(d)。這個(gè)成本模型考慮四種信息來(lái)決定最優(yōu)設(shè)備d = arg mind∈D Cν(d): 1)在給定設(shè)備上是否存在該節(jié)點(diǎn)的實(shí)現(xiàn)(核函數(shù))。比如,如果任何一個(gè)GPU上都不存在某種特定算子,那么選擇任意GPU的成本都是無(wú)限的。 2)估計(jì)一個(gè)節(jié)點(diǎn)的輸入和輸出丈量的大?。ò醋止?jié)計(jì)算)。 3)給定設(shè)備對(duì)于核函數(shù)的期望計(jì)算時(shí)間。 4)對(duì)于輸入丈量到相應(yīng)算子跨設(shè)備或者跨機(jī)器的傳輸成本進(jìn)行啟發(fā)式估算,萬(wàn)一該節(jié)點(diǎn)已經(jīng)賦值的輸入張量所在設(shè)備和當(dāng)前設(shè)備并不一樣。 3)cross-device execution(跨設(shè)備運(yùn)行):只要是用戶(hù)擁有多個(gè)設(shè)備,Tensorflow通常都會(huì)把節(jié)點(diǎn)分配到這些設(shè)備上去。這個(gè)過(guò)程是通過(guò)把節(jié)點(diǎn)分類(lèi),然后一類(lèi)分配到一個(gè)設(shè)備。這樣分配的話(huà),必須處理跨設(shè)備分配的節(jié)點(diǎn)依賴(lài)問(wèn)題。讓我們考慮A和B這樣兩個(gè)設(shè)備,其中節(jié)點(diǎn)v在設(shè)備A上。如果v的輸出張量作為另外兩個(gè)算子α, β的輸入,同時(shí)α, β在設(shè)備B上。那么就存在從A到B的跨設(shè)備的邊ν → α 和 ν → β。如下圖所示: 在實(shí)際運(yùn)行中,需要使用一些傳輸手段完成v的輸出張量從A設(shè)備,比如GPU,到設(shè)備B,比如CPU的過(guò)程。如下圖所示,send和recv兩類(lèi)節(jié)點(diǎn)被生成完成這個(gè)傳輸過(guò)程。 最后,Tensorflow使用“規(guī)范化”來(lái)優(yōu)化(send,recv)對(duì)。在上圖所示的例子中,我們看到兩個(gè)recv節(jié)點(diǎn),分別連接α, β。然而,一種等價(jià)的,但是更為有效的辦法就是在設(shè)備B上只運(yùn)行一個(gè)recv,如下圖所示。 C. 執(zhí)行優(yōu)化 為了保證Tensorflow運(yùn)行模型的最優(yōu)化,一系列優(yōu)化算法被內(nèi)置其中。本章節(jié)將重點(diǎn)介紹三種優(yōu)化方法:常用子圖消減法,執(zhí)行時(shí)序安排法和損失壓縮法。 1)Common Subgraph Elimination(常用子圖消減法):常用子表證消減法是眾多現(xiàn)代編譯器采用的優(yōu)化算法,憑借合并同類(lèi)計(jì)算項(xiàng)的辦法,編譯器可以將相同計(jì)算的多次實(shí)例合并成一個(gè)實(shí)例。結(jié)果會(huì)保存在一個(gè)臨時(shí)變量,這樣可以重復(fù)使用該結(jié)果。在Tensorflow圖的運(yùn)算中,也會(huì)出現(xiàn)相似的情形,一個(gè)算子對(duì)于相同輸入會(huì)被重復(fù)調(diào)用多次。如果就這樣直接運(yùn)算,那么效率是非常低下的,同時(shí)大量的內(nèi)存占用也是不合理的。因此,Tensorflow也應(yīng)用這種同類(lèi)消減法,或者更恰當(dāng)?shù)拿枋鍪腔谇按螆?zhí)行的子圖消減法。對(duì)于這種算法,遍歷計(jì)算圖,凡是遇到兩個(gè)或者更多對(duì)于同樣輸入張量的同種類(lèi)型的算子可以通過(guò)規(guī)范化合并成一個(gè)子圖。此算子的輸出張量然后會(huì)被轉(zhuǎn)移到所有相應(yīng)依賴(lài)的節(jié)點(diǎn)去。下圖給出了消減的一個(gè)示例。 2)Scheduling(時(shí)序安排法):一個(gè)簡(jiǎn)單而強(qiáng)大的優(yōu)化是盡可能晚地調(diào)度節(jié)點(diǎn)執(zhí)行。 確保操作結(jié)果僅在最小所需時(shí)間段內(nèi)保持在存儲(chǔ)器中,由此減少了存儲(chǔ)器峰值消耗,并且因此可以大大提高系統(tǒng)的整體性能。文獻(xiàn) [8]的作者指出,這對(duì)于諸如GPU之類(lèi)的設(shè)備尤為重要,因?yàn)樗鼈兊拇鎯?chǔ)器資源很少。 此外,仔細(xì)安排的調(diào)度還涉及激活send和recv節(jié)點(diǎn),這里面不僅存儲(chǔ)器而且網(wǎng)絡(luò)資源都被作為競(jìng)爭(zhēng)資源。 3)Lossy Compression(損失壓縮法):諸如分類(lèi),回歸或者其它機(jī)器學(xué)習(xí)算法的主要目的是建立穩(wěn)健的模型。我們這里說(shuō)“穩(wěn)健”的意思是指理想的優(yōu)化過(guò)的模型算法不會(huì)因?yàn)榕既辉肼曅盘?hào)改變響應(yīng)輸出。因此,算法對(duì)于計(jì)算數(shù)值的精度要求就會(huì)下降,比如使用16比特運(yùn)算就可以達(dá)到32比特計(jì)算的效果,但是很明顯這樣不僅僅是節(jié)約了小數(shù)點(diǎn)后位數(shù),也極大降低了計(jì)算開(kāi)銷(xiāo)?;谶@個(gè)原則,在Tensorflow里面,把節(jié)點(diǎn)轉(zhuǎn)化為計(jì)算圖的內(nèi)部加法采用了類(lèi)似的方法。當(dāng)數(shù)據(jù)通訊跨設(shè)備或者機(jī)器的時(shí)候,在發(fā)送端將32位浮點(diǎn)數(shù)字轉(zhuǎn)化為截?cái)嗟?6位表示,在接收端被截?cái)嗟臄?shù)字被簡(jiǎn)單的通過(guò)補(bǔ)零法恢復(fù)成32位,而不是使用取整運(yùn)算【8】。 D. 和基礎(chǔ)編程模型相關(guān)部分 討論了Tensorflow的基礎(chǔ)計(jì)算范式和執(zhí)行模型之后,我們將繼續(xù)討論另外三種和創(chuàng)建機(jī)器學(xué)習(xí)算法高度相關(guān)的高級(jí)主題。第一,我們討論Tensorflow如何處理被許多機(jī)器學(xué)習(xí)算法廣泛使用的梯度反饋(gradient back-propagation)的。然后,我們研究Tensorflow是如何控制數(shù)據(jù)流的。最后,我們簡(jiǎn)單談一下檢查點(diǎn)技術(shù)(checkpoint),因?yàn)樵谑褂么笮湍P偷臅r(shí)候非常有用的一種技巧和技術(shù)。 1)Back-Propagation Nodes(反向傳播節(jié)點(diǎn)):在相當(dāng)多的深度學(xué)習(xí)和其它機(jī)器學(xué)習(xí)算法里面,計(jì)算計(jì)算圖中特定節(jié)點(diǎn)相對(duì)其他一個(gè)或者多個(gè)節(jié)點(diǎn)的梯度是非常有必要的。比如,在神經(jīng)網(wǎng)絡(luò)中,我們可能通過(guò)將給定樣本進(jìn)行一系列非線(xiàn)性轉(zhuǎn)換來(lái)計(jì)算模型的成本函數(shù)c。如果神經(jīng)網(wǎng)絡(luò)包含兩個(gè)隱藏層,其表證函數(shù)f(x;w) =fx(w)和g(x;w) =gx(w),w是內(nèi)部權(quán)重。我們可以把其對(duì)應(yīng)該樣本的成本函數(shù)表示成c = (fx?gx)(w) =fx(gx(w))。通常我們會(huì)做微分dc/dw來(lái)求相應(yīng)的權(quán)重w,然后用得到的權(quán)重去更新原有權(quán)重。這種計(jì)算在反向傳播算法是通過(guò)反向遍歷圖去計(jì)算[fx(gx(w))]′=fx′(gx(w))·gx′(w)完成的。 在文獻(xiàn)【18】中,描述了兩種計(jì)算反向傳播梯度的算法。第一種稱(chēng)為symbol-to-number differentiation(圖數(shù)差異法)。它是接受一組輸入值,然后計(jì)算這組輸入的梯度數(shù)值。它通過(guò)明確的前向遍歷圖計(jì)算成本函數(shù),然后再反向遍歷計(jì)算剃度。第二種稱(chēng)為symbol-to-symbol derivatives(符號(hào)到符號(hào)導(dǎo)數(shù)法),這是和Tensorflow更相關(guān)的算法,在文獻(xiàn)【8】中被稱(chēng)為automatic gradient computation(自動(dòng)梯度計(jì)算法)。在這種情況下,梯度不是通過(guò)反向傳播算法的顯式實(shí)現(xiàn)來(lái)計(jì)算的。 相反,它將特殊節(jié)點(diǎn)添加到計(jì)算圖中,然后根據(jù)計(jì)算鏈規(guī)則計(jì)算該計(jì)算圖每個(gè)算子的梯度。 為了進(jìn)行反向傳播運(yùn)算,然后必須通過(guò)圖評(píng)估引擎簡(jiǎn)單地像任何其它節(jié)點(diǎn)那樣執(zhí)行這些節(jié)點(diǎn)。 因此,此方法不會(huì)求導(dǎo),而僅計(jì)算這些值的符號(hào)句柄。 當(dāng)Tensorflow需要計(jì)算一個(gè)特定節(jié)點(diǎn)v相對(duì)其它節(jié)點(diǎn)α的梯度時(shí),它會(huì)從v到α反向遍歷圖。在遍歷中遇到 的每一個(gè)算子o表示依賴(lài)α的函數(shù)和產(chǎn)生輸出張量連接集合(ν?...?o?...)(α)的一個(gè)連接。因此,Tensorflow為每一個(gè)選取前一個(gè)連接梯度,并且乘以它自己梯度的算子o添加梯度節(jié)點(diǎn)。在遍歷的最后,有一個(gè)節(jié)點(diǎn)帶有符號(hào)句柄表示目標(biāo)倒數(shù)dv/dα,即隱含實(shí)現(xiàn)了反向傳播算法?,F(xiàn)在可以很清楚的看到,在符號(hào)到符號(hào)方法僅僅是另一種沒(méi)有任何例外的算子。下圖展示了一張計(jì)算圖在梯度節(jié)點(diǎn)增加前后的樣子。 在文獻(xiàn)【8】中提到符號(hào)到符號(hào)導(dǎo)數(shù)法可能產(chǎn)生相當(dāng)可觀(guān)的計(jì)算開(kāi)銷(xiāo),包括內(nèi)存的開(kāi)銷(xiāo)。究其原因,需要理解兩組鏈?zhǔn)揭?guī)則的等價(jià)公式。第一組公式會(huì)重用之前計(jì)算結(jié)果需要為了前向傳播的需要,保存相應(yīng)的結(jié)果。對(duì)于等式1中的任意函數(shù)f,g,h:df/dw=f′(y)·g′(x)·h′(w)withy=g(x),x=h(w) (1) 對(duì)于計(jì)算鏈?zhǔn)揭?guī)則的第二種可能公式已經(jīng)表明,每個(gè)函數(shù)需要重新計(jì)算它們所有的參數(shù)并且調(diào)用它們依賴(lài)的每一個(gè)函數(shù)。該公式表示成:df/dw=f′(g(h(w)))·g′(h(w))·h′(w) (2) 根據(jù)文獻(xiàn)【8】,Tensorflow當(dāng)前使用第一組公式。假定如果不采用這種方法,并且考慮到這個(gè)連接可能包括數(shù)百或數(shù)千個(gè)操作,則對(duì)于此連接的幾乎每個(gè)連接必須重新計(jì)算最內(nèi)層函數(shù),因此這種選擇看起來(lái)是合理的。 然而,從另一方面來(lái)看,在存儲(chǔ)器中長(zhǎng)時(shí)間保存張量也不是最佳的,尤其是在諸如GPU的設(shè)備上,其存儲(chǔ)器資源是稀缺的。 對(duì)于等式2,由張量保持的存儲(chǔ)器在理論上可以在其已被其圖依賴(lài)性處理時(shí)被釋放。 因此,根據(jù)文獻(xiàn)[8]的說(shuō)法,TensorFlow的開(kāi)發(fā)團(tuán)隊(duì)稱(chēng),重新計(jì)算某些張量而不是將它們保留在內(nèi)存中的做法可能是未來(lái)進(jìn)行性能改進(jìn)的方向。 2)控制流:某些機(jī)器學(xué)習(xí)算法會(huì)從執(zhí)行流控制中受益,即只在特定條件下執(zhí)行特定流,或者僅僅執(zhí)行固定次數(shù)。對(duì)此,Tensorflow提供了一組流控制的基本體,包括if條件跳轉(zhuǎn)和loop循環(huán)。循環(huán)條件控制的存在是因?yàn)門(mén)ensorflow計(jì)算圖可能是循環(huán)的。如果對(duì)于循環(huán)的次數(shù)是已知或者固定的,那么循環(huán)體可以被展開(kāi)成非循環(huán)的計(jì)算序列【5】。但是為了支持可變迭代,Tensorflow需要強(qiáng)制跳轉(zhuǎn)到如文獻(xiàn)【8】描述的一組環(huán)運(yùn)算。 當(dāng)引入控制流需要特別注意的是在有反向傳播的場(chǎng)景。 在處理?xiàng)l件控制的情況下,如果if操作返回一個(gè)或另一個(gè)張量,則必須知道在向前傳播期間節(jié)點(diǎn)采取哪個(gè)分支,使得梯度節(jié)點(diǎn)僅被添加到該分支。 此外,當(dāng)循環(huán)體(其可以是小圖)被執(zhí)行特定次數(shù)時(shí),梯度計(jì)算不僅需要知道執(zhí)行的迭代的次數(shù),而且還需要訪(fǎng)問(wèn)所產(chǎn)生的每個(gè)中間值。 這種通過(guò)反向循環(huán)計(jì)算梯度的技術(shù)在文獻(xiàn)[5]中稱(chēng)為反向傳播時(shí)間。 3)Checkpoints(檢查點(diǎn)):Tensorflow另一個(gè)基本編程模型的擴(kuò)展就是檢查點(diǎn)技術(shù)的應(yīng)用。檢查點(diǎn)技術(shù)允許模型參數(shù)可以被序列化到文件,以備再次運(yùn)行模型的時(shí)候可以從文件中恢復(fù)模型參數(shù)和運(yùn)行變量。因此可以在計(jì)算圖中增加Save節(jié)點(diǎn)去保存相應(yīng)變量的張量。當(dāng)然,一個(gè)變量也可能連接到restore算子去恢復(fù)相應(yīng)的張量。這種方法對(duì)于那些需要長(zhǎng)時(shí)間才能訓(xùn)練的模型,對(duì)于模型計(jì)算容錯(cuò)性考量都是很好的技術(shù),特別是在分布式環(huán)境【8】。 四. Tensorflow的編程接口 在討論了Tensorflow計(jì)算模型之后,我們現(xiàn)在聚焦到更為實(shí)際的編程接口。我們先介紹可用的編程語(yǔ)言接口,再使用一個(gè)例子講解Python API的使用。最后,我們概括Tensorflow API的格局和怎么能夠快速創(chuàng)建算法原型。 A. 接口 Tensorflow擁有C++和Python兩種編程接口,允許用戶(hù)調(diào)用后端功能。Python API提供了豐富和完整的創(chuàng)建和運(yùn)行計(jì)算圖的編程接口,而C++的接口相對(duì)有限和與后端功能實(shí)現(xiàn)耦合度太高,僅僅允許去執(zhí)行計(jì)算圖和序列化圖到谷歌協(xié)議緩沖格式。對(duì)于創(chuàng)建圖的C++接口在文章發(fā)表的時(shí)候還很不完善。 值得注意的是,Tensorflow的API和NumPy有很好的集成。因此,我們可以看到Tensorflow的數(shù)據(jù)類(lèi)型tensor和NumPy的ndarrays在很多應(yīng)用場(chǎng)合都是可以互換的。 B. 示例解讀 接下來(lái)的幾個(gè)章節(jié),我們將一步一步的解讀一個(gè)Tensorflow的真實(shí)示例。我們將訓(xùn)練一個(gè)帶有一個(gè)輸入和一個(gè)輸出的簡(jiǎn)單的多層感知器(MLP),它將用于識(shí)別MNIST數(shù)據(jù)集中的手寫(xiě)字符。在這個(gè)數(shù)據(jù)集中,樣本時(shí)28x28像素的手寫(xiě)字符從0-9。我們將這些字符轉(zhuǎn)換成784灰度像素扁平的向量。對(duì)于每個(gè)樣本的標(biāo)簽是相應(yīng)字符的數(shù)字。 我們從加載輸入數(shù)據(jù)開(kāi)始解讀。這里數(shù)據(jù)已經(jīng)被處理好成為我們需要的格式,只需要調(diào)用read函數(shù)加載數(shù)據(jù)到內(nèi)存。進(jìn)一步來(lái)看,我們?cè)O(shè)置one_hot=True來(lái)指定是否使用一個(gè)10維向量(d1,。。。,d10)的轉(zhuǎn)置來(lái)表征某一個(gè)字符,即所有維度數(shù)字是0,只有表征該字符的位置是1。
接下來(lái),我們通過(guò)調(diào)用tf.Graph創(chuàng)建一個(gè)新計(jì)算圖。為了給這個(gè)圖增加算子,我們必須把這個(gè)圖注冊(cè)為缺省圖。在TensorflowAPI和庫(kù)設(shè)計(jì)里面,新的算子總是掛在缺省圖上。相應(yīng)代碼如下:
我們現(xiàn)在準(zhǔn)備通過(guò)增加算子來(lái)生成計(jì)算圖。讓我們先增加兩個(gè)占位節(jié)點(diǎn)examples和labels。占位者是一種特殊變量,在圖運(yùn)算的時(shí)候必須被確切的張量賦值。也就是說(shuō),上面創(chuàng)建的占位者必須在Session.run()被調(diào)用的時(shí)候,被feed_dict參數(shù)傳進(jìn)來(lái)的張量所取代。對(duì)于每一個(gè)這個(gè)的占位者,我們定義它的形狀和數(shù)據(jù)類(lèi)型。Tensorflow在這里可以使用None來(lái)描述占位者形狀的第一個(gè)維度。這就是為未來(lái)給此占位者賦值一個(gè)在此維度大小可變的張量。對(duì)于examples的列大小,我們指定每一幅圖的特征數(shù),即28 x 28 = 784個(gè)像素。labels占位者應(yīng)該有10列,代表著我們?cè)谏厦娑x的10維字符分類(lèi)向量。
給定一個(gè)example矩陣X屬于集合R是nx784,意味著包含n個(gè)圖像,學(xué)習(xí)算法將使用仿射變換X.W+b,這里W是一個(gè)784x10的權(quán)重矩陣,b是10維偏置向量。這個(gè)變換的結(jié)果產(chǎn)生Y是nx10的矩陣,包含我們模型對(duì)于每一個(gè)樣本圖像識(shí)別的結(jié)果。這些結(jié)果是一些任意數(shù)值而不是概率分布。為了轉(zhuǎn)換它們到一個(gè)有效概率分布,在給定似然Pr[x=i],即第x樣本圖像是數(shù)字i的概率,我們使用softmax函數(shù),公式和相應(yīng)代碼如下:
接下來(lái),我們計(jì)算我們的目標(biāo)函數(shù),產(chǎn)生模型在當(dāng)前權(quán)重W和偏差b下的成本或者損失。公式H(L,Y)i=?jLi,j·log(Yi,j)計(jì)算我們預(yù)測(cè)的結(jié)果和訓(xùn)練樣本實(shí)際結(jié)果之間的交叉熵。更精確而言,我們考慮的是所有訓(xùn)練樣本的交叉熵的均值作為成本或者損失。
現(xiàn)在,我們有了目標(biāo)函數(shù),就可以進(jìn)行隨機(jī)梯度下降計(jì)算去更新我們模型的權(quán)重矩陣。為此,Tensorflow提供了GradientDescentOptimizer這個(gè)類(lèi)實(shí)現(xiàn)這一過(guò)程。該類(lèi)使用算法的學(xué)習(xí)速度來(lái)初始化,同時(shí)提供算子minimize來(lái)處理我們的成本或損失張量。這就是我們?cè)赟ession環(huán)境里訓(xùn)練模型需要反復(fù)迭代的算子。
最后,我們就可以實(shí)際訓(xùn)練模型了。在Tensorflow里,我們需要進(jìn)入一個(gè)會(huì)話(huà)環(huán)境,使用tf.Session來(lái)管理會(huì)話(huà)。通過(guò)Session,我們訓(xùn)練我們的模型,執(zhí)行計(jì)算圖上的算子。我們有幾種調(diào)用方法,最常見(jiàn)的方式是調(diào)用Session.run(),然后傳遞一組張量給它?;蛘?,我們也可以直接在張量上調(diào)用eval()和算子上run()。在評(píng)估算子之前,我們必須確保我們圖變量被初始化了。理論上,我們可以通過(guò)調(diào)用Variable.initializer算子來(lái)初始化每一個(gè)變量。然而,最常見(jiàn)的辦法是調(diào)用tf.initialize_all_variables()方法去一次性初始化所有變量。然后我們就可以迭代幾次隨機(jī)梯度下降,每一次我們選取一些樣本圖像和標(biāo)簽,傳遞給模型去計(jì)算成本或損失。最后,我們的成本或損失會(huì)越來(lái)越?。ㄎ覀兿M绱耍?/p>
這個(gè)例子完整的代碼參見(jiàn)附錄。 C. 抽象與封裝 你可以已經(jīng)注意到構(gòu)建模型和訓(xùn)練的過(guò)程是需要花費(fèi)大量的開(kāi)銷(xiāo)在創(chuàng)建一個(gè)非常簡(jiǎn)單的兩層網(wǎng)絡(luò)。通過(guò)深度學(xué)習(xí)的名稱(chēng)“深度”二字,就隱含了,你需要使用攜帶大量隱藏層的深度神經(jīng)網(wǎng)絡(luò)。因此,每一次構(gòu)建模型都需要花這么多時(shí)間構(gòu)建權(quán)重矩陣,偏置矩陣,計(jì)算矩陣乘法,加法,和應(yīng)用非線(xiàn)性激活函數(shù)的神經(jīng)網(wǎng)絡(luò)是效率低下的,因此,我們需要抽象和封裝這一過(guò)程。幸好,我們可以使用一些開(kāi)源的庫(kù)來(lái)完成這一過(guò)程,比如PrettyTensor,TFLearn和Keras。下面我們會(huì)分別論述PrettyTensor和TFLearn。 1)PrettyTensor:這是谷歌開(kāi)發(fā)的高級(jí)編程接口,主要的是通過(guò)Builder模式來(lái)調(diào)用Tensorflow的API。它允許用戶(hù)把Tensorflow的算子和張量封裝進(jìn)干凈的版本,然后迅速將任意層串接起來(lái)。比如,它可以用一行代碼完成一個(gè)輸入張量傳輸?shù)揭粋€(gè)全連接(稠密)神經(jīng)網(wǎng)絡(luò)層。下面的樣例代碼顯示了從創(chuàng)建一個(gè)占位者到初始化,到創(chuàng)建三層網(wǎng)絡(luò)到最后輸出softmax的一個(gè)分布。
2)TFLearn:這是另一個(gè)基于Tensorflow本體的封裝庫(kù),它有高度封裝的神經(jīng)網(wǎng)絡(luò)層和層連接API。而且,它比PrettyTensor一定要通過(guò)Session建立訓(xùn)練和評(píng)估模型更進(jìn)一步,它可以直接添加訓(xùn)練樣本和標(biāo)簽來(lái)訓(xùn)練模型。在TFLearn里面,有創(chuàng)建完整神經(jīng)層次的函數(shù),有返回原始Tensorflow對(duì)象的函數(shù),有混合Tensorflow本體代碼調(diào)用的編程。比如,我們可以取代Tensorflow的輸出層,自己完整構(gòu)建它,但是依然保持其它部分不發(fā)生改變。下面10行代碼完成了附錄65行代碼才能完成的功能。
五. Tensorflow圖的可視化 深度學(xué)習(xí)模型通常使用錯(cuò)綜復(fù)雜的神經(jīng)網(wǎng)絡(luò)。比如,文獻(xiàn)【19】描述的谷歌Inception模型就是一個(gè)有36000個(gè)獨(dú)立單元的卷積神經(jīng)網(wǎng)絡(luò)。而文獻(xiàn)【8】描述的某種長(zhǎng)短時(shí)記憶模型(LSTM)用到了15000個(gè)節(jié)點(diǎn)。為了使用和調(diào)優(yōu)如此復(fù)雜的網(wǎng)絡(luò),必須依賴(lài)強(qiáng)有力的可視化工具。Tensorboard是Tensorflow的可視化儀表盤(pán)。本章節(jié)將講述Tensorboard的功能。 A. Tensorboard的特性 Tensorboard的核心特性就是構(gòu)建明細(xì)易懂計(jì)算圖的可視化界面。如下圖例子可以看到Tensorboard是如何展示計(jì)算圖的。 name scope(命名空間)是Tensorflow里面一個(gè)重要可視化分類(lèi)方法。它可以把同屬于一個(gè)命名空間的算子,關(guān)系,輸入和輸出顯示在同一個(gè)方框圖內(nèi)。下圖展示了,從上圖擴(kuò)展一個(gè)layer1命名空間的詳細(xì)算法細(xì)節(jié)。 另外,Tensorboard允許用戶(hù)追蹤單個(gè)變量在訓(xùn)練過(guò)程中的變化情況。你可以附加兩類(lèi)總結(jié)算子到計(jì)算圖上去生成報(bào)告(scalar summaries和histogram summaries)。Scalar summaries顯示的是張量伸縮的進(jìn)度圖,也就是在某次訓(xùn)練迭代的采樣數(shù)據(jù)。這樣你可以看到訓(xùn)練的精確度和損失變化情況。Histogram summaries節(jié)點(diǎn)允許用戶(hù)去追蹤數(shù)值分布情況,比如神經(jīng)網(wǎng)絡(luò)的權(quán)重或者softmax估值分布。下圖展示了這兩種報(bào)告。 我們注意到Tensorboard采用了網(wǎng)頁(yè)交互形式。一旦你的計(jì)算圖被加載進(jìn)來(lái),你就可以觀(guān)察模型,監(jiān)控算子運(yùn)行。具體的Tensorboard在線(xiàn)演示可以在這里找到:https://www./tensorboard/index.html B. Tensorboard的實(shí)操 為了把Tensorboard整合到你的Tensorflow代碼,你需要至少做三步。第一,你需要使用命名空間來(lái)規(guī)劃你的節(jié)點(diǎn)。第二,你需要給你的算子增加某種類(lèi)型的報(bào)告。最后第三部,你需要調(diào)用SummaryWriter去把Summaries得到的張量寫(xiě)到文件中去。與其分別去寫(xiě)每一個(gè)summaries,還不如調(diào)用tf.merge_all_summaries()整合所有的summaries,然后一次性寫(xiě)入文件。下面展示了Tensorboard的樣例代碼。
六. 比較其它深度學(xué)習(xí)框架 除了Tensorflow,我們還能找到其它一些深度學(xué)習(xí)框架。人氣比較高的有Theano,Torch和Caffe。在這一章,我們會(huì)探索這些框架和Tensorflow的異同點(diǎn),進(jìn)行一系列定性和定量的分析。 A. 定性分析 下面我們分別比較上述三種深度學(xué)習(xí)框架和Tensorflow的差異。下圖則是匯總了這個(gè)比較。 1)Theano:在我們需要討論的三種Tensorflow的替代框架中,Theano是最像Tensorflow的。如同Tensorflow一樣,Theano的編程模型也是聲明式而不是命令式的基于計(jì)算圖。而且,Theano也使用符號(hào)差異化。然后Theano有一個(gè)比較長(zhǎng)時(shí)間的圖編譯時(shí)間,因?yàn)樗枰裀ython代碼轉(zhuǎn)化為C++/CUDA代碼【5】。一方面,這是因?yàn)門(mén)heano采用了很多高級(jí)圖優(yōu)化的算法【5】,而Tensorflow僅僅是做了子圖消減優(yōu)化。對(duì)比Tensorboard, Theano的可視化工具可以用可憐來(lái)形容。它除了輸出一些可讀的文字來(lái)表示圖或靜態(tài)圖像,它還需要一個(gè)插件來(lái)生成類(lèi)似交互式網(wǎng)頁(yè)可視化,剩下來(lái)的東西,Theano就是泛善可陳了。 2)Torch:Torch和Tensorflow根本不同的地方是Torch是C/CUDAde后端實(shí)現(xiàn),加上Lua的前端編程接口。Lua確實(shí)是相當(dāng)快的腳本語(yǔ)言,可以快速構(gòu)建原型系統(tǒng),但是相對(duì)于Python,它算是非主流。雖然Lua 具備各種性能和功能上的優(yōu)勢(shì),但是對(duì)于工業(yè)界而言,對(duì)比Tensorflow的Python API而言,就顯得曲高和寡了。除了編程語(yǔ)言的考量,Torch的編程模型也和Tensorflow不同。它采用命令式編程,也不聲明計(jì)算圖。這就要求程序員還要仔細(xì)安排算子執(zhí)行的順序。這也暗示了Torch在前向和反向傳播計(jì)算梯度的時(shí)候,是用符號(hào)到數(shù)值而不是符號(hào)到符號(hào)差異化來(lái)優(yōu)化。 3)Caffe:Caffe和Tensorflow有著天壤之別。Caffe的模型創(chuàng)建擁有MATLAB和Python編程接口,主要使用谷歌的協(xié)議緩沖語(yǔ)言,這樣帶來(lái)和Python截然不同的編程體驗(yàn)。另外,在Caffe里面的基本構(gòu)建單元是神經(jīng)網(wǎng)絡(luò)的層,而不是Tensorflow里面的算子。這樣Tensorflow算是更為底層的框架。和Torch相似,Caffe也不是專(zhuān)注于構(gòu)建計(jì)算圖,或者符號(hào)。所以計(jì)算導(dǎo)數(shù)是通過(guò)符號(hào)到數(shù)值的方法。Caffe特別適合于開(kāi)發(fā)卷積神經(jīng)網(wǎng)絡(luò),用于圖像識(shí)別任務(wù)。但是,它就沒(méi)有Tensorflow那樣在神經(jīng)網(wǎng)絡(luò)領(lǐng)域具備普適性。比如Caffe在架構(gòu)上不支持循環(huán)架構(gòu),而這時(shí)RNN,LSTM這樣神經(jīng)網(wǎng)絡(luò)的基本構(gòu)造。另外,Caffe也不支持分布式系統(tǒng)。 B. 定量分析 接下來(lái),我們會(huì)對(duì)這幾種框架做定量分析來(lái),同時(shí)給出深度學(xué)習(xí)框架評(píng)估的整體發(fā)展趨勢(shì)。 文獻(xiàn)【20】的研究是由博世研發(fā)中心在2016年3月進(jìn)行的,他們對(duì)比了Tensorflow,Torch,Theano和Caffe這幾種不同的神經(jīng)網(wǎng)絡(luò)架構(gòu)。他們?cè)贗ntel Xeon E5-1650 v2 CPU @ 3.50 GHz and an NVIDIA GeForce GTX Titan X/PCIe/SSE2 GPU這樣配置的機(jī)器上安裝Ubuntu14.04,然后跑LeNet CNN模型【21】來(lái)看不同的框架的性能如何。他們特別留意神經(jīng)網(wǎng)絡(luò)的前向傳播速度,因?yàn)樗麄兿嘈胚@和框架部署策略有關(guān);還有反向傳播速度,這是和訓(xùn)練模型性能相關(guān)。我們摘要了他們結(jié)論如下表,這個(gè)結(jié)果是在兩種情況下得到的,(a)是一個(gè)CPU跑12個(gè)線(xiàn)程,(b)是GPU。從結(jié)果來(lái)看,有趣的是Tensorflow無(wú)論是在CPU還是在GPU都跑不過(guò)Torch。最有意思的是Tensorflow在跑GPU的時(shí)候成績(jī)掉的很厲害,是倒數(shù)第一。文獻(xiàn)【20】的作者注意到這可能是由于測(cè)試Tensorflow使用的是NVIDIA的cuDNNv2,而其他庫(kù)用的是v3. 他們?cè)谖闹袕?qiáng)調(diào),這么做是因?yàn)門(mén)ensorflow的官方文檔建議這么配置cuDNN的版本。 我們能夠拿到的第二個(gè)測(cè)試來(lái)源是文獻(xiàn)【22】卷積神經(jīng)網(wǎng)絡(luò)評(píng)測(cè)平臺(tái)的結(jié)果,你可以在github上找到他們。它是由Facebook的一個(gè)AI研究工程師Soumith Chintala維護(hù)的。我們參考的結(jié)果是2016年五月25日提交的。Chintala提交了圍繞卷積神經(jīng)網(wǎng)絡(luò)的許多框架的實(shí)現(xiàn),當(dāng)然包括Tensorflow,Torch和Caffe。Theano沒(méi)有參加評(píng)測(cè),所以我們這里看不到它的有關(guān)數(shù)據(jù)。該作者聲稱(chēng)的硬件配置是6-core Intel Core i7-5930K CPU @ 3.50GHz配有an NVIDIA Titan X graphics chip,跑的是Ubuntu14.04。該評(píng)測(cè)也分別給出了正向傳播和反向傳播的速度如下表: 不出意料,Tensorflow是僅次于Torch的框架。最后我們來(lái)看一下文獻(xiàn)【5】的評(píng)測(cè),這是由Theano開(kāi)發(fā)團(tuán)隊(duì)在2016年5月9日提交的報(bào)告。除了CNN模型之外,他們還測(cè)試了之前我們提到的AlexNet架構(gòu),測(cè)試還包括了在Penn Treebank【24】上跑LSTM的結(jié)果。他們的評(píng)測(cè)針對(duì)小模型(200個(gè)節(jié)點(diǎn)的單隱藏層,每個(gè)序列長(zhǎng)20),統(tǒng)計(jì)每秒處理多少單詞;針對(duì)大模型(兩個(gè)650個(gè)節(jié)點(diǎn)的隱藏層,序列長(zhǎng)為50)。在文獻(xiàn)【5】還提到一個(gè)中等模型的測(cè)試,這里我們不做討論,評(píng)測(cè)結(jié)果如下圖所示: 這個(gè)結(jié)果是在硬件配置NVIDIA Digits DevBox with 4 Titan X GPUs and an Intel Core i7-5930K CPU的機(jī)器上跑出來(lái)的。,而且他們給所有框架使用的都是cuDNN v4。Caffe的運(yùn)行結(jié)果沒(méi)有在列。在他們的評(píng)測(cè)結(jié)果里,Tensorflow在小模型表現(xiàn)最好,大模型稍差。這個(gè)表格來(lái)源于文獻(xiàn)【5】。 當(dāng)Tensorflow剛剛發(fā)布的時(shí)候,表現(xiàn)奇差,整個(gè)深度學(xué)習(xí)社區(qū)都表示失望。隨后,不斷推出的新版本,不斷改進(jìn)和增強(qiáng)功能,給深度學(xué)習(xí)帶來(lái)了驚人的進(jìn)步。這也反映在我們的一系列挑選工作中。最早的三次評(píng)測(cè)【20】表明,Tensorflow完全不能和Torch,Theano和Caffe相提并論。但是僅僅兩個(gè)月后,【22】的結(jié)果已經(jīng)顯示Tensorflow追趕上來(lái),到了最后的評(píng)測(cè)【5】的時(shí)候,Tensorflow已經(jīng)處在領(lǐng)先的地位了。我們預(yù)測(cè)Tensorflow的性能還會(huì)繼續(xù)改進(jìn),前途無(wú)量。特別是在分布式處理的場(chǎng)景下,目前和可以預(yù)見(jiàn)的將來(lái),Tensorflow都將是領(lǐng)先地位。 七. Tensorflow的實(shí)際應(yīng)用 Tensorflow發(fā)布才短短六個(gè)月,學(xué)術(shù)界和工業(yè)界還沒(méi)有能夠完全擁抱Tensorflow,一些現(xiàn)有應(yīng)用的移植還需要時(shí)間,新的研究還需時(shí)日。但是一點(diǎn)毋庸置疑,谷歌已經(jīng)積極應(yīng)用和推廣Tensorflow在各種任務(wù)中【19】【25】【26】【27】【28】。我們將選擇性的看一看這些應(yīng)用是怎么使用Tensorflow的。 A. 學(xué)術(shù)界 首先提到Tensorflow的是2016年二月的文獻(xiàn)【29】,來(lái)自谷歌大腦組的Szegedy,Ioffe和Vanhoucke發(fā)表的文章。他們用Tensorflow改進(jìn)了Inception模型【19】,主要用于圖像識(shí)別。作者給出了ImageNet測(cè)試集最高5檔3.08%偏差的好成績(jī)。 在文獻(xiàn)【25】,Ramsunder等人對(duì)于藥品發(fā)現(xiàn)的多任務(wù)網(wǎng)絡(luò)的討論使用了Tensorflow,這是斯坦福大學(xué)和谷歌之間的聯(lián)合研究工作。這篇文章描述了使用Tensorflow構(gòu)建深層神經(jīng)網(wǎng)絡(luò)做虛擬掃描來(lái)選擇潛在的候選藥物。這是為了幫助醫(yī)藥公司和科研社區(qū)尋找為治療疾病的新藥。 August和Ni應(yīng)用Tensorflow創(chuàng)建了遞歸神經(jīng)網(wǎng)絡(luò)優(yōu)化動(dòng)態(tài)解耦,一種在量子內(nèi)存抑制誤差的技術(shù)?!?0】作者旨在維持量子糾纏態(tài),這是構(gòu)建通用量子計(jì)算機(jī)的關(guān)鍵需求。 最后,文獻(xiàn)【31】研究了自然語(yǔ)言處理的sequence-to-sequence模型,作者使用Tensorflow,采用滑動(dòng)窗口技術(shù)去做字符級(jí)別的英語(yǔ)到拉脫維亞語(yǔ)在音頻和視頻內(nèi)容的翻譯。作者用這個(gè)方法去分類(lèi)電視節(jié)目,廣播節(jié)目,并且聚類(lèi)單個(gè)的故事。 B. 工業(yè)界 除了谷歌之外,工業(yè)界還很少有人使用Tensorflow,至少公開(kāi)的情況來(lái)看是這個(gè)樣子。所以我們來(lái)看看谷歌的Tensorflow應(yīng)用情況。 最近,谷歌在調(diào)整它的核心業(yè)務(wù)算法PageRank【32】系統(tǒng)RankBrain【33】,使用的技術(shù)就是Tensorflow。RankBrain使用大規(guī)模分布式深度神經(jīng)網(wǎng)絡(luò)來(lái)做搜索排序。根據(jù)文獻(xiàn)【33】,超過(guò)15%發(fā)給www.google.com的查詢(xún)是全新查詢(xún)。RankBrain系統(tǒng)可以通過(guò)相似性比對(duì)來(lái)給出未知查詢(xún)建議。 另一個(gè)應(yīng)用是谷歌的智能郵件回復(fù)系統(tǒng)【27】。谷歌已經(jīng)整合了智能回復(fù)功能在其郵件系統(tǒng)的Inbox里面。系統(tǒng)使用遞歸神經(jīng)網(wǎng)絡(luò)和特定LSTM模塊進(jìn)行序列到序列的學(xué)習(xí)和自然語(yǔ)言理解。一個(gè)編碼器映射一個(gè)語(yǔ)料庫(kù)到一個(gè)“思維向量”,而解碼器則在語(yǔ)法和語(yǔ)義上合成正確的回復(fù),然后以建議的形式呈現(xiàn)給用戶(hù)。 在文獻(xiàn)【26】,谷歌在使用卷積神經(jīng)網(wǎng)絡(luò)做圖像識(shí)別和自動(dòng)文本翻譯。作為谷歌手機(jī)的內(nèi)置功能,對(duì)于用戶(hù)來(lái)說(shuō)的外文可以被算法識(shí)別,然后翻譯成用戶(hù)識(shí)別語(yǔ)言,并且呈現(xiàn)在原文所在圖像上。這種方法可以用來(lái)翻譯圖片里的街道名。文獻(xiàn)【26】特別強(qiáng)調(diào)如果部署這種算法在低端手機(jī)設(shè)備和慢速網(wǎng)絡(luò)上。因此,神經(jīng)網(wǎng)絡(luò)訓(xùn)練小模型,然后應(yīng)用于資源首先的場(chǎng)景也在發(fā)展。 最后,我們已經(jīng)注意到谷歌旗下的DeppMind,著名的AI研究機(jī)構(gòu)已經(jīng)把他們的研究平臺(tái)從Torch7轉(zhuǎn)移到Tensorflow了。【28】有消息稱(chēng)【17】,DeepMind使用Tensorflow,在谷歌新發(fā)布的張量處理單元(TPU)上訓(xùn)練他們的AlphaGo模型。該文獻(xiàn)作者就是谷歌DeepMind的雇員,揭示了為什么Tensorflow對(duì)于DeepMind有好處的四個(gè)理由: 1)Tensorflow的大規(guī)模應(yīng)用是構(gòu)建在谷歌云平臺(tái)上,可以很容易提供充足的計(jì)算力。 2)Tensorflow支持TPU這樣的硬件擴(kuò)展。 3)Tensorflow的主要編程接口是Python,這是谷歌的核心編程語(yǔ)言,要比Lua能夠得到更好的支持。 4)Tensorflow是能夠很好支持多GPU的框架。 八. 總結(jié) 我們已經(jīng)從各個(gè)方面完整的討論了Tensorflow,這種基于計(jì)算圖的開(kāi)源深度學(xué)習(xí)庫(kù)的各種特性,包括能夠快速計(jì)算梯度,它固有的支持分布式計(jì)算的特性,強(qiáng)有力可視化工具。它能夠在很細(xì)顆粒度構(gòu)建神經(jīng)網(wǎng)絡(luò),允許高度定制模型,同時(shí)也支持一些快速原型搭建的封裝庫(kù),比如TFLearn。相比Torch,Theano之類(lèi)的框架,Tensorflow增加了新特征和改進(jìn)了現(xiàn)有特性。它在性能方面的表現(xiàn),開(kāi)始不盡人意,但是隨著時(shí)間的推移,不斷的隨著新庫(kù)的發(fā)布在改進(jìn)。 我們注意到對(duì)于Tensorflow的分布式運(yùn)行性能的評(píng)測(cè)目前開(kāi)展的很少。我們認(rèn)為這是很重要的一環(huán),也是學(xué)術(shù)界需要深入研究的一點(diǎn)。 Tensorflow已經(jīng)在開(kāi)源社區(qū)取得了相當(dāng)?shù)娜藲夂蛷?qiáng)有力的第三方支持。谷歌已經(jīng)做出了明智的決定。我們相信,Tensorflow不僅僅對(duì)于它的所有者有利,也會(huì)惠及更為廣大的科研社區(qū);它會(huì)打開(kāi)通往更快更大規(guī)模的人工智能之門(mén)。 附錄-1 #!/usr/bin/env python # -*- coding: utf-8 -*- ''' A one-hidden-layer-MLP MNIST-classifier. ''' from__future__importabsolute_importfrom__future__importdivisionfrom__future__importprint_function # Import the training data (MNIST) fromtensorflow.examples.tutorials.mnistimportinput_data importtensorflow as tf # Possibly download and extract the MNIST data set. # Retrieve the labels as one-hot-encoded vectors.mnist = input_data.read_data_sets('/tmp/mnist', one_hot=True) # Create a new graph graph = tf.Graph() # Set our graph as the one to add nodes to with graph.as_default(): # Placeholder for input examples (None = variable dimension) examples = tf.placeholder(shape=[None, 784], dtype=tf.float32) # Placeholder for labels labels = tf.placeholder(shape=[None, 10], dtype=tf.float32) weights = tf.Variable(tf.truncated_normal(shape=[784, 10], stddev=0.1)) bias = tf.Variable(tf.constant(0.1, shape=[10])) # Apply an affine transformation to the input features logits = tf.matmul(examples, weights) + bias estimates = tf.nn.softmax(logits) # Compute the cross-entropy cross_entropy = -tf.reduce_sum(labels*tf.log(estimates), reduction_indices=[1]) loss = tf.reduce_mean(cross_entropy) # Create a gradient-descent optimizer that minimizes the loss. # We choose a learning rate of 0.01 optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss) # Find the indices where the predictions were correct correct_predictions = tf.equal( tf.argmax(estimates, dimension=1), tf.argmax(labels, dimension=1)) accuracy = tf.reduce_mean(tf.cast(correct_predictions, tf.float32)) with tf.Session(graph=graph) as session: tf.initialize_all_variables().run()forstepinrange(1001): # And finally the loss example_batch, label_batch = mnist.train.next_batch(100) feed_dict = {examples: example_batch, labels: label_batch} ifstep % 100 == 0: _, loss_value, accuracy_value = session.run( [optimizer, loss, accuracy], feed_dict=feed_dict )print('Loss at time {0}: {1}'.format(step, loss_value))print('Accuracy at time {0}: {1}'.format(step, accuracy_value)) optimizer.run(feed_dict) 參考文獻(xiàn) |
|
來(lái)自: 萬(wàn)皇之皇 > 《IT互聯(lián)》