{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "Tce3stUlHN0L" }, "source": [ "##### Copyright 2021 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2024-01-11T17:45:44.555656Z", "iopub.status.busy": "2024-01-11T17:45:44.555082Z", "iopub.status.idle": "2024-01-11T17:45:44.558628Z", "shell.execute_reply": "2024-01-11T17:45:44.558025Z" }, "id": "tuOe1ymfHZPu" }, "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": "D70XgUYdLwI6" }, "source": [ "# TensorFlow 1.x と TensorFlow 2 の比較 - 動作と API" ] }, { "cell_type": "markdown", "metadata": { "id": "MfBg1C5NB3X0" }, "source": [ "
![]() | \n",
" ![]() | \n",
" ![]() | \n",
" ![]() | \n",
"
tf.*
API 呼び出しを作成して抽象構文ツリー(グラフ)を手動でつなぎ合わせ、出力テンソルと入力テンソルのセットを `session.run` 呼び出しに渡し、抽象構文ツリーを手動でコンパイルする必要がありました。TF2 はこれを(Python が通常行うように)eagerly に実行し 、グラフとセッションは実装の詳細のような感覚になっています。\n",
"\n",
"Eager execution の副産物として注目しておきたいのは、`tf.control_dependencies` が不要になったという点です。これは、コードのすべての行が順に実行されるようになったためです(`tf.function` 内では、副次的影響のあるコードは記述された順に実行されます)。"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "LH3YizX-9S7g"
},
"source": [
"## global の排除\n",
"\n",
"TF1.X では、グローバル名前空間とコレクションに暗黙的に大きく依存していました。`tf.Variable` を呼び出すと、デフォルトのグラフにあるコレクションに配置され、それをポイントする Python 変数を追跡できなくなってもグラフに残っていました。その `tf.Variable` は復元できますが、その作成に使用された名前がわかっている場合のみでした。変数の作成を管理していないユーザーにとっては困難なことでした。その結果、変数をもう一度見つけ出すためのさまざまな仕組みが生まれただけでなく、変数スコープ、グローバルコレクション、`tf.get_global_step` のようなヘルパーメソッド、`tf.global_variables_initializer`、すべてのトレーニング可能な変数の勾配を暗黙的に計算するオプティマイザなど、ユーザー作成変数を検索するフレームワークが急増しました。TF2 は、これらすべての仕組み([Variables 2.0 RFC](https://github.com/tensorflow/community/pull/11))を排除し、自分の変数は自分で追跡するというデフォルトの仕組みを採択します。`tf.Variable` を追跡できなくなると、ガベージコレクションによって収集されます。\n",
"\n",
"変数を追跡する必要があるため、余分な作業が発生しますが、[モデリングシム](./model_mapping.ipynb)などのツールと、[`tf.Module` および `tf.keras.layers.Layer` の暗黙的なオブジェクト指向変数コレクション](https://www.tensorflow.org/guide/intro_to_modules)などの動作により、負担は最小限に抑えられます。"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "NXwBgAjJ98J2"
},
"source": [
"## セッションではなく関数\n",
"\n",
"`session.run` 呼び出しは、ほぼ関数呼び出しと変わりません。入力と呼び出される関数を指定すると、一連の出力が返されます。TF2 では、`tf.function` を使って、TensorFlow が単一のグラフとして実行できるように Python 関数に JIT コンパイルのマークをつけます([Functions 2.0 RFC](https://github.com/tensorflow/community/pull/20))。この仕組みにより、TF2 は Graph モードのすべてのメリットを得ることができます。\n",
"\n",
"- パフォーマンス: 関数を最適化できます(ノード枝狩り、カーネル融合など)\n",
"- 移植性: 関数をエクスポート/再インポート([SavedModel 2.0 RFC](https://github.com/tensorflow/community/pull/34))できるため、モジュール型 TensorFlow 関数を再利用し共有することができます。\n",
"\n",
"```python\n",
"# TF1.x\n",
"outputs = session.run(f(placeholder), feed_dict={placeholder: input})\n",
"# TF2\n",
"outputs = f(input)\n",
"```\n",
"\n",
"Python と TensorFlow コードを自由に混在させられるため、Python の表現力を活用できます。ただし、移植可能な TensorFlow は、モバイル、C++、JavaScript など、Python インタープリターを使用しないコンテキストで実行されます。`tf.function`を追加する際にコードの書き直しを避けるには、[AutoGraph](https://tensorflow.org/guide/function) を使用して、Python コンストラクトのサブセットを TensorFlow の同等のものに変換します。\n",
"\n",
"- `for`/`while` -> `tf.while_loop` (`break` と `continue` はサポートされています)\n",
"- `if `-> `tf.cond`\n",
"- `for _ in dataset` -> `dataset.reduce`\n",
"\n",
"AutoGraph では制御フローを任意にネストできるため、シーケンスモデル、強化学習、カスタムトレーニングループなど、多くの複雑な ML プログラムを効率的かつ簡潔に実装することができます。"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Mj3gaj4tpi7O"
},
"source": [
"## TF 2.x の動作変更への適応\n",
"\n",
"TF2 への移行は、TF2 の動作の完全なセットに移行して初めて完了します。`tf.compat.v1.enable_v2_behaviors` および `tf.compat.v1.disable_v2_behaviors` を介して、動作の完全なセットを有効または無効にすることができます。以下のセクションでは、それぞれの主要な動作の変更について詳しく説明します。"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "_M0zEtR9p0XD"
},
"source": [
"### `tf.function` の使用\n",
"\n",
"移行中のプログラムへの最大の変更は、基本的なプログラミング モデルのパラダイムグラフとセッションから Eager execution と `tf.function` へのシフトから生じる可能性があります。Eager execution および `tf.function` と互換性のない API からそれらと互換性のある API への移行の詳細については、[TF2 移行ガイド](https://tensorflow.org/guide/migrate)を参照してください。\n",
"\n",
"注意: 移行中に、`tf.compat.v1.enable_eager_execution` と `tf.compat.v1.disable_eager_execution` を使用して eager execusion を直接有効または無効にすることを選択できますが、これはプログラムの有効期間中に一度だけ行うことができます。\n",
"\n",
"以下は、`tf.Graph` および `tf.compat.v1.Session` から `tf.function` による Eager execution に切り替える場合に問題を引き起こす可能性がある、いずれかの API に関連付けられていない一般的なプログラムパターンの一部です。"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "UgwEtwwN2PWy"
},
"source": [
"#### パターン 1: 1 回のみ実行することを目的とした Python オブジェクトの操作と変数の作成が、複数回実行される。\n",
"\n",
"\n",
"\n",
"グラフとセッションに依存する TF1.x プログラムでは、通常、プログラム内のすべての Python ロジックが 1 回だけ実行されることが期待されます。ただし、Eager execution および `tf.function` を使用すると、Python ロジックは少なくとも 1 回実行されますが、場合によってはそれ以上の回数(eagerly に複数回、または異なる `tf.function` トレース全体で複数回)が実行される可能性があります。時には、`tf.function` が同じ入力を 2 回トレースし、予期しない動作を引き起こすことさえあります(例 1 と 2 をご覧ください)。詳細については、`tf.function` [ガイド](https://www.tensorflow.org/guide/function)を参照してください。\n",
"\n",
"注意: 通常、このパターンにより、`tf.function` なしで eagerly に実行すると、コードが警告なしに誤動作しますが、問題のあるコードを `tf.function` 内にラップしようとすると、一般に `InaccessibleTensorError` または `ValueError` が発生します。この問題を発見してデバッグするには、早い段階でコードを `tf.function` でラップし、[pdb](https://docs.python.org/3/library/pdb.html) または対話型デバッグを使用して `InaccessibleTensorError` のソースを特定することをお勧めします。\n",
"\n",
"**例 1: 変数の作成**\n",
"\n",
"関数が呼び出されたときに変数を作成する以下の例を考えてみましょう。\n",
"\n",
"```python\n",
"def f():\n",
" v = tf.Variable(1.0)\n",
" return v\n",
"\n",
"with tf.Graph().as_default():\n",
" with tf.compat.v1.Session() as sess:\n",
" res = f()\n",
" sess.run(tf.compat.v1.global_variables_initializer())\n",
" sess.run(res)\n",
"```\n",
"\n",
"ただし、変数の作成を含む上記の関数を単純に `tf.function` でラップすることは許可されていません。`tf.function`は、[最初の呼び出しでのシングルトン変数の作成](https://www.tensorflow.org/guide/function#creating_tfvariables)のみをサポートします。これを強制するには、tf.function が最初の呼び出しで変数の作成を検出すると、再度トレースを試行し、2 番目のトレースで変数の作成がある場合はエラーを発生させます。\n",
"\n",
"```python\n",
"@tf.function\n",
"def f():\n",
" print(\"trace\") # This will print twice because the python body is run twice\n",
" v = tf.Variable(1.0)\n",
" return v\n",
"\n",
"try:\n",
" f()\n",
"except ValueError as e:\n",
" print(e)\n",
"```\n",
"\n",
"回避策として、変数を最初の呼び出しで作成した後にキャッシュして再利用します。\n",
"\n",
"```python\n",
"class Model(tf.Module):\n",
" def __init__(self):\n",
" self.v = None\n",
"\n",
" @tf.function\n",
" def __call__(self):\n",
" print(\"trace\") # This will print twice because the python body is run twice\n",
" if self.v is None:\n",
" self.v = tf.Variable(0)\n",
" return self.v\n",
"\n",
"m = Model()\n",
"m()\n",
"```\n",
"\n",
"**例 2: `tf.function` の再トレースによる範囲外のテンソル**\n",
"\n",
"例 1 で示したように、最初の呼び出しで変数の作成を検出すると、`tf.function` は再トレースします。2 つのトレースで 2 つのグラフが作成されるため、これはさらに混乱を招く可能性があります。再トレースからの 2 番目のグラフが最初のトレース中に生成されたグラフからテンソルにアクセスしようとすると、Tensorflow ではテンソルが範囲外であるというエラーを発生します。シナリオを示すために、以下のコードは最初の `tf.function` 呼び出しでデータセットを作成します。これは期待どおりに実行されます。\n",
"\n",
"```python\n",
"class Model(tf.Module):\n",
" def __init__(self):\n",
" self.dataset = None\n",
"\n",
" @tf.function\n",
" def __call__(self):\n",
" print(\"trace\") # This will print once: only traced once\n",
" if self.dataset is None:\n",
" self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])\n",
" it = iter(self.dataset)\n",
" return next(it)\n",
"\n",
"m = Model()\n",
"m()\n",
"```\n",
"\n",
"ただし、最初の `tf.function` 呼び出しで変数を作成しようとすると、コードはデータセットが範囲外であることを通知するエラーを発生させます。これは、データセットが最初のグラフにあり、2 番目のグラフもそれにアクセスしようとしているためです。\n",
"\n",
"```python\n",
"class Model(tf.Module):\n",
" def __init__(self):\n",
" self.v = None\n",
" self.dataset = None\n",
"\n",
" @tf.function\n",
" def __call__(self):\n",
" print(\"trace\") # This will print twice because the python body is run twice\n",
" if self.v is None:\n",
" self.v = tf.Variable(0)\n",
" if self.dataset is None:\n",
" self.dataset = tf.data.Dataset.from_tensors([1, 2, 3])\n",
" it = iter(self.dataset)\n",
" return [self.v, next(it)]\n",
"\n",
"m = Model()\n",
"try:\n",
" m()\n",
"except TypeError as e:\n",
" print(e) # tf.function
の外部でこの初期値のキャッシュが発生するように、コードを誤って構造化することがよくあることに注意してください。そのため、プログラムがこのパターンの影響を受けやすいことがわかっている場合は、特に注意してください。\n",
"\n",
"このパターンの一般的な解決策は、コードを再構築するか、必要に応じて Python callable を使用して、値が誤ってキャッシュされるのではなく、毎回再計算されるようにすることです。\n",
"\n",
"**例 1: グローバルステップに依存する、学習率やハイパーパラメータなどのスケジュール**\n",
"\n",
"次のコードスニペットでは、セッションが実行されるたびに最新の `global_step` 値が読み取られ、新しい学習率が計算されることが期待されます。\n",
"\n",
"```python\n",
"g = tf.Graph()\n",
"with g.as_default():\n",
" ...\n",
" global_step = tf.Variable(0)\n",
" learning_rate = 1.0 / global_step\n",
" opt = tf.compat.v1.train.GradientDescentOptimizer(learning_rate)\n",
" ...\n",
" global_step.assign_add(1)\n",
"...\n",
"sess = tf.compat.v1.Session(graph=g)\n",
"sess.run(...)\n",
"```\n",
"\n",
"ただし、eager に切り替えようとする場合は、意図したスケジュールに従うのではなく、学習率が 1 回だけ計算されてから再利用されることに注意してください。\n",
"\n",
"```python\n",
"global_step = tf.Variable(0)\n",
"learning_rate = 1.0 / global_step # Wrong! Only computed once!\n",
"opt = tf.keras.optimizers.SGD(learning_rate)\n",
"\n",
"def train_step(...):\n",
" ...\n",
" opt.apply_gradients(...)\n",
" global_step.assign_add(1)\n",
" ...\n",
"```\n",
"\n",
"この特定の例は一般的なパターンであり、オプティマイザは各トレーニングステップではなく 1 回だけ初期化する必要があるため、TF2 オプティマイザは学習率やその他のハイパーパラメータの引数として `tf.keras.optimizers.schedules.LearningRateSchedule` スケジュールまたは Python callable をサポートします。\n",
"\n",
"**例 2: オブジェクト属性として割り当てられ、ポインターを介して再利用されるシンボリック乱数の初期化が、eager への切り替え時に誤ってキャッシュされる**\n",
"\n",
"次の `NoiseAdder` モジュールを考えてみましょう。\n",
"\n",
"```python\n",
"class NoiseAdder(tf.Module):\n",
" def __init__(shape, mean):\n",
" self.noise_distribution = tf.random.normal(shape=shape, mean=mean)\n",
" self.trainable_scale = tf.Variable(1.0, trainable=True)\n",
" \n",
" def add_noise(input):\n",
" return (self.noise_distribution + input) * self.trainable_scale\n",
"```\n",
"\n",
"TF1.x で次のように使用すると、セッションが実行されるたびに新しいランダムノイズテンソルが計算されます。\n",
"\n",
"```python\n",
"g = tf.Graph()\n",
"with g.as_default():\n",
" ...\n",
" # initialize all variable-containing objects\n",
" noise_adder = NoiseAdder(shape, mean)\n",
" ...\n",
" # computation pass\n",
" x_with_noise = noise_adder.add_noise(x)\n",
" ...\n",
"...\n",
"sess = tf.compat.v1.Session(graph=g)\n",
"sess.run(...)\n",
"```\n",
"\n",
"ただし、TF2 では、最初に `noise_adder` を初期化すると、`noise_distribution` が 1 回だけ計算され、すべてのトレーニングステップで凍結されます。\n",
"\n",
"```python\n",
"...\n",
"# initialize all variable-containing objects\n",
"noise_adder = NoiseAdder(shape, mean) # Freezes `self.noise_distribution`!\n",
"...\n",
"# computation pass\n",
"x_with_noise = noise_adder.add_noise(x)\n",
"...\n",
"```\n",
"\n",
"これを修正するには、毎回同じテンソルオブジェクトを参照する代わりに、新しいランダムテンソルが必要になるたびに `NoiseAdder` を呼び出すように `tf.random.normal` をリファクタリングします。\n",
"\n",
"```python\n",
"class NoiseAdder(tf.Module):\n",
" def __init__(shape, mean):\n",
" self.noise_distribution = lambda: tf.random.normal(shape=shape, mean=mean)\n",
" self.trainable_scale = tf.Variable(1.0, trainable=True)\n",
" \n",
" def add_noise(input):\n",
" return (self.noise_distribution() + input) * self.trainable_scale\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "j2PXkSflCaCl"
},
"source": [
"#### パターン 3: TF1.x コードはテンソルに直接依存し、名前で検索する\n",
"\n",
"\n",
"\n",
"TF1.x コードテストでは、グラフに存在するテンソルまたは演算のチェックに依存するのが一般的です。まれに、モデリングコードもこれらの名前による検索に依存することがあります。\n",
"\n",
"`tf.function` の外で eagerly に実行する場合、テンソル名はまったく生成されないため、`tf.Tensor.name` のすべての使用は `tf.function` 内で発生する必要があります。実際に生成された名前は、同じ `tf.function` 内であっても TF1.x と TF2 の間で異なる可能性が非常に高く、API 保証は TF バージョン間で生成された名前の安定性を保証しないことに注意してください。\n",
"\n",
"注意: 変数名は `tf.function` の外部でも生成されますが、[モデルマッピングガイド](./model_mapping.ipynb)の関連セクションに従う場合を除き、その名前が TF1.x と TF2 の間で一致することは保証されません。\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5NB3bycl5Lde"
},
"source": [
"#### パターン 4: TF1.x セッションが、生成されたグラフの一部のみを選択的に実行する\n",
"\n",
"\n",
"\n",
"TF1.x では、グラフを構築し、グラフ内のすべての演算を実行する必要のない入力と出力のセットを選択することにより、セッションでそのサブセットのみを選択的に実行することを選択できます。\n",
"\n",
"例えば、1 つのグラフ内にジェネレータとディスクリミネータの両方を持ち、個別の `tf.compat.v1.Session.run` 呼び出しを使用して、ディスクリミネータのみのトレーニングとジェネレータのみのトレーニングを交互に行うことができます。\n",
"\n",
"TF2 では、`tf.function` の自動制御の依存関係と Eager execution により、`tf.function` トレースの選択的な枝刈りはありません。例えば、ディスクリミネータまたはジェネレータの出力のみが `tf.function` から出力される場合でも、すべての変数の更新を含む完全なグラフが実行されます。\n",
"\n",
"したがって、プログラムのさまざまな部分を含む複数の `tf.function` を使用するか、実際に実行したいものだけを実行するために分岐する `tf.function` への条件付き引数を使用する必要があります。"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "CnNaUmROp5fV"
},
"source": [
"### コレクションの削除\n",
"\n",
"Eager execution が有効になっている場合、グラフコレクション関連の `compat.v1` API(`tf.compat.v1.trainable_variables` などの内部でコレクションを読み書きする API を含む)は使用できなくなります。`ValueError` を発生させるものもあれば、警告なしに空のリストを返すものもあります。\n",
"\n",
"TF1.x でのコレクションの最も標準的な使用法は、初期化子、グローバルステップ、重み、正則化損失、モデル出力損失、および `BatchNormalization` レイヤーなどから実行する必要がある変数の更新を維持することです。\n",
"\n",
"これらの標準的な使用法をそれぞれ処理するには、次のようにします。\n",
"\n",
"1. 初期化子 - 無視します。Eager execution が有効になっている場合、変数の手動初期化は必要ありません。\n",
"2. グローバルステップ - 移行手順については、`tf.compat.v1.train.get_or_create_global_step` のドキュメントをご覧ください。\n",
"3. 重み - モデルマッピングガイドのガイダンスに従って、モデルを `tf.Module`/`tf.keras.layers.Layer`/ tf.keras.Model
にマップし、`tf.module.trainable_variables` などのそれぞれの重み追跡の仕組みを使用します。\n",
"4. 正則化損失 - モデルマッピングガイドのガイダンスに従ってモデルを `tf.Module`/ `tf.keras.layers.Layer`/ tf.keras.Model
にマッピングし、 `tf.keras.losses` を使用します。または、正則化損失を手動で追跡することもできます。\n",
"5. モデル出力損失 - `tf.keras.Model` 損失管理の仕組みを使用するか、コレクションを使用せずに損失を個別に追跡します。\n",
"6. 重みの更新 - このコレクションを無視します。Eager execution と `tf.function`(autograph と auto-control-dependencies を使用)は、すべての変数の更新が自動的に実行されることを意味します。そのため、最後にすべての重みの更新を明示的に実行する必要はありませんが、コントロールの依存関係の使用方法によっては、TF1.x コードとは異なるタイミングで重みの更新が行われる可能性があることに注意してください。\n",
"7. 概要 - [移行概要 API ガイド](https://www.tensorflow.org/tensorboard/migrate)を参照してください。\n",
"\n",
"より複雑なコレクションの使用(カスタムコレクションの使用など)では、コードをリファクタリングして、独自のグローバルストアを維持するか、グローバルストアにまったく依存しないようにする必要がある場合があります。"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "8J_ckZstp8y1"
},
"source": [
"### `ReferenceVariables` の代わりに `ResourceVariables`\n",
"\n",
"`ResourceVariables` には、`ReferenceVariables` よりも強力な読み書き一貫性保証があります。これにより、変数を使用するときに以前の書き込みの結果を観察するかどうかについて、より予測可能で推論しやすいセマンティクスが得られます。この変更により、既存のコードでエラーが発生したり、 警告なしに中断したりする可能性はほとんどありません。\n",
"\n",
"ただし、これらの強力な一貫性の保証により、特定のプログラムのメモリ使用量が増加する***可能性は低いですが、あります***。これが当てはまる場合は、[問題](https://github.com/tensorflow/tensorflow/issues)を提出してください。さらに、変数の読み取りに対応するグラフ内の演算子名に対する正確な文字列比較に依存する単体テストがある場合は、リソース変数を有効にすると、これらの演算子名がわずかに変更される可能性があることに注意してください。\n",
"\n",
"コードに対するこの動作変更の影響を分離するために、Eager execution が無効になっている場合、`tf.compat.v1.disable_resource_variables()` および `tf.compat.v1.enable_resource_variables()` を使用して、この動作変更をグローバルに無効または有効にすることができます。Eager execution が有効になっている場合、`ResourceVariables` は常に使用されます。\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "FTU-4P1vux0e"
},
"source": [
"### 制御フロー v2\n",
"\n",
"TF1.x では、`tf.cond` や `tf.while_loop` などの制御フロー演算は、`Switch`、`Merge` などのインラインの低レベル演算です。TF2 は、すべてのブランチに対して個別の `tf.function` トレースで実装され、高次微分をサポートする、改善された関数制御フロー演算を提供します。\n",
"\n",
"コードに対するこの動作変更の影響を分離するために、eager execution が無効になっている場合、`tf.compat.v1.disable_control_flow_v2()` および `tf.compat.v1.enable_control_flow_v2()` を使用して、この動作変更をグローバルに無効または有効にすることができます。ただし、eager execution も無効になっている場合にのみ、制御フロー v2 を無効にできます。Eager execution を有効にすると、制御フロー v2 が常に使用されます。\n",
"\n",
"この動作の変更により、制御フローを使用する生成された TF プログラムの構造が劇的に変化する可能性があります。これは、1 つのフラットグラフではなく、複数のネストされた関数トレースが含まれるためです。そのため、生成されたトレースの正確なセマンティクスに大きく依存するコードは、何らかの変更が必要になる場合があります。これには次が含まれます。\n",
"\n",
"- 演算子名とテンソル名に依存するコード\n",
"- そのブランチの外側から TensorFlow 制御フローのブランチ内で作成されたテンソルを参照するコード。これは `InaccessibleTensorError` を生成する可能性があります\n",
"\n",
"この動作の変更は、パフォーマンスをニュートラルから肯定的にすることを目的としていますが、制御フロー v2 のパフォーマンスが TF1.x 制御フローよりも悪いという問題が発生した場合は、再現手順で[問題](https://github.com/tensorflow/tensorflow/issues)を報告してください。 "
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "W7VwgVCGqE9S"
},
"source": [
"## TensorShape API の動作の変更\n",
"\n",
"`TensorShape` クラスは、`tf.compat.v1.Dimension` オブジェクトの代わりに `int` を保持するように単純化されました。したがって、`int` を取得するために `.value` を呼び出す必要はありません。\n",
"\n",
"個々の tf.compat.v1.Dimension オブジェクトは依然として tf.TensorShape.dims からアクセス可能です。\n",
"\n",
"コードに対するこの動作変更の影響を分離するには、`tf.compat.v1.disable_v2_tensorshape()` および `tf.compat.v1.enable_v2_tensorshape()` を使用して、この動作変更をグローバルに無効または有効にすることができます。"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "x36cWcmM8Eu1"
},
"source": [
"以下は、TF1.x と TF2 の違いを示しています。"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:44.563968Z",
"iopub.status.busy": "2024-01-11T17:45:44.563404Z",
"iopub.status.idle": "2024-01-11T17:45:46.878221Z",
"shell.execute_reply": "2024-01-11T17:45:46.877530Z"
},
"id": "QF4un9UpVTRA"
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-01-11 17:45:44.989156: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:9261] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\n",
"2024-01-11 17:45:44.989204: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:607] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered\n",
"2024-01-11 17:45:44.990677: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1515] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\n"
]
}
],
"source": [
"import tensorflow as tf"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:46.882491Z",
"iopub.status.busy": "2024-01-11T17:45:46.881792Z",
"iopub.status.idle": "2024-01-11T17:45:46.888974Z",
"shell.execute_reply": "2024-01-11T17:45:46.888355Z"
},
"id": "PbpD-kHOZR4A"
},
"outputs": [
{
"data": {
"text/plain": [
"TensorShape([16, None, 256])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Create a shape and choose an index\n",
"i = 0\n",
"shape = tf.TensorShape([16, None, 256])\n",
"shape"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "kDFck03neNy0"
},
"source": [
"TF1.x に以下があるとします。\n",
"\n",
"```python\n",
"value = shape[i].value\n",
"```\n",
"\n",
"TF2 ではこのようになります。\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:46.892055Z",
"iopub.status.busy": "2024-01-11T17:45:46.891820Z",
"iopub.status.idle": "2024-01-11T17:45:46.895817Z",
"shell.execute_reply": "2024-01-11T17:45:46.895193Z"
},
"id": "KuR73QGEeNdH"
},
"outputs": [
{
"data": {
"text/plain": [
"16"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"value = shape[i]\n",
"value"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "bPWPNKRiZmkd"
},
"source": [
"TF1.x に以下があるとします。\n",
"\n",
"```python\n",
"for dim in shape:\n",
" value = dim.value\n",
" print(value)\n",
"```\n",
"\n",
"TF2 ではこのようになります。"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:46.899049Z",
"iopub.status.busy": "2024-01-11T17:45:46.898601Z",
"iopub.status.idle": "2024-01-11T17:45:46.901979Z",
"shell.execute_reply": "2024-01-11T17:45:46.901355Z"
},
"id": "y6s0vuuprJfc"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"16\n",
"None\n",
"256\n"
]
}
],
"source": [
"for value in shape:\n",
" print(value)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "YpRgngu3Zw-A"
},
"source": [
"TF1.x に以下があるとします(またはその他の次元メソッドを使用していたとします)。\n",
"\n",
"```python\n",
"dim = shape[i]\n",
"dim.assert_is_compatible_with(other_dim)\n",
"```\n",
"\n",
"TF2 ではこのようになります。"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:46.905355Z",
"iopub.status.busy": "2024-01-11T17:45:46.904796Z",
"iopub.status.idle": "2024-01-11T17:45:46.909655Z",
"shell.execute_reply": "2024-01-11T17:45:46.909075Z"
},
"id": "LpViGEcUZDGX"
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"other_dim = 16\n",
"Dimension = tf.compat.v1.Dimension\n",
"\n",
"if shape.rank is None:\n",
" dim = Dimension(None)\n",
"else:\n",
" dim = shape.dims[i]\n",
"dim.is_compatible_with(other_dim) # or any other dimension method"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:46.912597Z",
"iopub.status.busy": "2024-01-11T17:45:46.912365Z",
"iopub.status.idle": "2024-01-11T17:45:46.915622Z",
"shell.execute_reply": "2024-01-11T17:45:46.914957Z"
},
"id": "GaiGe36dOdZ_"
},
"outputs": [],
"source": [
"shape = tf.TensorShape(None)\n",
"\n",
"if shape:\n",
" dim = shape.dims[i]\n",
" dim.is_compatible_with(other_dim) # or any other dimension method"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "3kLLY0I3PI-l"
},
"source": [
"tf.TensorShape のブール型の値は、階数がわかっている場合は Trueで、そうでない場合は False です。"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:46.918891Z",
"iopub.status.busy": "2024-01-11T17:45:46.918356Z",
"iopub.status.idle": "2024-01-11T17:45:46.923237Z",
"shell.execute_reply": "2024-01-11T17:45:46.922646Z"
},
"id": "-Ow1ndKpOnJd"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"True\n",
"True\n",
"True\n",
"True\n",
"True\n",
"True\n",
"\n",
"False\n"
]
}
],
"source": [
"print(bool(tf.TensorShape([]))) # Scalar\n",
"print(bool(tf.TensorShape([0]))) # 0-length vector\n",
"print(bool(tf.TensorShape([1]))) # 1-length vector\n",
"print(bool(tf.TensorShape([None]))) # Unknown-length vector\n",
"print(bool(tf.TensorShape([1, 10, 100]))) # 3D tensor\n",
"print(bool(tf.TensorShape([None, None, None]))) # 3D tensor with no known dimensions\n",
"print()\n",
"print(bool(tf.TensorShape(None))) # A tensor with unknown rank."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "KvfEd-uSsWqN"
},
"source": [
"### TensorShape の変更による潜在的なエラー\n",
"\n",
"TensorShape の動作の変更によって、コードが警告なしに壊れることはほとんどありません。ただし、形状関連のコードが `AttributeError` を発生させ始めるかもしれません。`int` および `None` は `tf.compat.v1.Dimension` と同じ属性を持っていません。以下に、これらの `AttributeError` の例をいくつか示します。"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:46.926579Z",
"iopub.status.busy": "2024-01-11T17:45:46.926131Z",
"iopub.status.idle": "2024-01-11T17:45:46.930076Z",
"shell.execute_reply": "2024-01-11T17:45:46.929389Z"
},
"id": "r18f8JAGsQi6"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"'int' object has no attribute 'value'\n"
]
}
],
"source": [
"try:\n",
" # Create a shape and choose an index\n",
" shape = tf.TensorShape([16, None, 256])\n",
" value = shape[0].value\n",
"except AttributeError as e:\n",
" # 'int' object has no attribute 'value'\n",
" print(e)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:46.933305Z",
"iopub.status.busy": "2024-01-11T17:45:46.932790Z",
"iopub.status.idle": "2024-01-11T17:45:46.936749Z",
"shell.execute_reply": "2024-01-11T17:45:46.936139Z"
},
"id": "t9flHru1uIdT"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"'NoneType' object has no attribute 'assert_is_compatible_with'\n"
]
}
],
"source": [
"try:\n",
" # Create a shape and choose an index\n",
" shape = tf.TensorShape([16, None, 256])\n",
" dim = shape[1]\n",
" other_dim = shape[2]\n",
" dim.assert_is_compatible_with(other_dim)\n",
"except AttributeError as e:\n",
" # 'NoneType' object has no attribute 'assert_is_compatible_with'\n",
" print(e)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Og7H_TwJqIOF"
},
"source": [
"## 値によるテンソルの等価性\n",
"\n",
"変数とテンソルのバイナリ `==` および `!=` 演算子は、TF1.x でのようにオブジェクト参照で比較するのではなく、TF2 では値で比較するように変更されました。さらに、テンソルと変数は、値でハッシュすることができない可能性があるため、セットまたは dict キーで直接ハッシュ可能または使用可能ではなくなりました。代わりに、テンソルまたは変数へのハッシュ可能な参照を取得するために使用できる `.ref()` メソッドを公開します。\n",
"\n",
"この動作変更の影響を分離するために、`tf.compat.v1.disable_tensor_equality()` および `tf.compat.v1.enable_tensor_equality()` を使用して、この動作変更をグローバルに無効または有効にすることができます。"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "NGN4oL3lz0ki"
},
"source": [
"例えば、TF1.x では、`==` 演算子を使用すると、同じ値を持つ 2 つの変数は false を返します。"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:46.939841Z",
"iopub.status.busy": "2024-01-11T17:45:46.939591Z",
"iopub.status.idle": "2024-01-11T17:45:49.170965Z",
"shell.execute_reply": "2024-01-11T17:45:49.170340Z"
},
"id": "dkGPGpEZ5DI-"
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tf.compat.v1.disable_tensor_equality()\n",
"x = tf.Variable(0.0)\n",
"y = tf.Variable(0.0)\n",
"\n",
"x == y"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "RqbewjIFz_oz"
},
"source": [
"テンソル等価性チェックが有効になっている TF2 では、`x == y` は `True` を返します。"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"execution": {
"iopub.execute_input": "2024-01-11T17:45:49.174714Z",
"iopub.status.busy": "2024-01-11T17:45:49.174088Z",
"iopub.status.idle": "2024-01-11T17:45:49.184039Z",
"shell.execute_reply": "2024-01-11T17:45:49.183480Z"
},
"id": "V5P_Rwy-zxVE"
},
"outputs": [
{
"data": {
"text/plain": [
"