天天看點

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

雷鋒網(公衆号:雷鋒網)按:本文作者姚健,畢業于中科院計算所網絡資料實驗室,曾就職于360天眼實驗室,主要從事深度學習和增強學習相關研究工作。目前就職于騰訊mig事業部,從事神經機器翻譯工作。

摘要

2015年11月9日,google釋出深度學習架構tensorflow并宣布開源,并迅速得到廣泛關注,在圖形分類、音頻處理、推薦系統和自然語言處理等場景下都被大面積推廣。tensorflow系統更新快速,官方文檔教程齊全,上手快速且簡單易用,支援python和c++接口。本文依據對tensorflow(簡稱tf)白皮書[1]、tf github[2]和tf官方教程[3]的了解,從系統和代碼實作角度講解tf的内部實作原理。以tensorflow

r0.8.0為基礎,本文由淺入深的闡述tensor和flow的概念。先介紹了tensorflow的核心概念和基本概述,然後剖析了opkernels子產品、graph子產品、session子產品。

1. tf系統架構

1.1 tf依賴視圖

tf的依賴視圖如圖1所示[4],描述了tf的上下遊關系鍊。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖 1 tensorflow依賴視圖

tf托管在github平台,有google groups和contributors共同維護。

tf提供了豐富的深度學習相關的api,支援python和c/c++接口。

tf提供了可視化分析工具tensorboard,友善分析和調整模型。

tf支援linux平台,windows平台,mac平台,甚至手機移動裝置等各種平台。

1.2 tf系統架構

圖2是tf的系統架構,從底向上分為裝置管理和通信層、資料操作層、圖計算層、api接口層、應用層。其中裝置管理和通信層、資料操作層、圖計算層是tf的核心層。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖2 tf系統架構

底層裝置通信層負責網絡通信和裝置管理。裝置管理可以實作tf裝置異構的特性,支援cpu、gpu、mobile等不同裝置。網絡通信依賴grpc通信協定實作不同裝置間的資料傳輸和更新。

第二層是tensor的opkernels實作。這些opkernels以tensor為處理對象,依賴網絡通信和裝置記憶體配置設定,實作了各種tensor操作或計算。opkernels不僅包含matmul等計算操作,還包含queue等非計算操作,這些将在第5章kernels子產品詳細介紹。

第三層是圖計算層(graph),包含本地計算流圖和分布式計算流圖的實作。graph子產品包含graph的建立、編譯、優化和執行等部分,graph中每個節點都是opkernels類型表示。關于圖計算将在第6章graph子產品詳細介紹。

第四層是api接口層。tensor c api是對tf功能子產品的接口封裝,便于其他語言平台調用。

第四層以上是應用層。不同程式設計語言在應用層通過api接口層調用tf核心功能實作相關實驗和應用。

1.3 tf代碼目錄組織

圖3是tf的代碼結構視圖,下面将簡單介紹tf的目錄組織結構。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖3 tf代碼目錄組織結構

tensorflow/core目錄包含了tf核心子產品代碼。

public: api接口頭檔案目錄,用于外部接口調用的api定義,主要是session.h 和tensor_c_api.h。

client: api接口實作檔案目錄。

platform: os系統相關接口檔案,如file system, env等。

protobuf: 均為.proto檔案,用于資料傳輸時的結構序列化.

common_runtime: 公共運作庫,包含session, executor, threadpool, rendezvous, memory管理, 裝置配置設定算法等。

distributed_runtime: 分布式執行子產品,如rpc session, rpc master, rpc worker, graph manager。

framework: 包含基礎功能子產品,如log, memory, tensor

graph: 計算流圖相關操作,如construct, partition, optimize, execute等

kernels: 核心op,如matmul, conv2d, argmax, batch_norm等

lib: 公共基礎庫,如gif、gtl(google模闆庫)、hash、histogram等。

ops: 基本ops運算,ops梯度運算,io相關的ops,控制流和資料流操作

tensorflow/stream_executor目錄是并行計算架構,由google stream executor團隊開發。

 tensorflow/contrib目錄是contributor開發目錄。

tensroflow/python目錄是python api用戶端腳本。

