このページは Cloud Translation API によって翻訳されました。
Switch to English

効果的なTensorFlow 2

TensorFlow 2.0には、TensorFlowユーザーの生産性を高めるための複数の変更があります。 TensorFlow 2.0は冗長なAPIを削除し、APIの一貫性を高め( Unified RNNUnified Optimizers )、 Eager実行を備えたPythonランタイムとより適切に統合します。

多くのRFCがTensorFlow 2.0の作成に行った変更について説明しています。このガイドは、TensorFlow 2.0での開発がどのように見えるべきかについてのビジョンを提示します。 TensorFlow 1.xについてある程度理解していることを前提としています。

主な変更点の概要

APIクリーンアップ

多くのAPIは、TF 2.0で廃止または移動されています。主な変更点には、 tf.apptf.flags 、およびtf.loggingして、現在はオープンソースのabsl-pyを採用し 、tf.contribに存在していたプロジェクトをtf.contribし、メインのtf.*名前空間をクリーンアップすることが含まれます使用tf.math低い関数をtf.mathなどのサブパッケージに移動します。一部のAPIは、対応する2.0のtf.summarytf.keras.metrics 、およびtf.keras.optimizers置き換えられtf.keras.optimizers 。これらの名前変更を自動的に適用する最も簡単な方法は、 v2アップグレードスクリプトを使用することです。

熱心な実行

TensorFlow 1.Xでは、ユーザーがtf.* API呼び出しを実行して、手動で抽象構文ツリー (グラフ)をつなぎ合わせる必要があります。次に、一連の出力テンソルと入力テンソルをsession.run()呼び出しに渡して、抽象構文ツリーを手動でコンパイルする必要があります。 TensorFlow 2.0は熱心に実行され(Pythonの通常のように)、2.0では、グラフとセッションは実装の詳細のように感じられるはずです。

熱心な実行の注目すべき副産物の1つは、コードのすべての行が順番に実行されるため( tf.function内では、 tf.functionあるコードは記述された順に実行されるためtf.control_dependencies()tf.control_dependencies()が不要になったことです。

グローバルはもうありません

TensorFlow 1.Xは暗黙的にグローバルな名前空間に大きく依存していました。 tf.Variable()を呼び出すと、それはデフォルトのグラフに入れられ、たとえそれを指すPython変数の追跡を失っても、そこに残ります。その後、そのtf.Variableを復元できますが、それが作成されたときの名前を知っている場合に限ります。変数の作成を制御できない場合、これを行うのは困難でした。その結果、メカニズムのすべての種類は、再びその変数を見つけて、ユーザーが作成した変数見つけるためのフレームワークのためのヘルプのユーザーに試みるように増殖します。変数のスコープ、グローバルコレクション、のようなヘルパーメソッドtf.get_global_step() tf.global_variables_initializer()オプティマイザは、すべてのトレーニング可能な変数の勾配を暗黙的に計算します。 TensorFlow 2.0では、これらのメカニズム( 変数2.0 RFC )がすべて排除され、デフォルトのメカニズムが採用されています。変数を追跡してください! tf.Variable 、ガベージコレクションが行われます。

変数を追跡する必要があるため、ユーザーに追加の作業が発生しますが、Kerasオブジェクト(以下を参照)を使用すると、負担が最小限に抑えられます。

セッションではなく機能

session.run()呼び出しは、関数呼び出しとほぼ同じです。呼び出す入力と関数を指定すると、一連の出力が返されます。 TensorFlow 2.0では、 tf.function()を使用してPython関数を装飾し、JITコンパイル用にマークを付けて、TensorFlowが単一のグラフとして実行できるようにすることができます( Functions 2.0 RFC )。このメカニズムにより、TensorFlow 2.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追加するときにユーザーがコードを書き直す必要をなくすために、 オートグラフはPython構成要素のサブセットをTensorFlowの同等のものに変換します。

  • for / while > - tf.while_loopbreakcontinueサポートされています)
  • if - > tf.cond
  • for _ in dataset -> dataset.reduce

AutoGraphは、制御フローの任意のネストをサポートします。これにより、シーケンスモデル、強化学習、カスタムトレーニングループなど、多くの複雑なMLプログラムを効率的かつ簡潔に実装できます。

慣用的なTensorFlow 2.0の推奨事項

コードを小さな関数にリファクタリングする

TensorFlow 1.Xの一般的な使用パターンは「キッチンシンク」戦略でした。そこでは、可能なすべての計算の和集合が先制的にレイアウトされ、選択されたテンソルがsession.run()介して評価されました。 TensorFlow 2.0では、ユーザーはコードをリファクタリングして、必要に応じて呼び出される小さな関数にする必要があります。一般に、これらの小さな関数をtf.functiontf.function必要はありません。高レベルの計算を装飾するためにのみtf.functionを使用します-たとえば、トレーニングの1ステップまたはモデルのフォワードパス。

Kerasレイヤーとモデルを使用して変数を管理する

Kerasモデルとレイヤーは、便利なvariablestrainable_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オブジェクトからSavedModelを直接チェックポイントまたはエクスポートできます。これらの統合を利用するために、必ずしも.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が最適です。データセットは反復子(イテレーターではありません)であり、イーガーモードの他のPython 反復子と同じように機能します。コードをtf.function()でラップすることにより、データセットの非同期プリフェッチ/ストリーミング機能を完全に利用できます。これは、AutoGraphを使用して、Pythonの反復を同等のグラフ操作に置き換えます。

@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.condtf.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()呼び出しはありません。つまり、 step値を呼び出し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使用し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.functiontf.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]

これは、KerasモデルおよびEager実行をサポートするその他の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]))