細化 TensorFlow 的 Graph 有關知識

上兩節我們提到了兩個問題:

  • 為什麼要構建計算圖?
  • 怎麼使用多個計算圖?

其中的第一個問題已經在前面一篇文章中做了一個初步的回答,後來又找到了更加詳細的資料,所以在這裡進一步細化第一個問題。

什麼是計算圖?

TensorFlow 使用數據流圖 Graph 來表示計算過程以及計算過程之間的依賴關係。我們通常先構建好計算圖,然後在 session 裡面運行它,但是這是一種較為低級的 API,目前 TensorFlow 可以支持使用 Estimator 或者 Keras 這樣的高級 API 來構建神經網路,它會隱藏 Graph 和 session 相關的信息。

構建數據流圖 Graph 是一種常見的用於並行計算的編程模式,在計算圖中,節點代表一個計算單元,節點之間的邊代表該節點產生的數據; 比如在 TensorFlow 中,一個 tf.matmul 計算會被表示為一個節點,兩條輸入的邊,一條輸出的邊。

計算圖具有如下優勢:

  • 可以並行計算,因為顯式的使用邊來表示計算之間的依賴關係,所以可以很容易的清楚哪些計算可以並行操作
  • 分散式執行,同樣因為上面的原因,可以很容易的把程序的不同部分放在不同的設備上,另外,TensorFlow 加入了必要的通訊和同步機制
  • 編譯優勢,XLA 編譯器可以使用計算圖中的信息編譯運行的更快的代碼
  • 可移植性,TensorFlow 的計算圖是與編寫它的 Python 語言無關的,它只是記錄了網路的結構,可以在其他語言中重建

計算圖 Graph 包含了什麼?

計算圖內部包含兩個方面的信息:

  • 計算圖的結構。包括了計算圖的節點和邊,這些信息表明了單獨的操作是怎麼被聚合到一起的,但是裡面並不包含怎麼使用它們的信息。
  • 計算圖的集合。集合是 TensorFlow 提供的一個獨特的機制,用來存儲計算圖的元數據,包括各個功能如計算 loss、優化器等需要使用到的變數。

如何創建一個計算圖?

當你創建一個新的 OP,或者新的 tensor 的時候,都會激活默認的計算圖,可以看看以下操作在計算圖內部生成什麼樣的細節:

  • v = tf.Variable(0) ,它會添加一個 OP 到計算圖,裡面存儲著一個可以被更改的值,這個 OP 被 TensorFlow 的Variable對象 wrap,你能夠像使用一個 Tensor 一樣使用它,Variable對象具有 assign 方法,可以通過它來改變內部存儲的值
  • tf.train.Optimizer.minimize ,它會添加一個 OP 和一個 tensor (用來計算梯度)到默認的計算圖,並且會返回一個 OP 用來更新其他變數。

NOTE:調用 TensorFlow 裡面大多數的API 都只會在計算圖上添加 OP 和 tensor,但是不會實際執行計算,直到你把他們傳入一個 session。

為什麼要有計算圖的命名空間?

TensorFlow 會自動為每一個 OP 指定一個命名空間(注意只是 OP,沒有 tensor),但是仍然建議你自己為每一個 OP 指定名字,這是為了更加方便的看懂你的程序、計算圖、以及更加方便的 debug。有兩種方法來指定OP 的命名空間:

  • 在使用 API 的時候通過 name 參數傳入,比如tf.constant(42.0, name="answer"),就會創建一個新的命名空間為 answer 的 OP ,並且返回一個 tensor 名稱為 answer:0
  • 使用tf.name_scope 上下文管理器,它會自動為它的管理區的 OP 加上指定的名稱。

NOTE 1:如果碰到已經使用的名稱,TensorFlow 會自動在後面加上 _1, _2的後綴,以避免重複。

NOTE 2:tensor 的命名是在 OP 之後的,用產生它的 OP 的名稱作為前綴,以一種隱性的方法被命名,命名規則為 "<OP_NAME>:<i>" 。

NOTE 3:tensorboard 的可視化會使用命名空間來使得計算圖更加容易看懂。

如何將 OP 放置在不同設備上,什麼時候需要這樣做?

使用 tf.devices 函數可以達到這個目標,它會創建一個上下文管理器,使得它情景下的 OP 被特定設備執行,它的參數需要滿足如下格式:

/job:<JOB_NAME>/task:<TASK_INDEX>/device:<DEVICE_TYPE>:<DEVICE_INDEX>

完整的實例:

with tf.device("/job:xxx/task:1/device:GPU:0"): pass

通常情況下,你並不需要為每一個操作 OP 都指定它計算的設備是 CPU 還是 GPU,TensorFlow 會自動指定最適合的設備,但是在讀取文件、製作 tfrecord、 IO 密集的操作OP 中,最好指定使用 CPU,否則使用 GPU 進行大量 IO 操作會很慢。

通常情況下,job和 task 都不用特別指定,除非你使用分散式的計算任務需要指定參數伺服器 PS 和 計算伺服器 worker,它可以把所有的變數放在 PS 上,把 OP 操作放在 worker 上,類似這樣:

with tf.device("/job:ps/task:0"): weights_1 = tf.Variable(tf.truncated_normal([784, 100])) biases_1 = tf.Variable(tf.zeroes([100]))with tf.device("/job:ps/task:1"): weights_2 = tf.Variable(tf.truncated_normal([100, 10])) biases_2 = tf.Variable(tf.zeroes([10]))with tf.device("/job:worker"): layer_1 = tf.matmul(train_batch, weights_1) + biases_1 layer_2 = tf.matmul(train_batch, weights_2) + biases_2

tf.train.replica_device_setter 可以和 tf.devices 配合使用,達到更加靈活的指定 PS 和 worker 的作用。

類似 tensor 的對象

許多 TensorFlow 的 OP 都使用 tensor 作為輸入變數,但是,出於方便的原因考慮,它們也可以接受其他類似 tensor 的對象作為參數,在其後面隱性的使用 tf.convert_to_tensor 把輸入的類似 tensor 的對象轉換為 tensor , 這些類似 tensor 的對象包括:

  • numpy.ndarray
  • list
  • bool , float , int , str
  • tf.Variable

但是這種操作具有一定的風險,因為它每次都會生成一個新的 tensor, 如果傳入的對象較大,那麼在多次使用後會導致內存溢出,因此,最好還是人為的使用 tf.convert_to_tensor ,返回一個固定的 tensor 比較好。


推薦閱讀:

世界類腦AI巨系統研究綜述,論ET大腦是不是全球首個類腦架構AI
中國腦計劃顛覆性創新之路九,進化的方向,突破達爾文進化論局限
從互聯網進化的角度看AI+時代的巨頭競爭
城市雲腦研究之三,人工智慧在城市雲腦建設中的地位與作用

TAG:TensorFlow | 人工智慧 |