三, Tensorflow編程模型
在這一章狡赐,我們將深度討論Tensorflow的計算范式钦幔。開始鲤氢,我們會研究Tensorflow的基本架構(gòu)和計算原理,解釋機器學習算法如何在Tensorflow里面使用數(shù)據(jù)流圖語言表征哨颂。接下來威恼,我們研究Tensorflow的運行模型寝并,也就是Tensorflow如何把Tensorflow圖轉(zhuǎn)化為運行狀態(tài)衬潦。然后,我們調(diào)查Tensorflow內(nèi)在的弦牡,針對軟硬件的不同算法優(yōu)化。最后喊儡,我們列舉一系列算法擴展艾猜,以支撐用戶在Tensorflow對于計算模型捻悯,邏輯模型訓練。
A. 計算圖架構(gòu)
在Tensorflow里面算柳,機器學習算法被表證為計算圖瞬项。計算圖或者數(shù)據(jù)流圖是一種有向圖何荚。有向圖的頂點或者節(jié)點代表運算過程餐塘,有向圖的邊代表運算過程之間的數(shù)據(jù)流。如下圖所示税手,先看左邊芦倒,輸出變量z是輸入x和y的二值運算的結(jié)果熙暴,那么畫兩條分別從x和y向z的有向邊慌盯,并且在z上標明運算加號亚皂。一個更加完整和復雜的數(shù)據(jù)流圖在右邊灭必。下面,對于數(shù)據(jù)流圖里面的元素(operation - 算子跟衅,tensor - 張量伶跷,variable - 變量和session - 過程)進行更為詳盡的討論秘狞。
1)Operations(算子):使用圖來表示一個算法的主要好處不僅僅是直觀展示計算模型之間的依賴和關(guān)聯(lián)烁试,而且能夠更普適的定義運算節(jié)點。在Tensorflow里靖诗,node(節(jié)點)代表著算子刊橘,也就是一種運算悼院,更精確來說据途,代表了輸入的數(shù)據(jù)在有向圖上,如何流經(jīng)這個節(jié)點【8】位衩。一個算子可以是0到多個輸入糖驴,也能產(chǎn)生0到多個輸出佛致。因此俺榆,一個算子代表一個數(shù)學等式,一個變量定嗓,一個常量,一個有向控制流凌简,一個文件I/O操作或者甚至是一個網(wǎng)絡(luò)通訊連接端口雏搂。在算子可以表達為一個常量或者變量不是像表達成一個函數(shù)那樣直觀畔派,但是一個常量可以被當成一個沒有輸入润绵,而且輸出恒定的運算尘盼。相似的情況也適用于表征一個變量,也就是沒有輸入配紫,輸出當前狀態(tài)或者當前變量的值午阵。
? ? ? ? 任何算子必須要嚴格的定義和實現(xiàn)。在論文【8】的研究植袍,任何一種實現(xiàn)被當作一個算子的核函數(shù)于个。一種特定的核函數(shù)的具體編程都是針對某種硬件厅篓,比如CPU或者GPU的運算捶码。
2)Tensors(張量):在Tensorflow里面,代表數(shù)據(jù)流從一個算子流向另一個算子的邊稱之為張量档押。一個張量是一個具有固定類型同類數(shù)據(jù)的多維集合汇荐。張量維度也稱為rank(階)掀淘。張量的形狀(shape)描述張量大小的元祖(tuple)革娄。比如冕碟,對于每一個維度的元素個數(shù)。從數(shù)學的觀點來看厕妖,張量是一個二維矩陣加一個表達張量階的一維向量或者標量的生成物言秸。
? ? ? ? 從計算圖的觀點來看迎捺,張量是表征通向輸出的算子的符號把手(symbolic handle)凳枝。在內(nèi)存里岖瑰,張量本身并不包含和存儲數(shù)據(jù)锭环,但是它提供了張量代表的數(shù)據(jù)訪問的接口。在Tensorflow里面創(chuàng)建一個算子的時候难礼,比如x+y蛾茉,返回一個張量對象撩鹿。然后,這個張量可能作為其他計算的輸入础爬,從而張量是連接源算子和目的算子的邊看蚜」┭祝基于這樣的認知疾党,數(shù)據(jù)在Tensorflow的圖中川流不息。
? ? ? ? 除了通常意義的張量竭钝,Tensorflow還支持一種稱之為稀疏張量(SparseTensor)的數(shù)據(jù)結(jié)構(gòu)蜓氨。這是一種空間有效字典類的數(shù)據(jù)表證穴吹,也就是大量零值的稀疏張量嗜侮。
3)Variables(變量):在通常情況,比如做隨機梯度下降的單次運算的時候顷霹,機器學習模型的圖會從開始到結(jié)束反復運算多次淋淀。在兩次調(diào)用之間覆醇,圖中主要的張量并不會被保存永脓。但是整體上對于圖的求值是需要保存狀態(tài)的常摧,比如神經(jīng)網(wǎng)絡(luò)的權(quán)重和參數(shù)威创。因此肚豺,變量正是為了滿足這一需求而創(chuàng)建的算子详炬,能夠被添加到計算圖中。
? ? ? ? 變量可以看成是在內(nèi)存中持久不變的張量副本枪萄。因此瓷翻,變量的定義需要形狀和固定數(shù)據(jù)類型兩個特征齐帚。Tensorflow提供了一系列賦值函數(shù)完成圖運算对妄。
? ? ? ? 在Tensorflow的圖中創(chuàng)建一個變量節(jié)點的時候剪菱,需要定義相應的張量拴签,這樣在圖運行時蚓哩,變量可以隨之初始化。變量的形狀和數(shù)據(jù)類型也來自于這個初始器喜颁。有趣的是洛巢,變量自己并不存儲這個初始的張量次兆,相反構(gòu)造一個變量會增加三種不同的節(jié)點:
? ? ? ? 1)變量節(jié)點稿茉,保存持久狀態(tài)。
? ? ? ? 2)保存初始值的算子,通常是一個常量漓库。
? ? ? ? 3)初始器算子恃慧,在圖求值的時候把初始值賦值給變量張量。
? ? ? ? 一個例子如下圖所示:三個節(jié)點代表變量定義渺蒿。第一個變量v是一個變量的張量值在內(nèi)存中的持久副本痢士。第二個變量i是給變量提供初始值(可以是任意張量)的節(jié)點。最后一個賦值節(jié)點把初始值賦給變量茂装,在賦值節(jié)點產(chǎn)生一個新的張量具有初始值的變量v‘怠蹂。這樣,v'可能作為另外算子的一個輸入少态。
4)Session(會話):在Tensorflow里面侨歉,算子的運算和張量的估值會在一定的上下文中進行炮温,我們稱之為Session(會話)。會話的責任之一就是將分配和管理資源的工作封裝起來,比如緩存變量。更進一步來看祖很,Tensorflow庫里面的Session接口提供了一個run函數(shù)骨稿,作為整個圖計算的執(zhí)行入口形耗。這個方法將輸入節(jié)點帶入整個圖計算的過程拟糕,并且根據(jù)圖定義返回相應的結(jié)果累澡。另外哼蛆,一個可選的映射,即從任意節(jié)點到相應替代值的映射,被稱為feed nodes(反饋節(jié)點),可能也被run調(diào)用【8】腾节。
? ? ? ? 在調(diào)用run的時候,Tensorflow將會出輸出節(jié)點開始反向分析計算圖的節(jié)點依賴,計算所有節(jié)點的傳遞閉包同辣。這些節(jié)點可能被分配到一個或者多個物理計算單元(CPU,GPU等),這些計算單元可以在一臺或者多臺機器上。分配的規(guī)則由Tensorflow的placement algorithm(分配算法)定義。這個算法會在本文后面部分談到铡原。此外卵洗,因為存在節(jié)點評估順序的特定顯式可能性,姑且稱為控制依賴關(guān)系,執(zhí)行算法將確保這些依賴關(guān)系不變。
B. 執(zhí)行模型
? ? ? ? 如剛剛討論的那樣,對于執(zhí)行各種計算圖元素的組成,TensorFlow劃分其任務(wù)在四個不同的組中實現(xiàn):客戶端信不,主控端锰什,一組工作進程和一些設(shè)備嫩码。 當客戶端通過會話的run調(diào)用請求評估一個TensorFlow圖,這個請求被發(fā)送到主控端,主控端將任務(wù)委托給一個或多個工作進程處理和協(xié)調(diào)它們的執(zhí)行墙牌。 每個工作進程隨后負責一個或多個設(shè)備真實的運算和處理操作。
? ? ? ? 在這個模型中寄月,有兩個擴展度忱辅。 第一擴展度是關(guān)于執(zhí)行圖運算機器的數(shù)量损搬。 事實上伞鲫,第二擴展度指的是在每臺機器上吠架,可能會有更多設(shè)備魂仍,例如睁搭,五個獨立的GPU和/或三個CPU。 為此巍耗,存在兩個“版本”的TensorFlow,一個用于在單個機器上本地執(zhí)行(但可能有許多設(shè)備)霎迫,一個支持分布式在許多機器和許多設(shè)備上實現(xiàn)。
? ? ? ? 上圖展示了執(zhí)行模型和響應擴展度花墩。而且Tensorflow在開始發(fā)布的時候懂缕,確實只公布了單機版本弱睦,而多機版本是在2016年的4月13日才姍姍來遲【16】。
? ? ? 1)Devices(設(shè)備):設(shè)備是在TensorFlow執(zhí)行模型中最小,最基本的實體寿弱。 上圖中的所有節(jié)點筐咧,也就是每個算子的內(nèi)核,最終都必須是映射到要執(zhí)行的可用設(shè)備。 在實際執(zhí)行過程中,設(shè)備通常是CPU或GPU鸠删。 然而烘贴,TensorFlow能夠支持更多種類的物理執(zhí)行單元卫键。 例如惕蹄,在2016年5月,谷歌宣布其定制的ASIC(專用集成電路)張量處理單元(TPU)治专,是專門用于快速張量計算[17]卖陵。 因此,Tensorflow是可以容易地集成新出現(xiàn)的設(shè)備類新型硬件张峰。
? ? ? ? 為了整體評估在某個設(shè)備上的節(jié)點泪蔫,主控端生成相應的工作進程。 作為工作進程可以在單個機器上管理一個或多個設(shè)備喘批,設(shè)備是不僅通過名稱標識撩荣,還標識其所在工作進程組的索引。 例如饶深,特定組中的第一CPU可以由字符串“/ cpu:0”標識餐曹。
? ? ? ? 2)placement algorithm(分配算法):為了決定哪一個節(jié)點分配給那一個設(shè)備,Tensorflow使用了一種特定分配算法敌厘。該算法模擬計算圖的運算和遍歷從輸入張量到輸出張量的全部節(jié)點台猴。在遍歷節(jié)點過程中,要決定哪一個可用設(shè)備D={d1,d2,...dn}運行給定節(jié)點v俱两,算法使用一個成本模型Cv(d)饱狂。這個成本模型考慮四種信息來決定最優(yōu)設(shè)備d = arg mind∈D Cν(d):
? ? ? ? 1)在給定設(shè)備上是否存在該節(jié)點的實現(xiàn)(核函數(shù))。比如锋华,如果任何一個GPU上都不存在某種特定算子嗡官,那么選擇任意GPU的成本都是無限的箭窜。
? ? ? ? 2)估計一個節(jié)點的輸入和輸出丈量的大刑夯馈(按字節(jié)計算)。
? ? ? ? 3)給定設(shè)備對于核函數(shù)的期望計算時間。
? ? ? ? 4)對于輸入丈量到相應算子跨設(shè)備或者跨機器的傳輸成本進行啟發(fā)式估算纳猫,萬一該節(jié)點已經(jīng)賦值的輸入張量所在設(shè)備和當前設(shè)備并不一樣婆咸。
? ? 3)cross-device execution(跨設(shè)備運行):只要是用戶擁有多個設(shè)備,Tensorflow通常都會把節(jié)點分配到這些設(shè)備上去芜辕。這個過程是通過把節(jié)點分類尚骄,然后一類分配到一個設(shè)備。這樣分配的話侵续,必須處理跨設(shè)備分配的節(jié)點依賴問題倔丈。讓我們考慮A和B這樣兩個設(shè)備,其中節(jié)點v在設(shè)備A上状蜗。如果v的輸出張量作為另外兩個算子α, β的輸入需五,同時α, β在設(shè)備B上。那么就存在從A到B的跨設(shè)備的邊ν → α 和 ν → β轧坎。如下圖所示:
? ? ? ? 在實際運行中宏邮,需要使用一些傳輸手段完成v的輸出張量從A設(shè)備,比如GPU缸血,到設(shè)備B蜜氨,比如CPU的過程。如下圖所示捎泻,send和recv兩類節(jié)點被生成完成這個傳輸過程飒炎。
? ? ? ? 最后,Tensorflow使用“規(guī)范化”來優(yōu)化(send笆豁,recv)對厌丑。在上圖所示的例子中,我們看到兩個recv節(jié)點渔呵,分別連接α, β怒竿。然而,一種等價的扩氢,但是更為有效的辦法就是在設(shè)備B上只運行一個recv耕驰,如下圖所示。