TensorFlow 2.0 Alpha : 効果的な TensorFlow 2.0 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/09/2019
* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
効果的な TensorFlow 2.0
TensorFlow ユーザをより生産力を高くするために TensorFlow 2.0 で複数の変更があります。TensorFlow 2.0 は 冗長な API を除去し、API をより首尾一貫性のあるものとし (統一 (= unified) RNN, 統一 Optimizer)、そして Eager execution で Python ランタイムとより良く統合します。
多くの RFC は TensorFlow 2.0 の生成に注がれてきた変更を説明しています。このガイドは TensorFlow 2.0 の開発がどのようなものであるべきかのビジョンを提示します。貴方に TensorFlow 1.x への何某かの馴染みがあることが仮定されます。
主要な変更の簡潔な要約
API クリーンアップ
多くの API が なくなるか TF 2.0 に移されました。主要な変更の幾つかは、新しいオープンソース absl-py を支持して tf.app, tf.flags と tf.logging の除去、tf.contrib に存在するプロジェクトの rehome、そしてあまり使用されない関数を tf.math のようなサブパッケージに移動することによる主要な tf.* 名前空間のクリーンアップを含みます。幾つかの API はそれらの 2.0 同値 – tf.summary, tf.keras.metrics と tf.keras.optimizers に置き換えられました。これらの名前変更を自動的に適用する最も容易な方法は v2 upgrade スクリプト を利用することです。
Eager execution
TensorFlow 1.X は tf.* API コールによりユーザに 抽象構文木 (グラフ) を手動で一緒に縫い合わせる (= stitch) ことを要求しました。そしてそれは出力 tensor と入力 tensor のセットを session.run() コールに渡すことにより抽象構文木を手動でコンパイルすることをユーザに要求します。TensorFlow 2.0 は (Python が通常行なうように) しきりに・熱心に (= eagerly) 実行します、そして 2.0 ではグラフとセッションは実装詳細のような感触です。
eager execution の一つの注目すべき副産物は tf.control_dependencies() はもはや必要とされないことです、何故ならばコードの総ての行は順番に実行されるからです (tf.function 内で、副作用を持つコードは書かれた順番に実行されます)。
No more globals
TensorFlow 1.X はグローバル名前空間に暗黙的に大きく依拠していました。tf.Variable() を呼び出すとき、それはデフォルト・グラフに置かれ、それを指す Python 変数の追跡を貴方が失った場合でさえもそこに存続します。それからその tf.Variable をリカバーできる可能性がありますが、貴方がそれが作成されたときの名前を知る場合に限ります。貴方が変数の作成を管理していなかった場合にはこれは行なうことが困難でした。結果的に、ユーザがそれらの変数を再度見つけることを助けようとするために、そしてフレームワークのためにはユーザが作成した変数を見つけるために総ての種類のメカニズムが増殖しました : Variable scopes, global collections, tf.get_global_step(), tf.global_variables_initializer() のようなヘルパーメソッド、総ての訓練可能な変数に渡り暗黙的に勾配を計算する optimizer、等々。TensorFlow 2.0 はこれらのメカニズムの総てをデフォルト・メカニズムのために除去しました (Variables 2.0 RFC) : 貴方の変数を追跡してください!tf.Variable の追跡を失う場合、それはガーベッジが収集されます。変数を追跡するための要件は幾つかの追加の作業をユーザに作りますが、Keras オブジェクト (下を参照) によれば、重荷は最小化されます。
Functions, not sessions
session.run() コールは殆ど関数呼び出しのようなものです : 貴方は入力と呼び出される関数を指定し、出力のセットを取り戻します。TensorFlow 2.0 では、Python 関数をそれを JIT コンパイルのためにマークするために tf.function() を使用して修飾 (デコレート) できます、その結果 TensorFlow はそれを単一グラフとして実行します (Functions 2.0 RFC)。このメカニズムは TensorFlow 2.0 にグラフモードの総ての恩恵を得ることを可能にします。
- パフォーマンス: その関数は最適化できます (ノード枝狩り (= pruning), カーネル融合 etc.)。
- 可搬性 (= Portability): その関数はエクスポート/再インポート可能 (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 の表現力を完全に活用することを期待します。しかし portable TensorFlow は Python インタープリタがないコンテキストで実行します – モバイル, C++ そして JS. @tf.function を追加するときユーザが彼らのコードを書き直さなければならないことを回避する手助けをするために、AutoGraph は Python constructs のサブセットをそれらの TensorFlow の等価物に変換します :
- for/while -> tf.while_loop (break と continue がサポートされます)
- if -> tf.cond
- for _ in dataset -> dataset.reduce
AutoGraph は制御フローの任意のネスティングをサポートします、これはシークエンスモデル、 強化学習、カスタム訓練ループ等のような多くの複雑な ML プログラムを性能良くそして実装することを可能にします。
慣用的な TensorFlow 2.0 のための推奨
貴方のコードをより小さい関数にリファクタリングする
TensorFlow 1.X の一般的な使用方法パターンは「キッチン・シンク」ストラテジーでした、そこでは総ての可能な計算の結合が先取権のある状態で (= preemptively) 並べ立てられて、そしてそれから選択された tensors が session.run() を通して評価されました。TensorFlow 2.0 では、ユーザはコードを (必要に応じて呼び出される) より小さい関数にリファクタリングするべきです。一般に、これらのより小さい関数の各々を tf.function で修飾する必要はありません ; 高位な計算を修飾するために tf.function を使用するだけです – 例えば、訓練の 1 ステップや、モデルの forward パスです。
変数を管理するために 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) ... # 貴方は依然として w_i と b_i を管理しなければなりません、そしてそれらの shape はコードから離れて定義されます。
Keras バージョンと比較してください :
# 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, ...]
Keras 層/モデルは tf.train.Checkpointable から継承されて @tf.function で統合されます、これは Keras オブジェクトから直接的にチェックポイントするか SavedModels をエクスポートすることを可能にします。これらの統合を活用するために貴方は必ずしも Keras の 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]) # プライマリ・データセット上で訓練する for x, y in main_dataset: with tf.GradientTape() as tape: prediction = path1(x) loss = loss_fn_head1(prediction, y) # trunk と head1 重みを同時に最適化する gradients = tape.gradients(loss, path1.trainable_variables) optimizer.apply_gradients(gradients, path1.trainable_variables) # トランクを再利用して、2 番目のヘッドを再調整する for x, y in small_dataset: with tf.GradientTape() as tape: prediction = path2(x) loss = loss_fn_head2(prediction, y) # trunk 重みではなく、head2 重みだけを最適化する gradients = tape.gradients(loss, head2.trainable_variables) optimizer.apply_gradients(gradients, head2.trainable_variables) # 他の人が再利用するために trunk 計算だけを公開できます tf.saved_model.save(trunk, output_path)
tf.data.Datasets と @tf.function を結合する (= combine)
メモリに fit する訓練データに渡り iterate するとき、通常の Python iteration を自由に使用してください。そうでないならば、tf.data.Dataset はディスクから訓練データをストリームするために最善の方法です。Datasets は iterable で (iterator ではありません)、Eager モードではちょうど他の Python iterable のように動作します。貴方はコードを tf.function() でラップすることにより dataset 非同期 prefetching/streaming 特徴を完全に活用できます、それは AutoGraph を使用して Python iteration を同値のグラフ演算に置き換えます。
@tf.function def train(model, dataset, optimizer): for x, y in dataset: with tf.GradientTape() as tape: prediction = model(x) loss = loss_fn(prediction, y) gradients = tape.gradients(loss, model.trainable_variables) optimizer.apply_gradients(gradients, model.trainable_variables)
Keras .fit() API を使用する場合には、dataset iteration について心配する必要はないでしょう。
model.compile(optimizer=optimizer, loss=loss_fn) model.fit(dataset)
Python 制御フローで AutoGraph を活用する
AutoGraph はデータ依存制御フローを tf.cond と tf.while_loop のようなグラフモードの等価物に変換する方法を提供します。
データ依存制御フローが出現する一つの一般的な場所はシークエンス・モデル内です。tf.keras.layers.RNN は RNN セルをラップして、リカレンスを静的にあるいは動的に展開することを可能にします。デモのためには、動的展開 (= unroll) を次のように再実装できるでしょう :
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|…) を使用してそれをコンテキスト・マネージャを使用して writer にリダイレクトします。(コンテキスト・マネージャを省けば、何も起きません。)TF 1.x とは違い、要約は writer に直接送られます ; 個別の “merge” op と個別の add_summary() コールはなく、それは step value は callsite で提供されなければならないことを意味します。
summary_writer = tf.summary.create_file_writer('/tmp/summaries') with summary_writer.as_default(): tf.summary.scalar('loss', 0.1, step=42)
データをそれらをロギングする前に要約として累積するためには、tf.metrics を使用します。Metrics はステートフルです ; それらは値を累積して .result() を呼び出すときに累積結果を返します。累積値は .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): loss = loss_fn(model(test_x), 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.
以上