{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "SB93Ge748VQs" }, "source": [ "##### Copyright 2019 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "id": "0sK8X2O9bTlz" }, "outputs": [], "source": [ "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." ] }, { "cell_type": "markdown", "metadata": { "id": "HEYuO5NFwDK9" }, "source": [ "# tf.summary の使用箇所を TF 2.0 に移行する\n", "\n", "\n", " \n", " \n", " \n", " \n", "
TensorFlow.org で表示 Google Colab で実行 GitHubでソースを表示ノートブックをダウンロード
" ] }, { "cell_type": "markdown", "metadata": { "id": "56V5oun18ZdZ" }, "source": [ "> 注意: このドキュメントは、TensorFlow 1.x TensorBoard に精通している方で大規模な TensorFlow コードベースを TensorFlow 1.x から 2.0 に移行したい方を対象としています。TensorBoard にまだ新しい方は、[基礎](get_started.ipynb)ドキュメントをご覧ください。`tf.keras` を使用している場合は、TensorFlow 2.0 にアップグレードするための作業が必要ない場合があります。\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "c50hsFk2MiWs" }, "outputs": [], "source": [ "import tensorflow as tf" ] }, { "cell_type": "markdown", "metadata": { "id": "56XvRdPy-ewT" }, "source": [ "TensorFlow 2.0 では、TensorBoard での視覚化に使用する要約データを書き込む際の `tf.summary` API が大幅に変更されています。" ] }, { "cell_type": "markdown", "metadata": { "id": "V_JOBTVzU5Cx" }, "source": [ "## 変更点\n", "\n", "`tf.summary` API を 2 つのサブ API として考えると良いでしょう。\n", "\n", "- 個別の要約を記録するための一連の演算 - `summary.scalar()`、`summary.histogram()`、`summary.image()`、`summary.audio()`、および `summary.text()`。これらはモデルコードからインラインで呼び出されます。\n", "- 上記の個別の要約を収集して特別にフォーマットされたログファイル(TensorBoard が読み取って視覚化を生成するファイル)に書き込む書き込みロジック。" ] }, { "cell_type": "markdown", "metadata": { "id": "9-rVv-EYU8_E" }, "source": [ "### TF 1.x の場合\n", "\n", "上記の 2 つは、`Session.run()` でサマリー演算の出力をフェッチし、`FileWriter.add_summary(output, step)` で呼び出す、というように手動でつなぐ必要がありました。`v1.summary.merge_all()` 演算によって、グラフコレクションを使ってすべてのサマリー演算出力を集計するという方法で、この処理が簡単になっていますが、Eager execution と制御フローではあまりよく機能しなかったため、TF 2.0 には特に適していません。" ] }, { "cell_type": "markdown", "metadata": { "id": "rh8R2g5FWbsQ" }, "source": [ "### TF 2.X の場合\n", "\n", "上記の 2 つは密接に統合されており、個別の `tf.summary` 演算は実行時に直ちにデータを書き込むようになっています。モデルコードから API を使用する方法はあまり変わっていませんが、Eager execution との相性が改善されており、ほかのグラフモードとの互換性もそのままです。この 2 つの API を統合することで、`summary.FileWriter` は TensorFlow 実行コンテキストの一環となり、`tf.summary` 演算で直接アクセスできるため、ライターの構成が外見的な主な違いと言えます。" ] }, { "cell_type": "markdown", "metadata": { "id": "em7GQju5VA0I" }, "source": [ "次は、TF 2.x のデフォルトのモードである Eager execution を使用した例です。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "GgFXOtSeVFqP" }, "outputs": [], "source": [ "writer = tf.summary.create_file_writer(\"/tmp/mylogs/eager\")\n", "\n", "with writer.as_default():\n", " for step in range(100):\n", " # other model code would go here\n", " tf.summary.scalar(\"my_metric\", 0.5, step=step)\n", " writer.flush()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "h5fk_NG7QKve" }, "outputs": [], "source": [ "ls /tmp/mylogs/eager" ] }, { "cell_type": "markdown", "metadata": { "id": "FvBBeFxZVLzW" }, "source": [ "次は、tf.function グラフ実行の使用例です。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "kovK0LEEVKjR" }, "outputs": [], "source": [ "writer = tf.summary.create_file_writer(\"/tmp/mylogs/tf_function\")\n", "\n", "@tf.function\n", "def my_func(step):\n", " with writer.as_default():\n", " # other model code would go here\n", " tf.summary.scalar(\"my_metric\", 0.5, step=step)\n", "\n", "for step in tf.range(100, dtype=tf.int64):\n", " my_func(step)\n", " writer.flush()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Qw5nHhRUSM7_" }, "outputs": [], "source": [ "ls /tmp/mylogs/tf_function" ] }, { "cell_type": "markdown", "metadata": { "id": "5SY6eYitUJH_" }, "source": [ "次は、レガシー TF 1.x グラフ実行の使用例です。\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "OyQgeqZhVRNB" }, "outputs": [], "source": [ "g = tf.compat.v1.Graph()\n", "with g.as_default():\n", " step = tf.Variable(0, dtype=tf.int64)\n", " step_update = step.assign_add(1)\n", " writer = tf.summary.create_file_writer(\"/tmp/mylogs/session\")\n", " with writer.as_default():\n", " tf.summary.scalar(\"my_metric\", 0.5, step=step)\n", " all_summary_ops = tf.compat.v1.summary.all_v2_summary_ops()\n", " writer_flush = writer.flush()\n", "\n", "\n", "with tf.compat.v1.Session(graph=g) as sess:\n", " sess.run([writer.init(), step.initializer])\n", "\n", " for i in range(100):\n", " sess.run(all_summary_ops)\n", " sess.run(step_update)\n", " sess.run(writer_flush) " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "iqKOyawnNQSH" }, "outputs": [], "source": [ "ls /tmp/mylogs/session" ] }, { "cell_type": "markdown", "metadata": { "id": "xEJIh4btVVRb" }, "source": [ "## コードを変換する\n", "\n", "既存の `tf.summary` の使用箇所を TF 2.x API に変換する作業を確実に変換することは困難であるため、[`tf_upgrade_v2` スクリプト](https://www.tensorflow.org/guide/upgrade)は、すべてを `tf.compat.v1.summary` に書き換えることだけを行い、自動的に TF 2.x の動作を有効化することはありません。" ] }, { "cell_type": "markdown", "metadata": { "id": "1972f8ff0073" }, "source": [ "### 部分移行\n", "\n", "`tf.compat.v1.summary.scalar()` といった TF 1.x サマリー API のロギング演算に大きく依存しているモデルコードを使用するユーザーが TF 2.x により簡単に移行できるようにするには、先にライター API のみを移行して、後でモデルコード内の個別の TF 1.x サマリー演算を完全に移行することができます。\n", "\n", "このような移行をサポートするために、tf.compat.v1.summary は以下の条件で TF 2.x 相当に自動的に転送されます。\n", "\n", "- 最も外側のコンテキストが Eager モードである\n", "- デフォルトの TF 2.x サマリーライターが設定されている\n", "- 空でないステップの値がライターに設定済みである(tf.summary.SummaryWriter.as_defaulttf.summary.experimental.set_step、または tf.compat.v1.train.create_global_step を使用)\n", "\n", "TF 2.x サマリー実装が呼び出されると、戻り値は空のバイト文字列テンソルになり、サマリーの書き込みが重複しないようにされることに注意してください。また、入力引数のフォワーディングはベストエフォートであり、すべての引数が保持されるとは限りません(たとえば、`family` 引数はサポートされていますが、`collections` は取り除かれます)。\n", "\n", "以下は、tf.compat.v1.summary.scalartf.summary.scalar の動作を呼び出す例です。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "6457297c0b9d" }, "outputs": [], "source": [ "# Enable eager execution.\n", "tf.compat.v1.enable_v2_behavior()\n", "\n", "# A default TF 2.x summary writer is available.\n", "writer = tf.summary.create_file_writer(\"/tmp/mylogs/enable_v2_in_v1\")\n", "# A step is set for the writer.\n", "with writer.as_default(step=0):\n", " # Below invokes `tf.summary.scalar`, and the return value is an empty bytestring.\n", " tf.compat.v1.summary.scalar('float', tf.constant(1.0), family=\"family\")" ] }, { "cell_type": "markdown", "metadata": { "id": "Pq4Fy1bSUdrZ" }, "source": [ "### 完全移行\n", "\n", "TF 2.x に全移行するには、コードを次のように適合させる必要があります。\n", "\n", "1. サマリー演算を使用するには、`.as_default()` によるデフォルトのライターセットが存在する必要がある\n", "\n", " - つまり、演算を Eager で実行するか、グラフ構造で演算を使用する\n", " - デフォルトのライターがない場合、サマリー演算はサイレントの no-op になる\n", " - デフォルトのライターは(まだ)`@tf.function` 実行境界に伝搬しません。関数がトレースされた場合にのみ検出されるため、関数の本文で `writer.as_default()` を呼び出し、`@tf.function` が使用される限りライターオブジェクトが存在し続けられるようにすることが、ベストプラクティスと言えます。\n", "\n", "2. 「step」値は `step` 引数で各演算に渡される必要がある\n", "\n", " - TensorBoard には、時系列としてデータをレンダリングするステップ値が必要です\n", " - TF 1.x のグローバルステップは削除されており、各演算は読み取るための希望する step 変数を知っておくために、明示的に渡す必要があります\n", " - ボイラープレートを減らすために、デフォルトステップを登録するための実験的サポートは`tf.summary.experimental.set_step()` として提供されていますが、これは暫定機能であり、予告なく変更される場合があります\n", "\n", "3. 個々のサマリー演算の関数シグネチャが変更されている\n", "\n", " - 戻り値はブール型になっています(要約が実際に書き込まれたかどうかを示す)\n", " - 2 番目のパラメータ名(使用される場合)が`tensor` から `data` に代わっています\n", " - `collections` パラメータが削除されています。collections は TF 1.x のみのパラメータです\n", " - `family` パラメータが削除されています。`tf.name_scope()` を使用してください\n", "\n", "4. [レガシーグラフモードのみ / セッション実行ユーザー]\n", "\n", " - 最初に `v1.Session.run(writer.init())` でライターを初期化します\n", "\n", " - `v1.summary.all_v2_summary_ops()` を使用して、現在のグラフに関するすべての TF 2.0 サマリー演算を取得します(`Session.run()` で実行するためなど)。\n", "\n", " - `v1.Session.run(writer.flush())` でライターをフラッシュし、`close()` でも同様にフラッシュします\n", "\n", "TF 1.x コードで `tf.contrib.summary` API を使用していた場合は、TF 2.0 API にはるかに似ているため、`tf_upgrade_v2` スクリプトを使って、ほとんどの移行ステップ(および完全に移行できない使用箇所の発行警告またはエラー)を自動化できます。ほとんどにおいて、API 呼び出しを `tf.compat.v2.summary` に書き直すだけであるため、TF 2.0+ との互換性のみが必要である場合は、`compat.v2` を削除して、`tf.summary` として参照するようにすることができます。" ] }, { "cell_type": "markdown", "metadata": { "id": "1GUZRWSkW3ZC" }, "source": [ "## その他のヒント\n", "\n", "上記の重要な分野に加え、一部の補助的な側面も変更されています。\n", "\n", "- 条件付き記録(「100 ステップごとにログ」など)が新しくなりました\n", "\n", " - 演算と関連するコードを制御するには、通常の if ステートメント(Eager モードと[自動グラフ作成経由の `@tf.function`](https://www.tensorflow.org/alpha/guide/autograph) で機能)か、`tf.cond` でラップします\n", " - 要約のみを制御するには、新しい `tf.summary.record_if()` コンテキストマネージャを使用して、選択したブール条件を渡します\n", " - TF 1.x パターンを置き換えます\n", " ```\n", " if condition:\n", " writer.add_summary()\n", " ```\n" ] }, { "cell_type": "markdown", "metadata": { "id": "9VMYrKn4Uh52" }, "source": [ "- `tf.compat.v1.Graph` を直接書き込めない代わりにトレース関数を使用します\n", "\n", " - TF 2.x のグラフ実行では、明示的な Graph の代わりに `@tf.function` が使用されます\n", " - TF 2.0 では、新しいトレース式 API である`tf.summary.trace_on()` と `tf.summary.trace_export()` を使用して、実行した関数グラフを記録します\n" ] }, { "cell_type": "markdown", "metadata": { "id": "UGItA6U0UkDx" }, "source": [ "- `tf.summary.FileWriterCache` の使用により、logdir ごとのグローバルライターのキャッシュが不要になります\n", "\n", " - ユーザーは、ライターオブジェクトの独自のキャッシュ/共有を実装するか、個別のライターを使用する必要があります(TensorBoard の後者のサポートは[開発中](https://github.com/tensorflow/tensorboard/issues/1063)です)\n" ] }, { "cell_type": "markdown", "metadata": { "id": "d7BQJVcsUnMp" }, "source": [ "- イベントファイルのバイナリ表現が変更されました\n", "\n", " - TensorBoard 1.x は、新しい形式をサポート済みです。この違いは、要約データをイベントファイルから手動で解析しているユーザーのみに影響します)\n", " - 要約データはテンソルバイトとして保存されるようになりました。`tf.make_ndarray(event.summary.value[0].tensor)` を使って numpy に変換できます。" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "migrate.ipynb", "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 0 }