TensorFlow架構(gòu)

TensorFlow原文
又是好久沒有寫博客了,上班以來,感覺時間過得飛快,每天時間很緊,過得有點累,不知道自己的博客能堅持到何時,且行且珍惜。

本片博文是參考文獻[1]的閱讀筆記,特此聲明
TensorFlow,以下簡稱TF,是Google去年發(fā)布的機器學習平臺,發(fā)布以后由于其速度快,擴展性好,推廣速度還是蠻快的。江湖上流傳著Google的大戰(zhàn)略,Android占領(lǐng)了移動端,TF占領(lǐng)神經(jīng)網(wǎng)絡(luò)提供AI服務(wù),未來的趨勢恰好是語音圖像以及AI的時代,而Google IO上發(fā)布的Gbot似乎正是這一交叉領(lǐng)域的初步嘗試。

TF的特點之一就是可以支持很多種設(shè)備,大到GPU、CPU,小到手機平板,五花八門的設(shè)備都可以跑起來TF。不得不說這一點很有前瞻性,可以預見的是,mobile-end的用戶將會享受到越來越多的AI服務(wù)。說個極端,說不定以后某天,單機版的AlphaGo會出現(xiàn)也是可以的。
話不多說,開始正文。

Basic Concepts

張量(Tensor)

名字就是TensorFlow,直觀來看,就是張量的流動。張量(tensor),即任意維度的數(shù)據(jù),一維、二維、三維、四維等數(shù)據(jù)統(tǒng)稱為張量。而張量的流動則是指保持計算節(jié)點不變,讓數(shù)據(jù)進行流動。這樣的設(shè)計是針對連接式的機器學習算法,比如邏輯斯底回歸,神經(jīng)網(wǎng)絡(luò)等。連接式的機器學習算法可以把算法表達成一張圖,張量從圖中從前到后走一遍就完成了前向運算;而殘差從后往前走一遍,就完成了后向傳播。

算子(operation)

在TF的實現(xiàn)中,機器學習算法被表達成圖,圖中的節(jié)點是算子(operation),節(jié)點會有0到多個輸出,下圖是TF實現(xiàn)的一些算子。



每個算子都會有屬性,所有的屬性都在建立圖的時候被確定下來,比如,最常用的屬性是為了支持多態(tài),比如加法算子既能支持float32,又能支持int32計算。

核(kernel)

TF中還有一個概念是kernel,kernel是operation在某種設(shè)備上的具體實現(xiàn)。TF的庫通過注冊機制來定義op和kernel,所以可以通過鏈接一個其他的庫來進行kernel和op的擴展。

邊(edge)

TF的圖中的邊分為兩種:
正常邊,正常邊上可以流動數(shù)據(jù),即正常邊就是tensor
特殊邊,又稱作控制依賴,(control dependencies) 沒有數(shù)據(jù)從特殊邊上流動,但是特殊邊卻可以控制節(jié)點之間的依賴關(guān)系,在特殊邊的起始節(jié)點完成運算之前,特殊邊的結(jié)束節(jié)點不會被執(zhí)行。
也不僅僅非得有依賴關(guān)系才可以用特殊邊,還可以有其他用法,比如為了控制內(nèi)存的時候,可以讓兩個實際上并沒有前后依賴關(guān)系的運算分開執(zhí)行。
特殊邊可以在client端被直接使用

會話(Session)

客戶端使用會話來和TF系統(tǒng)交互,一般的模式是,建立會話,此時會生成一張空圖;在會話中添加節(jié)點和邊,形成一張圖,然后執(zhí)行。
下圖有一個TF的會話樣例和所對應(yīng)的圖示。



變量(Variables)

機器學習算法都會有參數(shù),而參數(shù)的狀態(tài)是需要保存的。而參數(shù)是在圖中有其固定的位置的,不能像普通數(shù)據(jù)那樣正常流動。因而,TF中將Variables實現(xiàn)為一個特殊的算子,該算子會返回它所保存的可變tensor的句柄。

Implementation

