1、TensorFlow系統架構
如圖為TensorFlow的系統架構圖:
TensorFlow的系統架構圖,自底向上分為設備層和網絡層、數據操作層、圖計算層、API層、應用層,其中設備層和網絡層,數據操作層,圖計算層是TensorFlow的核心層。
網絡通信層和設備層:
網絡通信層包括個gRPC(google Remote Procedure Call Protocol)和遠程直接數據存取(Remote DirectMemory Access,RDMA),這都是在分布式計算時需要用到的。設備管理層包括 TensorFlow 分別在 CPU、GPU、FPGA 等設備上的實現,也就是對上層提供了一個統一的接口,使上層只需要處理卷積等邏輯,而不需要關心在硬件上的卷積的實現過程。
數據操作層:
主要包括卷積函數、激活函數等操作
圖計算層:
包含本地計算圖和分布式計算圖的實現
API 層和應用層
編程語言的實現與應用
2、設計理念
TensorFlow的設計理念主要體現在兩個方面
(1)、將圖定義和圖運算完全分開。
TensorFlow 被認為是一個“符號主義”的庫。我們知道,編程模式通常分為命令式編程(imperative style programming)和符號式編程(symbolic style programming)。命令式編程就是編寫我們理解的通常意義上的程序,很容易理解和調試,按照原有邏輯執行。符號式編程涉及很多的嵌入和優化,不容易理解和調試,但運行速度相對有所提升。現有的深度學習框架中,Torch 是典型的命令式的,Caffe、MXNet 采用了兩種編程模式混合的方法,而 TensorFlow 完全采用符號式編程。
符號式計算一般是先定義各種變量,然后建立一個數據流圖,在數據流圖中規定各個變量間的計算關系,最后需要對據流圖進行編譯,但此時的數據流圖還是一個空殼兒,里面沒有任何實際數據,只有把需要運算的輸入放進去后,才能在整個模型中形成數據流,從而形成輸出值。
例如:
t = 8+ 9 print(t)
在傳統的程序操作中,定義了 t 的運算,在運行時就執行了,並輸出 17。而在 TensorFlow
中,數據流圖中的節點,實際上對應的是 TensorFlow API 中的一個操作,並沒有真正去運行:
import tensorflow as tf t = tf.add(8,9) print(t) #輸出 Tensor{"Add_1:0",shape={},dtype=int32}
(2)、TensorFlow 中涉及的運算都要放在圖中,而圖的運行只發生在會話(session)中。開啟會話后,就可以用數據去填充節點,進行運算;關閉會話后,就不能進行計算了。因此,會話提供了操作運行和 Tensor 求值的環境。
例如:
import tensorflow as tf #創建圖 a = tf.constant([1.0,2.0]) b = tf.constant([3.0,4.0]) c = a * b #創建會話 sess = tf.Session() #計算c print(sess.run(c))#進行矩陣乘法,輸出[3.,8.] sess.close()
3、TensorFlow編程模型
TensorFlow 是用數據流圖做計算的,因此我們先創建一個數據流圖(也稱為網絡結構圖),
如圖 下圖 所示,看一下數據流圖中的各個要素。
圖 講述了 TensorFlow 的運行原理。圖中包含輸入(input)、塑形(reshape)、Relu 層(Relulayer)、Logit 層(Logit layer)、Softmax、交叉熵(cross entropy)、梯度(gradient)、SGD 訓練(SGD Trainer)等部分,是一個簡單的回歸模型。
它的計算過程是,首先從輸入開始,經過塑形后,一層一層進行前向傳播運算。Relu 層(隱藏層)里會有兩個參數,即 W h1 和 b h1 ,在輸出前使用 ReLu(Rectified Linear Units)激活函數做非線性處理。然后進入 Logit 層(輸出層),學習兩個參數 W sm 和 b sm 。用 Softmax 來計算輸出結果中各個類別的概率分布。用交叉熵來度量兩個概率分布(源樣本的概率分布和輸出結果的概率分布)之間的相似性。然后開始計算梯度,這里是需要參數 W h1 、b h1 、W sm 和 b sm ,以及交叉熵后的結果。隨后進入 SGD 訓練,也就是反向傳播的過程,從上往下計算每一層的參數,依次進行更新。也就是說,計算和更新的順序為 b sm 、W sm 、b h1 和 W h1 。
顧名思義,TensorFlow 是指“張量的流動”。TensorFlow 的數據流圖是由節點(node)和邊edge)組成的有向無環圖(directed acycline graph,DAG)。TensorFlow 由 Tensor 和 Flow 兩部分組成,Tensor(張量)代表了數據流圖中的邊,而 Flow(流動)這個動作就代表了數據流圖中節點所做的操作
3.1、邊
TensorFlow 的邊有兩種連接關系:數據依賴和控制依賴。其中,實線邊表示數據依賴代表數據,即張量。任意維度的數據統稱為張量。在機器學習算法中,張量在數據流圖中從前往后流動一遍就完成了一次前向傳播(forword propagation),而殘差 從后向前流動一遍就完成了一次反向傳播(backword propagation)。
注:在數理統計中,殘差是指實際觀測值於訓練的估計值之間的差
還有一種特殊邊,一般畫為虛線邊,稱為控制依賴(control dependency),可以用於控制操作的運行,這被用來確保 happens-before 關系,這類邊上沒有數據流過,但源節點必須在目的節點開始執行前完成執行。常用代碼:
tf.Graph.control_dependencies(control_inputs)
Tensorflow支持的張量的數據屬性:
有關圖及張量的實現的源代碼均位於 tensorflow-1.1.0/tensorflow/python/framework/ops.py
3.2、節點
圖中的節點又稱為算子,它代表一個操作(operation,OP),一般用來表示施加的數學運算,也可以表示數據輸入(feed in)的起點以及輸出(push out)的終點,或者是讀取/寫入持久變量(persistent variable)的終點。下表列舉了一些 TensorFlow 實現的算子。算子支持表所示的張量的各種數據屬性,並且需要在建立圖的時候確定下來。
與操作相關的代碼位於 tensorflow-1.1.0/tensorflow/python/ops/目錄下。以數學運算為例,代碼為上述目錄下的 math_ops.py,里面定義了 add、subtract、multiply、scalar_mul、div、divide、truediv、floordiv 等數學運算,每個函數里面調用了 gen_math_ops.py 中的方法,這個文件是在編譯(安裝時)TensorFlow 時生成的,位於 Python 庫 site-packages/tensorflow/python/ops/gen_math_ops.py 中,隨后又調用了 tensorflow1.1.0/tensorflow/core/kernels/下面的核函數實現。再例如,數據運算的代碼位於 tensorflow-1.1.0/tensorflow/python/ops/array_ops.py 中,里面定義了concat、split、slice、size、rank 等運算,每個函數都調用了 gen_array_ops.py 中的方法,這個文件也是在編譯TensorFlow 生成的,位於Python 庫site-packages/tensorflow/python/ops/gen_array_ops.py中,隨后又調用了 tensorflow-1.1.0/tensorflow/core/kernels/下面的核函數實現。
3.3、圖
把操作任務描述成有向無環圖。那么,如何構建一個圖呢?構建圖的第一步是創建各個節
點。具體如下:
import tensorflow as tf # 創建一個常量運算操作,產生一個 1×2 矩陣 matrix1 = tf.constant([[3., 3.]]) # 創建另外一個常量運算操作,產生一個 2×1 矩陣 matrix2 = tf.constant([[2.],[2.]]) # 創建一個矩陣乘法運算 ,把 matrix1 和 matrix2 作為輸入 # 返回值 product 代表矩陣乘法的結果 product = tf.matmul(matrix1, matrix2)
3.4、會話
啟動圖的第一步是創建一個 Session 對象。會話(session)提供在圖中執行操作的一些方法。一般的模式是,建立會話,此時會生成一張空圖,在會話中添加節點和邊,形成一張圖,然后執行。要創建一張圖並運行操作的類,在 Python 的 API 中使用 tf.Session,在 C++ 的 API 中使用tensorflow::Session。示例如下:
with tf.Session() as sess: result = sess.run([product]) print result
在調用 Session 對象的 run()方法來執行圖時,傳入一些 Tensor,這個過程叫填充(feed);返回的結果類型根據輸入的類型而定,這個過程叫取回(fetch)。
與會話相關的源代碼位於 tensorflow-1.1.0/tensorflow/python/client/session.py。
會話是圖交互的一個橋梁,一個會話可以有個圖,會話可以修改圖的結構,也可以往圖中注入數據進行計算。因此,會話主要有兩個 API 接口:Extend 和 Run。Extend 操作是在 Graph中添加節點和邊,Run 操作是輸入計算的節點和填充必要的數據后,進行運算,並輸出運算結果。
3.5、設備
設備(device)是指一塊可以用來運算並且擁有自己的地址空間的硬件,如 GPU 和 CPU。TensorFlow 為了實現分布式執行操作,充分利用計算資源,可以明確指定操作在哪個設備上執行。具體如下:
with tf.Session() as sess: # 指定在第二個 gpu 上運行 with tf.device("/gpu:1"): matrix1 = tf.constant([[3., 3.]]) matrix2 = tf.constant([[2.],[2.]]) product = tf.matmul(matrix1, matrix2)
與設備相關的源代碼位於 tensorflow-1.1.0/tensorflow/python/framework/device.py
3.6、變量
· 變量(variable)是一種特殊的數據,它在圖中有固定的位置,不像普通張量那樣可以流動。例如,創建一個變量張量,使用 tf.Variable()構造函數,這個構造函數需要一個初始值,初始值的形狀和類型決定了這個變量的形狀和類型:
# 創建一個變量,初始化為標量 0 state = tf.Variable(0, name="counter") #創建一個常量張量: input1 = tf.constant(3.0)
TensorFlow 還提供了填充機制,可以在構建圖時使用 tf.placeholder()臨時替代任意操作的張量,在調用 Session 對象的 run()方法去執行圖時,使用填充數據作為調用的參數,調用結束后,填充數據就消失。代碼示例如下:
input1 = tf.placeholder(tf.float32) input2 = tf.placeholder(tf.float32) output = tf.multiply(input1, input2) with tf.Session() as sess: print sess.run([output], feed_dict={input1:[7.], input2:[2.]}) # 輸出 [array([ 14.], dtype=float32)]
與變量相關的源代碼位於 tensorflow/tensorflow/python/ops/variables.py。
3.7、內核
我們知道操作(operation)是對抽象操作(如 matmul 或者 add)的一個統稱,而內核(kernel)則是能夠運行在特定設備(如 CPU、GPU)上的一種對操作的實現。因此,同一個操作可能會對應多個內核。
當自定義一個操作時,需要把新操作和內核通過注冊的方式添加到系統中