【Tensorflow_DL_Note9】Tensorflow原始碼解讀1
一 摘要
2015年11月9日,Google釋出深度學習框架Tensorflow並宣佈開源,迅速得到廣泛的關注,在【影象分類】、【音訊處理】、【推薦系統】和【自然語言處理】等場景下大面積被推廣。Tensorflow系統更新的速度非常之快,官方文件的教程也比較齊全,上手快速,簡單易用,支援Python和C++介面。本文依據對Tensorflow(簡稱TF)的白皮書、TF Github和TF官方教程的簡單理解,從系統和程式碼實現的角度講解了TF的內部實現原理。以Tensorflowr0.8版本為基礎,由淺入深的闡述Tensor和Flow的 概念:
【1】首先介紹Tensorflow的核心概念和基本概述
【2】然後剖析OpKernels模組、Graph模組、Session模組
現在已經發展出來的版本有Tensorflow r0.8,Tensorflow r1.2,Tensorflow r1.3,1.4,15,1.6,1.6,Tensorflow r1.8版本。
二 TF系統架構
2.1 TF依賴檢視
TF的依賴檢視如下圖所示,描述了TF的上下游關係鏈。
【1】TF託管在github平臺上,由Google grouos和Contributors共同維護
【2】TF提供了豐富的深度學習相關的API呼叫,支援Python和C/C++語言
【3】TF提供了視覺化的分析工具【Tensorboard】方便分析和調整模型
【4】TF支援Linux平臺,Windows平臺,Mac平臺,甚至手機移動裝置等平臺
2.2 TF依賴檢視
下圖是TF的系統架構,從底向上分為【裝置管理和通訊層】、【資料操作層】、【圖計算層】、【API介面層】、【應用層】。其中裝置管理和通訊層、資料操作層、圖計算層是TF的核心層。
TF中的底層裝置通訊層負責網路通訊和裝置管理。裝置管理可以實現TF裝置異構的特性,支援CPU、GPU、Mobile等不同裝置。網路通訊依賴gRPC通訊協議實現不同裝置之間的資料傳輸和更新。
第二層是Tensor的OpKernels實現。這些OpKernels以Tensor為處理物件,依賴網路通訊和裝置記憶體分配,實現了各種Tensor操作或計算。Opkernels不僅包含Matmul等計算操作,還包含Queue等非計算操作。
第三層是圖計算層Graph,包含【本地計算流圖】和【分散式計算流圖】的實現。Graph模組包含Graph的建立、編譯、優化和執行等部分,Graph中的每個節點都是OpKernels型別表示。
第四層是API介面層。Tensor C API事對TF功能的介面封裝,便於其他語言平臺的呼叫。
第四層以上的是應用層。不同程式語言在應用層通過API介面層呼叫TF核心功能實現相關實驗和應用。
2.3 TF程式碼目錄組織
下面將簡單介紹TF的目錄組織結構。
如果Tensorflow是在Windows下安裝的,其目錄的結構如下所示:
Tensorflow/core目錄包含了TF核心模組程式碼:
【1】public:API介面標頭檔案目錄,用於外部介面呼叫的API頂一下,主要是session.h和tensor_c.api.h
【2】client:API介面實現檔案目錄
【3】platform:OS系統相關的介面檔案,如file system,env等
【4】prtobuf:均為.proto檔案,用於資料傳輸時的結構序列化
【5】common_runtime:公共執行庫,包含session,executor,threadpool,rendezvous,memory管理,裝置分配演算法等
【6】distributed_runtime:分散式執行模組,如rpc session,rpc master,rpc worker等
【7】framework:包含基礎功能模組,如log,memory,tensor等
【8】graph:計算流圖的相關操作,如construct,optimize等
【9】kernels:核心Operation,如matmul,conv2d,argmax,batch_norm等
【10】lib:公共基礎庫
【11】ops :基本的ops運算,ops梯度運算,io相關的ops,控制流和資料流操作
【12】tensorflow/contrib目錄是contributor開發的
【13】Tensorflow/python目錄是Python API客戶端指令碼
【14】Tensorflow/tensorboard目錄是視覺化分析工具,不僅可以模型視覺化,還可以監控模型引數的變化
【15】third_party目錄是TF第三方依賴庫
【16】eigen3:eigen 矩陣運算庫,TF基礎ops呼叫
【17】gpus:封裝了cuda/cudnn程式設計庫
三 TF核心概念
在數學上,Matrix表示二維的線性對映,Tensor表示多維的線性對映,Tensor是對Matrix的泛華,可以表示1-dim、2-dim、N-dim的高維矩陣。
圖4 Tensor contract
Tensor在高維空間數學運算比Matrix計算複雜,計算量也非常大,加速張量並行運算是TF優先考慮的問題,如add, contract, slice, reshape, reduce, shuffle等運算。
TF中Tensor的維數描述為階,數值是0階,向量是1階,矩陣是2階,以此類推,可以表示n階高維資料。
TF中Tensor支援的資料型別有很多,如tf.float16, tf.float32, tf.float64, tf.uint8, tf.int8, tf.int16, tf.int32, tf.int64, tf.string, tf.bool, tf.complex64等,所有Tensor運算都使用泛化的資料型別表示。
TF的Tensor定義和運算主要是呼叫Eigen矩陣計算庫完成的。TF中Tensor的UML定義如圖4。其中TensorBuffer指標指向Eigen::Tensor型別。其中,Eigen::Tensor[5][6]不屬於Eigen官方維護的程式,由貢獻者提供文件和維護,所以Tensor定義在Eigen unsupported模組中。
圖5 Tensor資料結構定義
圖5中,Tensor主要包含兩個變數m_data和m_dimension,m_data儲存了Tensor的資料塊,T是泛化的資料型別,m_dimensions儲存了Tensor的維度資訊。
Eigen:Tensor的成員變數很簡單,卻支援非常多的基本運算,再借助Eigen的加速機制實現快速計算,參考章節3.2。Eigen::Tensor主要包含了
一元運算(Unary),如sqrt、square、exp、abs等。
二元運算(Binary),如add,sub,mul,div等
選擇運算(Selection),即if / else條件運算
歸納運算(Reduce),如reduce_sum, reduce_mean等
幾何運算(Geometry),如reshape,slice,shuffle,chip,reverse,pad,concatenate,extract_patches,extract_image_patches等
張量積(Contract)和卷積運算(Convolve)是重點運算,後續會詳細講解。
2.2 符號程式設計
程式設計模式通常分為指令式程式設計(imperative style programs)和符號式程式設計(symbolic style programs)。
指令式程式設計容易理解和除錯,命令語句基本沒有優化,按原有邏輯執行。符號式程式設計涉及較多的嵌入和優化,不容易理解和除錯,但執行速度有同比提升。
這兩種程式設計模式在實際中都有應用,Torch是典型的命令式風格,caffe、theano、mxnet和Tensorflow都使用了符號式程式設計。其中caffe、mxnet採用了兩種程式設計模式混合的方法,而Tensorflow是完全採用了符號式程式設計,Theano和Tensorflow的程式設計模式更相近。
指令式程式設計是常見的程式設計模式,程式語言如python/C++都採用指令式程式設計。指令式程式設計明確輸入變數,並根據程式邏輯逐步運算,這種模式非常在除錯程式時進行單步跟蹤,分析中間變數。舉例來說,設A=10, B=10,計算邏輯:
第一步計算得出C=100,第二步計算得出D=101,輸出結果D=101。
符號式程式設計將計算過程抽象為計算圖,計算流圖可以方便的描述計算過程,所有輸入節點、運算節點、輸出節點均符號化處理。計算圖通過建立輸入節點到輸出節點的傳遞閉包,從輸入節點出發,沿著傳遞閉包完成數值計算和資料流動,直到達到輸出節點。這個過程經過計算圖優化,以資料(計算)流方式完成,節省記憶體空間使用,計算速度快,但不適合程式除錯,通常不用於程式語言中。舉上面的例子,先根據計算邏輯編寫符號式程式並生成計算圖
其中A和B是輸入符號變數,C和D是運算子號變數,compile函式生成計算圖F,如圖6所示。
圖6 符號程式設計的正向計算圖
最後得到A=10, B=10時變數D的值,這裡D可以複用C的記憶體空間,省去了中間變數的空間儲存。
圖 6是TF中的計算流圖,C=F(Relu(Add(MatMul(W, x), b))),其中每個節點都是符號化表示的。通過session建立graph,在呼叫session.run執行計算。
圖7 TF符號計算圖
和目前的符號語言比起來,TF最大的特點是強化了資料流圖,引入了mutation的概念。這一點是TF和包括Theano在內的符號程式設計框架最大的不同。所謂mutation,就是可以在計算的過程更改一個變數的值,而這個變數在計算的過程中會被帶入到下一輪迭代裡面去。
Mutation是機器學習優化演算法幾乎必須要引入的東西(雖然也可以通過immutable replacement來代替,但是會有效率的問題)。 Theano的做法是引入了update statement來處理mutation。TF選擇了純符號計算的路線,並且直接把更新引入了資料流圖中去。從目前的白皮書看還會支援條件和迴圈。這樣就幾乎讓TF本身成為一門獨立的語言。不過這一點會導致最後的API設計和使用需要特別小心,把mutation 引入到資料流圖中會帶來一些新的問題,比如如何處理寫與寫之間的依賴。[7]
2.3 梯度計算
梯度計算主要應用在誤差反向傳播和資料更新,是深度學習平臺要解決的核心問題。梯度計算涉及每個計算節點,每個自定義的前向計算圖都包含一個隱式的反向計算圖。從資料流向上看,正向計算圖是資料從輸入節點到輸出節點的流向過程,反向計算圖是資料從輸出節點到輸入節點的流向過程。
圖8是2.2節中圖6對應的反向計算圖。圖中,由於C=A*B,則dA=B*dC, dB=A*dC。在反向計算圖中,輸入節點dD,輸出節點dA和dB,計算表示式為dA=B*dC=B*dD, dB=A*dC=A*dD。每一個正向計算節點對應一個隱式梯度計算節點。
圖8 符號程式設計的反向計算圖
反向計算限制了符號程式設計中記憶體空間複用的優勢,因為在正向計算中的計算資料在反向計算中也可能要用到。從這一點上講,粗粒度的計算節點比細粒度的計算節點更有優勢,而TF大部分為細粒度操作,雖然靈活性很強,但細粒度操作涉及到更多的優化方案,在工程實現上開銷較大,不及粗粒度簡單直接。在神經網路模型中,TF將逐步側重粗粒度運算。
2.4 控制流
TF的計算圖如同資料流一樣,資料流向表示計算過程,如圖9。資料流圖可以很好的表達計算過程,為了擴充套件TF的表達能力,TF中引入控制流。
圖9 Graph的資料流
在程式語言中,if…else…是最常見的邏輯控制,在TF的資料流中也可以通過這種方式控制資料流向。介面函式如下,pred為判別表示式,fn1和fn2為運算表示式。當pred為true是,執行fn1操作;當pred為false時,執行fn2操作。
TF還可以協調多個資料流,在存在依賴節點的場景下非常有用,例如節點B要讀取模型引數θ更新後的值,而節點A負責更新引數θ,則節點B必須等節點A完成後才能執行,否則讀取的引數θ為更新前的數值,這時需要一個運算控制器。介面函式如下,tf.control_dependencies函式可以控制多個資料流執行完成後才能執行接下來的操作,通常與tf.group函式結合使用。
TF支援的控制運算元有Switch、Merge、Enter、Leave和NextIteration等。
TF不僅支援邏輯控制,還支援迴圈控制。TF使用和MIT Token-Tagged machine相似的表示系統,將迴圈的每次迭代標記為一個tag,迭代的執行狀態標記為一個frame,但迭代所需的資料準備好的時候,就可以開始計算,從而多個迭代可以同時執行。
轉載自:https://www.leiphone.com/news/201702/n0uj58iHaNpW9RJG.html?viewType=weixin相關文章
- Vue 原始碼解讀(1)—— 前言Vue原始碼
- React-Redux 原始碼解讀(1)ReactRedux原始碼
- PostgreSQL 原始碼解讀(1)- 插入資料#1SQL原始碼
- PostgreSQL 原始碼解讀(116)- MVCC#1(獲取快照#1)SQL原始碼MVCC#
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- PostgreSQL 原始碼解讀(145)- Storage Manager#1(RecordAndGetPageWithFreeSpace)SQL原始碼
- 【視覺化-原始碼閱讀】antvis / g-base解讀 - 1視覺化原始碼
- Laravel 原始碼解讀Laravel原始碼
- reselect原始碼解讀原始碼
- Swoft 原始碼解讀原始碼
- Seajs原始碼解讀JS原始碼
- ReentrantLock原始碼解讀ReentrantLock原始碼
- MJExtension原始碼解讀原始碼
- Axios 原始碼解讀iOS原始碼
- SDWebImage原始碼解讀Web原始碼
- MJRefresh原始碼解讀原始碼
- Handler原始碼解讀原始碼
- LifeCycle原始碼解讀原始碼
- LinkedHashMap原始碼解讀HashMap原始碼
- ConcurrentHashMap原始碼解讀HashMap原始碼
- Redux原始碼解讀Redux原始碼
- ThreadLocal原始碼解讀thread原始碼
- WeakHashMap,原始碼解讀HashMap原始碼
- ThreadLocal 原始碼解讀thread原始碼
- Masonry原始碼解讀原始碼
- ZooKeeper原始碼解讀原始碼
- HashMap原始碼解讀HashMap原始碼
- FairyGUI原始碼解讀AIGUI原始碼
- 【C++】【原始碼解讀】std::is_same函式原始碼解讀C++原始碼函式
- [原始碼解析] TensorFlow 分散式之 ParameterServerStrategy V1原始碼分散式Server
- PostgreSQL 原始碼解讀(192)- 查詢#108(排序#1 - ExecInitSort)SQL原始碼排序
- Spring原始碼解讀(1)-IOC容器BeanDefinition的載入Spring原始碼Bean
- PostgreSQL 原始碼解讀(92)- 分割槽表#1(資料插入路由#1)SQL原始碼路由
- httprunner3原始碼解讀(1)簡單介紹原始碼模組內容HTTP原始碼
- vuex 原始碼:原始碼系列解讀總結Vue原始碼
- Axios 原始碼解讀 —— 原始碼實現篇iOS原始碼
- Vue原始碼解讀一Vue原始碼
- axios 核心原始碼解讀iOS原始碼