有三種計算圖的構建方式:靜態計算圖,動態計算圖,以及Autograph.
在TensorFlow1.0時代,采用的是靜態計算圖,需要先使用TensorFlow的各種算子創建計算圖,然后再開啟一個會話Session,顯式執行計算圖。
而在TensorFlow2.0時代,采用的是動態計算圖,即每使用一個算子后,該算子會被動態加入到隱含的默認計算圖中立即執行得到結果,而無需開啟Session。
使用動態計算圖即Eager Excution的好處是方便調試程序,它會讓TensorFlow代碼的表現和Python原生代碼的表現一樣,寫起來就像寫numpy一樣,各種日志打印,控制流全部都是可以使用的。
使用動態計算圖的缺點是運行效率相對會低一些。因為使用動態圖會有許多次Python進程和TensorFlow的C++進程之間的通信。而靜態計算圖構建完成之后幾乎全部在TensorFlow內核上使用C++代碼執行,效率更高。此外靜態圖會對計算步驟進行一定的優化,剪去和結果無關的計算步驟。
如果需要在TensorFlow2.0中使用靜態圖,可以使用@tf.function裝飾器將普通Python函數轉換成對應的TensorFlow計算圖構建代碼。運行該函數就相當於在TensorFlow1.0中用Session執行代碼。使用tf.function構建靜態圖的方式叫做 Autograph.
一,計算圖簡介
計算圖由節點(nodes)和線(edges)組成。
節點表示操作符Operator,或者稱之為算子,線表示計算間的依賴。
實線表示有數據傳遞依賴,傳遞的數據即張量。
虛線通常可以表示控制依賴,即執行先后順序。
二,靜態計算圖
在TensorFlow1.0中,使用靜態計算圖分兩步,第一步定義計算圖,第二步在會話中執行計算圖。
TensorFlow 1.0靜態計算圖范例
import tensorflow as tf # 定義計算圖 g = tf.Graph() with g.as_default(): #placeholder為占位符,執行會話時候指定填充對象 x = tf.placeholder(name='x', shape=[], dtype=tf.string) y = tf.placeholder(name='y', shape=[], dtype=tf.string) z = tf.string_join([x,y],name = 'join',separator=' ') # 執行計算圖 with tf.Session(graph = g) as sess: print(sess.run(fetches = z,feed_dict = {x:"hello",y:"world"}))
TensorFlow2.0 懷舊版靜態計算圖
TensorFlow2.0為了確保對老版本tensorflow項目的兼容性,在tf.compat.v1子模塊中保留了對TensorFlow1.0那種靜態計算圖構建風格的支持。
可稱之為懷舊版靜態計算圖,已經不推薦使用了
import tensorflow as tf g = tf.compat.v1.Graph() with g.as_default(): x = tf.compat.v1.placeholder(name='x', shape=[], dtype=tf.string) y = tf.compat.v1.placeholder(name='y', shape=[], dtype=tf.string) z = tf.strings.join([x,y],name = "join",separator = " ") with tf.compat.v1.Session(graph = g) as sess: # fetches的結果非常像一個函數的返回值,而feed_dict中的占位符相當於函數的參數序列。 result = sess.run(fetches = z,feed_dict = {x:"hello",y:"world"}) print(result)
b'hello world'
三,動態計算圖
在TensorFlow2.0中,使用的是動態計算圖和Autograph.
在TensorFlow1.0中,使用靜態計算圖分兩步,第一步定義計算圖,第二步在會話中執行計算圖。
動態計算圖已經不區分計算圖的定義和執行了,而是定義后立即執行。因此稱之為 Eager Excution.
Eager這個英文單詞的原意是"迫不及待的",也就是立即執行的意思。
# 動態計算圖在每個算子處都進行構建,構建后立即執行 x = tf.constant("hello") y = tf.constant("world") z = tf.strings.join([x,y],separator=" ") tf.print(z)
hello world
# 可以將動態計算圖代碼的輸入和輸出關系封裝成函數 def strjoin(x,y): z = tf.strings.join([x,y],separator = " ") tf.print(z) return z result = strjoin(tf.constant("hello"),tf.constant("world")) print(result)
hello world
tf.Tensor(b'hello world', shape=(), dtype=string)
四,TensorFlow2.0的Autograph
動態計算圖運行效率相對較低。
可以用@tf.function裝飾器將普通Python函數轉換成和TensorFlow1.0對應的靜態計算圖構建代碼。
在TensorFlow1.0中,使用計算圖分兩步,第一步定義計算圖,第二步在會話中執行計算圖。
在TensorFlow2.0中,如果采用Autograph的方式使用計算圖,第一步定義計算圖變成了定義函數,第二步執行計算圖變成了調用函數。
不需要使用會話了,一些都像原始的Python語法一樣自然。
實踐中,我們一般會先用動態計算圖調試代碼,然后在需要提高性能的的地方利用@tf.function切換成Autograph獲得更高的效率。
當然,@tf.function的使用需要遵循一定的規范,我們后面章節將重點介紹。
import tensorflow as tf # 使用autograph構建靜態圖 @tf.function def strjoin(x,y): z = tf.strings.join([x,y],separator = " ") tf.print(z) return z result = strjoin(tf.constant("hello"),tf.constant("world")) print(result)
hello world
tf.Tensor(b'hello world', shape=(), dtype=string)
import datetime # 創建日志 stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") logdir = './data/autograph/%s' % stamp writer = tf.summary.create_file_writer(logdir) # 開啟autograph跟蹤 tf.summary.trace_on(graph=True, profiler=True) # 執行autograph result = strjoin("hello","world") # 將計算圖信息寫入日志 with writer.as_default(): tf.summary.trace_export( name="autograph", step=0, profiler_outdir=logdir) # 啟動 tensorboard在jupyter中的魔法命令 %load_ext tensorboard # 啟動tensorboard %tensorboard --logdir ./data/autograph/
參考:
開源電子書地址:https://lyhue1991.github.io/eat_tensorflow2_in_30_days/
GitHub 項目地址:https://github.com/lyhue1991/eat_tensorflow2_in_30_days