TensorFlowユーザーの生産性を高めるために、TensorFlow2.0には複数の変更があります。 TensorFlow 2.0は、冗長なAPIを削除し、APIの一貫性を高め( Unified RNN 、 Unified Optimizers )、 Eager実行を使用してPythonランタイムとより適切に統合します。
多くのRFCは、TensorFlow2.0の作成に加えられた変更について説明しています。このガイドでは、TensorFlow2.0での開発がどのようになるかについてのビジョンを示します。 TensorFlow1.xにある程度精通していることを前提としています。
主な変更点の簡単な要約
APIクリーンアップ
TF 2.0では、多くのAPIが廃止または移動されています。主な変更点には、 tf.flags
、 tf.app
、 tf.flags
をtf.logging
して、現在オープンソースのabsl-pyを優先すること、tf.contribにあったプロジェクトをtf.contrib
すること、メインのtf.*
名前空間をクリーンアップすることなどがありますtf.*
あまり使用されない関数をtf.math
ようなサブパッケージに移動します。一部のAPIは、2.0に相当するもの( tf.summary
、 tf.keras.metrics
、およびtf.keras.optimizers
置き換えられtf.keras.optimizers
。これらの名前変更を自動的に適用する最も簡単な方法は、 v2アップグレードスクリプトを使用することです。
熱心な実行
TensorFlow 1.Xでは、ユーザーがtf.*
API呼び出しを行って、抽象構文ツリー(グラフ)を手動でつなぎ合わせる必要があります。次に、ユーザーは、出力テンソルと入力テンソルのセットをsession.run()
呼び出しに渡すことにより、抽象構文ツリーを手動でコンパイルする必要があります。 TensorFlow 2.0は(Pythonが通常行うように)熱心に実行され、2.0では、グラフとセッションは実装の詳細のように感じるはずです。
熱心な実行の注目すべき副産物の1つは、コードのすべての行が順番に実行されるため、 tf.control_dependencies()
が不要になることです( tf.function
内では、 tf.function
あるコードは記述された順序で実行されます)。
これ以上のグローバルはありません
TensorFlow 1.Xは、暗黙的にグローバルな名前空間に大きく依存していました。 tf.Variable()
を呼び出すと、デフォルトのグラフに配置され、それを指しているPython変数を見失っても、そこに残ります。その後、そのtf.Variable
を回復できますが、それが作成された名前を知っている場合に限ります。変数の作成を制御できない場合、これを行うのは困難でした。その結果、あらゆる種類のメカニズムが急増し、ユーザーが変数を再度見つけられるようにし、フレームワークがユーザーが作成した変数を見つけられるようにしました。変数スコープ、グローバルコレクション、 tf.get_global_step()
、 tf.global_variables_initializer()
などのヘルパーメソッド、オプティマイザーは、トレーニング可能なすべての変数の勾配を暗黙的に計算します。 TensorFlow 2.0は、デフォルトのメカニズムを優先して、これらのメカニズム( Variables 2.0 RFC )をすべて排除します。変数を追跡します。 tf.Variable
、ガベージコレクションが行われます。
変数を追跡する必要があるため、ユーザーにとって余分な作業が発生しますが、Kerasオブジェクト(以下を参照)を使用すると、負担が最小限に抑えられます。
セッションではなく機能
session.run()
呼び出しは、関数呼び出しとほとんど同じです。呼び出す入力と関数を指定すると、一連の出力が返されます。 TensorFlow 2.0では、 tf.function()
を使用してPython関数を装飾し、JITコンパイル用にマークして、TensorFlowが単一のグラフとして実行するようにすることができます( Functions 2.0 RFC )。このメカニズムにより、TensorFlow2.0はグラフモードのすべての利点を得ることができます。
- パフォーマンス:機能を最適化できます(ノードプルーニング、カーネルフュージョンなど)
- 移植性:関数はエクスポート/再インポートでき( 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は、モバイル、C ++、JavaScriptなどのPythonインタープリターがないコンテキストで実行されます。ユーザーが@tf.function
追加するときにコードを書き直す必要がないように、 AutoGraphはPythonコンストラクトのサブセットを同等のTensorFlowに変換します。
-
for
/while
> -tf.while_loop
(break
とcontinue
サポートされています) -
if
- >tf.cond
-
for _ in dataset
->dataset.reduce
AutoGraphは、制御フローの任意のネストをサポートします。これにより、シーケンスモデル、強化学習、カスタムトレーニングループなど、多くの複雑なMLプログラムをパフォーマンスと簡潔に実装できます。
慣用的なTensorFlow2.0の推奨事項
コードをより小さな関数にリファクタリングする
TensorFlow 1.Xの一般的な使用パターンは、「キッチンシンク」戦略でした。この戦略では、可能なすべての計算の和集合がプリエンプティブにレイアウトされ、選択されたテンソルがsession.run()
介して評価されました。 TensorFlow 2.0では、ユーザーはコードを必要に応じて呼び出される小さな関数にリファクタリングする必要があります。一般に、これらの小さな関数のそれぞれをtf.function
で装飾する必要はありません。 tf.function
を使用して、高レベルの計算を装飾するだけです。たとえば、トレーニングの1つのステップや、モデルのフォワードパスなどです。
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)
...
# You still have to manage w_i and b_i, and their shapes are defined far away from the code.
Kerasバージョンの場合:
# Each layer can be called, with a signature equivalent to 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, ...]
tf.train.Checkpointable
レイヤー/モデルはtf.train.Checkpointable
を継承し、 @tf.function
と統合されているため、 tf.train.Checkpointable
オブジェクトからSavedModelsを直接チェックポイントまたはエクスポートできます。これらの統合を利用するために、必ずしも.fit()
の.fit()
APIを使用する必要はありません。
これは、Kerasが関連する変数のサブセットを簡単に収集できることを示す転移学習の例です。共有トランクを使用してマルチヘッドモデルをトレーニングしているとしましょう。
trunk = tf.keras.Sequential([...])
head1 = tf.keras.Sequential([...])
head2 = tf.keras.Sequential([...])
path1 = tf.keras.Sequential([trunk, head1])
path2 = tf.keras.Sequential([trunk, head2])
# Train on primary dataset
for x, y in main_dataset:
with tf.GradientTape() as tape:
# training=True is only needed if there are layers with different
# behavior during training versus inference (e.g. Dropout).
prediction = path1(x, training=True)
loss = loss_fn_head1(prediction, y)
# Simultaneously optimize trunk and head1 weights.
gradients = tape.gradient(loss, path1.trainable_variables)
optimizer.apply_gradients(zip(gradients, path1.trainable_variables))
# Fine-tune second head, reusing the trunk
for x, y in small_dataset:
with tf.GradientTape() as tape:
# training=True is only needed if there are layers with different
# behavior during training versus inference (e.g. Dropout).
prediction = path2(x, training=True)
loss = loss_fn_head2(prediction, y)
# Only optimize head2 weights, not trunk weights
gradients = tape.gradient(loss, head2.trainable_variables)
optimizer.apply_gradients(zip(gradients, head2.trainable_variables))
# You can publish just the trunk computation for other people to reuse.
tf.saved_model.save(trunk, output_path)
tf.data.Datasetsと@ tf.functionを組み合わせる
メモリに収まるトレーニングデータを反復処理する場合は、通常のPython反復を自由に使用してください。それ以外の場合、tf.data.Dataset
は、トレーニングデータをディスクからストリーミングするための最良の方法です。データセットはイテレータ(イテレータではありません)であり、Eagerモードの他のPythonイテレータと同じように機能します。コードをtf.function()
でラップすることにより、データセットの非同期プリフェッチ/ストリーミング機能を完全に利用できます。これにより、Pythonの反復がAutoGraphを使用した同等のグラフ操作に置き換えられます。
@tf.function
def train(model, dataset, optimizer):
for x, y in dataset:
with tf.GradientTape() as tape:
# training=True is only needed if there are layers with different
# behavior during training versus inference (e.g. Dropout).
prediction = model(x, training=True)
loss = loss_fn(prediction, y)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
.fit()
APIを使用する場合、データセットの反復について心配する必要はありません。
model.compile(optimizer=optimizer, loss=loss_fn)
model.fit(dataset)
Python制御フローでAutoGraphを利用する
AutoGraphは、データに依存する制御フローを、 tf.cond
やtf.while_loop
などの同等のグラフモードに変換する方法を提供します。
データ依存の制御フローが現れる一般的な場所の1つは、シーケンスモデルです。 tf.keras.layers.RNN
はRNNセルをラップし、静的または動的に繰り返しを展開できるようにします。デモンストレーションのために、次のように動的展開を再実装できます。
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の機能の詳細については、ガイドを参照してください。
tf.metricsはデータを集約し、tf.summaryはそれらをログに記録します
要約をログに記録するには、 tf.summary.(scalar|histogram|...)
を使用し、コンテキストマネージャーを使用してライターにリダイレクトします。 (コンテキストマネージャーを省略した場合、何も起こりません。)TF 1.xとは異なり、要約はライターに直接送信されます。個別の「マージ」操作や個別のadd_summary()
呼び出しはありません。つまり、呼び出しadd_summary()
step
値を指定する必要があります。
summary_writer = tf.summary.create_file_writer('/tmp/summaries')
with summary_writer.as_default():
tf.summary.scalar('loss', 0.1, step=42)
データを要約としてログに記録する前にデータを集計するには、 tf.metrics
使用しtf.metrics
。メトリックはステートフルです.result()
を呼び出すと、値が累積され、累積結果が返されます。 .reset_states()
を.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):
# training=False is only needed if there are layers with different
# behavior during training versus inference (e.g. Dropout).
loss = loss_fn(model(test_x, training=False), 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
デバッグ時にtf.config.experimental_run_functions_eagerly()を使用します
TensorFlow 2.0では、Eagerを実行すると、コードを段階的に実行して、形状、データ型、値を検査できます。 tf.function
、 tf.keras
などの特定のAPIは、パフォーマンスと移植性のためにグラフ実行を使用するように設計されています。デバッグするときは、 tf.config.experimental_run_functions_eagerly(True)
を使用して、このコード内でEager実行を使用します。
例えば:
@tf.function
def f(x):
if x > 0:
import pdb
pdb.set_trace()
x = x + 1
return x
tf.config.experimental_run_functions_eagerly(True)
f(tf.constant(1))
>>> f()
-> x = x + 1
(Pdb) l
6 @tf.function
7 def f(x):
8 if x > 0:
9 import pdb
10 pdb.set_trace()
11 -> x = x + 1
12 return x
13
14 tf.config.experimental_run_functions_eagerly(True)
15 f(tf.constant(1))
[EOF]
これは、Eagerの実行をサポートするKerasモデルやその他のAPI内でも機能します。
class CustomModel(tf.keras.models.Model):
@tf.function
def call(self, input_data):
if tf.reduce_mean(input_data) > 0:
return input_data
else:
import pdb
pdb.set_trace()
return input_data // 2
tf.config.experimental_run_functions_eagerly(True)
model = CustomModel()
model(tf.constant([-2, -4]))
>>> call()
-> return input_data // 2
(Pdb) l
10 if tf.reduce_mean(input_data) > 0:
11 return input_data
12 else:
13 import pdb
14 pdb.set_trace()
15 -> return input_data // 2
16
17
18 tf.config.experimental_run_functions_eagerly(True)
19 model = CustomModel()
20 model(tf.constant([-2, -4]))