首先是實現(xiàn)中的幾個部分:
TF中最重要的Tensor被支持的非常全面,8bit到64bit, signed和unsigned,IEEE float/double,complex number等等。使用引用計數(shù)來保存tensor,當計數(shù)到0時,tensor被回收。
客戶端,用戶會使用;與master和一些worker process交流
master,用來與客戶端交互,同時調(diào)度任務(wù);
worker process,工作節(jié)點,每個worker process可以訪問一到多個device。
device,TF的計算核心,通過將device的類型、job名稱、在worker process中的索引將device命名。可以通過注冊機制來添加新的device實現(xiàn),每個device實現(xiàn)需要負責內(nèi)存分配和管理調(diào)度TF系統(tǒng)所下達的核運算需求。

TF的實現(xiàn)分為了單機實現(xiàn)和分布式實現(xiàn),在分布式實現(xiàn)中,需要實現(xiàn)的是對client,master,worker process不在同一臺機器上時的支持。此時,關(guān)于這些進程的調(diào)度,使用的是原始論文中參考文獻51的調(diào)度方式。關(guān)于分布式和單機的不同如下圖所示:



Single-Device Execution
構(gòu)建好圖后,使用拓撲算法來決定執(zhí)行哪一個節(jié)點,即對每個節(jié)點使用一個計數(shù),值表示所依賴的未完成的節(jié)點數(shù)目,當一個節(jié)點的運算完成時,將依賴該節(jié)點的所有節(jié)點的計數(shù)減一。如果節(jié)點的計數(shù)為0,將其放入準備隊列待執(zhí)行

Multi-Device Execution
當系統(tǒng)到了分布式情況下時,事情就變得復雜了很多,還好前述調(diào)度用了現(xiàn)有的框架。那么對于TF來說,剩下的事情就是:
決定運算在哪個設(shè)備上運行
管理設(shè)備之間的數(shù)據(jù)傳遞

決定設(shè)備

使用一個cost model算法來進行預估時間,計算后使用貪心算法來分配設(shè)備。在決定設(shè)備的時候,也可以預先設(shè)置一些約束,比如,某個op只能在GPU上執(zhí)行等。
預估時間有兩種方法:
使用啟發(fā)式的算法,通過把輸入和輸出的類型以及tensor的大小輸入進去,得到時間的預估
使用模擬的方法,對圖的計算進行一個模擬,得到各個計算在其可用的設(shè)備上的時間。

尋找合適設(shè)備是TF系統(tǒng)區(qū)分與之前很多系統(tǒng)的地方,之前的系統(tǒng)比如Parameter Server,是參數(shù)分離出來,運算在一起,同時使用數(shù)據(jù)切分來達到分布式。而TF是把每個op都映射到某個機器上,意味著每個op可能在不同的機器上,這是對系統(tǒng)的進一步剖離,因而可以達到更高的可擴展性。

跨設(shè)備通信

當兩個需要通信的op在不同的機器上時,就需要跨設(shè)備通信,當它們需要通信時,TF會在它們之間的聯(lián)系中添加Send和Recv節(jié)點,通過Send和Recv之間進行通信來達到op之間通信的效果。如下所示:



為了優(yōu)化網(wǎng)絡(luò)通信,TF會將相同的數(shù)據(jù)傳送合并,如a->b和a->c的傳送合并,這一點可以通過Send和Recv很方便的實現(xiàn)。而通過實現(xiàn)Send和Recv,將master節(jié)點的通信調(diào)度任務(wù)解放出來,master就只需要向圖中的各個節(jié)點發(fā)出運行命令就夠了,增加了系統(tǒng)的可擴展性。
Send和Recv通過TCP或RDMA來傳輸數(shù)據(jù)
錯誤處理
在分布式系統(tǒng)中,常見的錯誤來自于兩個方面:
Send/Recv的網(wǎng)絡(luò)傳輸
master和worker process之間的心跳同步

當錯誤發(fā)生的時候,TF會將整個圖的計算停止,并從上一次保存的狀態(tài)重新執(zhí)行。為了保存狀態(tài),每個Variable的節(jié)點都去連接一個Save的節(jié)點。這些save節(jié)點會每隔一段時間或每隔幾次迭代運行一次。
自從TF將op剖離之后,所有的策略都依賴于節(jié)點來實現(xiàn),Variable利用節(jié)點實現(xiàn),狀態(tài)保存也用節(jié)點實現(xiàn)。感覺還是很不一樣的。 一個節(jié)點出了錯誤,要停掉整個圖的計算,我覺得這樣的恢復模式會不會代價太大?

Extensions