tensorflow/tensorboard目錄是可視化分析工具,不僅可以模型可視化,還可以監控模型參數變化。

third_party目錄是tf第三方依賴庫。

eigen3: eigen矩陣運算庫,tf基礎ops調用

gpus: 封裝了cuda/cudnn程式設計庫

2. tf核心概念

tf的核心是圍繞graph展開的,簡而言之,就是tensor沿着graph傳遞閉包完成flow的過程。是以在介紹graph之前需要講述一下符号程式設計、計算流圖、梯度計算、控制流的概念。

2.1 tensor

在數學上,matrix表示二維線性映射,tensor表示多元線性映射,tensor是對matrix的泛化,可以表示1-dim、2-dim、n-dim的高維空間。圖4對比了矩陣乘法(matrix

product)和張量積(tensor

contract),可以看出tensor的泛化能力,其中張量積運算在tf的matmul和conv2d運算中都有用到。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖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子產品中。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖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,計算邏輯:

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

第一步計算得出c=100,第二步計算得出d=101,輸出結果d=101。

符号式程式設計将計算過程抽象為計算圖,計算流圖可以友善的描述計算過程,所有輸入節點、運算節點、輸出節點均符号化處理。計算圖通過建立輸入節點到輸出節點的傳遞閉包,從輸入節點出發,沿着傳遞閉包完成數值計算和資料流動,直到達到輸出節點。這個過程經過計算圖優化,以資料(計算)流方式完成,節省記憶體空間使用,計算速度快,但不适合程式調試,通常不用于程式設計語言中。舉上面的例子,先根據計算邏輯編寫符号式程式并生成計算圖

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

其中a和b是輸入符号變量,c和d是運算符号變量,compile函數生成計算圖f,如圖6所示。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖6 符号程式設計的正向計算圖

最後得到a=10, b=10時變量d的值,這裡d可以複用c的記憶體空間,省去了中間變量的空間存儲。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖 6是tf中的計算流圖,c=f(relu(add(matmul(w, x), b))),其中每個節點都是符号化表示的。通過session建立graph,在調用session.run執行計算。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖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。每一個正向計算節點對應一個隐式梯度計算節點。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖8 符号程式設計的反向計算圖

反向計算限制了符号程式設計中記憶體空間複用的優勢,因為在正向計算中的計算資料在反向計算中也可能要用到。從這一點上講,粗粒度的計算節點比細粒度的計算節點更有優勢,而tf大部分為細粒度操作,雖然靈活性很強,但細粒度操作涉及到更多的優化方案,在工程實作上開銷較大,不及粗粒度簡單直接。在神經網絡模型中,tf将逐漸側重粗粒度運算。

2.4 控制流

tf的計算圖如同資料流一樣,資料流向表示計算過程,如圖9。資料流圖可以很好的表達計算過程,為了擴充tf的表達能力,tf中引入控制流。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

圖9 graph的資料流

在程式設計語言中,if…else…是最常見的邏輯控制,在tf的資料流中也可以通過這種方式控制資料流向。接口函數如下,pred為判别表達式,fn1和fn2為運算表達式。當pred為true是,執行fn1操作;當pred為false時,執行fn2操作。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

tf還可以協調多個資料流,在存在依賴節點的場景下非常有用,例如節點b要讀取模型參數θ更新後的值,而節點a負責更新參數θ,則節點b必須等節點a完成後才能執行,否則讀取的參數θ為更新前的數值,這時需要一個運算控制器。接口函數如下,tf.control_dependencies函數可以控制多個資料流執行完成後才能執行接下來的操作,通常與tf.group函數結合使用。

從系統和代碼實作角度解析TensorFlow的内部實作原理 | 深度

tf支援的控制算子有switch、merge、enter、leave和nextiteration等。

tf不僅支援邏輯控制,還支援循環控制。tf使用和mit token-tagged machine相似的表示系統,将循環的每次疊代标記為一個tag,疊代的執行狀态标記為一個frame,但疊代所需的資料準備好的時候,就可以開始計算,進而多個疊代可以同時執行。

未完待續……

本文作者:深度學習大講堂