摘要:是提供的一個非常具有前景的工具它能夠將一部分語法的代碼轉譯成高效的圖表示代碼由于從開始將會默認使用動態圖因此利用在理想情況下能讓我們實現用動態圖寫方便靈活用靜態圖跑高效穩定但是在使用的過程中如無意外肯定是會有意外的這篇文章就是指出一些和的奇
AutoGraph是TF提供的一個非常具有前景的工具, 它能夠將一部分python語法的代碼轉譯成高效的圖表示代碼. 由于從TF 2.0開始,?TF將會默認使用動態圖(eager execution), 因此利用AutoGraph,?在理想情況下, 能讓我們實現用動態圖寫(方便, 靈活), 用靜態圖跑(高效, 穩定).
但是! 在使用的過程中, 如無意外肯定是會有意外的, 這篇文章就是指出一些AutoGraph和tf.function的奇怪的行為, 讓你更愉快地使用它們.
本文假設讀者具有一定的Python和TensorFlow的使用經驗.
會話執行對tf1.X有經驗的讀者應該不會對讓我們又愛又恨的計算圖(tf.Graph)和執行會話(tf.Session)感到陌生, 一個常規的流程如下:
初始化一個計算圖并且將該計算圖設置為當前scope下的默認計算圖
用TF API設計計算圖(比如: y=tf.matmul(a, x) + b)
提前界定好參數共享并劃分相應的參數scope
創建并配置好tf.Session
將計算圖傳給tf.Session
初始化參數
用tf.Session.run來執行計算圖的節點, 被執行的節點會反向追蹤所有依賴的需要執行的節點并執行計算.
以下是上述過程的一個代碼例子:
g = tf.Graph() #初始化計算圖 with g.as_default(): # 設置為默認計算圖 a = tf.constant([[10,10],[11.,1.]]) x = tf.constant([[1.,0.],[0.,1.]]) b = tf.Variable(12.) y = tf.matmul(a, x) + b # 描述計算圖 init_op = tf.global_variables_initializer() # 待執行節點 with tf.Session() as sess: # 配置會話 sess.run(init_op) # 執行節點 print(sess.run(y)) # 輸出結果
在TF 2.0中, 由于默認為動態圖, 計算會直接被執行, 也就是說, 我們不需要
定義計算圖
會話執行
參數初始化
用scope定義參數分享
用tf.control_dependencies來聲明節點的非直接依賴
我們可以像寫普通python代碼(or pytorch)一樣, 寫了就執行:
a = tf.constant([[10,10],[11.,1.]]) x = tf.constant([[1.,0.],[0.,1.]]) b = tf.Variable(12.) y = tf.matmul(a, x) + b print(y.numpy())
一般來說, eager代碼會比執行相同操作的靜態圖代碼的效率低, 因為很多計算圖優化的方法只能用在數據流圖上.
如果想在TF 2.0上構建傳統的計算圖, 我們就需要用到tf.function.
函數, 而非會話TF 2.0的其中一個重要改變就是去除tf.Session(此處應有掌聲). 這個改變會迫使用戶用更好的方式來組織代碼: 不用再用讓人糾結的tf.Session來執行代碼, 就是一個個python函數, 加上一個簡單的裝飾器.
在TF 2.0里面, 如果需要構建計算圖, 我們只需要給python函數加上@tf.function的裝飾器.
上文提到靜態圖的執行效率更高, 但是加速并不是一定的. 一般來說, 計算圖越復雜, 加速效果越明顯. 對于復雜的計算圖, 比如訓練深度學習模型, 獲得的加速是巨大的. (譯者注: 個人感覺還是要結合實際來看, 如果某一部分的計算既有復雜的計算圖, 而計算圖的復雜性又帶來了額外的內存消耗
或者計算量, 那么加速會比較明顯, 但是很多時候, 比如一般的CNN模型, 主要計算量并不在于圖的復雜性, 而在于卷積、矩陣乘法等操作, 加速并不會很明顯. 此處想法有待驗證)
這個自動將python代碼轉成圖表示代碼的工具就叫做AutoGraph.
在TF 2.0中, 如果一個函數被@tf.function裝飾了, 那么AutoGraph將會被自動調用, 從而將python函數轉換成可執行的圖表示.
tf.function: 究竟發生了什么?在第一次調用被@tf.function裝飾的函數時, 下列事情將會發生:
該函數被執行并跟蹤。和Tensorflow 1.x類似, Eager會在這個函數中被禁用,因此每個tf.API只會定義一個生成tf.Tensor輸出的節點
AutoGraph用于檢測可以轉換為等效圖表示的Python操作(while→tf.while,for→tf.while,if→tf.cond,assert→tf.assert...)
為了保留執行順序,在每個語句之后自動添加tf.control_dependencies,以便在執行第i+1行時確保第i行已經被執行. 至此計算圖已經確定
根據函數名稱和輸入參數,創建唯一ID并將其與定義好的計算圖相關聯。計算圖被緩存到一個映射表中:map [id] = graph
如果ID配對上了,之后的函數調用都會直接使用該計算圖
下一節將會具體闡述如何將TF 1.X代碼塊分別改寫到eager和計算圖版本.
改寫到eager execution要使用tf.function, 第一步需要先將TF 1.X的設計計算圖的代碼放進python函數里面.
def f(): a = tf.constant([[10,10],[11.,1.]]) x = tf.constant([[1.,0.],[0.,1.]]) b = tf.Variable(12.) y = tf.matmul(a, x) + b return y
應為TF 2.0默認是eager的, 我們可以直接執行該函數(不需要tf.Session):
print(f().numpy())
我們就會得到輸出:
[[22. 22.] [23. 13.]]從eager到tf.function
我們可以直接用@tf.function來裝飾函數f, 我們在原來f的基礎上加上宇宙第一的debug大法: print來更好地看看究竟發生了什么.
@tf.function def f(): a = tf.constant([[10,10],[11.,1.]]) x = tf.constant([[1.,0.],[0.,1.]]) b = tf.Variable(12.) y = tf.matmul(a, x) + b print("PRINT: ", y) tf.print("TF-PRINT: ", y) return y f()
所以發生了什么呢?
@tf.function將函數f包進了tensorflow.python.eager.def_function.Function這個對象, 函數f被賦予到了這個對象的.python_function屬性.
當f()被執行的時候, 計算圖會同時被構建, 但是計算不會執行, 因此我們會得到以下結果, tf.的操作不會被執行:
PRINT: Tensor("add:0", shape=(2, 2), dtype=float32)
最終, 你會看到代碼會執行失敗:
ValueError: tf.function-decorated function tried to create variables on non-first call.
在?RFC: Functions, not Session里面有個非常明確的指示:
State (like tf.Variable objects) are only created the first time the function f is called. 狀態(比如tf.Variable) 只會在函數被第一次調用時創建.
但是?Alexandre Passos指出, 在函數轉換成圖表示時, 我們沒有辦法確定tf.function調用了多少次函數, 因此我們在第一次調用函數f時, 在圖構建的過程中, 可能會被執行了多次, 這就導致了上述錯誤.
造成這個錯誤的根源在于同樣的命令在動態圖和靜態圖中的不一致性. 在動態圖中, tf.Variable時一個普通的python變量, 超出了其作用域范圍就會被銷毀. 而在靜態圖中, tf.Variable則是計算圖中一個持續存在的節點, 不受python的作用域的影響. 因此, 這是使用tf.function的第一個教訓:
將一個在動態圖中可行的函數轉換成靜態圖需要用靜態圖的方式思考該函數是否可行
那么我們可以怎樣去規避這個錯誤呢?
將tf.Variable作為函數的參數傳入
將父作用域繼承tf.Variable
將tf.Variable作為類屬性來調用
用改變變量作用域來處理這里指方法2和方法3. 顯然的, 我們推薦使用方法3:
class F(): def __init__(self): self._b = None @tf.function def __call__(self): a = tf.constant([[10, 10], [11., 1.]]) x = tf.constant([[1., 0.], [0., 1.]]) if self._b is None: self._b = tf.Variable(12.) y = tf.matmul(a, x) + self._b print("PRINT: ", y) tf.print("TF-PRINT: ", y) return y f = F() f()將狀態作為傳入參數來處理
我們之后會看到, 我們并不能隨意地用tf.function來轉化eager的代碼并達到加速的目的, 我們需要想象一下轉化是怎么完成的, 在轉python的代碼到圖操作的時候究竟發生了什么, 這些轉化包含了什么黑魔法. 這里的例子比較簡單, 我們會在接下來的文章中更深入的探討.
@tf.function def f(b): a = tf.constant([[10,10],[11.,1.]]) x = tf.constant([[1.,0.],[0.,1.]]) y = tf.matmul(a, x) + b print("PRINT: ", y) tf.print("TF-PRINT: ", y) return y b = tf.Variable(12.) f(b)
上述函數會得到我們想要的結果, 另外, 作為參數被傳入的變量能夠在函數中直接更新, 而更新后的值會在函數外也適用. 下面的代碼會打印出1,2,3
a = tf.Variable(0) @tf.function def g(x): x.assign_add(1) return x print(g(a)) print(g(a)) print(g(a))總結
我們可以用@tf.function裝飾器來將python代碼轉成圖表示代碼
我們不能在被裝飾函數中初始化tf.Variable
可以用變量作用域繼承(對象屬性)或者參數傳入的方法使用在函數外初始化的變量
在之后的部分我們會更加深入地探討輸入參數類型對效率的影響, 以及python操作的轉換細節.
聲明: 本文翻譯自Paolo Galeone的博客, 已取得作者的同意, 如需轉載本文請聯系本人
Disclaimer: This is a translation of the article?Analyzing tf.function to discover AutoGraph strengths and subtleties?by Paolo Galeone.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/20045.html
隨著機器學習和深度學習的迅速發展,TensorFlow已經成為了當今最流行的深度學習框架之一。TensorFlow不斷地更新和發展,不斷改進其性能和功能。本文將介紹如何更新TensorFlow,并介紹一些新的編程技術,以便更好地使用和優化TensorFlow。 一、更新TensorFlow TensorFlow不斷地更新和改進,包括性能提升、API的變化以及新的功能等。更新TensorFlow...
摘要:簡介是針對移動設備和嵌入式設備的輕量化解決方案,占用空間小,低延遲。支持浮點運算和量化模型,并已針對移動平臺進行優化,可以用來創建和運行自定義模型。此外,轉換的方式有兩種,的方式和命令行方式。生成為了將模型轉為,模型需要導出。 簡介 Tensorflow Lite是針對移動設備和嵌入式設備的輕量化解決方案,占用空間小,低延遲。Tensorflow Lite在android8.1以上的設...
摘要:對象是中的一個內置對象,它為數學常量和數學函數提供了屬性和方法,而不是一個函數對象。創建日期的幾種方法為時間戳為表示日期的字符串注意代表月份的整數值是從月到月常用方法返回自時間標準時間至今所經過的毫秒數。 Math對象 Math 是js中的一個內置對象, 它為數學常量和數學函數提供了屬性和方法,而不是一個函數對象。 屬性 Math.PI = > 圓周率,一個圓的周長和直徑之比,悅等...
閱讀 3037·2021-10-13 09:39
閱讀 1883·2021-09-02 15:15
閱讀 2447·2019-08-30 15:54
閱讀 1808·2019-08-30 14:01
閱讀 2606·2019-08-29 14:13
閱讀 1417·2019-08-29 13:10
閱讀 2735·2019-08-28 18:15
閱讀 3888·2019-08-26 10:20