高效的TensorFlow 2.0 (tensorflow2官方教程翻譯)
最新版本:http://www.mashangxue123.com/tensorflow/tf2-guide-effective_tf2.html
英文版本:https://tensorflow.google.cn/alpha/guide/effective_tf2
翻譯建議PR:https://github.com/mashangxue/tensorflow2-zh/edit/master/r2/guide/effective_tf2.md
TensorFlow 2.0中有多處更改,以使TensorFlow用戶使用更高效问裕。TensorFlow 2.0刪除冗余 APIs,使API更加一致(統(tǒng)一 RNNs,統(tǒng)一優(yōu)化器),并通過Eager execution模式更好地與Python運行時集成
許多RFCs已經解釋了TensorFlow 2.0所帶來的變化。本指南介紹了TensorFlow 2.0應該是什么樣的開發(fā)凌盯,假設您對TensorFlow 1.x有一定的了解。
1. 主要變化的簡要總結
1.1. API清理
許多API在tensorflow 2.0中消失或移動烹玉。一些主要的變化包括刪除tf.app
十气、tf.flags
和tf.logging
,轉而支持現在開源的absl-py春霍,重新安置tf.contrib
中的項目砸西,并清理主要的 tf.*
命名空間,將不常用的函數移動到像 tf.math
這樣的子包中址儒。一些API已被2.0版本等效替換芹枷,如tf.summary
, tf.keras.metrics
和tf.keras.optimizers
。
自動應用這些重命名的最簡單方法是使用v2升級腳本莲趣。
1.2. Eager execution
TensorFlow 1.X要求用戶通過進行tf.*
API調用鸳慈,手動將抽象語法樹(圖形)拼接在一起。然后要求用戶通過將一組輸出張量和輸入張量傳遞給session.run()
來手動編譯抽象語法樹喧伞。
TensorFlow 2.0 默認Eager execution模式走芋,馬上就執(zhí)行代碼(就像Python通常那樣),在2.0中潘鲫,圖形和會話應該像實現細節(jié)一樣翁逞。
Eager execution的一個值得注意的地方是不在需要tf.control_dependencies()
,因為所有代碼按順序執(zhí)行(在tf.function
中溉仑,帶有副作用的代碼按寫入的順序執(zhí)行)挖函。
1.3. 沒有更多的全局變量
TensorFlow 1.X嚴重依賴于隱式全局命名空間。當你調用tf.Variable()
時浊竟,它會被放入默認圖形中怨喘,保留在那里,即使你忘記了指向它的Python變量振定。
然后必怜,您可以恢復該tf.Variable
,但前提是您知道它已創(chuàng)建的名稱后频,如果您無法控制變量的創(chuàng)建梳庆,這很難做到。結果,各種機制激增靠益,試圖幫助用戶再次找到他們的變量,并尋找框架來查找用戶創(chuàng)建的變量:變量范圍残揉、全局集合胧后、輔助方法如tf.get_global_step()
, tf.global_variables_initializer()
、優(yōu)化器隱式計算所有可訓練變量的梯度等等抱环。
TensorFlow 2.0取消了所有這些機制(Variables 2.0 RFC)壳快,支持默認機制:跟蹤變量!如果你失去了對tf.Variable的追蹤镇草,就會垃圾收集回收眶痰。
跟蹤變量的要求為用戶創(chuàng)建了一些額外的工作,但是使用Keras對象(見下文)梯啤,負擔被最小化竖伯。
1.4. Functions, not sessions
session.run()
調用幾乎就像一個函數調用:指定輸入和要調用的函數,然后返回一組輸出因宇。
在TensorFlow 2.0中七婴,您可以使用tf.function()
來裝飾Python函數以將其標記為JIT編譯,以便TensorFlow將其作為單個圖形運行(Functions 2.0 RFC)察滑。這種機制允許TensorFlow 2.0獲得圖形模式的所有好處:
- 性能:可以優(yōu)化功能(節(jié)點修剪打厘,內核融合等)
- 可移植性:該功能可以導出/重新導入(SavedModel 2.0 RFC),允許用戶重用和共享模塊化TensorFlow功能贺辰。
# TensorFlow 1.X
outputs = session.run(f(placeholder), feed_dict={placeholder: input})
# TensorFlow 2.0
outputs = f(input)
憑借能夠自由穿插Python和TensorFlow代碼户盯,我們希望用戶能夠充分利用Python的表現力。但是可移植的TensorFlow在沒有Python解釋器的情況下執(zhí)行-移動端饲化、C++和JS莽鸭,幫助用戶避免在添加 @tf.function
時重寫代碼,AutoGraph將把Python構造的一個子集轉換成它們等效的TensorFlow:
-
for
/while
->tf.while_loop
(支持break
和continue
) -
if
->tf.cond
-
for _ in dataset
->dataset.reduce
AutoGraph支持控制流的任意嵌套吃靠,這使得高效和簡潔地實現許多復雜的ML程序成為可能蒋川,比如序列模型、強化學習撩笆、自定義訓練循環(huán)等等捺球。
2. 使用TensorFlow 2.0的建議
2.1. 將代碼重構為更小的函數
TensorFlow 1.X中常見的使用模式是“kitchen sink”策略,在該策略中夕冲,所有可能的計算的并集被預先安排好氮兵,然后通過session.run()
對所選的張量進行評估。
TensorFlow 2.0中歹鱼,用戶應該根據需要將代碼重構為更小的函數泣栈。一般來說,沒有必須要使用tf.function
來修飾這些小函數,只用tf.function
來修飾高級計算-例如南片,一個訓練步驟掺涛,或者模型的前向傳遞。
2.2. 使用Keras層和模型來管理變量
Keras模型和層提供了方便的variables
和trainable_variables
屬性疼进,它們遞歸地收集所有的因變量薪缆。這使得本地管理變量到使用它們的地方變得非常容易。
對比如下:
def dense(x, W, b):
return tf.nn.sigmoid(tf.matmul(x, W) + b)
@tf.function
def multilayer_perceptron(x, w0, b0, w1, b1, w2, b2 ...):
x = dense(x, w0, b0)
x = dense(x, w1, b1)
x = dense(x, w2, b2)
...
# 您仍然必須管理w_i和b_i伞广,它們是在代碼的其他地方定義的拣帽。
Keras版本如下:
# 每個圖層都可以調用,其簽名等價于linear(x)
layers = [tf.keras.layers.Dense(hidden_size, activation=tf.nn.sigmoid) for _ in range(n)]
perceptron = tf.keras.Sequential(layers)
# layers[3].trainable_variables => returns [w3, b3]
# perceptron.trainable_variables => returns [w0, b0, ...]
Keras 層/模型繼承自 tf.train.Checkpointable
并與@tf.function
集成嚼锄,這使得從Keras對象導出保存模型成為可能减拭。
您不必使用Keras的.fit()
API來利用這些集成。
下面是一個轉移學習示例区丑,演示了Keras如何簡化收集相關變量子集的工作拧粪。假設你正在訓練一個擁有共享trunk的multi-headed模型:
trunk = tf.keras.Sequential([...])
head1 = tf.keras.Sequential([...])
head2 = tf.keras.Sequential([...])
path1 = tf.keras.Sequential([trunk, head1])
path2 = tf.keras.Sequential([trunk, head2])
# 訓練主要數據集
for x, y in main_dataset:
with tf.GradientTape() as tape:
prediction = path1(x)
loss = loss_fn_head1(prediction, y)
# 同時優(yōu)化trunk和head1的權重
gradients = tape.gradient(loss, path1.trainable_variables)
optimizer.apply_gradients(zip(gradients, path1.trainable_variables))
# 微調第二個頭部,重用trunk
for x, y in small_dataset:
with tf.GradientTape() as tape:
prediction = path2(x)
loss = loss_fn_head2(prediction, y)
# 只優(yōu)化head2的權重沧侥,不是trunk的權重
gradients = tape.gradient(loss, head2.trainable_variables)
optimizer.apply_gradients(zip(gradients, head2.trainable_variables))
# 你可以發(fā)布trunk計算既们,以便他人重用。
tf.saved_model.save(trunk, output_path)
2.3. 結合tf.data.Datesets和@tf.function
當迭代適合內存訓練的數據時正什,可以隨意使用常規(guī)的Python迭代啥纸。除此之外,tf.data.Datesets
是從磁盤中傳輸訓練數據的最佳方式婴氮。
數據集可迭代(但不是迭代器)斯棒,就像其他Python迭代器在Eager模式下工作一樣。
您可以通過將代碼包裝在tf.function()
中來充分利用數據集異步預取/流功能主经,該代碼將Python迭代替換為使用AutoGraph的等效圖形操作荣暮。
@tf.function
def train(model, dataset, optimizer):
for x, y in dataset:
with tf.GradientTape() as tape:
prediction = model(x)
loss = loss_fn(prediction, y)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
如果使用Keras.fit()
API,就不必擔心數據集迭代:
model.compile(optimizer=optimizer, loss=loss_fn)
model.fit(dataset)
2.4. 利用AutoGraph和Python控制流程
AutoGraph提供了一種將依賴于數據的控制流轉換為圖形模式等價的方法罩驻,如tf.cond
和tf.while_loop
穗酥。
數據依賴控制流出現的一個常見位置是序列模型。tf.keras.layers.RNN
封裝一個RNN單元格惠遏,允許你您靜態(tài)或動態(tài)展開遞歸砾跃。
為了演示,您可以重新實現動態(tài)展開如下:
class DynamicRNN(tf.keras.Model):
def __init__(self, rnn_cell):
super(DynamicRNN, self).__init__(self)
self.cell = rnn_cell
def call(self, input_data):
# [batch, time, features] -> [time, batch, features]
input_data = tf.transpose(input_data, [1, 0, 2])
outputs = tf.TensorArray(tf.float32, input_data.shape[0])
state = self.cell.zero_state(input_data.shape[1], dtype=tf.float32)
for i in tf.range(input_data.shape[0]):
output, state = self.cell(input_data[i], state)
outputs = outputs.write(i, output)
return tf.transpose(outputs.stack(), [1, 0, 2]), state
有關AutoGraph功能的更詳細概述节吮,請參閱指南.抽高。
2.5. 使用tf.metrics聚合數據和tf.summary來記錄它
要記錄摘要,請使用tf.summary.(scalar|histogram|...)
并使用上下文管理器將其重定向到writer透绩。(如果省略上下文管理器翘骂,則不會發(fā)生任何事情壁熄。)與TF 1.x不同,摘要直接發(fā)送給writer碳竟;沒有單獨的merger
操作草丧,也沒有單獨的add_summary()
調用,這意味著必須在調用點提供步驟值莹桅。
summary_writer = tf.summary.create_file_writer('/tmp/summaries')
with summary_writer.as_default():
tf.summary.scalar('loss', 0.1, step=42)
要在將數據記錄為摘要之前聚合數據昌执,請使用tf.metrics
,Metrics是有狀態(tài)的统翩;
當你調用.result()
時,它們會累計值并返回累計結果此洲。使用.reset_states()
清除累計值厂汗。
def train(model, optimizer, dataset, log_freq=10):
avg_loss = tf.keras.metrics.Mean(name='loss', dtype=tf.float32)
for images, labels in dataset:
loss = train_step(model, optimizer, images, labels)
avg_loss.update_state(loss)
if tf.equal(optimizer.iterations % log_freq, 0):
tf.summary.scalar('loss', avg_loss.result(), step=optimizer.iterations)
avg_loss.reset_states()
def test(model, test_x, test_y, step_num):
loss = loss_fn(model(test_x), test_y)
tf.summary.scalar('loss', loss, step=step_num)
train_summary_writer = tf.summary.create_file_writer('/tmp/summaries/train')
test_summary_writer = tf.summary.create_file_writer('/tmp/summaries/test')
with train_summary_writer.as_default():
train(model, optimizer, dataset)
with test_summary_writer.as_default():
test(model, test_x, test_y, optimizer.iterations)
通過將TensorBoard指向摘要日志目錄來顯示生成的摘要:
tensorboard --logdir /tmp/summaries
最新版本:http://www.mashangxue123.com/tensorflow/tf2-guide-effective_tf2.html
英文版本:https://tensorflow.google.cn/alpha/guide/effective_tf2
翻譯建議PR:https://github.com/mashangxue/tensorflow2-zh/edit/master/r2/guide/effective_tf2.md