將 tf.summary 用法遷移至 TF 2.x

在 TensorFlow.org 上檢視 在 Google Colab 中執行 在 GitHub 上檢視原始碼 下載筆記本
import tensorflow as tf
2022-12-14 12:08:16.683364: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory
2022-12-14 12:08:16.683474: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory
2022-12-14 12:08:16.683485: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.

TensorFlow 2.x 包括對 tf.summary API 的重大變更,此 API 用於寫入摘要資料,以便在 TensorBoard 中進行視覺化。

已變更的內容

tf.summary API 視為兩個子 API 會很有幫助

在 TF 1.x 中

這兩個部分必須手動連接在一起 - 透過 Session.run() 擷取摘要運算子輸出,並呼叫 FileWriter.add_summary(output, step)v1.summary.merge_all() 運算子使用圖形集合來彙總所有摘要運算子輸出,藉此簡化此作業,但這種方法在即時執行和控制流程方面仍然效果不佳,因此特別不適合 TF 2.x。

在 TF 2.X 中

這兩個部分緊密整合,現在個別的 tf.summary 運算子會在執行時立即寫入其資料。從您的模型程式碼使用 API 應該仍然看起來很熟悉,但現在它對即時執行很友善,同時保持與圖形模式相容。整合 API 的兩個部分表示 summary.FileWriter 現在是 TensorFlow 執行環境的一部分,並由 tf.summary 運算子直接存取,因此設定寫入器是看起來主要不同的部分。

搭配即時執行的範例用法,這是 TF 2.x 中的預設值

writer = tf.summary.create_file_writer("/tmp/mylogs/eager")

with writer.as_default():
  for step in range(100):
    # other model code would go here
    tf.summary.scalar("my_metric", 0.5, step=step)
    writer.flush()
ls /tmp/mylogs/eager
events.out.tfevents.1671019701.kokoro-gcp-ubuntu-prod-1817880111.7700.0.v2

搭配 tf.function 圖形執行的範例用法

writer = tf.summary.create_file_writer("/tmp/mylogs/tf_function")

@tf.function
def my_func(step):
  with writer.as_default():
    # other model code would go here
    tf.summary.scalar("my_metric", 0.5, step=step)

for step in tf.range(100, dtype=tf.int64):
  my_func(step)
  writer.flush()
ls /tmp/mylogs/tf_function
events.out.tfevents.1671019701.kokoro-gcp-ubuntu-prod-1817880111.7700.1.v2

搭配舊版 TF 1.x 圖形執行的範例用法

g = tf.compat.v1.Graph()
with g.as_default():
  step = tf.Variable(0, dtype=tf.int64)
  step_update = step.assign_add(1)
  writer = tf.summary.create_file_writer("/tmp/mylogs/session")
  with writer.as_default():
    tf.summary.scalar("my_metric", 0.5, step=step)
  all_summary_ops = tf.compat.v1.summary.all_v2_summary_ops()
  writer_flush = writer.flush()


with tf.compat.v1.Session(graph=g) as sess:
  sess.run([writer.init(), step.initializer])

  for i in range(100):
    sess.run(all_summary_ops)
    sess.run(step_update)
    sess.run(writer_flush)
ls /tmp/mylogs/session
events.out.tfevents.1671019702.kokoro-gcp-ubuntu-prod-1817880111.7700.2.v2

轉換您的程式碼

無法可靠地自動化將現有 tf.summary 用法轉換為 TF 2.x API,因此 tf_upgrade_v2 指令碼只會將其全部重寫為 tf.compat.v1.summary,且不會自動啟用 TF 2.x 行為。

部分遷移

為了讓仍然嚴重依賴 TF 1.x 摘要 API 記錄運算子 (例如 tf.compat.v1.summary.scalar()) 的模型程式碼使用者更容易遷移至 TF 2.x,可以先遷移寫入器 API,以便稍後完全遷移模型程式碼內的個別 TF 1.x 摘要運算子。

為了支援此遷移樣式,tf.compat.v1.summary 將在下列情況下自動轉發至其 TF 2.x 對等項目

請注意,當叫用 TF 2.x 摘要實作時,傳回值會是空的位元組字串張量,以避免重複寫入摘要。此外,輸入引數轉發會盡力而為,並非所有引數都會保留 (例如,將支援 family 引數,而將移除 collections)。