Gradient Computation(梯度計算)

連接式的機器學習算法往往需要使用梯度下降法來求取參數(shù),TF通過擴展圖的方式實現(xiàn)了自動求導,TF做法如下:
對于每張計算圖,得到從輸入I到輸出C的路徑,并從C到I回溯,回溯過程中對于路徑上的每個節(jié)點A,添加另一個節(jié)點來計算A’來計算偏導,在計算偏導的過程中,A’不僅僅將上一層傳下來的反向?qū)?shù)作為輸入,還可能將A的輸入和輸出也作為其輸入。


在執(zhí)行前向計算的時候,啟發(fā)式的優(yōu)化算法通過觀察圖中的節(jié)點的計算順序,來決定哪種操作放在哪個節(jié)點上,從而幫助用戶來內(nèi)存重用;當啟發(fā)式的算法無效的時候,用戶還可以通過添加控制依賴來自行實現(xiàn)內(nèi)存上的優(yōu)化。
而當反向傳播加入的時候,事情變得有點復雜,在正向計算中較前位置的計算數(shù)據(jù)在反向傳播的后期會被經(jīng)常用到。這就需要把這些數(shù)據(jù)存在內(nèi)存中,從而整個圖的內(nèi)存都將被占用,使得本來就少的GPU內(nèi)存更加的捉襟見肘。
有三種方法來對其進行優(yōu)化:
更加復雜的啟發(fā)式算法來決定圖的計算順序
重新計算這些向量而不是保存下來
將長期在GPU內(nèi)存中的tensor轉(zhuǎn)移到CPU內(nèi)存中

Partial Execution(局部執(zhí)行)
TF支持部分執(zhí)行,對于多輸出的TF圖,可能用戶只想獲取一個輸出,此時可以指定需要的輸入(feed)和輸出(fetch)值。然后TF會自動計算該輸出的依賴,然后只計算必要的部分。



如上圖所示,指定b為輸入,f為輸出,那么此時d、e是不會被計算的。
Control Flow(控制流)
雖然TF的圖已經(jīng)有了很強的表達能力,但還不夠,還需要控制流的表達,比如已經(jīng)實現(xiàn)的Switch、Merge、Enter、Leave和NextIteration等一系列控制算子。
TF使用和MIT Token-Tagged machine相似的表示系統(tǒng),將循環(huán)的每次迭代標記為一個tag,迭代的執(zhí)行狀態(tài)標記為一個frame,但迭代所需的數(shù)據(jù)準備好的時候,就可以開始計算,從而多個迭代可以同時執(zhí)行。
對于分布式系統(tǒng)來說,控制循環(huán)的狀態(tài)是一個大問題。而TF使用圖重寫的方式來實現(xiàn)它,在圖切分的時候,添加一個小的狀態(tài)機來監(jiān)控迭代的開始和結(jié)束,
而對于有梯度計算的圖來說,在有控制流的情況下,需要記錄各種狀態(tài),比如對于if算子,需要記錄哪個分支被運行了;而對于循環(huán),需要記錄循環(huán)了幾次。TF仍然使用圖重寫來實現(xiàn)記錄狀態(tài)的功能,細節(jié)不贅述了。
Input Operations(輸入操作)
為Input也構(gòu)建了一個Node,來管理數(shù)據(jù)從硬盤到內(nèi)存的過程。往往需要提前將數(shù)據(jù)讀入進來以減少內(nèi)存瓶頸。
Queues(隊列)
TF實現(xiàn)了一個隊列來支持異步操作,EnQueue可以阻塞直到隊列中的空間足夠;DeQueue也可以阻塞直到隊列中一系列的要求得到滿足。
隊列有兩個典型應(yīng)用:
讀入數(shù)據(jù),數(shù)據(jù)在隊列中,這樣可以達到數(shù)據(jù)處理和數(shù)據(jù)載入的并行
梯度的累加,讓梯度存儲在隊列中,直到隊列中的梯度積累到一定的數(shù)值,這樣可以達到多個mini-batch組成一個大的batch
句子的聚合,對LSTM中的輸入句子按長度來進行聚合,統(tǒng)一計算以提高效率。

