TensorFlow 2.0 Alpha : 既存コードを TensorFlow 2.0 に変換する (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/11/2019
* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha の以下のページを翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
既存コードを TensorFlow 2.0 に変換する
TensorFlow 2.0 で 1.X コードを実行することは、 (contrib を除いて) 修正なしで、依然として可能です :
import tensorflow.compat.v1 as tf tf.disable_v2_behavior()
けれども、これは貴方に TensorFlow 2.0 で作成された多くの改良を活用させてはくれません。このガイドは貴方のコードをアップグレードして、それをより単純に、よりパフォーマンスを高く、そして保守をより容易にすることを手助けします。
自動変換スクリプト
最初のステップは アップグレード・スクリプト の実行を試みることです。
これは貴方のコードを TensorFlow 2.0 にアップグレードする初期パスを行ないます。しかしそれは貴方のコードを TensorFlow 2.0 に慣用的にはできません。貴方のコードは依然としてプレースホルダー、sessions、collections そして他の 1.x-スタイルの機能にアクセスするために tf.compat.v1 エンドポイントを使用しているかもしれません。
コードを 2.0 ネイティブにする
このガイドは TensorFlow 1.x コードを TensorFlow 2.0 に変換する幾つかのサンプルをウォークスルーします。これらの変更は貴方のコードにパフォーマンス最適化と単純化された API コールを活用させます。
各ケースで、そのパターンは :
1. tf.Session.run コールを置き換える
総ての tf.Session.run コールは Python 関数で置き換えられるべきです。
- feed_dict と tf.placeholders は関数引数になります。
- fetches は関数の返り値になります。
貴方は pdb のような標準的な Python ツールを使用して関数に踏み込んでデバッグできます。
それが動作することに満足したとき、それをグラフモードで効率的に実行するために tf.function デコレータを追加します。これがどのように動作するかについてのより多くは Autograh ガイド を見てください。
2. python オブジェクト (to track) と variables と losses を使用する
tf.get_variable の代わりに tf.Variable を使用します。
総ての variable_scope は Python オブジェクトに変換可能です。典型的にはこれは次の一つです :
- tf.keras.layers.Layer
- tf.keras.Model
- tf.Module
(tf.Graph.get_collection(tf.GraphKeys.VARIABLES) のように) 変数のリストを集める必要がある場合には、Layer と Model オブジェクトの .variables と .trainable_variables 属性を使用してください。
これらの Layer と Model クラスはグローバル・コレクションの必要性を除去する幾つかの他のプロパティを実装しています。それらの .losses プロパティは tf.GraphKeys.LOSSES コレクションを使用するための置き換えになり得ます。
詳細については keras ガイド を見てください。
Warning: 多くの tf.compat.v1 シンボルはグローバル collections を暗黙的に使用しています。
3. 貴方の訓練ループをアップグレードする
貴方のユースケースで動作する最高位の API を使用してください。貴方自身の訓練ループを構築するよりも tf.keras.Model.fit を選択してください。
これらの高位関数は貴方自身の訓練ループを書く場合に簡単に見落としやすい低位な詳細の多くを管理します。例えば、それらは自動的に正則化損失を集めて、そしてモデルを呼び出しているとき training=True 引数を設定します。
4. 貴方のデータ入力パイプラインをアップグレードする
データ入力のためには tf.data datasets を使用してください。これらのオブジェクトは効率的で、表現力があり、そして tensorflow と上手く統合されています。
それらは tf.keras.Model.fit メソッドに直接渡すことができます。
model.fit(dataset, epochs=5)
それらは直接的に標準的な Python で iterate できます :
for example_batch, label_batch in dataset: break
モデルを変換する
セットアップ
from __future__ import absolute_import, division, print_function !pip install -q tensorflow==2.0.0-alpha0 import tensorflow as tf import tensorflow_datasets as tfds
低位変数 & 演算子実行
低位 API の使用の例は以下を含みます :
- 再利用を制御するために変数スコープを使用する
- tf.get_variable で変数を作成する
- コレクションにも明示的にアクセスする
- 次のようなメソッドでコレクションに暗黙的にアクセスする :
- tf.global_variables
- tf.losses.get_regularization_loss
- グラフ入力をセットアップするために tf.placeholder を使用する
- session.run でグラフを実行する
- 変数を手動で初期化する
変換前
これらのパターンが TensorFlow 1.x を使用したコードでどのように見えるかがここにあります。
in_a = tf.placeholder(dtype=tf.float32, shape=(2)) in_b = tf.placeholder(dtype=tf.float32, shape=(2)) def forward(x): with tf.variable_scope("matmul", reuse=tf.AUTO_REUSE): W = tf.get_variable("W", initializer=tf.ones(shape=(2,2)), regularizer=tf.contrib.layers.l2_regularizer(0.04)) b = tf.get_variable("b", initializer=tf.zeros(shape=(2))) return x * train_data + b out_a = model(in_a) out_b = model(in_b) reg_loss = tf.losses.get_regularization_loss(scope="matmul") with tf.Session() as sess: sess.run(tf.global_variables_initializer()) outs = sess.run([out_a, out_b, reg_loss], feed_dict={in_a: [1, 0], in_b: [0, 1]})
変換後
コンバートされたコードでは :
- 変数はローカル Python オブジェクトです。
- forward 関数は依然として計算を定義します。
- sess.run コールは forward へのコールで置き換えられます。
- オプションの tf.function デコレータはパフォーマンスのために追加できます。
- 正則化は手動で計算されます、どのようなグローバル・コレクションへの参照もなしに。
- No sessions or placeholders。
W = tf.Variable(tf.ones(shape=(2,2)), name="W") b = tf.Variable(tf.zeros(shape=(2)), name="b") @tf.function def forward(x): return W * x + b out_a = forward([1,0]) print(out_a)
tf.Tensor( [[1. 0.] [1. 0.]], shape=(2, 2), dtype=float32)
out_b = forward([0,1]) regularizer = tf.keras.regularizers.l2(0.02) reg_loss = regularizer(W)
tf.layers ベースのモデル
tf.layers モジュールは以前は変数を定義して再利用するために tf.variable_scope に依拠する layer-関数を含みました。
変換前
def model(x, training, scope='model'): with tf.variable_scope(scope, reuse=tf.AUTO_REUSE): x = tf.layers.conv2d(x, 32, 3, activation=tf.nn.relu, kernel_regularizer=tf.contrib.layers.l2_regularizer(0.04)) x = tf.layers.max_pooling2d(x, (2, 2), 1) x = tf.layers.flatten(x) x = tf.layers.dropout(x, 0.1, training=training) x = tf.layers.dense(x, 64, activation=tf.nn.relu) x = tf.layers.batch_normalization(x, training=training) x = tf.layers.dense(x, 10, activation=tf.nn.softmax) return x train_out = model(train_data, training=True) test_out = model(test_data, training=False)
変換後
- 層の単純なスタックが tf.keras.Sequential にきちんとはまります。(より複雑なモデルについては custom layers and models と the functional API を見てください。)
- モデルが変数、そして正則化損失を追跡します。
- 変換は一対一対応でした、何故ならば tf.layers から tf.keras.layers への直接的なマッピングがあるからです。
殆どの引数はそのままです。しかし違いに気がついてください :
- training 引数はモデルによりそれが実行されるとき各層に渡されます。
- 元の model 関数への最初の引数 (input x) はなくなりました。これはオブジェクト層がモデルの呼び出しからモデルの構築を分離するからです。
また以下にも注意してください :
- tf.contrib からの initializer の正則化を使用していた場合、これらは他よりも多くの引数変更を持ちます。
- コードはもはやコレクションに書きませんので、tf.losses.get_regularization_loss のような関数はもはやこれらの値を返しません、潜在的には貴方の訓練ループをブレークします。
model = tf.keras.Sequential([ tf.keras.layers.Conv2D(32, 3, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.02), input_shape=(28, 28, 1)), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Flatten(), tf.keras.layers.Dropout(0.1), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.BatchNormalization(), tf.keras.layers.Dense(10, activation='softmax') ]) train_data = tf.ones(shape=(1, 28, 28, 1)) test_data = tf.ones(shape=(1, 28, 28, 1))
train_out = model(train_data, training=True) print(train_out)
tf.Tensor([[0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]], shape=(1, 10), dtype=float32)
test_out = model(test_data, training=False) print(test_out)
tf.Tensor( [[0.16283211 0.06043335 0.08143548 0.12280242 0.12175005 0.13779742 0.09735528 0.06814758 0.0588802 0.08856612]], shape=(1, 10), dtype=float32)
# Here are all the trainable variables. len(model.trainable_variables)
8
# Here is the regularization loss. model.losses
[<tf.Tensor: id=397, shape=(), dtype=float32, numpy=0.040700875>]
混合 variables & tf.layers
既存のコードはしばしば TF 1.x 変数と演算子に高位 tf.layers を混ぜていました。
変換前
def model(x, training, scope='model'): with tf.variable_scope(scope, reuse=tf.AUTO_REUSE): W = tf.get_variable( "W", dtype=tf.float32, initializer=tf.ones(shape=x.shape), regularizer=tf.contrib.layers.l2_regularizer(0.04), trainable=True) if training: x = x + W else: x = x + W * 0.5 x = tf.layers.conv2d(x, 32, 3, activation=tf.nn.relu) x = tf.layers.max_pooling2d(x, (2, 2), 1) x = tf.layers.flatten(x) return x train_out = model(train_data, training=True) test_out = model(test_data, training=False)
変換後
このコードを変換するためには、前の例内のように層から層へのマッピングのパターンに従います。
一般的なパターンは :
- __init__ で層パラメータを集める。
- build で変数を構築する。
- call で計算を実行して、結果を返す。
tf.variable_scope は事実上それ自身の層です。従ってそれを tf.keras.layers.Layer として書き直します。詳細は ガイド を見てください。
'# Create a custom layer for part of the model class CustomLayer(tf.keras.layers.Layer): def __init__(self, *args, **kwargs): super(CustomLayer, self).__init__(*args, **kwargs) def build(self, input_shape): self.w = self.add_weight( shape=input_shape[1:], dtype=tf.float32, initializer=tf.keras.initializers.ones(), regularizer=tf.keras.regularizers.l2(0.02), trainable=True) # Call method will sometimes get used in graph mode, # training will get turned into a tensor @tf.function def call(self, inputs, training=None): if training: return inputs + self.w else: return inputs + self.w * 0.5
custom_layer = CustomLayer() print(custom_layer([1]).numpy()) print(custom_layer([1], training=True).numpy())
[1.5] [2.]
train_data = tf.ones(shape=(1, 28, 28, 1)) test_data = tf.ones(shape=(1, 28, 28, 1)) # Build the model including the custom layer model = tf.keras.Sequential([ CustomLayer(input_shape=(28, 28, 1)), tf.keras.layers.Conv2D(32, 3, activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Flatten(), ]) train_out = model(train_data, training=True) test_out = model(test_data, training=False)
注意すべき幾つか :
- サブクラス化された Keras モデル & 層は v1 グラフ (自動制御依存性なし) と eager モードの両者で実行される必要があります。
- autograph と自動制御依存性を得るために call() を tf.function() にラップします。
- 呼び出すための training 引数を受け取ることを忘れないでください。
- ときにそれは tf.Tensor です。
- ときにそれは Python boolean です。
- コンストラクタか def build() で self.add_weight() を使用してモデル変数を作成します。
- build では input shape へのアクセスを持ちますので、適合する shape で重みを作成できます。
- tf.keras.layers.Layer.add_weight の使用は Keras に変数と正則化損失を追跡することを可能にします。
- 貴方のオブジェクトで tf.Tensors を保持しないでください。
- それらは tf.function か eager コンテキスト内のいずれかで作成されるかもしれません、そしてそれらの tensor は異なって振る舞います。
- 状態のために tf.Variables を使用してください、それらは常に両者のコンテキストから利用可能です。
- tf.Tensors は中間値のためだけです。
Slim & contrib.layers のノート
古い TensorFlow 1.x コードの多くの総量が Slim ライブラリを使用しています、これは tf.contrib.layers として TensorFlow 1.x でパッケージ化されていました。contrib モジュールとしては、これはもはや TensorFlow 2.0 では利用可能ではありません、tf.compat.v1 内でさえも。Slim を使用するコードの TF 2.0 への変換は tf.layers を使用するレポジトリの変換より複雑です。実際に、貴方の Slim コードを最初に tf.layers に変換してから Keras に変換することは意味があるかもしれません。
- arg_scopes を除去します、総ての args は明示的である必要があります。
- それらを使用する場合、 normalizer_fn と activation_fn をそれら自身の層に分割します。
- 分離可能な conv 層は一つまたはそれ以上の異なる Keras 層にマップします (depthwise, pointwise そして分離可能 Keras 層)
- Slim と tf.layers は異なる arg 名 & デフォルト値を持ちます。
- 幾つかの args は異なるスケールを持ちます。
- Slim 事前訓練モデルを使用する場合、tf.keras.applications や TFHub を試してみてください。
幾つかの tf.contrib 層は core TensorFlow に移動しなかったかもしれませんが代わりに TF アドオン・パッケージ に移動しました。
訓練
データを tf.keras モデルに供給する多くの方法があります。それらは入力として Python ジェネレータと Numpy 配列を受け取ります。
モデルにデータを供給する推奨方法は tf.data パッケージを使用することです、これはデータを操作するための高パフォーマンスなクラスのコレクションを含みます。
依然として tf.queue を使用している場合、これらは入力パイプラインとしてではなく、データ構造としてのみサポートされます。
Datasets を使用する
TensorFlow Datasets パッケージ (tfds) は事前定義された dataset を tf.data.Dataset オブジェクトとしてロードするためのユティリティを含みます。
このサンプルのために、tfds を使用して、MNISTdataset をロードしてみます :
datasets, ds_info = tfds.load(name='mnist', with_info=True, as_supervised=True) mnist_train, mnist_test = datasets['train'], datasets['test']
それから訓練のためにデータを準備します :
- 各画像をリスケールする。
- サンプルの順序をシャッフルする。
- 画像とラベルのバッチを集める。
BUFFER_SIZE = 10 # Use a much larger value for real code. BATCH_SIZE = 64 NUM_EPOCHS = 5 def scale(image, label): image = tf.cast(image, tf.float32) image /= 255 return image, label train_data = mnist_train.map(scale).shuffle(BUFFER_SIZE).batch(BATCH_SIZE).take(5) test_data = mnist_test.map(scale).batch(BATCH_SIZE).take(5)
サンプルを短く保つために、dataset を 5 バッチだけを返すように trim します :
STEPS_PER_EPOCH = 5 train_data = train_data.take(STEPS_PER_EPOCH) test_data = test_data.take(STEPS_PER_EPOCH)
image_batch, label_batch = next(iter(train_data))
Keras 訓練ループを使用する
貴方の訓練プロセスの低位制御を必要としないのであれば、Keras 組み込みの fit, evaluate と predict メソッドの使用が推奨されます。これらのメソッドは (sequential, functional あるいはサブクラスの) 実装にかかわらずモデルを訓練するための統一されたインタープリタを提供します。
これらのメソッドの優位点は以下を含みます :
- それらは Numpy 配列, Python ジェネレータ と tf.data.Datasets を受け取ります。
- それらは正則化と活性化損失を自動的に適用します。
- マルチデバイス訓練 のために tf.distribute をサポートします。
- それらは任意の callable を損失とメトリクスとしてサポートします。
- それらは tf.keras.callbacks.TensorBoard のようなコールバックとカスタム・コールバックをサポートします。
- それらは自動的に TensorFlow グラフを使用してパフォーマンスが高いです。
ここに Dataset を使用してモデルを訓練するサンプルがあります。(これがどのように動作するかの詳細については チュートリアル を見てください。)
model = tf.keras.Sequential([ tf.keras.layers.Conv2D(32, 3, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.02), input_shape=(28, 28, 1)), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Flatten(), tf.keras.layers.Dropout(0.1), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.BatchNormalization(), tf.keras.layers.Dense(10, activation='softmax') ]) # Model is the full model w/o custom layers model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(train_data, epochs=NUM_EPOCHS) loss, acc = model.evaluate(test_data) print("Loss {}, Accuracy {}".format(loss, acc))
Epoch 1/5 5/5 [==============================] - 1s 214ms/step - loss: 1.5402 - accuracy: 0.5063 Epoch 2/5 5/5 [==============================] - 0s 18ms/step - loss: 0.5344 - accuracy: 0.8875 Epoch 3/5 5/5 [==============================] - 0s 19ms/step - loss: 0.3505 - accuracy: 0.9406 Epoch 4/5 5/5 [==============================] - 0s 17ms/step - loss: 0.2635 - accuracy: 0.9781 Epoch 5/5 5/5 [==============================] - 0s 19ms/step - loss: 0.2163 - accuracy: 0.9750 5/Unknown - 0s 47ms/step - loss: 1.6750 - accuracy: 0.7781Loss 1.6750491619110108, Accuracy 0.778124988079071
貴方自身のループを書く
Keras モデルの訓練ステップが貴方のために動作するが、しかし貴方がそのステップの外でより制御を必要とする場合、貴方自身の data-iteration ループで tf.keras.model.train_on_batch メソッドの使用を考えてください。
Remember: 多くのことが tf.keras.Callback として実装できます。
このメソッド前のセクションで言及したメソッドの多くの優位点を持ちますが、outer ループのユーザ制御を与えます。
訓練の間のパフォーマンスをチェックするために tf.keras.model.test_on_batch か tf.keras.Model.evaluate もまた使用できます。
Note: train_on_batch と test_on_batch、デフォルトで単一バッチのための損失とメトリクスを与えます。もし reset_metrics=False を渡す場合にはそれらは累積されたメトリクスを返しそして貴方はメトリック累算器を適切にリセットすることを覚えていてください。また AUC のような幾つかのメトリクスは正しく計算されるために reset_metrics=False を要求することもまた覚えていてください。
上のモデルの訓練を続けるために :
# Model is the full model w/o custom layers model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) metrics_names = model.metrics_names for epoch in range(NUM_EPOCHS): #Reset the metric accumulators model.reset_metrics() for image_batch, label_batch in train_data: result = model.train_on_batch(image_batch, label_batch) print("train: ", "{}: {:.3f}".format(metrics_names[0], result[0]), "{}: {:.3f}".format(metrics_names[1], result[1])) for image_batch, label_batch in test_data: result = model.test_on_batch(image_batch, label_batch, # return accumulated metrics reset_metrics=False) print("\neval: ", "{}: {:.3f}".format(metrics_names[0], result[0]), "{}: {:.3f}".format(metrics_names[1], result[1]))
train: loss: 0.279 accuracy: 0.938 train: loss: 0.270 accuracy: 0.953 train: loss: 0.282 accuracy: 0.922 train: loss: 0.181 accuracy: 0.984 train: loss: 0.226 accuracy: 0.984 eval: loss: 1.611 accuracy: 0.719 train: loss: 0.119 accuracy: 1.000 train: loss: 0.123 accuracy: 1.000 train: loss: 0.106 accuracy: 1.000 train: loss: 0.135 accuracy: 1.000 train: loss: 0.233 accuracy: 0.953 eval: loss: 1.610 accuracy: 0.784 train: loss: 0.095 accuracy: 1.000 train: loss: 0.097 accuracy: 1.000 train: loss: 0.106 accuracy: 1.000 train: loss: 0.165 accuracy: 0.953 train: loss: 0.165 accuracy: 0.969 eval: loss: 1.599 accuracy: 0.800 train: loss: 0.082 accuracy: 1.000 train: loss: 0.079 accuracy: 1.000 train: loss: 0.067 accuracy: 1.000 train: loss: 0.084 accuracy: 1.000 train: loss: 0.130 accuracy: 0.984 eval: loss: 1.571 accuracy: 0.797 train: loss: 0.069 accuracy: 1.000 train: loss: 0.067 accuracy: 1.000 train: loss: 0.054 accuracy: 1.000 train: loss: 0.061 accuracy: 1.000 train: loss: 0.121 accuracy: 0.984 eval: loss: 1.535 accuracy: 0.819
訓練ステップをカスタマイズする
より多くの柔軟性と制御を必要とする場合、貴方自身の訓練ループを実装することでそれを持つことができます。3 つのステップがあります :
- サンプルのバッチを得るために Python ジェネレータか tf.data.Dataset に渡り iterate します。
- 勾配を集めるために tf.GradientTape を使用します。
- モデルの変数に重み更新を適用するために tf.keras.optimizer を使用します。
Remember:
- サブクラス化された層とモデルの call メソッドで training 引数を常に含めます。
- training 引数を正しくセットしてモデルを呼び出すことを確実にしてください。
- 使用方法に依拠して、モデルがデータのバッチ上で実行されるまでモデル変数は存在しないかもしれません。
- モデルのために正則化損失のようなものを手動で処理する必要があります。
v1 に比較した単純化に注意してください :
- 変数 initializer を実行する必要はありません。変数は作成時に初期化されます。
- 手動の制御依存性を追加する必要はありません。tf.function の演算が eager mode として振る舞う場合でさえも。
model = tf.keras.Sequential([ tf.keras.layers.Conv2D(32, 3, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.02), input_shape=(28, 28, 1)), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Flatten(), tf.keras.layers.Dropout(0.1), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.BatchNormalization(), tf.keras.layers.Dense(10, activation='softmax') ]) optimizer = tf.keras.optimizers.Adam(0.001) loss_fn = tf.keras.losses.SparseCategoricalCrossentropy() @tf.function def train_step(inputs, labels): with tf.GradientTape() as tape: predictions = model(inputs, training=True) regularization_loss = tf.math.add_n(model.losses) pred_loss = loss_fn(labels, predictions) total_loss = pred_loss + regularization_loss gradients = tape.gradient(total_loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) for epoch in range(NUM_EPOCHS): for inputs, labels in train_data: train_step(inputs, labels) print("Finished epoch", epoch)
Finished epoch 0 Finished epoch 1 Finished epoch 2 Finished epoch 3 Finished epoch 4
新しいスタイルのメトリクス
TensorFlow 2.0 では、メトリクスはオブジェクトです。メトリック・オブジェクトは eagerly にそして tf.function 内の両者で動作します。メトリック・オブジェクトは次のメソッドを持ちます :
- update_state() — 新しい観測を追加する
- result() — 観測値が与えられたとき、メトリックの現在の結果を得る
- reset_states() — 総ての観測をクリアする
オブジェクトそれ自身は callable です。update_state と同様に、呼び出しは新しい観測で状態の更新を更新して、メトリックの新しい結果を返します。
メトリックの変数を手動で初期化しなくてもかまいません、そして TensorFlow 2.0 は自動制御依存性を持ちますので、それらについて心配する必要もまたありません。
下のコードはカスタム訓練ループ内で観測された mean 損失を追跡するためにメトリックを使用します。
# Create the metrics loss_metric = tf.keras.metrics.Mean(name='train_loss') accuracy_metric = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy') @tf.function def train_step(inputs, labels): with tf.GradientTape() as tape: predictions = model(inputs, training=True) regularization_loss = tf.math.add_n(model.losses) pred_loss = loss_fn(labels, predictions) total_loss = pred_loss + regularization_loss gradients = tape.gradient(total_loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) # Update the metrics loss_metric.update_state(total_loss) accuracy_metric.update_state(labels, predictions) for epoch in range(NUM_EPOCHS): # Reset the metrics loss_metric.reset_states() accuracy_metric.reset_states() for inputs, labels in train_data: train_step(inputs, labels) # Get the metric results mean_loss = loss_metric.result() mean_accuracy = accuracy_metric.result() print('Epoch: ', epoch) print(' loss: {:.3f}'.format(mean_loss)) print(' accuracy: {:.3f}'.format(mean_accuracy))
Epoch: 0 loss: 0.214 accuracy: 0.984 Epoch: 1 loss: 0.170 accuracy: 0.991 Epoch: 2 loss: 0.142 accuracy: 0.994 Epoch: 3 loss: 0.122 accuracy: 1.000 Epoch: 4 loss: 0.114 accuracy: 0.997
Saving & Loading
チェックポイント互換性
TensorFlow 2.0 は オブジェクト・ベースのチェックポイント を使用します。
古いスタイルの名前ベースのチェックポイントは注意すれば依然としてロードできます。コード変換プロセスは変数名変更という結果になるかもしれませんが、回避方法はあります。
最も単純なアプローチはチェックポイント内の名前とともに新しいモデルの名前をラインアップすることです :
- Variable は総て依然として貴方が設定できる name 引数を持ちます。
- Keras モデルはまた name 引数を取ります、それらの変数のための prefix として設定されます。
- tf.name_scope 関数は変数名 prefix を設定するために使用できます。これは tf.variable_scope とは大きく異なります。それは名前だけに影響し、変数 & 再利用を追跡しません。
それが貴方のユースケースのために動作しない場合、tf.compat.v1.train.init_from_checkpoint 関数を試してください。それは assignment_map 引数を取ります、これは古い名前から新しい名前へのマッピングを指定します。
Note: loading を遅延できる オブジェクト・ベースのチェックポイントと違い、名前ベースのチェックポイントでは関数が呼び出される時総ての変数が構築されていることを要求します。あるモデルは build を呼び出すかデータのバッチでモデルを実行するまで変数の構築を遅延します。
セーブされたモデルの互換性
セーブされたモデルについて重要な互換性の懸念はありません。
- TensorFlow 1.x saved_models は TensorFlow 2.0 で動作します。
- TensorFlow 2.0 saved_models は総ての ops がサポートされれば TensorFlow 1.x でのワークをロード さえします。
Estimator
Estimator で訓練する
estimator は TensorFlow 2.0 でサポートされます。estimator を使用するとき、TensorFlow 1.x からの input_fn(), tf.estimator.TrainSpec と tf.estimator.EvalSpec を使用できます。
ここに train と evaluate specs を伴う input_fn を使用するサンプルがあります。
input_fn と train/eval specs を作成する
# Define the estimator's input_fn def input_fn(): datasets, ds_info = tfds.load(name='mnist', with_info=True, as_supervised=True) mnist_train, mnist_test = datasets['train'], datasets['test'] BUFFER_SIZE = 10000 BATCH_SIZE = 64 def scale(image, label): image = tf.cast(image, tf.float32) image /= 255 return image, label[..., tf.newaxis] train_data = mnist_train.map(scale).shuffle(BUFFER_SIZE).batch(BATCH_SIZE) return train_data.repeat() # Define train & eval specs train_spec = tf.estimator.TrainSpec(input_fn=input_fn, max_steps=STEPS_PER_EPOCH * NUM_EPOCHS) eval_spec = tf.estimator.EvalSpec(input_fn=input_fn, steps=STEPS_PER_EPOCH)
Keras モデル定義を使用する
TensorFlow 2.0 で貴方の estimator をどのように構築するかについて幾つかの違いがあります。
貴方のモデルを Keras を使用して定義することを勧めます、それからモデルを estimator に変更するために tf.keras.model_to_estimator ユティリティを使用します。下のコードは estimator を作成して訓練するときこのユティリティをどのように使用するかを示します。
def make_model(): return tf.keras.Sequential([ tf.keras.layers.Conv2D(32, 3, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.02), input_shape=(28, 28, 1)), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Flatten(), tf.keras.layers.Dropout(0.1), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.BatchNormalization(), tf.keras.layers.Dense(10, activation='softmax') ])
model = make_model() model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) estimator = tf.keras.estimator.model_to_estimator( keras_model = model ) tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)
W0307 18:10:32.629323 140330712794880 estimator.py:1799] Using temporary folder as model directory: /tmp/tmp8dhzipj4 W0307 18:10:33.441684 140330712794880 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/training_util.py:238: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version. Instructions for updating: Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts. W0307 18:10:36.949727 140330712794880 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/ops/metrics_impl.py:363: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Use `tf.cast` instead. W0307 18:10:37.080184 140330712794880 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/saver.py:1276: checkpoint_exists (from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version. Instructions for updating: Use standard file APIs to check for files with this prefix. ({'accuracy': 0.65, 'global_step': 25, 'loss': 1.6268775}, [])
カスタム model_fn を使用する
貴方が保守する必要がある既存のカスタム estimator model_fn を持つ場合、model_fn を Keras モデルを使用するように変換できます。
けれども、互換性の理由のために、カスタム model_fn は依然として 1.x スタイルのグラフモードで動作します。これは eager execution はなく自動制御依存性もないことも意味します。
カスタム model_fn で Keras モデルを使用することはそれをカスタム訓練ループで使用することに類似しています :
- mode 引数を基に、訓練段階を適切に設定します。
- モデルの trainable_variables を optimizer に明示的に渡します。
しかし カスタムループ に比較して、重要な違いがあります、
- model.losses を使用する代わりに、tf.keras.Model.get_losses_for を使用して損失を抽出します。
- tf.keras.Model.get_updates_for を使用してモデルの更新を抽出します。
Note: 「更新」は各バッチの後にモデルに適用される必要がある変更です。例えば、tf.keras.layers.BatchNormalization 層の平均と分散の移動平均です。
次のコードはカスタム model_fn から estimator を作成します、これらの関連総てを示します :
def my_model_fn(features, labels, mode): model = make_model() optimizer = tf.compat.v1.train.AdamOptimizer() loss_fn = tf.keras.losses.SparseCategoricalCrossentropy() training = (mode == tf.estimator.ModeKeys.TRAIN) predictions = model(features, training=training) reg_losses = model.get_losses_for(None) + model.get_losses_for(features) total_loss = loss_fn(labels, predictions) + tf.math.add_n(reg_losses) accuracy = tf.compat.v1.metrics.accuracy(labels=labels, predictions=tf.math.argmax(predictions, axis=1), name='acc_op') update_ops = model.get_updates_for(None) + model.get_updates_for(features) with tf.control_dependencies(update_ops): train_op = optimizer.minimize( total_loss, var_list=model.trainable_variables, global_step=tf.compat.v1.train.get_or_create_global_step()) return tf.estimator.EstimatorSpec( mode=mode, predictions=predictions, loss=total_loss, train_op=train_op, eval_metric_ops={'accuracy': accuracy}) # Create the Estimator & Train estimator = tf.estimator.Estimator(model_fn=my_model_fn) tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)
W0307 18:10:38.249397 140330712794880 estimator.py:1799] Using temporary folder as model directory: /tmp/tmpj7quc09l W0307 18:10:39.147113 140330712794880 deprecation.py:506] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/training/slot_creator.py:187: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version. Instructions for updating: Call initializer instance with the dtype argument instead of passing it to the constructor ({'accuracy': 0.51875, 'global_step': 25, 'loss': 1.619755}, [])
TensorShape
このクラスは ints を保持するために単純化されました。tf.compat.v1.Dimension オブジェクトの代わりです。従って int を得るために .value() を呼び出す必要はありません。
個々の tf.compat.v1.Dimension オブジェクトは依然として tf.TensorShape.dims からアクセス可能です。
以下は TensorFlow 1.x と TensorFlow 2.0 間の違いを示します。
# Create a shape and choose an index i = 0 shape = tf.TensorShape([16, None, 256]) shape
TensorShape([16, None, 256])
もし TF 1.x でこれを持ったのであれば :
value = shape[i].value
TF 2.0 でこれを行ないます :
value = shape[i] value
16
もし TF 1.x でこれを持ったのであれば :
for dim in shape: value = dim.value print(value)
TF 2.0 でこれを行ないます :
for value in shape: print(value)
16 None 256
もし TF 1.x でこれを持ったのであれば (あるいは任意の他の dimension メソッドを使用したのであれば) :
dim = shape[i] dim.assert_is_compatible_with(other_dim)
TF 2.0 でこれを行ないます :
other_dim = 16 Dimension = tf.compat.v1.Dimension if shape.rank is None: dim = Dimension(None) else: dim = shape.dims[i] dim.is_compatible_with(other_dim) # or any other dimension method
True
shape = tf.TensorShape(None) if shape: dim = shape.dims[i] dim.is_compatible_with(other_dim) # or any other dimension method
rank が知られていれば tf.TensorShape の boolean 値は True です。そうでなければ False です。
print(bool(tf.TensorShape([]))) # Scalar print(bool(tf.TensorShape([0]))) # 0-length vector print(bool(tf.TensorShape([1]))) # 1-length vector print(bool(tf.TensorShape([None]))) # Unknown-length vector print(bool(tf.TensorShape([1, 10, 100]))) # 3D tensor print(bool(tf.TensorShape([None, None, None]))) # 3D tensor with no known dimensions print() print(bool(tf.TensorShape(None))) # A tensor with unknown rank.
True True True True True True False
他の挙動の変更
貴方が遭遇するかもしれない TensorFlow 2.0 の 2, 3 の他の挙動変更があります。
ResourceVariables
TensorFlow 2.0 はデフォルトで ResourceVariables を作成します、RefVariables ではありません。
ResourceVariables は書くためにロックされますので、より直感的な一貫性保証を提供します。
- これは edge クラスで挙動を変更するかもしれません。
- これは場合によっては余分な (= extra) コピーを作成するかもしれません、より高いメモリ使用を持つ可能性があります。
- use_resource=False を tf.Variable コンストラクタに渡すことにより無効にできます。
制御フロー
制御フロー op 実装は単純化されましたので、TensorFlow 2.0 では異なるグラフを生成します。
終わりに
全体のプロセスは :
- アップグレード・スクリプトを実行する。
- contrib シンボルを除去する。
- 貴方のモデルをオブジェクト指向スタイル (Keras) に切り替える。
- 可能なところでは tf.keras か tf.estimator 訓練と評価ループを使用する。
- そうでないならば、カスタムループを使用するが、sessions & collections を回避することを確実にしてください。
コードを慣用的な TensorFlow 2.0 に変換するには少しの作業がかかりますが、総ての変更は次のような結果になります :
- コードのより少ない行。
- 明瞭さと単純性の増加。
- より容易なデバッグ。
以上