tf.compat.v1.summary.scalar 中叫用 tf.summary.scalar 行為的範例

# Enable eager execution.
tf.compat.v1.enable_v2_behavior()

# A default TF 2.x summary writer is available.
writer = tf.summary.create_file_writer("/tmp/mylogs/enable_v2_in_v1")
# A step is set for the writer.
with writer.as_default(step=0):
  # Below invokes `tf.summary.scalar`, and the return value is an empty bytestring.
  tf.compat.v1.summary.scalar('float', tf.constant(1.0), family="family")

完整遷移

若要完整遷移至 TF 2.x,您需要調整您的程式碼,如下所示

  1. 必須存在透過 .as_default() 設定的預設寫入器,才能使用摘要運算子

    • 這表示以即時方式執行運算子,或在圖形建構中使用運算子
    • 如果沒有預設寫入器,摘要運算子會變成靜音的無運算子
    • 預設寫入器 (尚未) 跨 @tf.function 執行界限傳播 - 它們僅在追蹤函式時偵測到 - 因此最佳做法是在函式主體內呼叫 writer.as_default(),並確保寫入器物件在 @tf.function 使用期間持續存在
  2. "步驟"值必須透過 step 引數傳遞至每個運算子

    • TensorBoard 需要步驟值才能將資料呈現為時間序列
    • 明確傳遞是必要的,因為已移除 TF 1.x 中的全域步驟,因此每個運算子都必須知道要讀取的所需步驟變數
    • 為了減少重複程式碼,以 tf.summary.experimental.set_step() 形式提供註冊預設步驟值的實驗性支援,但這是暫時性功能,可能會在不另行通知的情況下變更
  3. 個別摘要運算子的函式簽名已變更

    • 傳回值現在是布林值 (表示是否實際寫入摘要)
    • 第二個參數名稱 (如果使用) 已從 tensor 變更為 data
    • 已移除 collections 參數;集合僅適用於 TF 1.x
    • 已移除 family 參數;只需使用 tf.name_scope() 即可
  4. [僅適用於舊版圖形模式/工作階段執行使用者]

    • 首先使用 v1.Session.run(writer.init()) 初始化寫入器

    • 使用 v1.summary.all_v2_summary_ops() 取得目前圖形的所有 TF 2.x 摘要運算子,例如透過 Session.run() 執行它們

    • 使用 v1.Session.run(writer.flush()) 排清寫入器,close() 也是如此

如果您的 TF 1.x 程式碼改為使用 tf.contrib.summary API,則它與 TF 2.x API 更相似,因此 tf_upgrade_v2 指令碼將自動化大部分遷移步驟 (並針對任何無法完全遷移的用法發出警告或錯誤)。在大多數情況下,它只會將 API 呼叫重寫為 tf.compat.v2.summary;如果您只需要與 TF 2.x 相容,則可以捨棄 compat.v2,並直接將其參考為 tf.summary

其他提示

除了上述重要領域外,一些輔助方面也已變更

  • 條件式記錄 (例如「每 100 個步驟記錄一次」) 有新的外觀

    • 若要控制運算子和相關聯的程式碼,請將它們包裝在一般 if 陳述式中 (這在即時模式和 @tf.function 透過 autograph 中運作) 或 tf.cond
    • 若要僅控制摘要,請使用新的 tf.summary.record_if() 內容管理員,並將您選擇的布林條件傳遞給它
    • 這些取代了 TF 1.x 模式

      if condition:
        writer.add_summary()
      
  • 不直接寫入 tf.compat.v1.Graph - 請改為使用追蹤函式

  • 不再有每個 logdir 的全域寫入器快取與 tf.summary.FileWriterCache

    • 使用者應實作自己的寫入器物件快取/共用,或僅使用個別寫入器 (TensorBoard 對後者的支援正在進行中)
  • 事件檔案二進位表示法已變更

    • TensorBoard 1.x 已支援新格式;此差異僅影響手動從事件檔案剖析摘要資料的使用者
    • 摘要資料現在儲存為張量位元組;您可以使用 tf.make_ndarray(event.summary.value[0].tensor) 將其轉換為 numpy