除了FIFO隊列外,TF還實現(xiàn)了一個shuffle隊列。
Containers(容器)
普通的Cotainer可以長期的存儲可變狀態(tài),但Container不止于此,用Container,甚至不同的會話中的圖之間也可以通過Container來共享狀態(tài)。
Optimizaiton
TF給了用戶以極其易用的接口,這就需要底層來自動的做很多優(yōu)化。
Common Subexpression Elimination
用戶給出的圖定義中可能會存在重復的計算操作,TF使用Click(原始論文參考文獻12)中的算法來進行圖的重復表達式的刪減
Controlling Data Combination and Memory Usage
對于復雜的網(wǎng)絡(luò)模型,GPU是必須的;而對于GPU來說,它的內(nèi)存是不足的,因而要用良好的管理來提高GPU內(nèi)存的使用效率。
在這一點上,TF主要關(guān)注數(shù)據(jù)的網(wǎng)絡(luò)傳輸,這主要集中在Recv節(jié)點何時去遠程讀取數(shù)據(jù),TF會自動分析圖上的關(guān)鍵路徑,通過設(shè)置依賴的方式來使得非關(guān)鍵路徑上的數(shù)據(jù)傳輸如何不影響關(guān)鍵路徑。
Asynchronous Kernels
異步核在執(zhí)行后立即返回,同時會執(zhí)行一個回調(diào)函數(shù)。這樣,可以防止等待計算完成的同時眼看著沒有做的IO任務(wù)也不做。即異步核也可以提升并行能力。異步核的典型樣例就是Recv節(jié)點和Enqueue和Dequeue操作。
Optimized Libraries for Kernel Implementations
對于已經(jīng)存在的線性代數(shù)庫自然是要利用的,但TF團隊對一些庫還擴展了其對任意維度的tensor的支持。
常見的線性計算庫包括:
BLAS、cuBLAS,在很多設(shè)備上都優(yōu)化了矩陣乘法
cuda-convnet、CuDNN,在GPU上優(yōu)化

Lossy Compression
在數(shù)據(jù)傳輸過程中,為了加快傳輸效率,往往需要對精度進行壓縮。在TF中,傳輸之前將32bit的float轉(zhuǎn)變?yōu)?6float,在傳輸完之后再轉(zhuǎn)回來,轉(zhuǎn)回來時用0補齊。
Common Programming Idioms
上面講的都是一些系統(tǒng)級別的優(yōu)化,還有一些機器學習算法所用到的技巧。這里假定用戶都用SGD來求解機器學習算法。
Data Parallel Training
通過數(shù)據(jù)并行的方式來提升SGD的效率,比如,假如每次SGD的mini-batch是1000個樣本,那么,切成10份,每份100個,然后將模型復制10份,每份都將梯度傳到參數(shù)服務(wù)器上。
數(shù)據(jù)切分也分為同步和異步兩種方式,同步的切分是等待每個獨立的model傳上來的梯度都到齊后再進行更新,這樣和一個大的batch沒有區(qū)別。異步的切分則是不用等待,每個獨立的模型的參數(shù)更新直接更新。
如下圖所示:



Model Parallel Training
還可以對模型進行切分,讓模型的不同部分執(zhí)行在不同的設(shè)備上,這樣可以一個迭代的樣本可以在不同的設(shè)備上同時執(zhí)行。如下圖所示的LSTM模型:



Concurrent Steps for Model Computation PipeLine
為了充分利用同一臺設(shè)備的計算能力,TF會盡量讓相鄰的計算在同一臺設(shè)備上,這樣可以節(jié)省網(wǎng)絡(luò)開銷,比如對于模型并行來說,如果放在同一個設(shè)備上,如下圖所示:

** 我個人覺得這是TF區(qū)分與Parameter Server的一個大區(qū)別 **,對于TF來說,計算節(jié)點是分離的,參數(shù)也是分離的,因而其PS也是分離的。每個設(shè)備上可能分配了計算節(jié)點,然后其對應(yīng)的ps也在該設(shè)備上。因而,傳統(tǒng)的PS中,計算和參數(shù)是分開的,但計算和參數(shù)他們分別是在一起的;而TF中,計算本身是分離的,參數(shù)也是分離的,而計算和參數(shù)是在一起的。

Related Work

TF出現(xiàn)之前,已經(jīng)有很多的類似的平臺了。
Theano
Torch
Caffe
Chainer
Computational Network

可以這么說,TF從他們每一個中都吸取了一些feature和設(shè)計思想。根據(jù)進化論的觀點,我不能說TF要優(yōu)于他們,而要說,TF更能適應(yīng)當前的需求。在之前寫Parameter Server的時候我就隱隱的感覺到,一種設(shè)計的產(chǎn)生是和它的需求緊緊相關(guān)的,TF的設(shè)計可能會有很多人想到,但TF卻只能由google設(shè)計和實現(xiàn),因為需求。而TF的產(chǎn)生也是google大一統(tǒng)移動和PC和Server的戰(zhàn)略需求。
TF的易用性、跨平臺能力是其功能亮點,而其可擴展性和高效性則是其根基。不知TF一出,下一代的平臺會是什么樣子? 還是說平臺的演化是否到此就像android和iOS那樣已經(jīng)比較成熟了?
而我更感興趣的其實是,對于這樣一個大的平臺,大哥是怎么調(diào)試的呢? 恐怕這是程序員的能力所在。扯遠了。

TF與其他平臺的區(qū)別于聯(lián)系:

支持符號推導,如Theano和Chainer
使用C++寫內(nèi)核,從而跨平臺部署很方便,如Caffe,
支持跨設(shè)備計算,讓用戶高層表達模型,如Adam和Distbelief,但比Adam和DistBelief優(yōu)越的是,更有彈性,支持更多的模型。
相對于Adam、DistBelief和Parameter Server,TF把參數(shù)節(jié)點化,更新操作也是圖中的一個節(jié)點。而Adam等三個會有一個獨立的Parameter Server。
Halide擁有和TF相似的中間表達但卻有更高級的語義表示,在并行方面優(yōu)化的更多,但卻是單機的,TF有意向向此方向發(fā)展,將Halide的東西添加到TF中來。

其他還有很多像TF那樣支持數(shù)據(jù)流圖的分布式平臺,比如:
Dryad、Flume、CIEL(數(shù)據(jù)流調(diào)度算法從此借鑒而來)
Naind(分布式架構(gòu)非常像)
Spark、Naind(在內(nèi)存足夠的情況下,TF和他們一樣運行的很好)
Dandelion(跨設(shè)備)
TF的迭代是混合的方法:多個數(shù)據(jù)流但卻一次執(zhí)行,同時共享狀態(tài)。多個數(shù)據(jù)流通過變量來共享,或使用圖中的同步機制,比如隊列。TF還支持圖內(nèi)的迭代,這是CIEL和Naiad的混合,簡單來說,像CIEL一樣每個節(jié)點只有當準備好之后才會啟動;但為了效率整張圖則表示為一個靜態(tài)的循環(huán)的圖,像Naiad一樣。

TF內(nèi)的迭代似乎是很重要的一個點,但論文中含糊不清,想必機器學習算法中的控制流都是很糾結(jié)的。

Reference
[1]. Abadi M, Agarwal A, Barham P, et al. Tensorflow: Large-scale machine learning on heterogeneous distributed systems[J]. arXiv preprint arXiv:1603.04467, 2016.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 開源書籍 關(guān)于TensorFlow的內(nèi)核詳細原理與運行機制,請查閱我的開源技術(shù)書籍:TensorFlow內(nèi)核剖析。...
    劉光聰閱讀 39,709評論 4 109
  • TensorFlow的系統(tǒng)結(jié)構(gòu)以C API為界,將整個系統(tǒng)分為「前端」和「后端」兩個子系統(tǒng)。前端系統(tǒng)扮演了Clie...
    劉光聰閱讀 15,997評論 8 39
  • 今天是2016年5月8日,母親節(jié)。 老媽還是像往常一樣的忙碌。早上擇菜,然后緊接著就是開始準備午飯…… 我看著日歷...
    莫殮春傷閱讀 300評論 0 0
  • 行人在雨里 而你在我心里 夢里的故事里有你 醒來的生活里也有你 清早的晨曦中有你 落日的余暉中也有你 寒亭海邊的傳...
    儷曦張閱讀 319評論 0 0
  • 愿上帝賜給我勇氣,讓我改變那些可以改變的事; 愿上帝賜給我平和,讓我接受那些我無法改變的事; 愿上帝賜給我智慧,讓...
    嘟喵喵閱讀 312評論 0 0

友情鏈接更多精彩內(nèi)容