ホーム » 「TensorFlow 2.0 Advanced Tutorials (Alpha)」タグがついた投稿

タグアーカイブ: TensorFlow 2.0 Advanced Tutorials (Alpha)

TensorFlow 2.0 Alpha : 上級 Tutorials : 分散訓練 :- 訓練ループで tf.distribute.Strategy

TensorFlow 2.0 Alpha : 上級 Tutorials : 分散訓練 :- 訓練ループで tf.distribute.Strategy (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/13/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – Distributed training の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

分散訓練 :- 訓練ループで tf.distribute.Strategy

このチュートリアルはカスタム訓練ループで tf.distribute.Strategy をどのように使用するかを実演します。
fashion MNIST データセット上で単純な CNN モデルを訓練します。fashion MNIST データセットはサイズ 28 x 28 の 60000 訓練画像とサイズ 28 x 28 の 10000 テスト画像を含みます。

モデルを訓練するためにカスタム訓練ループを使用しています、何故ならばそれらは訓練の上で柔軟性とより素晴らしい制御を与えてくれるからです。更に、モデルと訓練ループをデバッグすることを容易にします。

from __future__ import absolute_import, division, print_function, unicode_literals

# Import TensorFlow
!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf

# Helper libraries
import numpy as np
import os

print(tf.__version__)
2.0.0-alpha0

 

fashion mnist データセットをダウンロードする

fashion_mnist = tf.keras.datasets.fashion_mnist

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

# Adding a dimension to the array -> new shape == (28, 28, 1)
# We are doing this because the first layer in our model is a convolutional 
# layer and it requires a 4D input (batch_size, height, width, channels).
# batch_size dimension will be added later on.
train_images = train_images[..., None]
test_images = test_images[..., None]

# Getting the images in [0, 1] range.
train_images = train_images / np.float32(255)
test_images = test_images / np.float32(255)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 1s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/5148 [===============================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step

 

変数とグラフを分散するためのストラテジーを作成する

tf.distribute.MirroredStrategy ストラテジーはどのように動作するのでしょう?

  • 総ての変数とモデル・グラフはレプリカ上で複製されます。
  • 入力はレプリカに渡り均等に分散されます。
  • 各レプリカはそれが受け取った入力のための損失と勾配を計算します。
  • 勾配はそれらを総計することにより総てのレプリカに渡り同期されます。
  • 同期後、同じ更新が各レプリカ上の変数のコピーに行われます。

Note: 下の総てのコードを単一のスコープの内側に配置できます。説明のためにそれを幾つかのコードセルに分割しています。

# If the list of devices is not specified in the 
# `tf.distribute.MirroredStrategy` constructor, it will be auto-detected.
strategy = tf.distribute.MirroredStrategy()
WARNING: Logging before flag parsing goes to stderr.
W0405 15:21:07.446863 140252361586432 cross_device_ops.py:1111] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.
print ('Number of devices: {}'.format(strategy.num_replicas_in_sync))
Number of devices: 1

 

入力パイプラインをセットアップする

モデルがマルチ GPU 上で訓練されるのであれば、特別な計算パワーを効果的に利用するためにバッチサイズはそれに従って増やされるべきです。更に、学習率もそれに従って調整されるべきです。

BUFFER_SIZE = len(train_images)

BATCH_SIZE_PER_REPLICA = 64
BATCH_SIZE = BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync

EPOCHS = 10
train_steps_per_epoch = len(train_images) // BATCH_SIZE
test_steps_per_epoch = len(test_images) // BATCH_SIZE

strategy.experimental_make_numpy_iterator は総てのレプリカに渡りデータを均等に分散する iterator を作成します。

これは tf.data.Dataset.from_tensor_slices を直接的に使用するよりも効率的です、何故ならばそれは訓練データをグラフの定数として記録することを回避するからです。

strategy.experimental_make_numpy_iterator を使用していない場合、このように strategy.scope の内側に iterator を作成します :

train_dataset = tf.data.Dataset.from_tensor_slices( (train_images, train_labels)).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
train_iterator = strategy.make_dataset_iterator(train_dataset)

Note: この API は近い将来に変更される可能性があります。

with strategy.scope():
  train_iterator = strategy.experimental_make_numpy_iterator(
      (train_images, train_labels), BATCH_SIZE, shuffle=BUFFER_SIZE)

  test_iterator = strategy.experimental_make_numpy_iterator(
      (test_images, test_labels), BATCH_SIZE, shuffle=None)

 

モデル作成

tf.keras.Sequential を使用してモデルを作成します。これを行なうために Model Subclassing API を使用することもできます。

def create_model():
  model = tf.keras.Sequential([
      tf.keras.layers.Conv2D(32, 3, activation='relu'),
      tf.keras.layers.MaxPooling2D(),
      tf.keras.layers.Conv2D(64, 3, activation='relu'),
      tf.keras.layers.MaxPooling2D(),
      tf.keras.layers.Flatten(),
      tf.keras.layers.Dense(64, activation='relu'),
      tf.keras.layers.Dense(10, activation='softmax')
    ])

  return model
# Create a checkpoint directory to store the checkpoints.
checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")

 

損失関数を定義する

通常は、1 GPU/CPU を持つ単一マシン上では、損失は入力のバッチにおけるサンプルの数により除算されます。

そこで、tf.distribute.Strategy を使用するとき損失はどのように計算されるのでしょう?

  • 例えば、貴方は 4 GPU と 64 のバッチサイズを持つとしましょう。入力の一つのバッチはレプリカ (4 GPU) に渡り分散され、各レプリカはサイズ 16 の入力を得ます。
  • 各レプリカ上のモデルはそれぞれの入力で forward パスを行ないそして損失を計算します。今は、損失をそれぞれの入力のサンプル数 (16) で除算する代わりに、損失はグローバルな入力サイズ (64) で除算されます。

何故これが行われるのでしょう?

  • これが行われるのは各レプリカ上で勾配が計算された後、それらを総計することによりそれらはレプリカに渡り同期されるからです。

TensorFlow でこれをどのようにに処理しますか?

  • tf.keras.losses がこれを自動的に処理します。
  • カスタム損失関数を分散する場合、それを tf.reduce_mean (これはローカルのバッチサイズで除算します) を使用して実装しないでください、総計をグローバルなバッチサイズで除算してください : scale_loss = tf.reduce_sum(loss) * (1. / global_batch_size)
with strategy.scope():
  loss_object = tf.keras.losses.SparseCategoricalCrossentropy()

 

損失と精度を追跡するためにメトリクスを定義する

これらのメトリクスは損失と精度を追跡します。累積された統計情報を得るためにいつでも .result() を使用できます。

with strategy.scope():
  train_loss = tf.keras.metrics.Mean(name='train_loss')
  test_loss = tf.keras.metrics.Mean(name='test_loss')

  train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
      name='train_accuracy')
  test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
      name='test_accuracy')

 

訓練ループ

# model and optimizer must be created under `strategy.scope`.
with strategy.scope():
  model = create_model()

  optimizer = tf.keras.optimizers.Adam()
  
  checkpoint = tf.train.Checkpoint(optimizer=optimizer, model=model)
with strategy.scope():
  # Train step
  def train_step(inputs):
    images, labels = inputs

    with tf.GradientTape() as tape:
      predictions = model(images, training=True)
      loss = loss_object(labels, predictions)

    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

    train_loss(loss)
    train_accuracy(labels, predictions)

  # Test step
  def test_step(inputs):
    images, labels = inputs

    predictions = model(images, training=False)
    t_loss = loss_object(labels, predictions)

    test_loss(t_loss)
    test_accuracy(labels, predictions)
with strategy.scope():
  # `experimental_run` replicates the provided computation and runs it 
  # with the distributed input.
  
  @tf.function
  def distributed_train():
    return strategy.experimental_run(train_step, train_iterator)
  
  @tf.function
  def distributed_test():
    return strategy.experimental_run(test_step, test_iterator)
    
  for epoch in range(EPOCHS):
    # Note: This code is expected to change in the near future.
    
    # TRAIN LOOP
    # Initialize the iterator
    train_iterator.initialize()
    for _ in range(train_steps_per_epoch):
      distributed_train()

    # TEST LOOP
    test_iterator.initialize()
    for _ in range(test_steps_per_epoch):
      distributed_test()
    
    if epoch % 2 == 0:
      checkpoint.save(checkpoint_prefix)

    template = ("Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, "
                "Test Accuracy: {}")
    print (template.format(epoch+1, train_loss.result(), 
                           train_accuracy.result()*100, test_loss.result(), 
                           test_accuracy.result()*100))
    
    train_loss.reset_states()
    test_loss.reset_states()
    train_accuracy.reset_states()
    test_accuracy.reset_states()
Epoch 1, Loss: 0.5225825309753418, Accuracy: 80.9698486328125, Test Loss: 0.432892769575119, Test Accuracy: 84.58534240722656
Epoch 2, Loss: 0.34400758147239685, Accuracy: 87.69676971435547, Test Loss: 0.34699392318725586, Test Accuracy: 87.46995544433594
Epoch 3, Loss: 0.2954360246658325, Accuracy: 89.28929138183594, Test Loss: 0.2975158095359802, Test Accuracy: 89.20272827148438
Epoch 4, Loss: 0.26176968216896057, Accuracy: 90.3832015991211, Test Loss: 0.28567320108413696, Test Accuracy: 89.95392608642578
Epoch 5, Loss: 0.23702645301818848, Accuracy: 91.29869079589844, Test Loss: 0.2787027060985565, Test Accuracy: 89.69351196289062
Epoch 6, Loss: 0.21761010587215424, Accuracy: 92.06243133544922, Test Loss: 0.2878926694393158, Test Accuracy: 89.53324890136719
Epoch 7, Loss: 0.19936144351959229, Accuracy: 92.64107513427734, Test Loss: 0.2678224444389343, Test Accuracy: 90.64503479003906
Epoch 8, Loss: 0.184087872505188, Accuracy: 93.18470001220703, Test Loss: 0.28124910593032837, Test Accuracy: 90.25440979003906
Epoch 9, Loss: 0.1695927232503891, Accuracy: 93.6966323852539, Test Loss: 0.2541474401950836, Test Accuracy: 90.96554565429688
Epoch 10, Loss: 0.15550555288791656, Accuracy: 94.21858215332031, Test Loss: 0.2556819021701813, Test Accuracy: 90.79527282714844

 

最新のチェックポイントを復元してテストする

tf.distribute.Strategy でチェックポイントされたモデルはストラテジーとともに、またはストラテジーなしでリストアできます。

eval_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
      name='eval_accuracy')

new_model = create_model()
new_optimizer = tf.keras.optimizers.Adam()

test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(BATCH_SIZE)
@tf.function
def eval_step(images, labels):
  predictions = new_model(images, training=False)
  eval_accuracy(labels, predictions)
checkpoint = tf.train.Checkpoint(optimizer=new_optimizer, model=new_model)
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))

for images, labels in test_dataset:
  eval_step(images, labels)

print ('Accuracy after restoring the saved model without strategy: {}'.format(
    eval_accuracy.result()*100))
W0405 15:22:20.914693 140252361586432 tf_logging.py:161] Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d07c8> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
W0405 15:22:20.920872 140252361586432 tf_logging.py:161] Entity <method-wrapper '__call__' of weakref object at 0x7f8e14520598> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
W0405 15:22:20.925516 140252361586432 tf_logging.py:161] Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d02c8> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
W0405 15:22:20.930133 140252361586432 tf_logging.py:161] Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d0318> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
W0405 15:22:20.934626 140252361586432 tf_logging.py:161] Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d0368> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
W0405 15:22:20.938569 140252361586432 tf_logging.py:161] Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d03b8> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
W0405 15:22:20.942585 140252361586432 tf_logging.py:161] Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d0408> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
W0405 15:22:20.946619 140252361586432 tf_logging.py:161] Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d06d8> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.

WARNING: Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d07c8> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
WARNING: Entity <method-wrapper '__call__' of weakref object at 0x7f8e14520598> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
WARNING: Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d02c8> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
WARNING: Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d0318> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
WARNING: Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d0368> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
WARNING: Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d03b8> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
WARNING: Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d0408> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
WARNING: Entity <method-wrapper '__call__' of weakref object at 0x7f8e145d06d8> could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
Accuracy after restoring the saved model without strategy: 90.97000122070312
 

以上






TensorFlow 2.0 Alpha : 上級 Tutorials : 分散訓練 :- TensorFlow の分散訓練

TensorFlow 2.0 Alpha : 上級 Tutorials : 分散訓練 :- TensorFlow の分散訓練 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/12/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – Distributed training の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

分散訓練 :- TensorFlow の分散訓練

概要

tf.distribute.Strategy API は複数の処理ユニットに渡り貴方の訓練を分散するための抽象を提供します。目標はユーザに既存のモデルと訓練コードを (最小限の変更で) 使用して分散訓練を可能にすることです。

このチュートリアルは tf.distribute.MirroredStrategy を使用します、これは一つのマシン上の多くの GPU 上で同期訓練を伴う in-graph リプリケーションを行ないます。本質的には、それはモデルの変数の総てを各プロセッサにコピーします。それから、それは総てのプロセッサからの勾配を結合するために all-reduce を使用して結合された値をモデルの総てのコピーに適用します。

MirroredStategy は TensorFlow コアで利用可能な幾つかの分散ストラテジーの一つです。より多くのストラテジーについて 分散ストラテジー・ガイド で読むことができます。

 

Keras API

このサンプルはモデルと訓練ループを構築するために tf.kera API を使用します。カスタム訓練ループについては、このチュートリアル を見てください。

 

Import 依存性

from __future__ import absolute_import, division, print_function, unicode_literals
# Import TensorFlow
!pip install -q tensorflow==2.0.0-alpha0 
import tensorflow_datasets as tfds
import tensorflow as tf

import os

 

データセットをダウンロードする

MNIST データセットをダウンロードしてそれを TensorFlow Datasets からロードします。これは tf.data フォーマットの dataset を返します。

with_info を True に設定するとデータセット全体に対するメタデータを含みます、これはここでは ds_info にセーブされます。他のものの中で、このメタデータは訓練とテストサンプルの数を含みます。

datasets, ds_info = tfds.load(name='mnist', with_info=True, as_supervised=True)
mnist_train, mnist_test = datasets['train'], datasets['test']
Dl Completed...: 0 url [00:00, ? url/s]
Dl Size...: 0 MiB [00:00, ? MiB/s]

Dl Completed...:   0%|          | 0/1 [00:00<?, ? url/s]
Dl Size...: 0 MiB [00:00, ? MiB/s]

Dl Completed...:   0%|          | 0/2 [00:00<?, ? url/s]
Dl Size...: 0 MiB [00:00, ? MiB/s]

Dl Completed...:   0%|          | 0/3 [00:00<?, ? url/s]
Dl Size...: 0 MiB [00:00, ? MiB/s]

Dl Completed...:   0%|          | 0/4 [00:00<?, ? url/s]
Dl Size...: 0 MiB [00:00, ? MiB/s]

Dl Completed...:   0%|          | 0/4 [00:00<?, ? url/s]
Dl Size...: 0 MiB [00:00, ? MiB/s]

Extraction completed...: 0 file [00:00, ? file/s]
Downloading / extracting dataset mnist (11.06 MiB) to /root/tensorflow_datasets/mnist/1.0.0...

Dl Completed...:  25%|██▌       | 1/4 [00:00<00:00,  5.02 url/s]
Dl Size...: 0 MiB [00:00, ? MiB/s]

Dl Completed...:  25%|██▌       | 1/4 [00:00<00:00,  5.02 url/s]
Dl Size...:   0%|          | 0/1 [00:00<?, ? MiB/s]

Dl Completed...:  25%|██▌       | 1/4 [00:00<00:00,  5.02 url/s]
Dl Size...:   0%|          | 0/10 [00:00<?, ? MiB/s]

Dl Completed...:  25%|██▌       | 1/4 [00:00<00:00,  5.02 url/s]
Dl Size...:   0%|          | 0/10 [00:00<?, ? MiB/s]

Extraction completed...:   0%|          | 0/1 [00:00<?, ? file/s]

Dl Completed...:  25%|██▌       | 1/4 [00:00<00:00,  5.02 url/s]
Dl Size...:   0%|          | 0/10 [00:00<?, ? MiB/s]

Dl Completed...:  25%|██▌       | 1/4 [00:00<00:00,  5.02 url/s]
Dl Size...:   0%|          | 0/10 [00:00<?, ? MiB/s]

Dl Completed...:  50%|█████     | 2/4 [00:00<00:00,  5.29 url/s]
Dl Size...:   0%|          | 0/10 [00:00<?, ? MiB/s]

Dl Completed...:  50%|█████     | 2/4 [00:00<00:00,  5.29 url/s]
Dl Size...:   0%|          | 0/10 [00:00<?, ? MiB/s]

Extraction completed...:  50%|█████     | 1/2 [00:00<00:00,  4.26 file/s]

Dl Completed...:  50%|█████     | 2/4 [00:00<00:00,  5.29 url/s]
Dl Size...:   0%|          | 0/10 [00:00<?, ? MiB/s]

Extraction completed...: 100%|██████████| 2/2 [00:00<00:00,  4.76 file/s]
Dl Completed...:  50%|█████     | 2/4 [00:00<00:00,  5.29 url/s]
Dl Size...:  10%|█         | 1/10 [00:00<00:05,  1.59 MiB/s]

Dl Completed...:  50%|█████     | 2/4 [00:00<00:00,  5.29 url/s]
Dl Size...:  20%|██        | 2/10 [00:00<00:05,  1.59 MiB/s]

Dl Completed...:  75%|███████▌  | 3/4 [00:00<00:00,  4.29 url/s]
Dl Size...:  20%|██        | 2/10 [00:00<00:05,  1.59 MiB/s]

Dl Completed...:  75%|███████▌  | 3/4 [00:00<00:00,  4.29 url/s]
Dl Size...:  20%|██        | 2/10 [00:00<00:05,  1.59 MiB/s]

Extraction completed...:  67%|██████▋   | 2/3 [00:00<00:00,  4.76 file/s]
Dl Completed...:  75%|███████▌  | 3/4 [00:00<00:00,  4.29 url/s]
Dl Size...:  30%|███       | 3/10 [00:00<00:03,  2.16 MiB/s]

Extraction completed...:  67%|██████▋   | 2/3 [00:00<00:00,  4.76 file/s]

Dl Completed...:  75%|███████▌  | 3/4 [00:00<00:00,  4.29 url/s]
Dl Size...:  30%|███       | 3/10 [00:00<00:03,  2.16 MiB/s]

Extraction completed...: 100%|██████████| 3/3 [00:00<00:00,  3.61 file/s]
Dl Completed...:  75%|███████▌  | 3/4 [00:00<00:00,  4.29 url/s]
Dl Size...:  40%|████      | 4/10 [00:00<00:02,  2.75 MiB/s]

Dl Completed...:  75%|███████▌  | 3/4 [00:00<00:00,  4.29 url/s]
Dl Size...:  50%|█████     | 5/10 [00:00<00:01,  2.75 MiB/s]

Extraction completed...: 100%|██████████| 3/3 [00:00<00:00,  3.61 file/s]
Dl Completed...:  75%|███████▌  | 3/4 [00:01<00:00,  4.29 url/s]
Dl Size...:  60%|██████    | 6/10 [00:01<00:01,  3.50 MiB/s]

Dl Completed...:  75%|███████▌  | 3/4 [00:01<00:00,  4.29 url/s]
Dl Size...:  70%|███████   | 7/10 [00:01<00:00,  3.50 MiB/s]

Extraction completed...: 100%|██████████| 3/3 [00:01<00:00,  3.61 file/s]
Dl Completed...:  75%|███████▌  | 3/4 [00:01<00:00,  4.29 url/s]
Dl Size...:  80%|████████  | 8/10 [00:01<00:00,  4.47 MiB/s]

Extraction completed...: 100%|██████████| 3/3 [00:01<00:00,  3.61 file/s]
Dl Completed...:  75%|███████▌  | 3/4 [00:01<00:00,  4.29 url/s]
Dl Size...:  90%|█████████ | 9/10 [00:01<00:00,  5.31 MiB/s]

Dl Completed...:  75%|███████▌  | 3/4 [00:01<00:00,  4.29 url/s]
Dl Size...: 100%|██████████| 10/10 [00:01<00:00,  5.31 MiB/s]

Dl Completed...: 100%|██████████| 4/4 [00:01<00:00,  2.51 url/s]
Dl Size...: 100%|██████████| 10/10 [00:01<00:00,  5.31 MiB/s]

Dl Completed...: 100%|██████████| 4/4 [00:01<00:00,  2.51 url/s]
Dl Size...: 100%|██████████| 10/10 [00:01<00:00,  5.31 MiB/s]

Extraction completed...:  75%|███████▌  | 3/4 [00:01<00:00,  3.61 file/s]

Dl Completed...: 100%|██████████| 4/4 [00:01<00:00,  2.51 url/s]
Dl Size...: 100%|██████████| 10/10 [00:01<00:00,  5.31 MiB/s]

Extraction completed...: 100%|██████████| 4/4 [00:01<00:00,  1.94 file/s]
Dl Size...: 100%|██████████| 10/10 [00:01<00:00,  5.24 MiB/s]
1 examples [00:00,  6.16 examples/s]




60000 examples [00:13, 4349.69 examples/s]
Shuffling...:   0%|          | 0/10 [00:00<?, ? shard/s]WARNING: Logging before flag parsing goes to stderr.
W0405 15:23:16.461484 140384515561216 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow_datasets/core/file_format_adapter.py:249: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`

Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 260273.29 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Writing...: 100%|██████████| 6000/6000 [00:00<00:00, 156708.54 examples/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 271998.27 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Shuffling...:  20%|██        | 2/10 [00:00<00:00, 13.66 shard/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 294326.80 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Writing...: 100%|██████████| 6000/6000 [00:00<00:00, 144187.84 examples/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 252869.48 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Shuffling...:  40%|████      | 4/10 [00:00<00:00, 13.64 shard/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 280211.82 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Writing...: 100%|██████████| 6000/6000 [00:00<00:00, 150449.41 examples/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 284910.10 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Shuffling...:  60%|██████    | 6/10 [00:00<00:00, 13.86 shard/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 238819.31 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Writing...: 100%|██████████| 6000/6000 [00:00<00:00, 137965.23 examples/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 244280.96 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Shuffling...:  80%|████████  | 8/10 [00:00<00:00, 13.58 shard/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 292693.93 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Writing...: 100%|██████████| 6000/6000 [00:00<00:00, 148655.99 examples/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 6000 examples [00:00, 232564.68 examples/s]
Writing...:   0%|          | 0/6000 [00:00<?, ? examples/s]
Shuffling...: 100%|██████████| 10/10 [00:00<00:00, 13.61 shard/s]
10000 examples [00:02, 4444.35 examples/s]
Shuffling...:   0%|          | 0/1 [00:00<?, ? shard/s]
Reading...: 0 examples [00:00, ? examples/s]
Reading...: 10000 examples [00:00, 313138.63 examples/s]
Writing...:   0%|          | 0/10000 [00:00<?, ? examples/s]
Shuffling...: 100%|██████████| 1/1 [00:00<00:00,  9.25 shard/s]

 

分散ストラテジーを定義する

MirroredStrategy オブジェクトを作成します。これは分散を処理し、内側でモデルを構築するためのコンテキストマネージャ (tf.distribute.MirroredStrategy.scope) を提供します。

strategy = tf.distribute.MirroredStrategy()
W0405 15:23:20.099184 140384515561216 cross_device_ops.py:1111] Not all devices in `tf.distribute.Strategy` are visible to TensorFlow.
print ('Number of devices: {}'.format(strategy.num_replicas_in_sync))
Number of devices: 1

 

入力パイプラインをセットアップする

モデルがマルチ GPU 上で訓練されるのであれば、特別な計算パワーを効果的に利用するためにバッチサイズはそれに従って増やされるべきです。更に、学習率もそれに従って調整されるべきです。

# You can also do ds_info.splits.total_num_examples to get the total 
# number of examples in the dataset.

num_train_examples = ds_info.splits['train'].num_examples
num_test_examples = ds_info.splits['test'].num_examples

BUFFER_SIZE = 10000

BATCH_SIZE_PER_REPLICA = 64
BATCH_SIZE = BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync

0-255 のピクセル値は 0-1 範囲に正規化されなければなりません。このスケールを関数で定義します。

def scale(image, label):
  image = tf.cast(image, tf.float32)
  image /= 255
  
  return image, label

この関数を訓練とテストデータに適用し、訓練データをシャッフルし、そして 訓練のためにそれをバッチ化します

train_dataset = mnist_train.map(scale).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
eval_dataset = mnist_test.map(scale).batch(BATCH_SIZE)

 

モデルを作成する

strategy.scope のコンテキストで Keras モデルを作成してコンパイルします。

with strategy.scope():
  model = tf.keras.Sequential([
      tf.keras.layers.Conv2D(32, 3, activation='relu', input_shape=(28, 28, 1)),
      tf.keras.layers.MaxPooling2D(),
      tf.keras.layers.Flatten(),
      tf.keras.layers.Dense(64, activation='relu'),
      tf.keras.layers.Dense(10, activation='softmax')
  ])
  
  model.compile(loss='sparse_categorical_crossentropy',
                optimizer=tf.keras.optimizers.Adam(),
                metrics=['accuracy'])

 

コールバックを定義する

ここで使用されるコールバックは :

  • Tensorboard: このコールバックはグラフを可視化することを可能にする TensorBoard のためのログを書きます。
  • モデル・チェックポイント: このコールバックは総てのエポック後にモデルをセーブします。
  • 学習率スケジューラ: このコールバックを使用すると、総てのエポック/バッチ後に変更する学習率をスケジューリングできます。

説明目的で、このノートブックでは学習率を表示するための print コールバックを追加します。

# Define the checkpoint directory to store the checkpoints

checkpoint_dir = './training_checkpoints'
# Name of the checkpoint files
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}")
# Function for decaying the learning rate.
# You can define any decay function you need.
def decay(epoch):
  if epoch < 3:
    return 1e-3
  elif epoch >= 3 and epoch < 7:
    return 1e-4
  else:
    return 1e-5
# Callback for printing the LR at the end of each epoch.
class PrintLR(tf.keras.callbacks.Callback):
  def on_epoch_end(self, epoch, logs=None):
    print ('\nLearning rate for epoch {} is {}'.format(epoch + 1, 
                                                       model.optimizer.lr.numpy()))
callbacks = [
    tf.keras.callbacks.TensorBoard(log_dir='./logs'),
    tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_prefix, 
                                       save_weights_only=True),
    tf.keras.callbacks.LearningRateScheduler(decay),
    PrintLR()
]

 

訓練そして評価する

さて、通常の方法でモデルを訓練します、モデル上で fit を呼び出してチュートリアルの最初に作成されたデータセットを渡します。このステップは貴方が訓練を分散していてもそうでなくても同じです。

model.fit(train_dataset, epochs=10, callbacks=callbacks)
W0405 15:23:21.675539 140384515561216 distributed_training_utils.py:182] Your input callback is not one of the predefined Callbacks that supports DistributionStrategy. You might encounter an error if you access one of the model's attributes as part of the callback since these attributes are not set. You can access each of the individual distributed models using the `_grouped_model` attribute of your original model.
W0405 15:23:21.676841 140384515561216 distributed_training_utils.py:182] Your input callback is not one of the predefined Callbacks that supports DistributionStrategy. You might encounter an error if you access one of the model's attributes as part of the callback since these attributes are not set. You can access each of the individual distributed models using the `_grouped_model` attribute of your original model.
W0405 15:23:21.677886 140384515561216 distributed_training_utils.py:182] Your input callback is not one of the predefined Callbacks that supports DistributionStrategy. You might encounter an error if you access one of the model's attributes as part of the callback since these attributes are not set. You can access each of the individual distributed models using the `_grouped_model` attribute of your original model.
W0405 15:23:21.678794 140384515561216 distributed_training_utils.py:182] Your input callback is not one of the predefined Callbacks that supports DistributionStrategy. You might encounter an error if you access one of the model's attributes as part of the callback since these attributes are not set. You can access each of the individual distributed models using the `_grouped_model` attribute of your original model.

Epoch 1/10
    938/Unknown - 9s 9ms/step - loss: 0.1977 - accuracy: 0.9434
Learning rate for epoch 1 is 0.0010000000474974513
938/938 [==============================] - 9s 9ms/step - loss: 0.1977 - accuracy: 0.9434
Epoch 2/10
930/938 [============================>.] - ETA: 0s - loss: 0.0680 - accuracy: 0.9791
Learning rate for epoch 2 is 0.0010000000474974513
938/938 [==============================] - 7s 7ms/step - loss: 0.0678 - accuracy: 0.9791
Epoch 3/10
933/938 [============================>.] - ETA: 0s - loss: 0.0463 - accuracy: 0.9862
Learning rate for epoch 3 is 0.0010000000474974513
938/938 [==============================] - 7s 8ms/step - loss: 0.0464 - accuracy: 0.9861
Epoch 4/10
935/938 [============================>.] - ETA: 0s - loss: 0.0256 - accuracy: 0.9927
Learning rate for epoch 4 is 9.999999747378752e-05
938/938 [==============================] - 7s 8ms/step - loss: 0.0255 - accuracy: 0.9927
Epoch 5/10
934/938 [============================>.] - ETA: 0s - loss: 0.0221 - accuracy: 0.9941
Learning rate for epoch 5 is 9.999999747378752e-05
938/938 [==============================] - 7s 8ms/step - loss: 0.0220 - accuracy: 0.9941
Epoch 6/10
936/938 [============================>.] - ETA: 0s - loss: 0.0202 - accuracy: 0.9947
Learning rate for epoch 6 is 9.999999747378752e-05
938/938 [==============================] - 7s 7ms/step - loss: 0.0201 - accuracy: 0.9947
Epoch 7/10
932/938 [============================>.] - ETA: 0s - loss: 0.0187 - accuracy: 0.9952
Learning rate for epoch 7 is 9.999999747378752e-05
938/938 [==============================] - 7s 7ms/step - loss: 0.0186 - accuracy: 0.9952
Epoch 8/10
935/938 [============================>.] - ETA: 0s - loss: 0.0161 - accuracy: 0.9963
Learning rate for epoch 8 is 9.999999747378752e-06
938/938 [==============================] - 7s 8ms/step - loss: 0.0161 - accuracy: 0.9963
Epoch 9/10
932/938 [============================>.] - ETA: 0s - loss: 0.0158 - accuracy: 0.9964
Learning rate for epoch 9 is 9.999999747378752e-06
938/938 [==============================] - 7s 8ms/step - loss: 0.0158 - accuracy: 0.9964
Epoch 10/10
934/938 [============================>.] - ETA: 0s - loss: 0.0156 - accuracy: 0.9965
Learning rate for epoch 10 is 9.999999747378752e-06
938/938 [==============================] - 7s 7ms/step - loss: 0.0156 - accuracy: 0.9965


下で見れるように、チェックポイントはセーブされています。

# check the checkpoint directory
!ls {checkpoint_dir}
checkpoint           ckpt_5.data-00000-of-00001
ckpt_1.data-00000-of-00001   ckpt_5.index
ckpt_1.index             ckpt_6.data-00000-of-00001
ckpt_10.data-00000-of-00001  ckpt_6.index
ckpt_10.index            ckpt_7.data-00000-of-00001
ckpt_2.data-00000-of-00001   ckpt_7.index
ckpt_2.index             ckpt_8.data-00000-of-00001
ckpt_3.data-00000-of-00001   ckpt_8.index
ckpt_3.index             ckpt_9.data-00000-of-00001
ckpt_4.data-00000-of-00001   ckpt_9.index
ckpt_4.index

モデルがどのように遂行するかを見るために、最新のチェックポイントをロードしてテストデータ上で evaluate を呼び出します。

適切なデータセットを使用して前のように evaluate を呼び出します。

model.load_weights(tf.train.latest_checkpoint(checkpoint_dir))

eval_loss, eval_acc = model.evaluate(eval_dataset)
print ('Eval loss: {}, Eval Accuracy: {}'.format(eval_loss, eval_acc))
    157/Unknown - 2s 10ms/step - loss: 0.0388 - accuracy: 0.9872Eval loss: 0.03881146577201713, Eval Accuracy: 0.9872000217437744

出力を見るために、TensorBoard ログをダウンロードして端末で見ることができます。

$ tensorboard --logdir=path/to/log-directory
!ls -sh ./logs
total 12K
4.0K plugins  4.0K train  4.0K validation

 

SavedModel にエクスポートする

グラフと変数をエクスポートすることを望む場合、SavedModel はこれを行なうために最善の方法です。モデルはスコープとともに、あるいはスコープなしでロードし戻すことができます。更に、SavedModel はプラットフォーム不可知論者 (= agnostic) です。

path = 'saved_model/'
tf.keras.experimental.export_saved_model(model, path)
W0405 15:25:10.681121 140384515561216 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:253: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.
W0405 15:25:10.683329 140384515561216 tf_logging.py:161] Export includes no default signature!
W0405 15:25:11.132856 140384515561216 tf_logging.py:161] Export includes no default signature!

strategy.scope なしでモデルをロードします。

unreplicated_model = tf.keras.experimental.load_from_saved_model(path)

unreplicated_model.compile(
    loss='sparse_categorical_crossentropy', 
    optimizer=tf.keras.optimizers.Adam(), 
    metrics=['accuracy'])

eval_loss, eval_acc = unreplicated_model.evaluate(eval_dataset)
print ('Eval loss: {}, Eval Accuracy: {}'.format(eval_loss, eval_acc))
    157/Unknown - 2s 10ms/step - loss: 0.0388 - accuracy: 0.9872Eval loss: 0.03881146577201713, Eval Accuracy: 0.9872000217437744

strategy.scope とともにロードします。

with strategy.scope():
  replicated_model = tf.keras.experimental.load_from_saved_model(path)
  replicated_model.compile(loss='sparse_categorical_crossentropy',
                           optimizer=tf.keras.optimizers.Adam(),
                           metrics=['accuracy'])

  eval_loss, eval_acc = replicated_model.evaluate(eval_dataset)
  print ('Eval loss: {}, Eval Accuracy: {}'.format(eval_loss, eval_acc))
    157/Unknown - 1s 9ms/step - loss: 0.0388 - accuracy: 0.9872Eval loss: 0.03881146577201713, Eval Accuracy: 0.9872000217437744
 

以上






TensorFlow 2.0 Alpha : 上級 Tutorials : データのロード :- tf.data で画像をロードする

TensorFlow 2.0 Alpha : 上級 Tutorials : データのロード :- tf.data で画像をロードする (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/11/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – Loading data の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

データのロード :- tf.data で画像をロードする

このチュートリアルは tf.data を使用してどのように画像データセットをロードするかの単純なサンプルを提供します。

このサンプルで使用されるデータセットは画像のディレクトリとして分配・配置されます、ディレクトリ毎に画像の 1 クラスです。

 

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf
AUTOTUNE = tf.data.experimental.AUTOTUNE

 

データセットをダウンロードして調査する

画像を取得する

どのような訓練でも始める前に、認識することを望む新しいクラスについてネットワークに教えるための画像のセットが必要です。最初に使用するために creative-commons license の花の写真のアーカイブを作成しました。

import pathlib
data_root_orig = tf.keras.utils.get_file(origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
                                         fname='flower_photos', untar=True)
data_root = pathlib.Path(data_root_orig)
print(data_root)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228818944/228813984 [==============================] - 9s 0us/step
/root/.keras/datasets/flower_photos

218 MB をダウンロード後、今では利用可能な花の写真のコピーを持つはずです :

for item in data_root.iterdir():
  print(item)
/root/.keras/datasets/flower_photos/sunflowers
/root/.keras/datasets/flower_photos/LICENSE.txt
/root/.keras/datasets/flower_photos/tulips
/root/.keras/datasets/flower_photos/daisy
/root/.keras/datasets/flower_photos/roses
/root/.keras/datasets/flower_photos/dandelion
import random
all_image_paths = list(data_root.glob('*/*'))
all_image_paths = [str(path) for path in all_image_paths]
random.shuffle(all_image_paths)

image_count = len(all_image_paths)
image_count
3670
all_image_paths[:10]
['/root/.keras/datasets/flower_photos/sunflowers/9555827829_74e6f60f1d_m.jpg',
 '/root/.keras/datasets/flower_photos/dandelion/10437652486_aa86c14985.jpg',
 '/root/.keras/datasets/flower_photos/roses/1801614110_bb9fa46830.jpg',
 '/root/.keras/datasets/flower_photos/roses/7187035716_5d0fb95c31_n.jpg',
 '/root/.keras/datasets/flower_photos/tulips/8677713853_1312f65e71.jpg',
 '/root/.keras/datasets/flower_photos/sunflowers/16832961488_5f7e70eb5e_n.jpg',
 '/root/.keras/datasets/flower_photos/roses/685724528_6cd5cbe203.jpg',
 '/root/.keras/datasets/flower_photos/tulips/8689672277_b289909f97_n.jpg',
 '/root/.keras/datasets/flower_photos/tulips/16582481123_06e8e6b966_n.jpg',
 '/root/.keras/datasets/flower_photos/dandelion/2453532367_fc373df4de.jpg']

 

画像を調べる

画像の 2,3 を簡単に見てみましょう、そうすれば何を処理しているのかを知るでしょう :

import os
attributions = (data_root/"LICENSE.txt").open(encoding='utf-8').readlines()[4:]
attributions = [line.split(' CC-BY') for line in attributions]
attributions = dict(attributions)
import IPython.display as display

def caption_image(image_path):
    image_rel = pathlib.Path(image_path).relative_to(data_root)
    return "Image (CC BY 2.0) " + ' - '.join(attributions[str(image_rel)].split(' - ')[:-1])
    
for n in range(3):
  image_path = random.choice(all_image_paths)
  display.display(display.Image(image_path))
  print(caption_image(image_path))
  print()

Image (CC BY 2.0)  by Warren Rachele

Image (CC BY 2.0)  by William Warby

Image (CC BY 2.0)  by liz west

 

各画像に対するラベルを決定する

利用可能なラベルをリストする :

label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())
label_names
['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']

各ラベルにインデックスを割り当てます :

label_to_index = dict((name, index) for index,name in enumerate(label_names))
label_to_index
{'daisy': 0, 'dandelion': 1, 'roses': 2, 'sunflowers': 3, 'tulips': 4}

総てのファイルのリスト、そしてそのラベル・インデックスを作成します。

all_image_labels = [label_to_index[pathlib.Path(path).parent.name]
                    for path in all_image_paths]

print("First 10 labels indices: ", all_image_labels[:10])
First 10 labels indices:  [3, 1, 2, 2, 4, 3, 2, 4, 4, 1]

 

画像をロードしてフォーマットする

TensorFlow は画像をロードして処理するために必要な総てのツールを含みます :

img_path = all_image_paths[0]
img_path
'/root/.keras/datasets/flower_photos/sunflowers/9555827829_74e6f60f1d_m.jpg'

ここに生データがあります :

img_raw = tf.io.read_file(img_path)
print(repr(img_raw)[:100]+"...")
<tf.Tensor: id=1, shape=(), dtype=string, numpy=b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x...

それを画像 tensor にデコードします :

img_tensor = tf.image.decode_image(img_raw)

print(img_tensor.shape)
print(img_tensor.dtype)
(240, 160, 3)

それを貴方のモデルのためにリサイズします :

img_final = tf.image.resize(img_tensor, [192, 192])
img_final = img_final/255.0
print(img_final.shape)
print(img_final.numpy().min())
print(img_final.numpy().max())
(192, 192, 3)
0.0
0.99987745

後のためにこれらを単純な関数にラップします。

def preprocess_image(image):
  image = tf.image.decode_jpeg(image, channels=3)
  image = tf.image.resize(image, [192, 192])
  image /= 255.0  # normalize to [0,1] range

  return image
def load_and_preprocess_image(path):
  image = tf.io.read_file(path)
  return preprocess_image(image)
import matplotlib.pyplot as plt

image_path = all_image_paths[0]
label = all_image_labels[0]

plt.imshow(load_and_preprocess_image(img_path))
plt.grid(False)
plt.xlabel(caption_image(img_path))
plt.title(label_names[label].title())
print()

 

tf.data.Dataset を構築する

画像のデータセット

tf.data.Dataset を構築するための最も容易な方法は from_tensor_slices メソッドを使用することです

文字列の配列をスライスすると、文字列のデータセットという結果になります :

path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)

shapes と types はデータセットの各アイテムの内容を記述します。この場合それはスカラー binary-strings のセットです。

print(path_ds)
<TensorSliceDataset shapes: (), types: tf.string>

さてパスのデータセットに渡り preprocess_image をマップすることにより、画像を on the fly にロードしてフォーマットする新しいデータセットを作成します。

image_ds = path_ds.map(load_and_preprocess_image, num_parallel_calls=AUTOTUNE)
import matplotlib.pyplot as plt

plt.figure(figsize=(8,8))
for n,image in enumerate(image_ds.take(4)):
  plt.subplot(2,2,n+1)
  plt.imshow(image)
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])
  plt.xlabel(caption_image(all_image_paths[n]))

 

(image, label) ペアのデータセット

同じ from_tensor_slices メソッドを使用してラベルのデータセットを構築できます。

label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels, tf.int64))
for label in label_ds.take(10):
  print(label_names[label.numpy()])
sunflowers
dandelion
roses
roses
tulips
sunflowers
roses
tulips
tulips
dandelion

データセットは同じ順序にあるので (image, label) ペアのデータセットを得るために単にそれらを一緒に zip できます。

image_label_ds = tf.data.Dataset.zip((image_ds, label_ds))

新しいデータセットの shapes と types も shapes と types のタプルで、各フィールドを記述します :

print(image_label_ds)
<ZipDataset shapes: ((192, 192, 3), ()), types: (tf.float32, tf.int64)>

Note: all_image_labels と all_image_paths のような配列を持つとき tf.data.dataset.Dataset.zip の代替は配列のペアをスライスすることです。

ds = tf.data.Dataset.from_tensor_slices((all_image_paths, all_image_labels))

# The tuples are unpacked into the positional arguments of the mapped function 
def load_and_preprocess_from_path_label(path, label):
  return load_and_preprocess_image(path), label

image_label_ds = ds.map(load_and_preprocess_from_path_label)
image_label_ds
<MapDataset shapes: ((192, 192, 3), ()), types: (tf.float32, tf.int32)>

 

訓練のための基本メソッド

このデータセットでモデルを訓練するためにはデータに以下を望むでしょう :

  • 上手くシャッフルされる。
  • バッチ化される。
  • 永久に反復する。
  • 可能な限り早くバッチが利用可能となる。

これらの特徴は tf.data api を使用して容易に追加できます。

BATCH_SIZE = 32

# Setting a shuffle buffer size as large as the dataset ensures that the data is
# completely shuffled.
ds = image_label_ds.shuffle(buffer_size=image_count)
ds = ds.repeat()
ds = ds.batch(BATCH_SIZE)
# `prefetch` lets the dataset fetch batches, in the background while the model is training.
ds = ds.prefetch(buffer_size=AUTOTUNE)
ds
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int32)>

ここで注意すべき 2, 3 のことがあります :

  1. 順序は重要です。
    • .repeat の前の .shuffle はエポック境界を越えて項目をシャッフルします (ある項目は他が見られる前に 2 度見られるでしょう)。
    • .batch 後の .shuffle はバッチの順序をシャッフルしますが、バッチを越えて項目をシャッフルはしません。
  2. 完全なシャッフルのために buffer_size をデータセットと同じサイズ使用します。データセットのサイズまで、巨大な値はより良いランダム化を提供しますが、より多くのメモリを使用します。
  3. shuffle バッファは任意の要素がそれから引き出される前に満たされます。そのため巨大な buffer_size はデータセットが開始されるときに遅延を引き起こすかもしれません。
  4. シャッフルされたデータセットは shuffle-buffer が完全に空になるまでデータセットの終わりを報告しません。データセットは .repeat により再スタートされ、shuffle-buffer が満たされるためのもう一つの wait を引き起こします。

この最後のポイントは融合された tf.data.experimental.shuffle_and_repeat 関数を伴い tf.data.Dataset.apply メソッドを使用することによりアドレスされます :

ds = image_label_ds.apply(
  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds = ds.batch(BATCH_SIZE)
ds = ds.prefetch(buffer_size=AUTOTUNE)
ds
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int32)>

 

データセットをモデルにパイプする

tf.keras.applications から MobileNet v2 のコピーを取得します。

これは単純な転移学習サンプルのために使用されます。

MobileNet 重みを非訓練可能として設定します :

mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192, 192, 3), include_top=False)
mobile_net.trainable=False
Downloading data from https://github.com/JonathanCMitchell/mobilenet_v2_keras/releases/download/v1.1/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_192_no_top.h5
9412608/9406464 [==============================] - 1s 0us/step

このモデルはその入力に [-1, 1] 範囲に正規化されることを想定します :

help(keras_applications.mobilenet_v2.preprocess_input)
...
This function applies the "Inception" preprocessing which converts
the RGB values from [0, 255] to [-1, 1] 
...

そこでそれを MobilNet モデルに渡す前に、入力を [0, 1] の範囲から [-1, 1] に変換する必要があります。

def change_range(image,label):
  return 2*image-1, label

keras_ds = ds.map(change_range)

MobileNet は各画像に対して特徴の 6×6 空間グリッドを返します。

次を見るためにそれに画像のバッチを渡します :

# The dataset may take a few seconds to start, as it fills its shuffle buffer.
image_batch, label_batch = next(iter(keras_ds))
feature_map_batch = mobile_net(image_batch)
print(feature_map_batch.shape)
(32, 6, 6, 1280)

それから MobileNet をラップしたモデルを構築して、そして出力 tf.keras.layers.Dense 層の前にそれらの空間次元に渡り平均するために tf.keras.layers.GlobalAveragePooling2D を使用します :

model = tf.keras.Sequential([
  mobile_net,
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(len(label_names))])

今ではそれは期待された shape の出力を生成します :

logit_batch = model(image_batch).numpy()

print("min logit:", logit_batch.min())
print("max logit:", logit_batch.max())
print()

print("Shape:", logit_batch.shape)
min logit: -2.1251462
max logit: 2.2703485

Shape: (32, 5)

訓練手続きを記述するためにモデルをコンパイルします :

model.compile(optimizer=tf.keras.optimizers.Adam(), 
              loss='sparse_categorical_crossentropy',
              metrics=["accuracy"])

2 つ訓練可能な変数があります : Dense 重みとバイアスです :

len(model.trainable_variables) 
2
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
mobilenetv2_1.00_192 (Model) (None, 6, 6, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dense (Dense)                (None, 5)                 6405      
=================================================================
Total params: 2,264,389
Trainable params: 6,405
Non-trainable params: 2,257,984
_________________________________________________________________

モデルを訓練します。

通常はエポック毎に現実的なステップ数を指定するでしょうが、デモ目的で 3 ステップだけを実行します。

steps_per_epoch=tf.math.ceil(len(all_image_paths)/BATCH_SIZE).numpy()
steps_per_epoch
115.0
model.fit(ds, epochs=1, steps_per_epoch=3)
3/3 [==============================] - 10s 3s/step - loss: 9.4592 - accuracy: 0.1562


 

パフォーマンス

Note: このセクションはパフォーマンスの助けとなるかもしれない 2, 3 の容易なトリックを示します。深いガイドについては 入力パイプライン・パフォーマンス を見てください。

上で使用された単純なパイプラインは各ファイルを個々に読みます、各エポックで。これは CPU 上のローカル訓練のためには良いですが GPU 訓練のためには十分でないかもしれません、そしてどのような種類の分散訓練のためには総合的に不適当です。

調査するために、最初にデータセットのパフォーマンスをチェックする単純な関数を構築します :

import time
default_timeit_steps = 2*steps_per_epoch+1

def timeit(ds, steps=default_timeit_steps):
  overall_start = time.time()
  # Fetch a single batch to prime the pipeline (fill the shuffle buffer),
  # before starting the timer
  it = iter(ds.take(steps+1))
  next(it)

  start = time.time()
  for i,(images,labels) in enumerate(it):
    if i%10 == 0:
      print('.',end='')
  print()
  end = time.time()

  duration = end-start
  print("{} batches: {} s".format(steps, duration))
  print("{:0.5f} Images/s".format(BATCH_SIZE*steps/duration))
  print("Total time: {}s".format(end-overall_start))

現在のデータセットのパフォーマンスは :

ds = image_label_ds.apply(
  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds = ds.batch(BATCH_SIZE).prefetch(buffer_size=AUTOTUNE)
ds
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int32)>
timeit(ds)
........................
231.0 batches: 22.15600061416626 s
333.63422 Images/s
Total time: 31.439151525497437s

 

キャッシュ

エポックを越えて計算を容易にキャッシュするために tf.data.Dataset.cache を使用します。これは dataq がメモリに収まる場合には特にパフォーマンスが高いです。

ここで画像が前処理 (デコードとリサイズ) された後に、キャッシュされます :

ds = image_label_ds.cache()
ds = ds.apply(
  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds = ds.batch(BATCH_SIZE).prefetch(buffer_size=AUTOTUNE)
ds
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int32)>
timeit(ds)
........................
231.0 batches: 0.7710902690887451 s
9586.42625 Images/s
Total time: 9.224573850631714s

in-memory キャッシュを使用する一つのデメリットはキャッシュが各実行で再構築されなければならないことで、データセットが開始されるたびに同じスタートアップ遅延を与えます :

timeit(ds)
........................
231.0 batches: 0.8165614604949951 s
9052.59476 Images/s
Total time: 9.110528230667114s

データがメモリに収まらない場合は、キャッシュ・ファイルを使用します :

ds = image_label_ds.cache(filename='./cache.tf-data')
ds = ds.apply(
  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds = ds.batch(BATCH_SIZE).prefetch(1)
ds
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int32)>
timeit(ds)
........................
231.0 batches: 2.653285264968872 s
2785.98012 Images/s
Total time: 15.944914102554321s

キャッシュファイルはまた、キャッシュを再構築することなしに迅速にデータセットを再起動するために使用できるという優位点を持ちます。それが 2 回目にどのくらい早いかに注意してください :

timeit(ds)
........................
231.0 batches: 2.185948371887207 s
3381.59862 Images/s
Total time: 3.409513235092163s

 

TFRecord ファイル

生画像データ

TFRecord ファイルはバイナリ blob のシークエンスをストアするための単純なフォーマットです。複数のサンプルを同じファイルにパックすることにより、TensorFlow は一度に複数のサンプルを読むことができ、それは GCS のようなリモート・ストレージサービスを使用するときパフォーマンスのために特に重要です。

最初に、生画像データから TFRecord ファイルを構築します :

image_ds = tf.data.Dataset.from_tensor_slices(all_image_paths).map(tf.io.read_file)
tfrec = tf.data.experimental.TFRecordWriter('images.tfrec')
tfrec.write(image_ds)

次に先に定義した preprocess_image 関数を使用して TFRecord ファイルから読み、画像をデコード/再フォーマットするデータセットを構築します。

image_ds = tf.data.TFRecordDataset('images.tfrec').map(preprocess_image)

想定する (image, label) ペアを得るためにそれを先に定義したラベル・データセットと共に zip します。

ds = tf.data.Dataset.zip((image_ds, label_ds))
ds = ds.apply(
  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds=ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)
ds
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int64)>
timeit(ds)
........................
231.0 batches: 21.571000337600708 s
342.68230 Images/s
Total time: 31.82367253303528s

これはキャッシュ・バージョンよりも遅いです、何故ならば前処理をキャッシュしていないからです。

 

Serialized Tensors

ある前処理を TFRecord ファイルにセーブするためには、最初に処理された画像のデータセットを作成します、前のように :

paths_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)
image_ds = paths_ds.map(load_and_preprocess_image)
image_ds
<MapDataset shapes: (192, 192, 3), types: tf.float32>

今では .jpeg 文字列のデータセットの代わりに、これは tensor のデータセットです。これを TFRecord ファイルにシリアライズするためには、最初に tensor のデータセットを文字列 (= strings) のデータセットに変換します。

ds = image_ds.map(tf.io.serialize_tensor)
ds
<MapDataset shapes: (), types: tf.string>
tfrec = tf.data.experimental.TFRecordWriter('images.tfrec')
tfrec.write(ds)

キャッシュされた前処理と共に、データは TFRecord ファイルから極めて効率的にロードできます。それを使用しようとする前に tensor を単にデシリアライズすることを覚えていてください。

ds = tf.data.TFRecordDataset('images.tfrec')

def parse(x):
  result = tf.io.parse_tensor(x, out_type=tf.float32)
  result = tf.reshape(result, [192, 192, 3])
  return result

ds = ds.map(parse, num_parallel_calls=AUTOTUNE)
ds
<ParallelMapDataset shapes: (192, 192, 3), types: tf.float32>

今、ラベルを追加して前のように同じ標準的な演算を適用します :

ds = tf.data.Dataset.zip((ds, label_ds))
ds = ds.apply(
  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds=ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)
ds
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int64)>
timeit(ds)
........................
231.0 batches: 1.9944682121276855 s
3706.25110 Images/s
Total time: 2.8945322036743164s
 

以上






TensorFlow 2.0 Alpha : 上級 Tutorials : GAN と VAE :- Pix2Pix

TensorFlow 2.0 Alpha : 上級 Tutorials : GAN と VAE :- Pix2Pix (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/11/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – GANs and VAEs の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

GAN と VAE :- Pix2Pix

このノートブックは Image-to-Image Translation with Conditional Adversarial Networks で記述されている、条件付き GAN を使用して画像から画像への変換を実演します。このテクニックを使用して白黒写真を彩色したり、google マップを google earth に変換する等のことができます。ここでは、建物の正面 (= facade) をリアルな建物に変換します。

サンプルでは、CMP Facade データベース を使用します、これは プラハの Czech Technical UniversityCenter for Machine Perception により役立つように提供されています。サンプルを短く保持するために、上の ペーパー の著者により作成された、このデータセットの前処理された コピー を使用します。

各エポックは単一の P100 上で 58 秒前後かかります。

下は 200 エポックの間モデルを訓練した後に生成された出力です。


 

TensorFlow と他のライブラリをインポートする

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow-gpu==2.0.0-alpha0
import tensorflow as tf

import os
import time
import matplotlib.pyplot as plt
from IPython.display import clear_output

 

データセットをロードする

データセットと類似のデータセットを ここ からダウンロードできます。(上の) ペーパーで言及されているように、訓練データセットにランダムに jittering とミラーリングを適用します。* ランダム jittering では、画像は 286 x 286 にリサイズされてからランダムに 256 x 256 にクロップされます。* ランダム・ミラーリングでは、画像は水平に i.e. 左から右にランダムにフリップ (反転) されます。

_URL = 'https://people.eecs.berkeley.edu/~tinghuiz/projects/pix2pix/datasets/facades.tar.gz'

path_to_zip = tf.keras.utils.get_file('facades.tar.gz',
                                      origin=_URL, 
                                      extract=True)

PATH = os.path.join(os.path.dirname(path_to_zip), 'facades/')
Downloading data from https://people.eecs.berkeley.edu/~tinghuiz/projects/pix2pix/datasets/facades.tar.gz
31694848/31692800 [==============================] - 0s 0us/step
31703040/31692800 [==============================] - 0s 0us/step
BUFFER_SIZE = 400
BATCH_SIZE = 1
IMG_WIDTH = 256
IMG_HEIGHT = 256
def load(image_file):
  image = tf.io.read_file(image_file)
  image = tf.image.decode_jpeg(image)

  w = tf.shape(image)[1]

  w = w // 2
  real_image = image[:, :w, :]
  input_image = image[:, w:, :]

  input_image = tf.cast(input_image, tf.float32)
  real_image = tf.cast(real_image, tf.float32)
  
  return input_image, real_image
inp, re = load(PATH+'train/100.jpg')
# casting to int for matplotlib to show the image
plt.figure()
plt.imshow(inp/255.0)
plt.figure()
plt.imshow(re/255.0)
<matplotlib.image.AxesImage at 0x7f7a22c7d850>

def resize(input_image, real_image, height, width):
  input_image = tf.image.resize(input_image, [height, width], 
                                method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)
  real_image = tf.image.resize(real_image, [height, width], 
                               method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)
  
  return input_image, real_image
def random_crop(input_image, real_image):
  stacked_image = tf.stack([input_image, real_image], axis=0)
  cropped_image = tf.image.random_crop(
      stacked_image, size=[2, IMG_HEIGHT, IMG_WIDTH, 3])
  
  return cropped_image[0], cropped_image[1]
# normalizing the images to [-1, 1]

def normalize(input_image, real_image):
  input_image = (input_image / 127.5) - 1
  real_image = (real_image / 127.5) - 1
  
  return input_image, real_image
@tf.function()
def random_jitter(input_image, real_image):
  # resizing to 286 x 286 x 3
  input_image, real_image = resize(input_image, real_image, 286, 286)

  # randomly cropping to 256 x 256 x 3
  input_image, real_image = random_crop(input_image, real_image)

  if tf.random.uniform(()) > 0.5:
    # random mirroring
    input_image = tf.image.flip_left_right(input_image)
    real_image = tf.image.flip_left_right(real_image)
    
  return input_image, real_image
# As you can see in the images below
# that they are going through random jittering
# Random jittering as described in the paper is to
# 1. Resize an image to bigger height and width
# 2. Randomnly crop to the original size
# 3. Randomnly flip the image horizontally 

plt.figure(figsize=(6, 6))
for i in range(4):
  rj_inp, rj_re = random_jitter(inp, re)  
  plt.subplot(2, 2, i+1)
  plt.imshow(rj_inp/255.0)
  plt.axis('off')
plt.show()

def load_image_train(image_file):
  input_image, real_image = load(image_file)
  input_image, real_image = random_jitter(input_image, real_image)
  input_image, real_image = normalize(input_image, real_image)

  return input_image, real_image
def load_image_test(image_file):
  input_image, real_image = load(image_file)
  input_image, real_image = resize(input_image, real_image, 
                                   IMG_HEIGHT, IMG_WIDTH) 
  input_image, real_image = normalize(input_image, real_image)
 
  return input_image, real_image

 

入力パイプライン

train_dataset = tf.data.Dataset.list_files(PATH+'train/*.jpg')
train_dataset = train_dataset.shuffle(BUFFER_SIZE)
train_dataset = train_dataset.map(load_image_train, 
                                  num_parallel_calls=tf.data.experimental.AUTOTUNE)
train_dataset = train_dataset.batch(1)
test_dataset = tf.data.Dataset.list_files(PATH+'test/*.jpg')
# shuffling so that for every epoch a different image is generated 
# to predict and display the progress of our model.
train_dataset = train_dataset.shuffle(BUFFER_SIZE)
test_dataset = test_dataset.map(load_image_test)
test_dataset = test_dataset.batch(1)

 

Generator を構築する

  • generator のアーキテクチャは変更された U-Net です。
  • エンコーダの各ブロックは (Conv -> Batchnorm -> Leaky ReLU)
  • デコーダの各ブロックは (Transposed Conv -> Batchnorm -> Dropout(applied to the first 3 blocks) -> ReLU)
  • エンコーダとデコーダの間に (U-Net 内のように) スキップ・コネクションがあります。
OUTPUT_CHANNELS = 3
def downsample(filters, size, apply_batchnorm=True):
  initializer = tf.random_normal_initializer(0., 0.02)

  result = tf.keras.Sequential()
  result.add(
      tf.keras.layers.Conv2D(filters, size, strides=2, padding='same',
                             kernel_initializer=initializer, use_bias=False))

  if apply_batchnorm:
    result.add(tf.keras.layers.BatchNormalization())
    
  result.add(tf.keras.layers.LeakyReLU())

  return result
down_model = downsample(3, 4)
down_result = down_model(tf.expand_dims(inp, 0))
print (down_result.shape)
(1, 128, 128, 3)
def upsample(filters, size, apply_dropout=False):
  initializer = tf.random_normal_initializer(0., 0.02)

  result = tf.keras.Sequential()
  result.add(
    tf.keras.layers.Conv2DTranspose(filters, size, strides=2,
                                    padding='same', 
                                    kernel_initializer=initializer,
                                    use_bias=False))

  result.add(tf.keras.layers.BatchNormalization())
  
  if apply_dropout:
      result.add(tf.keras.layers.Dropout(0.5))

  result.add(tf.keras.layers.ReLU())

  return result
up_model = upsample(3, 4)
up_result = up_model(down_result)
print (up_result.shape)
(1, 256, 256, 3)
def Generator():
  down_stack = [
    downsample(64, 4, apply_batchnorm=False), # (bs, 128, 128, 64)
    downsample(128, 4), # (bs, 64, 64, 128)
    downsample(256, 4), # (bs, 32, 32, 256)
    downsample(512, 4), # (bs, 16, 16, 512)
    downsample(512, 4), # (bs, 8, 8, 512)
    downsample(512, 4), # (bs, 4, 4, 512)
    downsample(512, 4), # (bs, 2, 2, 512)
    downsample(512, 4), # (bs, 1, 1, 512)
  ]

  up_stack = [
    upsample(512, 4, apply_dropout=True), # (bs, 2, 2, 1024)
    upsample(512, 4, apply_dropout=True), # (bs, 4, 4, 1024)
    upsample(512, 4, apply_dropout=True), # (bs, 8, 8, 1024)
    upsample(512, 4), # (bs, 16, 16, 1024)
    upsample(256, 4), # (bs, 32, 32, 512)
    upsample(128, 4), # (bs, 64, 64, 256)
    upsample(64, 4), # (bs, 128, 128, 128)
  ]

  initializer = tf.random_normal_initializer(0., 0.02)
  last = tf.keras.layers.Conv2DTranspose(OUTPUT_CHANNELS, 4, 
                                         strides=2, 
                                         padding='same',
                                         kernel_initializer=initializer,
                                         activation='tanh') # (bs, 256, 256, 3)

  concat = tf.keras.layers.Concatenate() 

  inputs = tf.keras.layers.Input(shape=[None,None,3])
  x = inputs
  
  # Downsampling through the model
  skips = []
  for down in down_stack:
    x = down(x)
    skips.append(x)

  skips = reversed(skips[:-1])

  # Upsampling and establishing the skip connections
  for up, skip in zip(up_stack, skips):
    x = up(x)
    x = concat([x, skip])

  x = last(x)

  return tf.keras.Model(inputs=inputs, outputs=x)
generator = Generator()

gen_output = generator(inp[tf.newaxis,...], training=False)
plt.imshow(gen_output[0,...])
<matplotlib.image.AxesImage at 0x7f7a220700d0>

 

Discriminator を構築する

  • discriminator は PatchGAN です。
  • discriminator の各ブロックは (Conv -> BatchNorm -> Leaky ReLU) です。
  • 最後の層の後の出力の shape は (batch_size, 30, 30, 1) です。
  • 出力の各 30×30 パッチが入力画像の 70×70 の断片を分類します (そのようなアーキテクチャは PatchGAN と呼ばれます)。
  • discriminator は 2 つの入力を受け取ります。
    • 入力画像とターゲット画像、これは real として分類されるべきです。
    • 入力画像と生成画像 (generator の出力)、これは fake として分類されるべきです。
    • これらの 2 つの入力をコード (tf.concat([inp, tar], axis=-1)) で一緒に結合します。
def Discriminator():
  initializer = tf.random_normal_initializer(0., 0.02)

  inp = tf.keras.layers.Input(shape=[None, None, 3], name='input_image')
  tar = tf.keras.layers.Input(shape=[None, None, 3], name='target_image')
  
  x = tf.keras.layers.concatenate([inp, tar]) # (bs, 256, 256, channels*2)
  
  down1 = downsample(64, 4, False)(x) # (bs, 128, 128, 64)
  down2 = downsample(128, 4)(down1) # (bs, 64, 64, 128)
  down3 = downsample(256, 4)(down2) # (bs, 32, 32, 256)

  zero_pad1 = tf.keras.layers.ZeroPadding2D()(down3) # (bs, 34, 34, 256)
  conv = tf.keras.layers.Conv2D(512, 4, strides=1, 
                                kernel_initializer=initializer, 
                                use_bias=False)(zero_pad1) # (bs, 31, 31, 512)
  
  batchnorm1 = tf.keras.layers.BatchNormalization()(conv) 

  leaky_relu = tf.keras.layers.LeakyReLU()(batchnorm1)
  
  zero_pad2 = tf.keras.layers.ZeroPadding2D()(leaky_relu) # (bs, 33, 33, 512)
  
  last = tf.keras.layers.Conv2D(1, 4, strides=1,
                                kernel_initializer=initializer)(zero_pad2) # (bs, 30, 30, 1)
  
  return tf.keras.Model(inputs=[inp, tar], outputs=last)
discriminator = Discriminator()
disc_out = discriminator([inp[tf.newaxis,...], gen_output], training=False)
plt.imshow(disc_out[0,...,-1], vmin=-20, vmax=20, cmap='RdBu_r')
plt.colorbar()
<matplotlib.colorbar.Colorbar at 0x7f7a1e7a6b10>

アーキテクチャとハイパーパラメータについて更に学習するためには、ペーパー を参照できます。

 

損失関数と optimizer を定義する

  • Discriminator 損失
    • discriminator 損失関数は 2 つの入力を取ります; real 画像、生成画像です。
    • real_loss は real 画像と 1 の配列 (何故ならばこれらは real 画像だからです) の sigmod 交差エントロピー損失です。
    • generated_loss は生成画像とゼロの配列 (何故ならばこれらは fake 画像だからです) の sigmod 交差エントロピー損失です。
    • それから total_loss は real_loss と generated_loss の合計です。
  • Generator 損失
    • それは生成画像と 1 の配列の sigmoid 交差エントロピー損失です。
    • ペーパー はまた L1 損失を含みます、これは生成画像とターゲット画像の間の MAE (mean absolute error, 平均絶対誤差) です。
    • これは生成画像にターゲット画像に構造的に類似することを可能にします。
    • 総計の generator 損失を計算するための式は = gan_loss + LAMBDA * l1_loss, ここで LAMBDA = 100 です。この値は ペーパー の著者により決められました。
LAMBDA = 100
loss_object = tf.keras.losses.BinaryCrossentropy(from_logits=True)
def discriminator_loss(disc_real_output, disc_generated_output):
  real_loss = loss_object(tf.ones_like(disc_real_output), disc_real_output)
  
  generated_loss = loss_object(tf.zeros_like(disc_generated_output), disc_generated_output)

  total_disc_loss = real_loss + generated_loss

  return total_disc_loss
def generator_loss(disc_generated_output, gen_output, target):
  gan_loss = loss_object(tf.ones_like(disc_generated_output), disc_generated_output)
  
  # mean absolute error
  l1_loss = tf.reduce_mean(tf.abs(target - gen_output))

  total_gen_loss = gan_loss + (LAMBDA * l1_loss)

  return total_gen_loss
generator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)
discriminator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)

 

チェックポイント (オブジェクトベースのセーブ)

checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,
                                 discriminator_optimizer=discriminator_optimizer,
                                 generator=generator,
                                 discriminator=discriminator)

 

訓練

  • データセットに渡り反復することから開始します。
  • generator は入力画像を取り、そして私達は生成された出力を得ます。
  • discriminator は最初の入力として input_image と生成画像を受け取ります。2 番目の入力は input_image と target_image です。
  • 次に generator と discriminator 損失を計算します。
  • それから、generator と discrimator 変数 (入力) の両者に関する損失の勾配を計算してそれらを optimizer に適用します。
  • この手続き全体は下の画像で示されます。

 

生成画像を生成する

  • 訓練後、幾つかの画像を生成するときです!
  • テスト・データセットから generator に画像を渡します。
  • それから generator は入力画像を私達が期待する出力に翻訳します。
  • 最後のステップは予測をプロットすることです、そして voila !
EPOCHS = 200
def generate_images(model, test_input, tar):
  # the training=True is intentional here since
  # we want the batch statistics while running the model
  # on the test dataset. If we use training=False, we will get 
  # the accumulated statistics learned from the training dataset
  # (which we don't want)
  prediction = model(test_input, training=True)
  plt.figure(figsize=(15,15))

  display_list = [test_input[0], tar[0], prediction[0]]
  title = ['Input Image', 'Ground Truth', 'Predicted Image']

  for i in range(3):
    plt.subplot(1, 3, i+1)
    plt.title(title[i])
    # getting the pixel values between [0, 1] to plot it.
    plt.imshow(display_list[i] * 0.5 + 0.5)
    plt.axis('off')
  plt.show()
@tf.function
def train_step(input_image, target):
  with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
    gen_output = generator(input_image, training=True)

    disc_real_output = discriminator([input_image, target], training=True)
    disc_generated_output = discriminator([input_image, gen_output], training=True)

    gen_loss = generator_loss(disc_generated_output, gen_output, target)
    disc_loss = discriminator_loss(disc_real_output, disc_generated_output)

  generator_gradients = gen_tape.gradient(gen_loss, 
                                          generator.trainable_variables)
  discriminator_gradients = disc_tape.gradient(disc_loss, 
                                               discriminator.trainable_variables)

  generator_optimizer.apply_gradients(zip(generator_gradients, 
                                          generator.trainable_variables))
  discriminator_optimizer.apply_gradients(zip(discriminator_gradients, 
                                              discriminator.trainable_variables))
def train(dataset, epochs):  
  for epoch in range(epochs):
    start = time.time()

    for input_image, target in dataset:
      train_step(input_image, target)
      
    clear_output(wait=True)
    for inp, tar in test_dataset.take(1):
      generate_images(generator, inp, tar)
          
    # saving (checkpoint) the model every 20 epochs
    if (epoch + 1) % 20 == 0:
      checkpoint.save(file_prefix = checkpoint_prefix)

    print ('Time taken for epoch {} is {} sec\n'.format(epoch + 1,
                                                        time.time()-start))
train(train_dataset, EPOCHS)

Time taken for epoch 200 is 70.411645174 sec

 

最新のチェックポイントをリストアしてテストする

!ls {checkpoint_dir}
# restoring the latest checkpoint in checkpoint_dir
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))

 

テスト・データセット全体上のテスト

# Run the trained model on the entire test dataset
for inp, tar in test_dataset:
  generate_images(generator, inp, tar)
 

以上






TensorFlow 2.0 Alpha : 上級 Tutorials : GAN と VAE :- 深層畳み込み敵対的生成ネットワーク

TensorFlow 2.0 Alpha : 上級 Tutorials : GAN と VAE :- 深層畳み込み敵対的生成ネットワーク (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/10/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – GANs and VAEs の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

GAN と VAE :- 深層畳み込み敵対的生成ネットワーク

このチュートリアルは DCGAN (Deep Convolutional Generative Adversarial Network, 深層畳み込み敵対的生成ネットワーク) を使用して手書き数字の画像をどのように生成するかを実演します。コードは tf.GradientTape 訓練ループで Keras Sequential API を使用して書かれています。

 

GAN とは何でしょう?

敵対的生成ネットワーク (GAN, Generative Adversarial Networks) は現在コンピュータ・サイエンスで最も興味深いアイデアの一つです。2 つのモデルが敵対するプロセスにより同時に訓練されます。generator (「芸術家 = the artist」) がリアルに見える画像を作成することを学習し、その一方で discriminator (「芸術評論家 = the art critic」) がフェイクからリアル画像を識別することを学習します。

訓練の間、generator はリアルに見える画像を作成することに次第に上達し、その一方でdiscriminator はそれらを識別することに上達します。discriminator がもはやリアル画像をフェイクから識別できないとき、プロセスは均衡に達します。

このノートブックは MNIST データセット上でこのプロセスを実演します。次のアニメーションは 50 エポックの間訓練された generator により生成された画像のシリーズを示します。画像はランダム・ノイズとして始まり、そして時間とともに手書き数字にだんだん似ていきます。

 

TensorFlow と他のライブラリをインポートする

from __future__ import absolute_import, division, print_function, unicode_literals
!pip install -q tensorflow-gpu==2.0.0-alpha0
Collecting tensorflow-gpu==2.0.0-alpha0
[?25l  Downloading https://files.pythonhosted.org/packages/1a/66/32cffad095253219d53f6b6c2a436637bbe45ac4e7be0244557210dc3918/tensorflow_gpu-2.0.0a0-cp36-cp36m-manylinux1_x86_64.whl (332.1MB)
Successfully installed tensorflow-gpu-2.0.0a0
import tensorflow as tf
tf.__version__
'2.0.0-alpha0'
# To generate GIFs
!pip install -q imageio
Requirement already satisfied: imageio in /usr/local/lib/python3.6/dist-packages (2.4.1)
Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from imageio) (1.14.6)
Requirement already satisfied: pillow in /usr/local/lib/python3.6/dist-packages (from imageio) (4.1.1)
Requirement already satisfied: olefile in /usr/local/lib/python3.6/dist-packages (from pillow->imageio) (0.46)
import glob
import imageio
import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
from tensorflow.keras import layers
import time

from IPython import display

 

データセットをロードして準備する

generator と discriminator を訓練するために MNIST データセットを使用していきます。generator は MNIST データに似ている手書き数字を生成します。

(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5 # Normalize the images to [-1, 1]
BUFFER_SIZE = 60000
BATCH_SIZE = 256
# Batch and shuffle the data
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)

 

モデルを作成する

generator と discriminator の両者は Keras Sequential API を使用して定義されます。

 

Generator

generator は seed (ランダムノイズ) から画像を生成するために tf.keras.layers.Conv2DTranspose (upsampling) 層を使用します。この seed を入力として取る Dense 層から始めて、それから 28x28x1 の望まれるサイズに達するまで幾度か upsample します。tanh を使用する出力層を除いて、各層のためには tf.keras.layers.LeakyReLU 活性が (使用されることが) 分かるでしょう。

def make_generator_model():
    model = tf.keras.Sequential()
    model.add(layers.Dense(7*7*256, use_bias=False, input_shape=(100,)))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())
      
    model.add(layers.Reshape((7, 7, 256)))
    assert model.output_shape == (None, 7, 7, 256) # Note: None is the batch size
    
    model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
    assert model.output_shape == (None, 7, 7, 128)  
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
    assert model.output_shape == (None, 14, 14, 64)    
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
    assert model.output_shape == (None, 28, 28, 1)
  
    return model

画像を作成するために (まだ訓練されていない) generator を使用します。

generator = make_generator_model()

noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)

plt.imshow(generated_image[0, :, :, 0], cmap='gray')
<matplotlib.image.AxesImage at 0x7f18c841c6d8>

 

Discriminator

discriminator は CNN-ベースの画像分類器です。

def make_discriminator_model():
    model = tf.keras.Sequential()
    model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', 
                                     input_shape=[28, 28, 1]))
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))
      
    model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))
       
    model.add(layers.Flatten())
    model.add(layers.Dense(1))
     
    return model

生成された画像をリアルかフェイクか分類するために (まだ訓練されていない) discriminator を使用します。モデルはリアル画像のためにはポジティブ値を、そしてフェイク画像のためにはネガティブ値を出力するように訓練されます。

discriminator = make_discriminator_model()
decision = discriminator(generated_image)
print (decision)
tf.Tensor([[-0.00070298]], shape=(1, 1), dtype=float32)

 

損失と optimizer を定義する

両者のモデルのために損失関数と optimizer を定義します。

# This method returns a helper function to compute cross entropy loss
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

 

Discriminator 損失

このメソッドは discriminator がフェイク画像からリアル画像をどの程度上手く識別できるかを数値化します。それはリアル画像上の discriminator の予測を 1 の配列と、そしてフェイク (生成された) 画像上の discriminator の予測を 0 の配列と比較します。

def discriminator_loss(real_output, fake_output):
    real_loss = cross_entropy(tf.ones_like(real_output), real_output)
    fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
    total_loss = real_loss + fake_loss
    return total_loss

 

Generator 損失

generator 損失はそれがどの程度上手く discriminator を欺くことができたかを数値化します。直感的には、generator が上手く遂行していれば、discriminator はフェイク画像をリアル (or 1) として分類するでしょう。ここでは、生成された画像上の discriminator の決定を 1 の配列と比較します。

def generator_loss(fake_output):
    return cross_entropy(tf.ones_like(fake_output), fake_output)

discriminator and the generator の optimizer は異なります、何故ならば 2 つのネットワークを別々に訓練するからです。

generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)

 

チェックポイントをセーブする

このノートブックはモデルをどのようにセーブしてリストアするかも実演します、これは訓練の長い実行が中断された場合に有用であり得ます。

checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,
                                 discriminator_optimizer=discriminator_optimizer,
                                 generator=generator,
                                 discriminator=discriminator)

 

訓練ループを定義する

EPOCHS = 50
noise_dim = 100
num_examples_to_generate = 16

# We will reuse this seed overtime (so it's easier)
# to visualize progress in the animated GIF)
seed = tf.random.normal([num_examples_to_generate, noise_dim])

訓練ループは generator が入力としてランダムシードを受け取ることから開始されます。そのシードは画像を生成するために使用されます。それから discriminator は (訓練セットからドローされた) リアル画像と (generator により生成された) フェイク画像を分類するために使用されます。損失はこれらのモデルの各々のために計算されて、勾配は generator と discriminator を更新するために使用されます。

# Notice the use of `tf.function`
# This annotation causes the function to be "compiled".
@tf.function
def train_step(images):
    noise = tf.random.normal([BATCH_SIZE, noise_dim])

    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
      generated_images = generator(noise, training=True)

      real_output = discriminator(images, training=True)
      fake_output = discriminator(generated_images, training=True)

      gen_loss = generator_loss(fake_output)
      disc_loss = discriminator_loss(real_output, fake_output)

    gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
    gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)

    generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
    discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
def train(dataset, epochs):  
  for epoch in range(epochs):
    start = time.time()
    
    for image_batch in dataset:
      train_step(image_batch)

    # Produce images for the GIF as we go
    display.clear_output(wait=True)
    generate_and_save_images(generator,
                             epoch + 1,
                             seed)
    
    # Save the model every 15 epochs
    if (epoch + 1) % 15 == 0:
      checkpoint.save(file_prefix = checkpoint_prefix)
    
    print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))
    
  # Generate after the final epoch
  display.clear_output(wait=True)
  generate_and_save_images(generator,
                           epochs,
                           seed)

画像を生成してセーブする

def generate_and_save_images(model, epoch, test_input):
  # Notice `training` is set to False. 
  # This is so all layers run in inference mode (batchnorm).
  predictions = model(test_input, training=False)

  fig = plt.figure(figsize=(4,4))
  
  for i in range(predictions.shape[0]):
      plt.subplot(4, 4, i+1)
      plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray')
      plt.axis('off')
        
  plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
  plt.show()

 

モデルを訓練する

generator と discriminator を同時に訓練するためには上で定義された train() メソッドを呼び出します。注意してください、GAN の訓練はトリッキーです。generator と discriminator は互いに圧倒 (= overpower) しないこと (e.g., それらは同様なレートで訓練します) は重要です。

訓練の最初では、生成された画像はランダムノイズのように見えます。訓練が進むにつれて、生成される数字は段々とリアルに見えます。およそ 50 エポック後、それらは MNIST 数字に似ます。Colab のデフォルト設定で約 1 分 / エポックかかるかもしれません。

%%time
train(train_dataset, EPOCHS)

CPU times: user 3min 10s, sys: 48.1 s, total: 3min 58s
Wall time: 10min 52s

最新のチェックポイントをリストアします。

checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f1840619d30>

 

GIF を作成する

# Display a single image using the epoch number
def display_image(epoch_no):
  return PIL.Image.open('image_at_epoch_{:04d}.png'.format(epoch_no))
display_image(EPOCHS)

訓練の間にセーブされた画像を使用して animated gif を作成するために imageio を使用します。

anim_file = 'dcgan.gif'

with imageio.get_writer(anim_file, mode='I') as writer:
  filenames = glob.glob('image*.png')
  filenames = sorted(filenames)
  last = -1
  for i,filename in enumerate(filenames):
    frame = 2*(i**0.5)
    if round(frame) > round(last):
      last = frame
    else:
      continue
    image = imageio.imread(filename)
    writer.append_data(image)
  image = imageio.imread(filename)
  writer.append_data(image)

import IPython
if IPython.version_info > (6,2,0,''):
  display.Image(filename=anim_file)

Colab で作業しているのであれば、下のコードでアニメーションをダウンロードできます :

try:
  from google.colab import files
except ImportError:
  pass
else:
  files.download(lite_model_path)

 

Next steps

このチュートリアルは GAN を書いて訓練するために必要な完全なコードを示しました。次のステップとして、異なるデータセットで実験することを望むかもしれません、例えば、Kaggle で利用可能な Large-scale Celeb Faces Attributes (CelebA) データセットです。GAN についてより多く学習するためには NIPS 2016 Tutorial: Generative Adversarial Networks を勧めます。

 

以上






TensorFlow 2.0 Alpha : 上級 Tutorials : GAN と VAE :- 畳み込み変分オートエンコーダ

TensorFlow 2.0 Alpha : 上級 Tutorials : GAN と VAE :- 畳み込み変分オートエンコーダ (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/09/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – GANs and VAEs の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

GAN と VAE :- 畳み込み変分オートエンコーダ

このノートブックは変分オートエンコーダを訓練することにより手書き数字をどのように生成するかを実演します (1, 2)。

# to generate gifs
!pip install -q imageio
Requirement already satisfied: imageio in /usr/local/lib/python3.6/dist-packages (2.4.1)
Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from imageio) (1.14.6)
Requirement already satisfied: pillow in /usr/local/lib/python3.6/dist-packages (from imageio) (4.1.1)
Requirement already satisfied: olefile in /usr/local/lib/python3.6/dist-packages (from pillow->imageio) (0.46)

 

TensorFlow と他のライブラリをインポートする

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow-gpu==2.0.0-alpha0
import tensorflow as tf

import os
import time
import numpy as np
import glob
import matplotlib.pyplot as plt
import PIL
import imageio

from IPython import display
Requirement already satisfied: tf-nightly-gpu-2.0-preview in /usr/local/lib/python3.6/dist-packages (2.0.0.dev20190329)
Requirement already satisfied: tensorflow-estimator-2.0-preview in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (1.14.0.dev2019032900)
Requirement already satisfied: tb-nightly<1.15.0a0,>=1.14.0a0 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (1.14.0a20190319)
Requirement already satisfied: keras-applications>=1.0.6 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (1.0.7)
Requirement already satisfied: absl-py>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (0.7.1)
Requirement already satisfied: protobuf>=3.6.1 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (3.7.1)
Requirement already satisfied: google-pasta>=0.1.2 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (0.1.4)
Requirement already satisfied: grpcio>=1.8.6 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (1.15.0)
Requirement already satisfied: numpy<2.0,>=1.14.5 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (1.14.6)
Requirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (1.1.0)
Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (0.33.1)
Requirement already satisfied: keras-preprocessing>=1.0.5 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (1.0.9)
Requirement already satisfied: astor>=0.6.0 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (0.7.1)
Requirement already satisfied: gast>=0.2.0 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (0.2.2)
Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/dist-packages (from tf-nightly-gpu-2.0-preview) (1.11.0)
Requirement already satisfied: werkzeug>=0.11.15 in /usr/local/lib/python3.6/dist-packages (from tb-nightly<1.15.0a0,>=1.14.0a0->tf-nightly-gpu-2.0-preview) (0.15.1)
Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.6/dist-packages (from tb-nightly<1.15.0a0,>=1.14.0a0->tf-nightly-gpu-2.0-preview) (3.1)
Requirement already satisfied: h5py in /usr/local/lib/python3.6/dist-packages (from keras-applications>=1.0.6->tf-nightly-gpu-2.0-preview) (2.8.0)
Requirement already satisfied: setuptools in /usr/local/lib/python3.6/dist-packages (from protobuf>=3.6.1->tf-nightly-gpu-2.0-preview) (40.8.0)

 

MNIST データセットをロードする

各 MNIST 画像は元々は 784 整数ベクトルで、その各々は 0-255 の間でピクセル強度を表します。私達のモデルでは各ピクセルを Bernoulli 分布でモデル化して、データセットを統計的に二値化します。

(train_images, _), (test_images, _) = tf.keras.datasets.mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
11501568/11490434 [==============================] - 0s 0us/step
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
test_images = test_images.reshape(test_images.shape[0], 28, 28, 1).astype('float32')

# Normalizing the images to the range of [0., 1.]
train_images /= 255.
test_images /= 255.

# Binarization
train_images[train_images >= .5] = 1.
train_images[train_images < .5] = 0.
test_images[test_images >= .5] = 1.
test_images[test_images < .5] = 0.
TRAIN_BUF = 60000
BATCH_SIZE = 100

TEST_BUF = 10000

 

生成と推論ネットワークを tf.keras.Sequential で配線する (= wire up)

私達の VAE サンプルでは、生成と推論ネットワークのために 2 つの小さな ConvNet を使用します。これらのニューラルネットは小さいので、コードを単純化するために tf.keras.Sequential を使用します。以下の記述において $x$ と $z$ をそれぞれ観測と潜在 (= latent) 変数を示すものとします。

 

生成ネットワーク (= Generative Network)

これは生成モデルを定義します、これは入力として潜在エンコーディングを取り、観測の条件付き分布, i.e. $p(x|z)$ のためのパラメータを出力します。更に、潜在変数のために単位ガウス事前分布 (訳注: i.e. 標準正規事前分布) $p(z)$ を使用します。

 

推論ネットワーク

これは近似事後分布 $q(z|x)$ を定義します、これは入力として観測を取り潜在表現の条件付き分布のためのパラメータのセットを出力します。この例では、この分布を単純に対角ガウス分布 (= diagonal Gaussian) としてモデル化します。この場合、推論ネットワークは factorized Gaussian の平均 (= mean) と対数分散 (= log-variance) パラメータを出力します (分散の代わりに対数分散は直接的には数値的安定性のためです)。

 

再パラメータ化 (= Reparameterization) トリック

最適化の間、最初に標準正規分布からサンプリングすることにより $q(z|x)$ からサンプリングできて、それから標準偏差を乗じて平均を加えます。これは勾配がサンプルを通して推論ネットワーク・パラメータに渡せることを確かなものにします。

 

ネットワーク・アーキテクチャ

推論ネットワークのためには、2 つの畳み込み層と続く完全結合層を使用します。生成ネットワークでは、完全結合層と続く 3 つの convolution transpose 層を使用してこのアーキテクチャを反映 (= mirror) します (a.k.a. あるコンテキストでは deconvolutional 層)。注意してください、VAE を訓練するときバッチ正規化の使用を回避することは一般的な実践です、何故ならばミニバッチを使用することによる追加的な偶然性はサンプリングからの偶然性の上に不安定性を悪化させるかもしれないためです。

class CVAE(tf.keras.Model):
  def __init__(self, latent_dim):
    super(CVAE, self).__init__()
    self.latent_dim = latent_dim
    self.inference_net = tf.keras.Sequential(
      [
          tf.keras.layers.InputLayer(input_shape=(28, 28, 1)),
          tf.keras.layers.Conv2D(
              filters=32, kernel_size=3, strides=(2, 2), activation='relu'),
          tf.keras.layers.Conv2D(
              filters=64, kernel_size=3, strides=(2, 2), activation='relu'),
          tf.keras.layers.Flatten(),
          # No activation
          tf.keras.layers.Dense(latent_dim + latent_dim),
      ]
    )

    self.generative_net = tf.keras.Sequential(
        [
          tf.keras.layers.InputLayer(input_shape=(latent_dim,)),
          tf.keras.layers.Dense(units=7*7*32, activation=tf.nn.relu),
          tf.keras.layers.Reshape(target_shape=(7, 7, 32)),
          tf.keras.layers.Conv2DTranspose(
              filters=64,
              kernel_size=3,
              strides=(2, 2),
              padding="SAME",
              activation='relu'),
          tf.keras.layers.Conv2DTranspose(
              filters=32,
              kernel_size=3,
              strides=(2, 2),
              padding="SAME",
              activation='relu'),
          # No activation
          tf.keras.layers.Conv2DTranspose(
              filters=1, kernel_size=3, strides=(1, 1), padding="SAME"),
        ]
    )

  def sample(self, eps=None):
    if eps is None:
      eps = tf.random.normal(shape=(100, self.latent_dim))
    return self.decode(eps, apply_sigmoid=True)

  def encode(self, x):
    mean, logvar = tf.split(self.inference_net(x), num_or_size_splits=2, axis=1)
    return mean, logvar

  def reparameterize(self, mean, logvar):
    eps = tf.random.normal(shape=mean.shape)
    return eps * tf.exp(logvar * .5) + mean

  def decode(self, z, apply_sigmoid=False):
    logits = self.generative_net(z)
    if apply_sigmoid:
      probs = tf.sigmoid(logits)
      return probs

    return logits

 

損失関数と optimizer を定義する

VAE は周辺対数尤度上のエビデンス下限 (ELBO, evidence lower bound) を最大化することにより訓練されます :

$$\log p(x) \ge \text{ELBO} = \mathbb{E}_{q(z|x)}\left[\log \frac{p(x, z)}{q(z|x)}\right].$$

実際に、この期待値の単一サンプルのモンテカルロ推定を最適化します :

$$\log p(x| z) + \log p(z) - \log q(z|x),$$

ここで $z$ は $q(z|x)$ からサンプリングされます。

Note: 私達はまた KL 項を解析的に計算することもできますが、ここでは単純化のために総ての 3 つの項をモンテカルロ estimator に組み入れます。

optimizer = tf.keras.optimizers.Adam(1e-4)

def log_normal_pdf(sample, mean, logvar, raxis=1):
  log2pi = tf.math.log(2. * np.pi)
  return tf.reduce_sum(
      -.5 * ((sample - mean) ** 2. * tf.exp(-logvar) + logvar + log2pi),
      axis=raxis)

def compute_loss(model, x):
  mean, logvar = model.encode(x)
  z = model.reparameterize(mean, logvar)
  x_logit = model.decode(z)

  cross_ent = tf.nn.sigmoid_cross_entropy_with_logits(logits=x_logit, labels=x)
  logpx_z = -tf.reduce_sum(cross_ent, axis=[1, 2, 3])
  logpz = log_normal_pdf(z, 0., 0.)
  logqz_x = log_normal_pdf(z, mean, logvar)
  return -tf.reduce_mean(logpx_z + logpz - logqz_x)

def compute_gradients(model, x):
  with tf.GradientTape() as tape:
    loss = compute_loss(model, x)
  return tape.gradient(loss, model.trainable_variables), loss

def apply_gradients(optimizer, gradients, variables):
  optimizer.apply_gradients(zip(gradients, variables))

 

訓練

  • データセットに渡り反復することから始めます。
  • 各反復の間、近似事後分布 $q(z|x)$ の平均と対数分散パラメータのセットを得るために画像をエンコーダに渡します。
  • それから $q(z|x)$ からサンプリングするために再パラメータ化トリックを適用します。
  • 最後に、生成分布 $p(x|z)$ のロジットを得るために再パラメータ化されたサンプルをデコーダに渡します。
  • Note: 訓練セットの 60k データポイントとテストセットの 10k データポイント持つ、keras によりロードされたデータセットを使用しますので、テストセット上の結果としての ELBO は (Larochelle の MNIST の動的二値化 (= dynamic binarization) を使用している) 文献で報告されている結果よりも僅かにより高いです。

 

画像を生成する

  • 訓練の後、幾つかの画像を生成する時です。
  • 単位正規事前分布 $p(z)$ から潜在ベクトルのセットをサンプリングすることから始めます。
  • それから generator は潜在サンプル $z$ を観測のロジットに変換し、分布 $p(x|z)$ を与えます。
  • ここで Bernoulli 分布の確率をプロットします。
epochs = 100
latent_dim = 50
num_examples_to_generate = 16

# keeping the random vector constant for generation (prediction) so
# it will be easier to see the improvement.
random_vector_for_generation = tf.random.normal(
    shape=[num_examples_to_generate, latent_dim])
model = CVAE(latent_dim)
def generate_and_save_images(model, epoch, test_input):
  predictions = model.sample(test_input)
  fig = plt.figure(figsize=(4,4))

  for i in range(predictions.shape[0]):
      plt.subplot(4, 4, i+1)
      plt.imshow(predictions[i, :, :, 0], cmap='gray')
      plt.axis('off')

  # tight_layout minimizes the overlap between 2 sub-plots
  plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
  plt.show()
generate_and_save_images(model, 0, random_vector_for_generation)

for epoch in range(1, epochs + 1):
  start_time = time.time()
  for train_x in train_dataset:
    gradients, loss = compute_gradients(model, train_x)
    apply_gradients(optimizer, gradients, model.trainable_variables)
  end_time = time.time()

  if epoch % 1 == 0:
    loss = tf.keras.metrics.Mean()
    for test_x in test_dataset:
      loss(compute_loss(model, test_x))
    elbo = -loss.result()
    display.clear_output(wait=False)
    print('Epoch: {}, Test set ELBO: {}, '
          'time elapse for current epoch {}'.format(epoch,
                                                    elbo,
                                                    end_time - start_time))
    generate_and_save_images(
        model, epoch, random_vector_for_generation)
Epoch: 100, Test set ELBO: -78.32722473144531, time elapse for current epoch 27.248212575912476

 

エポック数を使用して画像を表示する

def display_image(epoch_no):
  return PIL.Image.open('image_at_epoch_{:04d}.png'.format(epoch_no))
plt.imshow(display_image(epochs))
plt.axis('off')# Display images
(-0.5, 287.5, 287.5, -0.5)

 

総てのセーブされた画像の GIF を生成する

anim_file = 'cvae.gif'

with imageio.get_writer(anim_file, mode='I') as writer:
  filenames = glob.glob('image*.png')
  filenames = sorted(filenames)
  last = -1
  for i,filename in enumerate(filenames):
    frame = 2*(i**0.5)
    if round(frame) > round(last):
      last = frame
    else:
      continue
    image = imageio.imread(filename)
    writer.append_data(image)
  image = imageio.imread(filename)
  writer.append_data(image)
    
import IPython
if IPython.version_info >= (6,2,0,''):
  display.Image(filename=anim_file)

Colab で作業している場合には下のコードでアニメーションをダウンロードできます :

try:
  from google.colab import files
except ImportError:
  pass
else:
  files.download(anim_file)
 

以上






TensorFlow 2.0 Alpha : 上級 Tutorials : テキストとシークエンス :- 画像キャプショニング with Attention

TensorFlow 2.0 Alpha : 上級 Tutorials : テキストとシークエンス :- 画像キャプショニング with Attention (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/08/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – Text and sequences の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

テキストとシークエンス :- 画像キャプショニング with Attention

下のような画像が与えられたとき、私達の目標は “a surfer riding on a wave” のようなキャプションを生成することです。

Image Source, License: Public Domain

 
ここでは、attention ベースのモデルを使用します。これは、(モデルが) キャプションを生成するとき画像のどのパートにモデルがフォーカスするかを見ることを可能にします。

下のこのモデル・アーキテクチャは Show, Attend and Tell: Neural Image Caption Generation with Visual Attention に類似しています。

このノートブックは end-to-end なサンプルです。実行する時、それは MS-COCO データセットをダウンロードして Inception V3 を使用して画像のサブセットを前処理してキャッシュして、エンコーダ-デコーダ・モデルを訓練して、そして新しい画像上でキャプションを生成するためにそれを使用します。

このサンプルでは、比較的小さい総量のデータ上でモデルを訓練します。モデルは最初の 30,000 キャプション上で訓練されます (シャッフルに依拠しておよそ ~20,000 画像に相当します、データセットの画像毎に複数のキャプションがあるからです)。

from __future__ import absolute_import, division, print_function, unicode_literals
!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf

# We'll generate plots of attention in order to see which parts of an image
# our model focuses on during captioning
import matplotlib.pyplot as plt

# Scikit-learn includes many helpful utilities
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle

import re
import numpy as np
import os
import time
import json
from glob import glob
from PIL import Image
import pickle

 

MS-COCO データセットをダウンロードして準備する

モデルを訓練するために MS-COCO データセットを使用します。このデータセットは 82,000 以上の画像を含み、その各々は少なくとも 5 つの異なるキャプションでアノテートされています。下のコードはデータセットを自動的にダウンロードして抽出します。

警告: これから先に巨大なダウンロード があります。訓練セットを使用します、それは 13 GB ファイルです。

annotation_zip = tf.keras.utils.get_file('captions.zip', 
                                          cache_subdir=os.path.abspath('.'),
                                          origin = 'http://images.cocodataset.org/annotations/annotations_trainval2014.zip',
                                          extract = True)
annotation_file = os.path.dirname(annotation_zip)+'/annotations/captions_train2014.json'

name_of_zip = 'train2014.zip'
if not os.path.exists(os.path.abspath('.') + '/' + name_of_zip):
  image_zip = tf.keras.utils.get_file(name_of_zip, 
                                      cache_subdir=os.path.abspath('.'),
                                      origin = 'http://images.cocodataset.org/zips/train2014.zip',
                                      extract = True)
  PATH = os.path.dirname(image_zip)+'/train2014/'
else:
  PATH = os.path.abspath('.')+'/train2014/'

 

オプションで、より高速な訓練のために訓練セットのサイズを制限する

このサンプルのために、30,000 キャプションのサブセットを選択し、これらと対応する画像をモデルを訓練するために使用します。通例のように、より多くのデータを使用することを選択すれば、キャプショニング品質は改善します。

# read the json file
with open(annotation_file, 'r') as f:
    annotations = json.load(f)

# storing the captions and the image name in vectors
all_captions = []
all_img_name_vector = []

for annot in annotations['annotations']:
    caption = ' ' + annot['caption'] + ' '
    image_id = annot['image_id']
    full_coco_image_path = PATH + 'COCO_train2014_' + '%012d.jpg' % (image_id)
    
    all_img_name_vector.append(full_coco_image_path)
    all_captions.append(caption)

# shuffling the captions and image_names together
# setting a random state
train_captions, img_name_vector = shuffle(all_captions,
                                          all_img_name_vector,
                                          random_state=1)

# selecting the first 30000 captions from the shuffled set
num_examples = 30000
train_captions = train_captions[:num_examples]
img_name_vector = img_name_vector[:num_examples]
len(train_captions), len(all_captions)
(30000, 414113)

 

InceptionV3 を使用して画像を前処理する

次に、各画像を分類するために (Imagenet で事前訓練された) InceptionV3 を使用します。最後の畳込み層から特徴を抽出します。

最初に、画像を次により inceptionV3 が想定するフォーマットに変換する必要があります : * 画像を (299, 299) にリサイズします。* preprocess_input メソッドを使用してピクセルを -1 から 1 の範囲に置きます (InceptionV3 を訓練するために使用された画像のフォーマットに適合させるためです)。

def load_image(image_path):
    img = tf.io.read_file(image_path)
    img = tf.image.decode_jpeg(img, channels=3)
    img = tf.image.resize(img, (299, 299))
    img = tf.keras.applications.inception_v3.preprocess_input(img)
    return img, image_path

 

InceptionV3 を初期化して事前訓練された Imagenet 重みをロードする

そのため、tf.keras モデルを作成します、そこでは出力層は InceptionV3 アーキテクチャの最後の畳み込み層です。* 各画像はネットワークを通して forward されて最後に得られるベクトルは辞書にストアされます (image_name –> feature_vector)。* 最後の畳み込み層を使用します、何故ならばこのサンプルで attention を使用しているからです。この層の出力の shape は 8x8x2048 です。* 訓練の間はこれを行なうことを回避しますので、ボトルネックにはなりません。* 総ての画像がネットワークを通された後、辞書を pickle 化してそれをディスクにセーブします。

image_model = tf.keras.applications.InceptionV3(include_top=False, 
                                                weights='imagenet')
new_input = image_model.input
hidden_layer = image_model.layers[-1].output

image_features_extract_model = tf.keras.Model(new_input, hidden_layer)

 

InceptionV3 から抽出した特徴をキャッシュする

各画像を InceptionV3 で前処理して出力をディスクにキャッシュします。RAM に出力をキャッシングすることはより高速ですがメモリ集約的で、画像毎に 8 * 8 * 2048 floats を必要とします。書く時点で、これは Colab のメモリ制限を超過するでしょう (これらは変更されるかもしれませんが、現在インスタンスはおよそ 12 GB メモリを持つようです)。

パフォーマンスは更に多いコードの代償でより洗練されたキャッシング・ストラテジー (e.g., ディスク I/O へのランダムアクセスを減じるために画像をシャーディングする) で改善されるかもしれません。

これは GPU を伴う Colab でおよそ 10 分間かかります。進捗バーを見ることを望むのであれば、tqdm をインストールして (!pip install tqdm)、そしてこの行を変更することができるでしょう :

for img, path in image_dataset:

to:

for img, path in tqdm(image_dataset):.
# getting the unique images
encode_train = sorted(set(img_name_vector))

# feel free to change the batch_size according to your system configuration
image_dataset = tf.data.Dataset.from_tensor_slices(encode_train)
image_dataset = image_dataset.map(
  load_image, num_parallel_calls=tf.data.experimental.AUTOTUNE).batch(16)

for img, path in image_dataset:
  batch_features = image_features_extract_model(img)
  batch_features = tf.reshape(batch_features, 
                              (batch_features.shape[0], -1, batch_features.shape[3]))

  for bf, p in zip(batch_features, path):
    path_of_feature = p.numpy().decode("utf-8")
    np.save(path_of_feature, bf.numpy())

 

キャプションを前処理してトークン化する

  • 最初にキャプションをトークン化します (e.g., スペースで分割することにより)。これはデータの総ての一意な単語の語彙を与えます (e.g., “surfing”, “football”, etc)。
  • 次に、メモリを節約するために語彙サイズを top 5,000 単語に制限します。総ての他の単語をトークン “UNK” (for unknown) で置き替えます。
  • 最後に word –> index マッピング (and vice-versa) を作成します。
  • それから最長のものと同じ長さになるように総てのシークエンスをパッドします。
# This will find the maximum length of any caption in our dataset
def calc_max_length(tensor):
    return max(len(t) for t in tensor)
# The steps above is a general process of dealing with text processing

# choosing the top 5000 words from the vocabulary
top_k = 5000
tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words=top_k, 
                                                  oov_token="<unk>", 
                                                  filters='!"#$%&()*+.,-/:;=?@[\]^_`{|}~ ')
tokenizer.fit_on_texts(train_captions)
train_seqs = tokenizer.texts_to_sequences(train_captions)
tokenizer.word_index['<pad>'] = 0
tokenizer.index_word[0] = '<pad>'
# creating the tokenized vectors
train_seqs = tokenizer.texts_to_sequences(train_captions)
# padding each vector to the max_length of the captions
# if the max_length parameter is not provided, pad_sequences calculates that automatically
cap_vector = tf.keras.preprocessing.sequence.pad_sequences(train_seqs, padding='post')
# calculating the max_length 
# used to store the attention weights
max_length = calc_max_length(train_seqs)

 

データを訓練とテストに分割する

# Create training and validation sets using 80-20 split
img_name_train, img_name_val, cap_train, cap_val = train_test_split(img_name_vector, 
                                                                    cap_vector, 
                                                                    test_size=0.2, 
                                                                    random_state=0)
len(img_name_train), len(cap_train), len(img_name_val), len(cap_val)
(24000, 24000, 6000, 6000)

 

画像とキャプションの準備ができました!次に、モデルを訓練するために使用する tf.data データセットを作成しましょう。

# feel free to change these parameters according to your system's configuration

BATCH_SIZE = 64
BUFFER_SIZE = 1000
embedding_dim = 256
units = 512
vocab_size = len(tokenizer.word_index) + 1
num_steps = len(img_name_train) // BATCH_SIZE
# shape of the vector extracted from InceptionV3 is (64, 2048)
# these two variables represent that
features_shape = 2048
attention_features_shape = 64
# loading the numpy files 
def map_func(img_name, cap):
  img_tensor = np.load(img_name.decode('utf-8')+'.npy')
  return img_tensor, cap
dataset = tf.data.Dataset.from_tensor_slices((img_name_train, cap_train))

# using map to load the numpy files in parallel
dataset = dataset.map(lambda item1, item2: tf.numpy_function(
          map_func, [item1, item2], [tf.float32, tf.int32]), 
          num_parallel_calls=tf.data.experimental.AUTOTUNE)

# shuffling and batching
dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)

 

モデル

面白いことに、下のデコーダは ニューラル機械翻訳 with Attention のためのサンプルのものと同一です。

モデル・アーキテクチャは Show, Attend and Tell ペーパーによりインスパイアされています。

  • このサンプルでは、InceptionV3 のより低い畳み込み層から特徴を抽出します、これは shape (8, 8, 2048) のベクトルを与えます。
  • それを (64, 2048) の shape に押しつぶします。
  • それからこのベクトルは CNN エンコーダを通して渡されます (これは単一の完全結合層から成ります)。
  • RNN (ここでは GRU) が次の単語を予測するために画像を注視します。
class BahdanauAttention(tf.keras.Model):
  def __init__(self, units):
    super(BahdanauAttention, self).__init__()
    self.W1 = tf.keras.layers.Dense(units)
    self.W2 = tf.keras.layers.Dense(units)
    self.V = tf.keras.layers.Dense(1)
  
  def call(self, features, hidden):
    # features(CNN_encoder output) shape == (batch_size, 64, embedding_dim)
    
    # hidden shape == (batch_size, hidden_size)
    # hidden_with_time_axis shape == (batch_size, 1, hidden_size)
    hidden_with_time_axis = tf.expand_dims(hidden, 1)
    
    # score shape == (batch_size, 64, hidden_size)
    score = tf.nn.tanh(self.W1(features) + self.W2(hidden_with_time_axis))
    
    # attention_weights shape == (batch_size, 64, 1)
    # we get 1 at the last axis because we are applying score to self.V
    attention_weights = tf.nn.softmax(self.V(score), axis=1)
    
    # context_vector shape after sum == (batch_size, hidden_size)
    context_vector = attention_weights * features
    context_vector = tf.reduce_sum(context_vector, axis=1)
    
    return context_vector, attention_weights
class CNN_Encoder(tf.keras.Model):
    # Since we have already extracted the features and dumped it using pickle
    # This encoder passes those features through a Fully connected layer
    def __init__(self, embedding_dim):
        super(CNN_Encoder, self).__init__()
        # shape after fc == (batch_size, 64, embedding_dim)
        self.fc = tf.keras.layers.Dense(embedding_dim)
        
    def call(self, x):
        x = self.fc(x)
        x = tf.nn.relu(x)
        return x
class RNN_Decoder(tf.keras.Model):
  def __init__(self, embedding_dim, units, vocab_size):
    super(RNN_Decoder, self).__init__()
    self.units = units

    self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
    self.gru = tf.keras.layers.GRU(self.units, 
                                   return_sequences=True, 
                                   return_state=True, 
                                   recurrent_initializer='glorot_uniform')
    self.fc1 = tf.keras.layers.Dense(self.units)
    self.fc2 = tf.keras.layers.Dense(vocab_size)
    
    self.attention = BahdanauAttention(self.units)
        
  def call(self, x, features, hidden):
    # defining attention as a separate model
    context_vector, attention_weights = self.attention(features, hidden)
    
    # x shape after passing through embedding == (batch_size, 1, embedding_dim)
    x = self.embedding(x)
    
    # x shape after concatenation == (batch_size, 1, embedding_dim + hidden_size)
    x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
    
    # passing the concatenated vector to the GRU
    output, state = self.gru(x)
    
    # shape == (batch_size, max_length, hidden_size)
    x = self.fc1(output)
    
    # x shape == (batch_size * max_length, hidden_size)
    x = tf.reshape(x, (-1, x.shape[2]))
    
    # output shape == (batch_size * max_length, vocab)
    x = self.fc2(x)

    return x, state, attention_weights

  def reset_state(self, batch_size):
    return tf.zeros((batch_size, self.units))
encoder = CNN_Encoder(embedding_dim)
decoder = RNN_Decoder(embedding_dim, units, vocab_size)
optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

def loss_function(real, pred):
  mask = tf.math.logical_not(tf.math.equal(real, 0))
  loss_ = loss_object(real, pred)

  mask = tf.cast(mask, dtype=loss_.dtype)
  loss_ *= mask
  
  return tf.reduce_mean(loss_)

 

チェックポイント

checkpoint_path = "./checkpoints/train"
ckpt = tf.train.Checkpoint(encoder=encoder, 
                           decoder=decoder,
                           optimizer = optimizer)
ckpt_manager = tf.train.CheckpointManager(ckpt, checkpoint_path, max_to_keep=5)
start_epoch = 0
if ckpt_manager.latest_checkpoint:
  start_epoch = int(ckpt_manager.latest_checkpoint.split('-')[-1])

 

訓練

  • それぞれの .npy ファイルにストアされている特徴を抽出してからそれらの特徴をエンコーダを通して渡します。
  • エンコーダ出力、(0 に初期化された) 隠れ状態そしてデコーダ入力 (それは start トークンです) がデコーダに渡されます。
  • デコーダは予測とデコーダ隠れ状態を返します。
  • そしてデコーダ隠れ状態はモデルに渡し戻されて予測は損失を計算するために使用されます。
  • デコーダへの次の入力を決めるために teacher forcing を使用します。
  • teacher forcing はそこではターゲット単語がデコーダへの次の入力として渡されるようなテクニックです。
  • 最後のステップは勾配を計算してそれを optimizer に適用してそして backpropagate します。
# adding this in a separate cell because if you run the training cell 
# many times, the loss_plot array will be reset
loss_plot = []
@tf.function
def train_step(img_tensor, target):
  loss = 0
        
  # initializing the hidden state for each batch
  # because the captions are not related from image to image
  hidden = decoder.reset_state(batch_size=target.shape[0])

  dec_input = tf.expand_dims([tokenizer.word_index['']] * BATCH_SIZE, 1)
  
  with tf.GradientTape() as tape:
      features = encoder(img_tensor)
      
      for i in range(1, target.shape[1]):
          # passing the features through the decoder
          predictions, hidden, _ = decoder(dec_input, features, hidden)

          loss += loss_function(target[:, i], predictions)
          
          # using teacher forcing
          dec_input = tf.expand_dims(target[:, i], 1)
  
  total_loss = (loss / int(target.shape[1]))
  
  trainable_variables = encoder.trainable_variables + decoder.trainable_variables
  
  gradients = tape.gradient(loss, trainable_variables) 
  
  optimizer.apply_gradients(zip(gradients, trainable_variables))

  return loss, total_loss
EPOCHS = 20

for epoch in range(start_epoch, EPOCHS):
    start = time.time()
    total_loss = 0
    
    for (batch, (img_tensor, target)) in enumerate(dataset):
        batch_loss, t_loss = train_step(img_tensor, target)
        total_loss += t_loss
        
        if batch % 100 == 0:
            print ('Epoch {} Batch {} Loss {:.4f}'.format(
              epoch + 1, batch, batch_loss.numpy() / int(target.shape[1])))
    # storing the epoch end loss value to plot later
    loss_plot.append(total_loss / num_steps)

    if epoch % 5 == 0:
      ckpt_manager.save()
    
    print ('Epoch {} Loss {:.6f}'.format(epoch + 1, 
                                         total_loss/num_steps))
    print ('Time taken for 1 epoch {} sec\n'.format(time.time() - start))
Epoch 1 Batch 0 Loss 2.0556
Epoch 1 Batch 100 Loss 1.0668
Epoch 1 Batch 200 Loss 0.8879
Epoch 1 Batch 300 Loss 0.8524
Epoch 1 Loss 1.009767
Time taken for 1 epoch 256.95692324638367 sec

Epoch 2 Batch 0 Loss 0.8081
Epoch 2 Batch 100 Loss 0.7681
Epoch 2 Batch 200 Loss 0.6946
Epoch 2 Batch 300 Loss 0.7042
Epoch 2 Loss 0.756167
Time taken for 1 epoch 186.68594098091125 sec

Epoch 3 Batch 0 Loss 0.6851
Epoch 3 Batch 100 Loss 0.6817
Epoch 3 Batch 200 Loss 0.6316
Epoch 3 Batch 300 Loss 0.6391
Epoch 3 Loss 0.679992
Time taken for 1 epoch 186.36522102355957 sec

Epoch 4 Batch 0 Loss 0.6381
Epoch 4 Batch 100 Loss 0.6314
Epoch 4 Batch 200 Loss 0.5915
Epoch 4 Batch 300 Loss 0.5961
Epoch 4 Loss 0.635389
Time taken for 1 epoch 186.6236436367035 sec

Epoch 5 Batch 0 Loss 0.5991
Epoch 5 Batch 100 Loss 0.5896
Epoch 5 Batch 200 Loss 0.5607
Epoch 5 Batch 300 Loss 0.5670
Epoch 5 Loss 0.602497
Time taken for 1 epoch 187.06984400749207 sec

Epoch 6 Batch 0 Loss 0.5679
Epoch 6 Batch 100 Loss 0.5558
Epoch 6 Batch 200 Loss 0.5350
Epoch 6 Batch 300 Loss 0.5461
Epoch 6 Loss 0.575848
Time taken for 1 epoch 187.72310757637024 sec

Epoch 7 Batch 0 Loss 0.5503
Epoch 7 Batch 100 Loss 0.5283
Epoch 7 Batch 200 Loss 0.5120
Epoch 7 Batch 300 Loss 0.5242
Epoch 7 Loss 0.551446
Time taken for 1 epoch 187.74794459342957 sec

Epoch 8 Batch 0 Loss 0.5432
Epoch 8 Batch 100 Loss 0.5078
Epoch 8 Batch 200 Loss 0.5003
Epoch 8 Batch 300 Loss 0.4915
Epoch 8 Loss 0.529145
Time taken for 1 epoch 186.81623315811157 sec

Epoch 9 Batch 0 Loss 0.5156
Epoch 9 Batch 100 Loss 0.4842
Epoch 9 Batch 200 Loss 0.4923
Epoch 9 Batch 300 Loss 0.4677
Epoch 9 Loss 0.509899
Time taken for 1 epoch 189.49438571929932 sec

Epoch 10 Batch 0 Loss 0.4995
Epoch 10 Batch 100 Loss 0.4710
Epoch 10 Batch 200 Loss 0.4750
Epoch 10 Batch 300 Loss 0.4601
Epoch 10 Loss 0.492096
Time taken for 1 epoch 189.16131472587585 sec

Epoch 11 Batch 0 Loss 0.4797
Epoch 11 Batch 100 Loss 0.4495
Epoch 11 Batch 200 Loss 0.4552
Epoch 11 Batch 300 Loss 0.4408
Epoch 11 Loss 0.474645
Time taken for 1 epoch 190.57548332214355 sec

Epoch 12 Batch 0 Loss 0.4787
Epoch 12 Batch 100 Loss 0.4315
Epoch 12 Batch 200 Loss 0.4504
Epoch 12 Batch 300 Loss 0.4293
Epoch 12 Loss 0.457647
Time taken for 1 epoch 190.24215531349182 sec

Epoch 13 Batch 0 Loss 0.4621
Epoch 13 Batch 100 Loss 0.4107
Epoch 13 Batch 200 Loss 0.4271
Epoch 13 Batch 300 Loss 0.4133
Epoch 13 Loss 0.442507
Time taken for 1 epoch 187.96875071525574 sec

Epoch 14 Batch 0 Loss 0.4383
Epoch 14 Batch 100 Loss 0.3987
Epoch 14 Batch 200 Loss 0.4239
Epoch 14 Batch 300 Loss 0.3913
Epoch 14 Loss 0.429215
Time taken for 1 epoch 185.89738130569458 sec

Epoch 15 Batch 0 Loss 0.4121
Epoch 15 Batch 100 Loss 0.3933
Epoch 15 Batch 200 Loss 0.4079
Epoch 15 Batch 300 Loss 0.3788
Epoch 15 Loss 0.415965
Time taken for 1 epoch 186.6773328781128 sec

Epoch 16 Batch 0 Loss 0.4062
Epoch 16 Batch 100 Loss 0.3752
Epoch 16 Batch 200 Loss 0.3947
Epoch 16 Batch 300 Loss 0.3715
Epoch 16 Loss 0.402814
Time taken for 1 epoch 186.04795384407043 sec

Epoch 17 Batch 0 Loss 0.3793
Epoch 17 Batch 100 Loss 0.3604
Epoch 17 Batch 200 Loss 0.3941
Epoch 17 Batch 300 Loss 0.3504
Epoch 17 Loss 0.391162
Time taken for 1 epoch 187.62019681930542 sec

Epoch 18 Batch 0 Loss 0.3685
Epoch 18 Batch 100 Loss 0.3496
Epoch 18 Batch 200 Loss 0.3744
Epoch 18 Batch 300 Loss 0.3480
Epoch 18 Loss 0.382786
Time taken for 1 epoch 185.68778085708618 sec

Epoch 19 Batch 0 Loss 0.3608
Epoch 19 Batch 100 Loss 0.3384
Epoch 19 Batch 200 Loss 0.3500
Epoch 19 Batch 300 Loss 0.3229
Epoch 19 Loss 0.371033
Time taken for 1 epoch 185.8159191608429 sec

Epoch 20 Batch 0 Loss 0.3568
Epoch 20 Batch 100 Loss 0.3288
Epoch 20 Batch 200 Loss 0.3357
Epoch 20 Batch 300 Loss 0.2945
Epoch 20 Loss 0.358618
Time taken for 1 epoch 186.8766734600067 sec
plt.plot(loss_plot)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Loss Plot')
plt.show()

 

キャプション!

  • evaluate 関数は訓練ループに類似しています、teacher forcing をここでは使用しないことを除いて。各時間ステップにおけるデコーダへの入力は隠れ状態とエンコーダ出力に加えてその前の予測です。
  • モデルが end トークンを予測するとき予測は停止します。
  • そして総ての時間ステップのために attention 重みをストアします。
def evaluate(image):
    attention_plot = np.zeros((max_length, attention_features_shape))

    hidden = decoder.reset_state(batch_size=1)

    temp_input = tf.expand_dims(load_image(image)[0], 0)
    img_tensor_val = image_features_extract_model(temp_input)
    img_tensor_val = tf.reshape(img_tensor_val, (img_tensor_val.shape[0], -1, img_tensor_val.shape[3]))

    features = encoder(img_tensor_val)

    dec_input = tf.expand_dims([tokenizer.word_index['']], 0)
    result = []

    for i in range(max_length):
        predictions, hidden, attention_weights = decoder(dec_input, features, hidden)

        attention_plot[i] = tf.reshape(attention_weights, (-1, )).numpy()

        predicted_id = tf.argmax(predictions[0]).numpy()
        result.append(tokenizer.index_word[predicted_id])

        if tokenizer.index_word[predicted_id] == '':
            return result, attention_plot

        dec_input = tf.expand_dims([predicted_id], 0)

    attention_plot = attention_plot[:len(result), :]
    return result, attention_plot
def plot_attention(image, result, attention_plot):
    temp_image = np.array(Image.open(image))

    fig = plt.figure(figsize=(10, 10))
    
    len_result = len(result)
    for l in range(len_result):
        temp_att = np.resize(attention_plot[l], (8, 8))
        ax = fig.add_subplot(len_result//2, len_result//2, l+1)
        ax.set_title(result[l])
        img = ax.imshow(temp_image)
        ax.imshow(temp_att, cmap='gray', alpha=0.6, extent=img.get_extent())

    plt.tight_layout()
    plt.show()
# captions on the validation set
rid = np.random.randint(0, len(img_name_val))
image = img_name_val[rid]
real_caption = ' '.join([tokenizer.index_word[i] for i in cap_val[rid] if i not in [0]])
result, attention_plot = evaluate(image)

print ('Real Caption:', real_caption)
print ('Prediction Caption:', ' '.join(result))
plot_attention(image, result, attention_plot)
# opening the image
Image.open(img_name_val[rid])
Real Caption: <start> a man gets ready to hit a ball with a bat <end>
Prediction Caption: a baseball player begins to bat <end>

 

貴方自身の画像でそれを試してください

楽しみのために、貴方自身の画像を丁度訓練したモデルでキャプションするために使用できるメソッドを下で提供しました。留意してください、それはデータの比較的小さい量の上で訓練されました、そして貴方の画像は訓練データとは異なるかもしれません (そのため奇妙な結果に備えてください!)

image_url = 'https://tensorflow.org/images/surf.jpg'
image_extension = image_url[-4:]
image_path = tf.keras.utils.get_file('image'+image_extension, 
                                     origin=image_url)

result, attention_plot = evaluate(image_path)
print ('Prediction Caption:', ' '.join(result))
plot_attention(image_path, result, attention_plot)
# opening the image
Image.open(image_path)
Prediction Caption: a man riding a surf board in the water <end>

 

以上






TensorFlow 2.0 Alpha : 上級 Tutorials :- テキストとシークエンス : ニューラル機械翻訳 with Attention

TensorFlow 2.0 Alpha : 上級 Tutorials : テキストとシークエンス :- ニューラル機械翻訳 with Attention (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/07/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – Text and sequences の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

テキストとシークエンス :- ニューラル機械翻訳 with Attention

このノートブックは西英翻訳のための sequence to sequence (seq2seq) モデルを訓練します。これは sequence to sequence モデルの何某かの知識を仮定した上級サンプルです。

このノートブックでモデルを訓練した後、”¿todavia estan en casa?” のようなスペイン語のセンテンスを入力することができて、英語翻訳: “are you still at home?” を返すことができます。

翻訳品質は toy サンプルのために合理的ですが、生成された attention プロットは多分より興味深いです。これは翻訳の間に入力センテンスのどの部分がモデルの attention を持つかを示します :

Note: このサンプルは単一の P100 GPU 上で実行するためにおよそ 10 分かかります。

from __future__ import absolute_import, division, print_function

!pip install -q tensorflow-gpu==2.0.0-alpha0
import tensorflow as tf

import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

import unicodedata
import re
import numpy as np
import os
import io
import time

 

データセットをダウンロードして準備する

私達は http://www.manythings.org/anki/ により提供される言語データセットを使用します。このデータセットは次のフォーマットの言語翻訳ペアを含みます :

May I borrow this book? ¿Puedo tomar prestado este libro?

利用可能な様々な言語がありますが、英語-スペイン語データセットを使用します。便宜上、このデータセットのコピーを Google Cloud 上にホストしましたが、貴方自身のコピーをダウンロードすることもできます。データセットをダウンロードした後、データを準備するために取るステップがここにあります :

  1. 各センテンスに start と end トークンを追加します。
  2. 特殊文字を除去してセンテンスをクリーンアップします。
  3. 単語インデックスとリバース単語インデックス (単語 → id と id → 単語のマッピングをする辞書) を作成します。
  4. 各センテンスを最大長にパッドします。
# Download the file
path_to_zip = tf.keras.utils.get_file(
    'spa-eng.zip', origin='http://storage.googleapis.com/download.tensorflow.org/data/spa-eng.zip', 
    extract=True)

path_to_file = os.path.dirname(path_to_zip)+"/spa-eng/spa.txt"
# Converts the unicode file to ascii
def unicode_to_ascii(s):
    return ''.join(c for c in unicodedata.normalize('NFD', s)
        if unicodedata.category(c) != 'Mn')


def preprocess_sentence(w):
    w = unicode_to_ascii(w.lower().strip())
    
    # creating a space between a word and the punctuation following it
    # eg: "he is a boy." => "he is a boy ." 
    # Reference:- https://stackoverflow.com/questions/3645931/python-padding-punctuation-with-white-spaces-keeping-punctuation
    w = re.sub(r"([?.!,¿])", r" \1 ", w)
    w = re.sub(r'[" "]+', " ", w)
    
    # replacing everything with space except (a-z, A-Z, ".", "?", "!", ",")
    w = re.sub(r"[^a-zA-Z?.!,¿]+", " ", w)
    
    w = w.rstrip().strip()
    
    # adding a start and an end token to the sentence
    # so that the model know when to start and stop predicting.
    w = ' ' + w + ' '
    return w
en_sentence = u"May I borrow this book?"
sp_sentence = u"¿Puedo tomar prestado este libro?"
print(preprocess_sentence(en_sentence))
print(preprocess_sentence(sp_sentence).encode('utf-8'))
<start> may i borrow this book ? <end>
<start> ¿ puedo tomar prestado este libro ? <end>
# 1. Remove the accents
# 2. Clean the sentences
# 3. Return word pairs in the format: [ENGLISH, SPANISH]
def create_dataset(path, num_examples):
    lines = io.open(path, encoding='UTF-8').read().strip().split('\n')
    
    word_pairs = [[preprocess_sentence(w) for w in l.split('\t')]  for l in lines[:num_examples]]
    
    return zip(*word_pairs)
en, sp = create_dataset(path_to_file, None)
print(en[-1])
print(sp[-1])
<start> if you want to sound like a native speaker , you must be willing to practice saying the same sentence over and over in the same way that banjo players practice the same phrase over and over until they can play it correctly and at the desired tempo . <end>
<start> si quieres sonar como un hablante nativo , debes estar dispuesto a practicar diciendo la misma frase una y otra vez de la misma manera en que un musico de banjo practica el mismo fraseo una y otra vez hasta que lo puedan tocar correctamente y en el tiempo esperado . <end>
def max_length(tensor):
    return max(len(t) for t in tensor)
def tokenize(lang):
  lang_tokenizer = tf.keras.preprocessing.text.Tokenizer(
      filters='')
  lang_tokenizer.fit_on_texts(lang)
  
  tensor = lang_tokenizer.texts_to_sequences(lang)
  
  tensor = tf.keras.preprocessing.sequence.pad_sequences(tensor,
                                                         padding='post')
  
  return tensor, lang_tokenizer
def load_dataset(path, num_examples=None):
    # creating cleaned input, output pairs
    targ_lang, inp_lang = create_dataset(path, num_examples)

    input_tensor, inp_lang_tokenizer = tokenize(inp_lang)
    target_tensor, targ_lang_tokenizer = tokenize(targ_lang)

    return input_tensor, target_tensor, inp_lang_tokenizer, targ_lang_tokenizer

 

実験をより速くするためにデータセットのサイズを制限する (オプション)

センテンス > 100,000 の完全なデータセット上で訓練するのは時間がかかります。より速く訓練するために、データセットのサイズを 30,000 センテンスに制限することができます (もちろん、翻訳品質はより少ないデータでは劣化します) :

# Try experimenting with the size of that dataset
num_examples = 30000
input_tensor, target_tensor, inp_lang, targ_lang = load_dataset(path_to_file, num_examples)

# Calculate max_length of the target tensors
max_length_targ, max_length_inp = max_length(target_tensor), max_length(input_tensor)
# Creating training and validation sets using an 80-20 split
input_tensor_train, input_tensor_val, target_tensor_train, target_tensor_val = train_test_split(input_tensor, target_tensor, test_size=0.2)

# Show length
len(input_tensor_train), len(target_tensor_train), len(input_tensor_val), len(target_tensor_val)
(24000, 24000, 6000, 6000)
def convert(lang, tensor):
  for t in tensor:
    if t!=0:
      print ("%d ----> %s" % (t, lang.index_word[t]))
print ("Input Language; index to word mapping")
convert(inp_lang, input_tensor_train[0])
print ()
print ("Target Language; index to word mapping")
convert(targ_lang, target_tensor_train[0])
Input Language; index to word mapping
1 ----> <start>
431 ----> vuestro
92 ----> perro
7 ----> es
36 ----> muy
189 ----> grande
3 ----> .
2 ----> <end>

Target Language; index to word mapping
1 ----> <start>
31 ----> your
104 ----> dog
8 ----> is
48 ----> very
155 ----> big
3 ----> .
2 ----> <end>

 

tf.data データセットを作成する

BUFFER_SIZE = len(input_tensor_train)
BATCH_SIZE = 64
steps_per_epoch = len(input_tensor_train)//BATCH_SIZE
embedding_dim = 256
units = 1024
vocab_inp_size = len(inp_lang.word_index)+1
vocab_tar_size = len(targ_lang.word_index)+1

dataset = tf.data.Dataset.from_tensor_slices((input_tensor_train, target_tensor_train)).shuffle(BUFFER_SIZE)
dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)
example_input_batch, example_target_batch = next(iter(dataset))
example_input_batch.shape, example_target_batch.shape
(TensorShape([64, 16]), TensorShape([64, 11]))

 

エンコーダとデコーダ・モデルを書く

ここで、attention を持つエンコーダ-デコーダ・モデルを実装します、これについては TensorFlow Neural Machine Translation (seq2seq) チュートリアル で読むことができます。このサンプルは API のより新しいセットを使用しています。このノートブックは seq2seq チュートリアルからの attention 式を実装しています。次の図は attention メカニズムにより各入力単語に重みが割り当てられて、それからそれがセンテンスの次の単語を予測するためにデコーダにより使用されることを示しています。

入力はエンコーダ・モデルの中を通され、これは shape (batch_size, max_length, hidden_size) のエンコーダ出力と shape (batch_size, hidden_size) のエンコーダ隠れ状態を与えます。

ここに実装される等式があります :

私達は Bahdanau attention を使用しています。単純化された形式を書く前に記法を定めましょう :

  • FC = 完全結合 (dense) 層
  • EO = エンコーダ出力
  • H = 隠れ状態
  • X = デコーダへの入力

そして擬似コードは :

  • score = FC(tanh(FC(EO) + FC(H)))
  • attention weights = softmax(score, axis = 1)。デフォルトでは softmax は最後の軸上に適用されますがここではそれを 1st 軸上で適用することを望みます、何故ならばスコアの shape は (batch_size, max_length, hidden_size) だからです。Max_length は入力の長さです。各入力に重みを割り当てようとしていますので、softmax はその軸上で適用されるべきです。
  • context vector = sum(attention weights * EO, axis = 1)。上と同じ理由で軸として 1 を選択します。
  • embedding output = デコーダへの入力 X は埋め込み層を通されます。
  • merged vector = concat(embedding output, context vector)
  • それからこのマージされたベクトルが GRU に与えられます。

各ステップにおける総てのベクトルの shape はコードのコメントで指定されます :

class Encoder(tf.keras.Model):
  def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
    super(Encoder, self).__init__()
    self.batch_sz = batch_sz
    self.enc_units = enc_units
    self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
    self.gru = tf.keras.layers.GRU(self.enc_units, 
                                   return_sequences=True, 
                                   return_state=True, 
                                   recurrent_initializer='glorot_uniform')

  def call(self, x, hidden):
    x = self.embedding(x)
    output, state = self.gru(x, initial_state = hidden)        
    return output, state

  def initialize_hidden_state(self):
    return tf.zeros((self.batch_sz, self.enc_units))
encoder = Encoder(vocab_inp_size, embedding_dim, units, BATCH_SIZE)

# sample input
sample_hidden = encoder.initialize_hidden_state()
sample_output, sample_hidden = encoder(example_input_batch, sample_hidden)
print ('Encoder output shape: (batch size, sequence length, units) {}'.format(sample_output.shape))
print ('Encoder Hidden state shape: (batch size, units) {}'.format(sample_hidden.shape))
Encoder output shape: (batch size, sequence length, units) (64, 16, 1024)
Encoder Hidden state shape: (batch size, units) (64, 1024)
class BahdanauAttention(tf.keras.Model):
  def __init__(self, units):
    super(BahdanauAttention, self).__init__()
    self.W1 = tf.keras.layers.Dense(units)
    self.W2 = tf.keras.layers.Dense(units)
    self.V = tf.keras.layers.Dense(1)
  
  def call(self, query, values):
    # hidden shape == (batch_size, hidden size)
    # hidden_with_time_axis shape == (batch_size, 1, hidden size)
    # we are doing this to perform addition to calculate the score
    hidden_with_time_axis = tf.expand_dims(query, 1)

    # score shape == (batch_size, max_length, hidden_size)
    score = self.V(tf.nn.tanh(
        self.W1(values) + self.W2(hidden_with_time_axis)))

    # attention_weights shape == (batch_size, max_length, 1)
    # we get 1 at the last axis because we are applying score to self.V
    attention_weights = tf.nn.softmax(score, axis=1)

    # context_vector shape after sum == (batch_size, hidden_size)
    context_vector = attention_weights * values
    context_vector = tf.reduce_sum(context_vector, axis=1)
    
    return context_vector, attention_weights
attention_layer = BahdanauAttention(10)
attention_result, attention_weights = attention_layer(sample_hidden, sample_output)

print("Attention result shape: (batch size, units) {}".format(attention_result.shape))
print("Attention weights shape: (batch_size, sequence_length, 1) {}".format(attention_weights.shape))
Attention result shape: (batch size, units) (64, 1024)
Attention weights shape: (batch_size, sequence_length, 1) (64, 16, 1)
class Decoder(tf.keras.Model):
  def __init__(self, vocab_size, embedding_dim, dec_units, batch_sz):
    super(Decoder, self).__init__()
    self.batch_sz = batch_sz
    self.dec_units = dec_units
    self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
    self.gru = tf.keras.layers.GRU(self.dec_units, 
                                   return_sequences=True, 
                                   return_state=True, 
                                   recurrent_initializer='glorot_uniform')
    self.fc = tf.keras.layers.Dense(vocab_size)

    # used for attention
    self.attention = BahdanauAttention(self.dec_units)

  def call(self, x, hidden, enc_output):
    # enc_output shape == (batch_size, max_length, hidden_size)
    context_vector, attention_weights = self.attention(hidden, enc_output)

    # x shape after passing through embedding == (batch_size, 1, embedding_dim)
    x = self.embedding(x)

    # x shape after concatenation == (batch_size, 1, embedding_dim + hidden_size)
    x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)

    # passing the concatenated vector to the GRU
    output, state = self.gru(x)

    # output shape == (batch_size * 1, hidden_size)
    output = tf.reshape(output, (-1, output.shape[2]))

    # output shape == (batch_size, vocab)
    x = self.fc(output)

    return x, state, attention_weights
decoder = Decoder(vocab_tar_size, embedding_dim, units, BATCH_SIZE)

sample_decoder_output, _, _ = decoder(tf.random.uniform((64, 1)), 
                                      sample_hidden, sample_output)

print ('Decoder output shape: (batch_size, vocab size) {}'.format(sample_decoder_output.shape))
Decoder output shape: (batch_size, vocab size) (64, 4935)

 

optimizer と損失関数を定義する

optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

def loss_function(real, pred):
  mask = tf.math.logical_not(tf.math.equal(real, 0))
  loss_ = loss_object(real, pred)

  mask = tf.cast(mask, dtype=loss_.dtype)
  loss_ *= mask
  
  return tf.reduce_mean(loss_)

 

チェックポイント (オブジェクトベースのセーブ)

checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(optimizer=optimizer,
                                 encoder=encoder,
                                 decoder=decoder)

 

訓練

  1. 入力をエンコーダを通します、これはエンコーダ出力とエンコーダ隠れ状態を返します。
  2. エンコーダ出力、エンコーダ隠れ状態とデコーダ入力 (これは start トークンです) がデコーダに渡されます。
  3. デコーダは予測とデコーダ隠れ状態を返します。
  4. それからデコーダ隠れ状態はモデルに渡し返されて予測は損失を計算するために使用されます。
  5. デコーダへの次の入力を決めるために teacher forcing を使用します。
  6. teacher forcing はターゲット単語がデコーダへの次の入力として渡されるテクニックです。
  7. 最後のステップは勾配を計算してそれを optimizer に適用して backpropagate します。
@tf.function
def train_step(inp, targ, enc_hidden):
  loss = 0
        
  with tf.GradientTape() as tape:
    enc_output, enc_hidden = encoder(inp, enc_hidden)

    dec_hidden = enc_hidden

    dec_input = tf.expand_dims([targ_lang.word_index['']] * BATCH_SIZE, 1)       

    # Teacher forcing - feeding the target as the next input
    for t in range(1, targ.shape[1]):
      # passing enc_output to the decoder
      predictions, dec_hidden, _ = decoder(dec_input, dec_hidden, enc_output)

      loss += loss_function(targ[:, t], predictions)

      # using teacher forcing
      dec_input = tf.expand_dims(targ[:, t], 1)

  batch_loss = (loss / int(targ.shape[1]))

  variables = encoder.trainable_variables + decoder.trainable_variables

  gradients = tape.gradient(loss, variables)

  optimizer.apply_gradients(zip(gradients, variables))
  
  return batch_loss
EPOCHS = 10

for epoch in range(EPOCHS):
  start = time.time()

  enc_hidden = encoder.initialize_hidden_state()
  total_loss = 0

  for (batch, (inp, targ)) in enumerate(dataset.take(steps_per_epoch)):
    batch_loss = train_step(inp, targ, enc_hidden)
    total_loss += batch_loss

    if batch % 100 == 0:
        print('Epoch {} Batch {} Loss {:.4f}'.format(epoch + 1,
                                                     batch,
                                                     batch_loss.numpy()))
  # saving (checkpoint) the model every 2 epochs
  if (epoch + 1) % 2 == 0:
    checkpoint.save(file_prefix = checkpoint_prefix)

  print('Epoch {} Loss {:.4f}'.format(epoch + 1,
                                      total_loss / steps_per_epoch))
  print('Time taken for 1 epoch {} sec\n'.format(time.time() - start))

Epoch 1 Batch 0 Loss 4.5657
Epoch 1 Batch 100 Loss 2.1792
Epoch 1 Batch 200 Loss 1.7583
Epoch 1 Batch 300 Loss 1.7384
Epoch 1 Loss 1.9949
Time taken for 1 epoch 49.0501441956 sec

Epoch 2 Batch 0 Loss 1.5181
Epoch 2 Batch 100 Loss 1.4801
Epoch 2 Batch 200 Loss 1.2197
Epoch 2 Batch 300 Loss 1.2561
Epoch 2 Loss 1.3435
Time taken for 1 epoch 34.9393060207 sec

Epoch 3 Batch 0 Loss 1.0709
Epoch 3 Batch 100 Loss 1.0403
Epoch 3 Batch 200 Loss 0.7950
Epoch 3 Batch 300 Loss 0.8709
Epoch 3 Loss 0.9174
Time taken for 1 epoch 34.288944006 sec

Epoch 4 Batch 0 Loss 0.7071
Epoch 4 Batch 100 Loss 0.6888
Epoch 4 Batch 200 Loss 0.4978
Epoch 4 Batch 300 Loss 0.5841
Epoch 4 Loss 0.6134
Time taken for 1 epoch 35.33512187 sec

Epoch 5 Batch 0 Loss 0.4685
Epoch 5 Batch 100 Loss 0.4926
Epoch 5 Batch 200 Loss 0.3000
Epoch 5 Batch 300 Loss 0.3866
Epoch 5 Loss 0.4136
Time taken for 1 epoch 34.1087429523 sec

Epoch 6 Batch 0 Loss 0.3144
Epoch 6 Batch 100 Loss 0.3042
Epoch 6 Batch 200 Loss 0.1984
Epoch 6 Batch 300 Loss 0.2658
Epoch 6 Loss 0.2875
Time taken for 1 epoch 35.1170010567 sec

Epoch 7 Batch 0 Loss 0.2284
Epoch 7 Batch 100 Loss 0.2275
Epoch 7 Batch 200 Loss 0.1428
Epoch 7 Batch 300 Loss 0.1976
Epoch 7 Loss 0.2032
Time taken for 1 epoch 33.9758181572 sec

Epoch 8 Batch 0 Loss 0.1724
Epoch 8 Batch 100 Loss 0.1635
Epoch 8 Batch 200 Loss 0.0983
Epoch 8 Batch 300 Loss 0.1408
Epoch 8 Loss 0.1461
Time taken for 1 epoch 35.9068500996 sec

Epoch 9 Batch 0 Loss 0.1353
Epoch 9 Batch 100 Loss 0.1123
Epoch 9 Batch 200 Loss 0.0888
Epoch 9 Batch 300 Loss 0.0953
Epoch 9 Loss 0.1107
Time taken for 1 epoch 34.0839440823 sec

Epoch 10 Batch 0 Loss 0.1103
Epoch 10 Batch 100 Loss 0.0954
Epoch 10 Batch 200 Loss 0.0654
Epoch 10 Batch 300 Loss 0.0824
Epoch 10 Loss 0.0885
Time taken for 1 epoch 35.4287509918 sec

 

翻訳する

  • evaluate 関数は訓練ループに似ています、ここでは teacher forcing を使用しないことを除いて。各時間ステップでのデコーダへの入力は隠れ状態とエンコーダ出力と共にその前の予測になります。
  • モデルが end トークンを予測するとき予測を停止します。
  • そして総ての時間ステップのために attention 重みをストアします。

Note: エンコーダ出力は一つの入力に対して一度だけ計算されます。

def evaluate(sentence):
    attention_plot = np.zeros((max_length_targ, max_length_inp))
    
    sentence = preprocess_sentence(sentence)

    inputs = [inp_lang.word_index[i] for i in sentence.split(' ')]
    inputs = tf.keras.preprocessing.sequence.pad_sequences([inputs], 
                                                           maxlen=max_length_inp, 
                                                           padding='post')
    inputs = tf.convert_to_tensor(inputs)
    
    result = ''

    hidden = [tf.zeros((1, units))]
    enc_out, enc_hidden = encoder(inputs, hidden)

    dec_hidden = enc_hidden
    dec_input = tf.expand_dims([targ_lang.word_index['']], 0)
    
    for t in range(max_length_targ):
        predictions, dec_hidden, attention_weights = decoder(dec_input, 
                                                             dec_hidden, 
                                                             enc_out)
        
        # storing the attention weights to plot later on
        attention_weights = tf.reshape(attention_weights, (-1, ))
        attention_plot[t] = attention_weights.numpy()

        predicted_id = tf.argmax(predictions[0]).numpy()

        result += targ_lang.index_word[predicted_id] + ' '

        if targ_lang.index_word[predicted_id] == '':
            return result, sentence, attention_plot
        
        # the predicted ID is fed back into the model
        dec_input = tf.expand_dims([predicted_id], 0)

    return result, sentence, attention_plot
# function for plotting the attention weights
def plot_attention(attention, sentence, predicted_sentence):
    fig = plt.figure(figsize=(10,10))
    ax = fig.add_subplot(1, 1, 1)
    ax.matshow(attention, cmap='viridis')
    
    fontdict = {'fontsize': 14}
    
    ax.set_xticklabels([''] + sentence, fontdict=fontdict, rotation=90)
    ax.set_yticklabels([''] + predicted_sentence, fontdict=fontdict)

    plt.show()
def translate(sentence):
    result, sentence, attention_plot = evaluate(sentence)
        
    print('Input: %s' % (sentence).encode('utf-8'))
    print('Predicted translation: {}'.format(result))
    
    attention_plot = attention_plot[:len(result.split(' ')), :len(sentence.split(' '))]
    plot_attention(attention_plot, sentence.split(' '), result.split(' '))

 

最新のチェックポイントを復元してテストする

# restoring the latest checkpoint in checkpoint_dir
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))
translate(u'hace mucho frio aqui.')
Input: <start> hace mucho frio aqui . <end>
Predicted translation: it s very cold here . <end>

translate(u'esta es mi vida.')
Input: <start> esta es mi vida . <end>
Predicted translation: this is my life . <end>

translate(u'¿todavia estan en casa?')
Input: <start> ¿ todavia estan en <end>
Predicted translation: are you still home ? <end>

# wrong translation
translate(u'trata de averiguarlo.')
Input: <start> trata de averiguarlo . <end>
Predicted translation: try to figure it out . <end> 

 

Next steps

  • 翻訳の実験をするために 異なるデータセットをダウンロード します、例えば、英語 to 独語、あるいは英語 to 仏語です。
  • より巨大なデータセット上の訓練で実験します、あるいはより多いエポックを使用します。
 

以上






TensorFlow 2.0 Alpha : 上級 Tutorials : テキストとシークエンス :- RNN でテキスト生成

TensorFlow 2.0 Alpha : 上級 Tutorials : テキストとシークエンス :- RNN でテキスト生成 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/06/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – Text and sequences の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

テキストとシークエンス :- RNN でテキスト生成

このチュートリアルは文字ベース RNN を使用してテキストをどのように生成するかを実演します。私達は Andrej Karpathy の The Unreasonable Effectiveness of Recurrent Neural Networks から Shakespeare の著作のデータセットで作業します。このデータ (“Shakespear”) から文字のシークエンスが与えられたとき、シークエンスの次の文字 (“e”) を予測するモデルを訓練します。モデルを繰り返し呼び出すことによりテキストのより長いシークエンスが生成できます。

Note: このノートブックを高速に実行するために GPU アクセラレーションを有効にしてください。Colab では : Runtime > Change runtime type > Hardware acclerator > GPU です。ローカルで実行する場合 TensorFlow version >= 1.11 を確実にしてください。

このチュートリアルは tf.keras と eager execution を使用して実装された実行可能なコードを含みます。次は 30 エポックの間訓練されたこのチュートリアルのモデルが文字列 (= string) “Q” で開始されたときのサンプル出力です :

QUEENE:
I had thought thou hadst a Roman; for the oracle,
Thus by All bids the man against the word,
Which are so weak of care, by old care done;
Your children were in your holy love,
And the precipitation through the bleeding throne.

BISHOP OF ELY:
Marry, and will, my lord, to weep in such a one were prettiest;
Yet now I was adopted heir
Of the world's lamentable day,
To watch the next way with his father with his face?

ESCALUS:
The cause why then we are all resolved more sons.

VOLUMNIA:
O, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, it is no sin it should be dead,
And love and pale as any will to that word.

QUEEN ELIZABETH:
But how long have I heard the soul for this world,
And show his hands of life be proved to stand.

PETRUCHIO:
I say he look'd on, if I must be content
To stay him from the fatal of our country's bliss.
His lordship pluck'd from this sentence then for prey,
And then let us twain, being the moon,
were she such a case as fills m

センテンスの幾つかが文法的に正しい一方で、多くは意味を成しません。モデルは単語の意味を学習していませんが、以下を考えてください :

  • 私達のモデルは文字ベースです 訓練を始めたとき、モデルは英単語をどのようにスペルするか、あるいは単語がテキストのユニットであることさえ知りませんでした。
  • 出力の構造は演劇に類似しています — テキストのブロックは一般に話者名で始まり、元のデータセットと同様に総て大文字です。
  • 下で実演されるように、モデルはテキストの小さいバッチ (それぞれ 100 文字) 上で訓練され、コヒーレント構造を持つテキストのより長いシークエンスを依然として生成することが可能です。

 

Setup

TensorFlow と他のライブラリをインポートする

from __future__ import absolute_import, division, print_function

!pip install -q tensorflow-gpu==2.0.0-alpha0
import tensorflow as tf

import numpy as np
import os
import time
Collecting tensorflow-gpu==2.0.0-alpha0
Successfully installed google-pasta-0.1.4 tb-nightly-1.14.0a20190303 tensorflow-estimator-2.0-preview-1.14.0.dev2019030300 tensorflow-gpu==2.0.0-alpha0-2.0.0.dev20190303

 

Shakespeare データセットをダウンロードする

貴方自身のデータ上でこのコードを実行するためには次の行を変更します。

path_to_file = tf.keras.utils.get_file('shakespeare.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt
1122304/1115394 [==============================] - 0s 0us/step

 

データを読む

最初にテキストを少し見てみます :

# Read, then decode for py2 compat.
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')
# length of text is the number of characters in it
print ('Length of text: {} characters'.format(len(text)))
Length of text: 1115394 characters
# Take a look at the first 250 characters in text
print(text[:250])
First Citizen:
Before we proceed any further, hear me speak.

All:
Speak, speak.

First Citizen:
You are all resolved rather to die than to famish?

All:
Resolved. resolved.

First Citizen:
First, you know Caius Marcius is chief enemy to the people.
# The unique characters in the file
vocab = sorted(set(text))
print ('{} unique characters'.format(len(vocab)))
65 unique characters

 

テキストを処理する

テキストをベクトル化する

訓練前に、文字列を数字表現にマップする必要があります。2 つの検索テーブルを作成します: 一つは文字を数字にマップし、そしてもう一つは数字から文字のためです。

# Creating a mapping from unique characters to indices
char2idx = {u:i for i, u in enumerate(vocab)}
idx2char = np.array(vocab)

text_as_int = np.array([char2idx[c] for c in text])

今では各文字に対する整数表現を持ちます。文字を 0 から len(unique) のインデックスとしてマップしたことが分かるでしょう。

print('{')
for char,_ in zip(char2idx, range(20)):
    print('  {:4s}: {:3d},'.format(repr(char), char2idx[char]))
print('  ...\n}')
{
  '\n':   0,
  ' ' :   1,
  '!' :   2,
  '$' :   3,
  '&' :   4,
  "'" :   5,
  ',' :   6,
  '-' :   7,
  '.' :   8,
  '3' :   9,
  ':' :  10,
  ';' :  11,
  '?' :  12,
  'A' :  13,
  'B' :  14,
  'C' :  15,
  'D' :  16,
  'E' :  17,
  'F' :  18,
  'G' :  19,
  ...
}
# Show how the first 13 characters from the text are mapped to integers
print ('{} ---- characters mapped to int ---- > {}'.format(repr(text[:13]), text_as_int[:13]))
'First Citizen' ---- characters mapped to int ---- > [18 47 56 57 58  1 15 47 58 47 64 43 52]

 

予測タスク

文字、あるいは文字のシークエンスが与えられたとき、最もありそうな次の文字は何でしょう?これが私達がモデルを訓練して遂行するタスクです。モデルへの入力は文字のシークエンスで、出力 — 各時間ステップにおける次の文字を予測するためにモデルを訓練します。RNN は前に見た要素に依拠する内部状態を維持しますので、この瞬間までに計算された総ての文字が与えられたとき、次の文字は何でしょう?

 

訓練サンプルとターゲットを作成する

次にテキストをサンプル・シークエンスに分割します。各入力シークエンスはテキストから seq_length 文字を含みます。

各入力シークエンスに対して、対応するターゲットはテキストの同じ長さを含みます、一つの文字が右にシフトされていることを除いて。

そこでテキストを seq_length+1のチャンクに分解します。例えば、seq_length が 4 そしてテキストが “Hello” であるとします。入力シークエンスは “Hell” で、そしてターゲット・シークエンスは “ello” です。

これを行なうために最初にテキストベクトルを文字インデックスのストリームに変換するために tf.data.Dataset.from_tensor_slices 関数を使用します。

# The maximum length sentence we want for a single input in characters
seq_length = 100
examples_per_epoch = len(text)//seq_length

# Create training examples / targets
char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)

for i in char_dataset.take(5):
  print(idx2char[i.numpy()])
F
i
r
s
t

batch メソッドはこれらの個々の文字を望まれるサイズのシークエンスに容易に変換させます。

sequences = char_dataset.batch(seq_length+1, drop_remainder=True)

for item in sequences.take(5):
  print(repr(''.join(idx2char[item.numpy()])))
'First Citizen:\nBefore we proceed any further, hear me speak.\n\nAll:\nSpeak, speak.\n\nFirst Citizen:\nYou '
'are all resolved rather to die than to famish?\n\nAll:\nResolved. resolved.\n\nFirst Citizen:\nFirst, you k'
"now Caius Marcius is chief enemy to the people.\n\nAll:\nWe know't, we know't.\n\nFirst Citizen:\nLet us ki"
"ll him, and we'll have corn at our own price.\nIs't a verdict?\n\nAll:\nNo more talking on't; let it be d"
'one: away, away!\n\nSecond Citizen:\nOne word, good citizens.\n\nFirst Citizen:\nWe are accounted poor citi'

各シークエンスについて、各バッチに単純な関数を適用するために map メソッドを使用して入力とターゲットテキストを形成するためにそれを複製してシフトします :

def split_input_target(chunk):
    input_text = chunk[:-1]
    target_text = chunk[1:]
    return input_text, target_text

dataset = sequences.map(split_input_target)

最初のサンプル入力とターゲット値をプリントします :

for input_example, target_example in  dataset.take(1):
  print ('Input data: ', repr(''.join(idx2char[input_example.numpy()])))
  print ('Target data:', repr(''.join(idx2char[target_example.numpy()])))
Input data:  'First Citizen:\nBefore we proceed any further, hear me speak.\n\nAll:\nSpeak, speak.\n\nFirst Citizen:\nYou'
Target data: 'irst Citizen:\nBefore we proceed any further, hear me speak.\n\nAll:\nSpeak, speak.\n\nFirst Citizen:\nYou '

これらのベクトルの各インデックスは一つの時間ステップとして処理されます。時間ステップ 0 における入力については、モデルは “F” のためのインデックスを受け取りそして次の文字として “i” のためのインデックスを予測しようとします。次の時間ステップでは、それは同じことを行ないますが RNN は現在の入力文字に加えて前のステップのコンテキストも考慮します。

for i, (input_idx, target_idx) in enumerate(zip(input_example[:5], target_example[:5])):
    print("Step {:4d}".format(i))
    print("  input: {} ({:s})".format(input_idx, repr(idx2char[input_idx])))
    print("  expected output: {} ({:s})".format(target_idx, repr(idx2char[target_idx])))
Step    0
  input: 18 ('F')
  expected output: 47 ('i')
Step    1
  input: 47 ('i')
  expected output: 56 ('r')
Step    2
  input: 56 ('r')
  expected output: 57 ('s')
Step    3
  input: 57 ('s')
  expected output: 58 ('t')
Step    4
  input: 58 ('t')
  expected output: 1 (' ')

 

訓練バッチを作成する

テキストを扱いやすいシークエンスに分割するために tf.data を使用しました。しかしこのデータをモデルに供給する前に、データをシャッフルしてそれをバッチにパックする必要があります。

# Batch size 
BATCH_SIZE = 64

# Buffer size to shuffle the dataset
# (TF data is designed to work with possibly infinite sequences, 
# so it doesn't attempt to shuffle the entire sequence in memory. Instead, 
# it maintains a buffer in which it shuffles elements).
BUFFER_SIZE = 10000

dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)

dataset
<BatchDataset shapes: ((64, 100), (64, 100)), types: (tf.int64, tf.int64)>

 

モデルを構築する

モデルを定義するために tf.keras.Sequential を使用します。この単純なサンプルのためにモデルを定義するために 3 つの層が使用されます :

  • tf.keras.layers.Embedding: 入力層。各文字の数字を embedding_dim 次元を持つベクトルにマップする訓練可能な検索テーブルです;
  • tf.keras.layers.GRU: サイズ units=rnn_units を持つ RNN の型です (ここで LSTM 層を使用することもできます)。
  • tf.keras.layers.Dense: vocab_size 出力を持つ出力層。
# Length of the vocabulary in chars
vocab_size = len(vocab)

# The embedding dimension 
embedding_dim = 256

# Number of RNN units
rnn_units = 1024
def build_model(vocab_size, embedding_dim, rnn_units, batch_size):
  model = tf.keras.Sequential([
    tf.keras.layers.Embedding(vocab_size, embedding_dim, 
                              batch_input_shape=[batch_size, None]),
    tf.keras.layers.LSTM(rnn_units, 
                        return_sequences=True, 
                        stateful=True, 
                        recurrent_initializer='glorot_uniform'),
    tf.keras.layers.Dense(vocab_size)
  ])
  return model
model = build_model(
  vocab_size = len(vocab), 
  embedding_dim=embedding_dim, 
  rnn_units=rnn_units, 
  batch_size=BATCH_SIZE)
WARNING: Logging before flag parsing goes to stderr.
W0304 03:48:46.706135 140067035297664 tf_logging.py:161] <tensorflow.python.keras.layers.recurrent.UnifiedLSTM object at 0x7f637273ccf8>: Note that this layer is not optimized for performance. Please use tf.keras.layers.CuDNNLSTM for better performance on GPU.

各文字についてモデルは埋め込みを検索し、埋め込みを入力として 1 時間ステップ GRU を実行し、そして次の文字の log-尤度を予測するロジットを生成するために dense 層を適用します :

 

モデルを試す

さてモデルをそれが期待どおりに挙動するかを見るために実行します。

最初に出力の shape を確認します :

for input_example_batch, target_example_batch in dataset.take(1): 
  example_batch_predictions = model(input_example_batch)
  print(example_batch_predictions.shape, "# (batch_size, sequence_length, vocab_size)")
(64, 100, 65) # (batch_size, sequence_length, vocab_size)

上の例では入力のシークエンス長は 100 ですがモデルは任意の長さの入力上で実行できます :

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (64, None, 256)           16640     
_________________________________________________________________
unified_lstm (UnifiedLSTM)   (64, None, 1024)          5246976   
_________________________________________________________________
dense (Dense)                (64, None, 65)            66625     
=================================================================
Total params: 5,330,241
Trainable params: 5,330,241
Non-trainable params: 0
_________________________________________________________________

モデルから実際の予測を得るためには、実際の文字インデックスを得るために出力分布からサンプリングする必要があります。この分布は文字語彙に渡るロジットにより定義されます。

Note: この分布からサンプリングすることは重要です、というのは分布の argmax を取ることはループでモデルを容易に stuck させる可能性があるためです。

バッチの最初のサンプルのためにそれを試します :

sampled_indices = tf.random.categorical(example_batch_predictions[0], num_samples=1)
sampled_indices = tf.squeeze(sampled_indices,axis=-1).numpy()

これは、各時間ステップにおける、次の文字インデックスの予測を与えます :

sampled_indices
array([21,  2, 58, 40, 42, 32, 39,  7, 18, 38, 30, 58, 23, 58, 37, 10, 23,
       16, 52, 14, 43,  8, 32, 49, 62, 41, 53, 38, 17, 36, 24, 59, 41, 38,
        4, 27, 33, 59, 54, 34, 14,  1,  1, 56, 55, 40, 37,  4, 32, 44, 62,
       59,  1, 10, 20, 29,  2, 48, 37, 26, 10, 22, 58,  5, 26,  9, 23, 26,
       54, 43, 46, 36, 62, 57,  8, 53, 52, 23, 57, 42, 60, 10, 43, 11, 45,
       12, 28, 46, 46, 15, 51,  9, 56,  7, 53, 51,  2,  1, 10, 58])

この未訓練モデルにより予測されたテキストを見るためにこれらをデコードします :

print("Input: \n", repr("".join(idx2char[input_example_batch[0]])))
print()
print("Next Char Predictions: \n", repr("".join(idx2char[sampled_indices ])))
Input: 
 'to it far before thy time?\nWarwick is chancellor and the lord of Calais;\nStern Falconbridge commands'

Next Char Predictions: 
 "I!tbdTa-FZRtKtY:KDnBe.TkxcoZEXLucZ&OUupVB  rqbY&Tfxu :HQ!jYN:Jt'N3KNpehXxs.onKsdv:e;g?PhhCm3r-om! :t"

 

モデルを訓練する

この時点で問題は標準的な分類問題として扱うことができます。前の RNN 状態、そしてこの時間ステップで入力が与えられたとき、次の文字のクラスを予測します。

 

optimizer、そして損失関数を張り付ける

この場合では標準的な tf.keras.losses.sparse_softmax_crossentropy 損失関数が動作します、何故ならばそれは予測の最後の次元に渡り適用されるからです。

私達のモデルはロジットを返しますので、from_logits フラグを設定する必要があります。

def loss(labels, logits):
  return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)

example_batch_loss  = loss(target_example_batch, example_batch_predictions)
print("Prediction shape: ", example_batch_predictions.shape, " # (batch_size, sequence_length, vocab_size)") 
print("scalar_loss:      ", example_batch_loss.numpy().mean())
Prediction shape:  (64, 100, 65)  # (batch_size, sequence_length, vocab_size)
scalar_loss:       4.174188

tf.keras.Model.compile メソッドを使用して訓練手続きを configure します。デフォルト引数を伴う tf.keras.optimizers.Adam と損失関数を使用します。

model.compile(optimizer='adam', loss=loss)

 

チェックポイントを構成する

チェックポイントが訓練の間にセーブされることを確実にするために tf.keras.callbacks.ModelCheckpoint を使用します :

# Directory where the checkpoints will be saved
checkpoint_dir = './training_checkpoints'
# Name of the checkpoint files
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}")

checkpoint_callback=tf.keras.callbacks.ModelCheckpoint(
    filepath=checkpoint_prefix,
    save_weights_only=True)

 

訓練を実行する

訓練時間を合理的なものに保持するために、モデルを訓練するために 3 エポック (訳注: 原文ママ) を使用します。

EPOCHS=10
history = model.fit(dataset, epochs=EPOCHS, callbacks=[checkpoint_callback])
Epoch 1/10
172/172 [==============================] - 31s 183ms/step - loss: 2.7052
Epoch 2/10
172/172 [==============================] - 31s 180ms/step - loss: 2.0039
Epoch 3/10
172/172 [==============================] - 31s 180ms/step - loss: 1.7375
Epoch 4/10
172/172 [==============================] - 31s 179ms/step - loss: 1.5772
Epoch 5/10
172/172 [==============================] - 31s 179ms/step - loss: 1.4772
Epoch 6/10
172/172 [==============================] - 31s 180ms/step - loss: 1.4087
Epoch 7/10
172/172 [==============================] - 31s 179ms/step - loss: 1.3556
Epoch 8/10
172/172 [==============================] - 31s 179ms/step - loss: 1.3095
Epoch 9/10
172/172 [==============================] - 31s 179ms/step - loss: 1.2671
Epoch 10/10
172/172 [==============================] - 31s 180ms/step - loss: 1.2276

 

テキストを生成する

最新のチェックポイントを復元する

この予測ステップを単純に保持するために、1 のバッチサイズを使用します。

RNN 状態が時間ステップから時間ステップに渡される方法のために、モデルは一度構築された固定バッチサイズだけを受け取ります。

異なる batch_size でモデルを実行するためには、モデルを再構築してチェックポイントから重みを復元する必要があります。

tf.train.latest_checkpoint(checkpoint_dir)
'./training_checkpoints/ckpt_10'
model = build_model(vocab_size, embedding_dim, rnn_units, batch_size=1)

model.load_weights(tf.train.latest_checkpoint(checkpoint_dir))

model.build(tf.TensorShape([1, None]))
W0304 03:54:01.201246 140067035297664 tf_logging.py:161] <tensorflow.python.keras.layers.recurrent.UnifiedLSTM object at 0x7f636183c7f0>: Note that this layer is not optimized for performance. Please use tf.keras.layers.CuDNNLSTM for better performance on GPU.
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (1, None, 256)            16640     
_________________________________________________________________
unified_lstm_1 (UnifiedLSTM) (1, None, 1024)           5246976   
_________________________________________________________________
dense_1 (Dense)              (1, None, 65)             66625     
=================================================================
Total params: 5,330,241
Trainable params: 5,330,241
Non-trainable params: 0
_________________________________________________________________

 

予測ループ

次のコードブロックがテキストを生成します :

  • それは開始文字列を選択し、RNN 状態を初期化してそして生成する文字数を設定することから始まります。
  • 開始文字列と RNN 状態を使用して次の文字の予測分布を得ます。
  • それから、予測された文字のインデックスを計算するために categorical 分布を使用します。この予測された文字をモデルへの次の入力として使用します。
  • モデルにより返された RNN 状態はモデルに供給し戻されます、その結果それは今ではただ一つの単語よりも多くのコンテキストを代わりに持ちます。次の単語を予測した後、変更された RNN 状態が再度モデルに供給し戻されます、これがそれが前に予測された単語からより多くのコンテキストを得るときにどのように学習されるかです。

生成されたテキストを見れば、モデルがいつ大文字にするべきかを知り、パラグラフを作成してそして Shakespeare-like な著述語彙を模倣するのを見るでしょう。小さい数の訓練エポックでは、それは首尾一貫したセンテンスを形成することをまだ学習していません。

def generate_text(model, start_string):
  # Evaluation step (generating text using the learned model)

  # Number of characters to generate
  num_generate = 1000

  # Converting our start string to numbers (vectorizing) 
  input_eval = [char2idx[s] for s in start_string]
  input_eval = tf.expand_dims(input_eval, 0)

  # Empty string to store our results
  text_generated = []

  # Low temperatures results in more predictable text.
  # Higher temperatures results in more surprising text.
  # Experiment to find the best setting.
  temperature = 1.0

  # Here batch size == 1
  model.reset_states()
  for i in range(num_generate):
      predictions = model(input_eval)
      # remove the batch dimension
      predictions = tf.squeeze(predictions, 0)

      # using a categorical distribution to predict the word returned by the model
      predictions = predictions / temperature
      predicted_id = tf.random.categorical(predictions, num_samples=1)[-1,0].numpy()
      
      # We pass the predicted word as the next input to the model
      # along with the previous hidden state
      input_eval = tf.expand_dims([predicted_id], 0)
      
      text_generated.append(idx2char[predicted_id])

  return (start_string + ''.join(text_generated))
print(generate_text(model, start_string=u"ROMEO: "))
ROMEO: now to have weth hearten sonce,
No more than the thing stand perfect your self,
Love way come. Up, this is d so do in friends:
If I fear e this, I poisple
My gracious lusty, born once for readyus disguised:
But that a pry; do it sure, thou wert love his cause;
My mind is come too!

POMPEY:
Serve my master's him: he hath extreme over his hand in the
where they shall not hear they right for me.

PROSSPOLUCETER:
I pray you, mistress, I shall be construted
With one that you shall that we know it, in this gentleasing earls of daiberkers now
he is to look upon this face, which leadens from his master as
you should not put what you perciploce backzat of cast,
Nor fear it sometime but for a pit
a world of Hantua?

First Gentleman:
That we can fall of bastards my sperial;
O, she Go seeming that which I have
what enby oar own best injuring them,
Or thom I do now, I, in heart is nothing gone,
Leatt the bark which was done born.

BRUTUS:
Both Margaret, he is sword of the house person. If born, 

結果を改善するために貴方ができる最も容易なことはそれをより長く訓練することです (EPOCHS=30 を試してください)。

貴方はまた異なる開始文字列で実験したり、モデル精度を改良するためにもう一つの RNN 層を追加して試す、あるいは多少のランダム予測を生成するために temperature パラメータを調整することができます。

 

Advanced: カスタマイズされた訓練

上の訓練手続きは単純ですが、多くの制御を貴方に与えません。

そこでモデルを手動でどのように実行するかを見た今、訓練ループをアンパックしてそれを私達自身で実装しましょう。これは例えば、モデルの open-loop 出力を安定させる手助けをするためにカリキュラム学習を実装するための開始点を与えます。

勾配を追跡するために tf.GradientTape を使用します。eager execution ガイド を読むことによりこのアプローチについてより多く学習できます。

手続きは次のように動作します :

  • 最初に、RNN 状態を初期化します。tf.keras.Model.reset_states メソッドを呼び出すことによりこれを行ないます。
  • 次に、データセットに渡り (バッチ毎に) iterate して各々に関連する予測を計算します。
  • tf.GradientTape をオープンして、そしてそのコンテキストで予測と損失を計算します。
  • tf.GradientTape.grads メソッドを使用してモデル変数に関する損失の勾配を計算します。
  • 最期に、optimizer の tf.train.Optimizer.apply_gradients メソッドを使用してステップを下方に取ります。
model = build_model(
  vocab_size = len(vocab), 
  embedding_dim=embedding_dim, 
  rnn_units=rnn_units, 
  batch_size=BATCH_SIZE)
W0304 03:54:08.030432 140067035297664 tf_logging.py:161] <tensorflow.python.keras.layers.recurrent.UnifiedLSTM object at 0x7f63635efe80>: Note that this layer is not optimized for performance. Please use tf.keras.layers.CuDNNLSTM for better performance on GPU.
optimizer = tf.keras.optimizers.Adam()
@tf.function
def train_step(inp, target):
  with tf.GradientTape() as tape:
    predictions = model(inp)
    loss = tf.reduce_mean(
        tf.keras.losses.sparse_categorical_crossentropy(target, predictions))
  grads = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(grads, model.trainable_variables))
  
  return loss
# Training step
EPOCHS = 10

for epoch in range(EPOCHS):
  start = time.time()

  for (batch_n, (inp, target)) in enumerate(dataset):
    loss = train_step(inp, target)

    if batch_n % 100 == 0:
      template = 'Epoch {} Batch {} Loss {}'
      print(template.format(epoch+1, batch_n, loss))

  # saving (checkpoint) the model every 5 epochs
  if (epoch + 1) % 5 == 0:
    model.save_weights(checkpoint_prefix.format(epoch=epoch))

  print ('Epoch {} Loss {:.4f}'.format(epoch+1, loss))
  print ('Time taken for 1 epoch {} sec\n'.format(time.time() - start))

model.save_weights(checkpoint_prefix.format(epoch=epoch))
Epoch 1 Batch 0 Loss 8.132774353027344
Epoch 1 Batch 100 Loss 3.5028388500213623
Epoch 1 Loss 3.7314
Time taken for 1 epoch 31.78906798362732 sec

Epoch 2 Batch 0 Loss 3.766866445541382
Epoch 2 Batch 100 Loss 3.985184669494629
Epoch 2 Loss 3.9137
Time taken for 1 epoch 29.776747703552246 sec

Epoch 3 Batch 0 Loss 4.023300647735596
Epoch 3 Batch 100 Loss 3.921215534210205
Epoch 3 Loss 3.8976
Time taken for 1 epoch 30.094752311706543 sec

Epoch 4 Batch 0 Loss 3.916696071624756
Epoch 4 Batch 100 Loss 3.900864362716675
Epoch 4 Loss 3.9048
Time taken for 1 epoch 30.09034276008606 sec

Epoch 5 Batch 0 Loss 3.9154434204101562
Epoch 5 Batch 100 Loss 3.9020049571990967
Epoch 5 Loss 3.9725
Time taken for 1 epoch 30.17358922958374 sec

Epoch 6 Batch 0 Loss 3.9781394004821777
Epoch 6 Batch 100 Loss 3.920198917388916
Epoch 6 Loss 3.9269
Time taken for 1 epoch 30.19426202774048 sec

Epoch 7 Batch 0 Loss 3.9400787353515625
Epoch 7 Batch 100 Loss 3.8473968505859375
Epoch 7 Loss 3.8438
Time taken for 1 epoch 30.107476234436035 sec

Epoch 8 Batch 0 Loss 3.852555513381958
Epoch 8 Batch 100 Loss 3.8410544395446777
Epoch 8 Loss 3.8218
Time taken for 1 epoch 30.084821462631226 sec

Epoch 9 Batch 0 Loss 3.843691349029541
Epoch 9 Batch 100 Loss 3.829458236694336
Epoch 9 Loss 3.8420
Time taken for 1 epoch 30.13308310508728 sec

Epoch 10 Batch 0 Loss 3.8553621768951416
Epoch 10 Batch 100 Loss 3.7812960147857666
Epoch 10 Loss 3.7726
Time taken for 1 epoch 30.14617133140564 sec
 

以上






TensorFlow 2.0 Alpha : 上級 Tutorials : カスタマイズ :- tf.function

TensorFlow 2.0 Alpha : 上級 Tutorials : カスタマイズ :- tf.function (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/06/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Advanced Tutorials – Customization の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

カスタマイズ :- tf.function

TensorFlow 2.0 では eager execution はデフォルトで有効になっています。これは貴方に非常に直感的で柔軟なユーザインターフェイスを手に入れさせますが (一度限りの (= one-off) 演算の実行は遥かに容易で高速です)、これはパフォーマンスと配備性 (= deployability) の犠牲をもたらす可能性があります。

最高点のパフォーマンスを得て貴方のモデルをどこでも配備可能にするために、貴方のプログラムからグラフを作成するために使用できるツールとして tf.function を提供します。

from __future__ import absolute_import, division, print_function

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf
# 関数は op のようなものです。

@tf.function
def add(a, b):
  return a + b

add(tf.ones([2, 2]), tf.ones([2, 2]))  #  [[2., 2.], [2., 2.]]
<tf.Tensor: id=16, shape=(2, 2), dtype=float32, numpy=
array([[2., 2.],
       [2., 2.]], dtype=float32)>

貴方が定義する tf.function は丁度、コア TensorFlow 演算のようなものです : 貴方はそれを eagerly に実行することができて、それをグラフで使用できて、それは勾配を持ちます、etc.

# function は勾配を持ちます。

@tf.function
def add(a, b):
  return a + b

v = tf.Variable(1.0)
with tf.GradientTape() as tape:
  result = add(v, 1.0)
tape.gradient(result, v)
<tf.Tensor: id=44, shape=(), dtype=float32, numpy=1.0>
# function の内側で関数を使用することができます。

@tf.function
def dense_layer(x, w, b):
  return add(tf.matmul(x, w), b)

dense_layer(tf.ones([3, 2]), tf.ones([2, 2]), tf.ones([2]))
<tf.Tensor: id=74, shape=(3, 2), dtype=float32, numpy=
array([[3., 3.],
       [3., 3.],
       [3., 3.]], dtype=float32)>

 

ポリモーフィズム (= Polymorphism)

tf.function は Python 関数と同じように一般的 (= generic) であろうとします。総ての種類のシグネチャで Python 関数を呼び出すことができて、そして Python は通常は合理的な何かを行ないます。tf.function はこのタイプのポリモーフィズムを行ないます、それが生成する基礎的な TensorFlow グラフはそのシグネチャの特定の型に固有ですが。

何が起きるかを見るために異なる型の引数で function を呼び出せます。

# function は polymorphic です。

@tf.function
def add(a):
  return a + a

print("add 1", add(1))
print("add 1.1", add(1.1))
print("add string tensor", add(tf.constant("a")))
c = add.get_concrete_function(tf.TensorSpec(shape=None, dtype=tf.string))
c(a=tf.constant("a"))  # aa
add 1 tf.Tensor(2, shape=(), dtype=int32)
add 1.1 tf.Tensor(2.2, shape=(), dtype=float32)
add string tensor tf.Tensor(b'aa', shape=(), dtype=string)

<tf.Tensor: id=104, shape=(), dtype=string, numpy=b'aa>
# Functions can be faster than eager code, for graphs with many small ops

import timeit
conv_layer = tf.keras.layers.Conv2D(100, 3)

@tf.function
def conv_fn(image):
  return conv_layer(image)

image = tf.zeros([1, 200, 200, 100])
# warm up
conv_layer(image); conv_fn(image)
print("Eager conv:", timeit.timeit(lambda: conv_layer(image), number=10))
print("Function conv:", timeit.timeit(lambda: conv_fn(image), number=10))
print("Note how there's not much difference in performance for convolutions")

lstm_cell = tf.keras.layers.LSTMCell(10)

@tf.function
def lstm_fn(input, state):
  return lstm_cell(input, state)

input = tf.zeros([10, 10])
state = [tf.zeros([10, 10])] * 2
# warm up
lstm_cell(input, state); lstm_fn(input, state)
print("eager lstm:", timeit.timeit(lambda: lstm_cell(input, state), number=10))
print("function lstm:", timeit.timeit(lambda: lstm_fn(input, state), number=10))
Eager conv: 0.19106170209124684
Function conv: 0.17481591296382248
Note how there's not much difference in performance for convolutions
eager lstm: 0.0355632440187037
function lstm: 0.0081845159875229

 

tf.function の状態

一般的なデータフロー・グラフに渡る、プログラミングモデルとしての function の非常に魅力的な特質は function はコードの意図された挙動が何であったかについてのより多くの情報をランライムに与えられることです。

例えば、同じ変数への複数の read と write を持つコードを書く時、データフロー・グラフは元々意図された演算の順序を自然にエンコードしないかもしれません。けれども tf.function では、Python からトレースされたコードを変換していますので、意図された実行順序を知っています。

これは手動の制御依存性を追加する必要がないことを意味します ; tf.function は貴方のコードを正しく実行するために必要で十分な制御依存性の最小限のセットを追加するために十分にスマートです。

# Automatic control dependencies

a = tf.Variable(1.0)
b = tf.Variable(2.0)

@tf.function
def f(x, y):
  a.assign(y * b)
  b.assign_add(x * a)
  return a + b

f(1.0, 2.0)  # 10.0
<tf.Tensor: id=1610, shape=(), dtype=float32, numpy=10.0>

 

Variables

tf.function で変数作成と利用を非常に容易にするためにコードの意図された実行順序を活用するのと同じアイデアを利用できます。けれども、一つの非常に重要な警告があります。それは variable では eagerly に複数回呼び出された時とその出力 tensor が複数回評価される時に異なって挙動するコードを書くことが可能であることです。

ここに単純な例があります :

@tf.function
def f(x):
  v = tf.Variable(1.0)
  v.assign_add(x)
  return v

f(1.) # Note: BROKEN, will throw exception

これを eager execution で実行する場合、答えとして常に “2” を得るでしょう ; しかしグラフコンテキストで f(1.) から得られる Tensor を繰り返し評価する場合には増加する数を得るでしょう。

従って tf.function はそのようなコードを書くことを許容しません。

# Non-ambiguous code is ok though

v = tf.Variable(1.0)

@tf.function
def f(x):
  return v.assign_add(x)

f(1.0)  # 2.0
f(2.0)  # 4.0
<tf.Tensor: id=1635, shape=(), dtype=float32, numpy=4.0>
# You can also create variables inside a tf.function as long as we can prove
# that those variables are created only the first time the function is executed.

class C: pass
obj = C(); obj.v = None

@tf.function
def g(x):
  if obj.v is None:
    obj.v = tf.Variable(1.0)
  return obj.v.assign_add(x)

g(1.0)  # 2.0
g(2.0)  # 4.0
<tf.Tensor: id=1689, shape=(), dtype=float32, numpy=4.0>
# Variable initializers can depend on function arguments and on values of other
# variables. We can figure out the right initialization order using the same
# method we use to generate control dependencies.

state = []
@tf.function
def fn(x):
  if not state:
    state.append(tf.Variable(2.0 * x))
    state.append(tf.Variable(state[0] * 3.0))
  return state[0] * x * state[1]

fn(tf.constant(1.0))
fn(tf.constant(3.0))
WARNING: Logging before flag parsing goes to stderr.
W0307 18:49:58.824626 139675184367360 tf_logging.py:161] Entity  could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
W0307 18:49:58.830288 139675184367360 tf_logging.py:161] Entity  could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.

WARNING: Entity  could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.
WARNING: Entity  could not be transformed and will be staged without change. Error details can be found in the logs when running with the env variable AUTOGRAPH_VERBOSITY >= 1. Please report this to the AutoGraph team. Cause: Object conversion is not yet supported. If you are trying to convert code that uses an existing object, try including the creation of that object in the conversion. For example, instead of converting the method of a class, try converting the entire class instead. See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/README.md#using-the-functional-api for more information.

<tf.Tensor: id=1796, shape=(), dtype=float32, numpy=36.0>

 

制御フローと autograph

tf.cond と tf.while_loop が引続き tf.function で動作する一方で、貴方の Python コードの軽量コンパイルに基づくより良い代替を提供します。

autograph ライブラリは tf.function と完全に統合されていて、そしてそれはグラフで動的に実行される Tensor に依拠する条件節とループを書き直します。

# Simple loop

@tf.function
def f(x):
  while tf.reduce_sum(x) > 1:
    tf.print(x)
    x = tf.tanh(x)
  return x

f(tf.random.uniform([10]))
[0.31508553 0.721380234 0.129950762 ... 0.433342814 0.690496325 0.801644325]
[0.305056483 0.617763579 0.129224166 ... 0.408111185 0.598300755 0.664955139]
[0.295933127 0.549569 0.128509626 ... 0.386867732 0.535839319 0.581651628]
[0.28758648 0.500197113 0.127806813 ... 0.368656754 0.489832 0.523864806]
[0.279911876 0.462272167 0.127115428 ... 0.352816224 0.454083085 0.480677366]
[0.272823513 0.431934237 0.126435146 ... 0.338870734 0.425249487 0.446785957]
[0.266250134 0.406936526 0.125765696 ... 0.326468825 0.401343614 0.419253439]
[0.260132134 0.38586846 0.125106782 ... 0.315344274 0.381098 0.396301299]
[0.254419088 0.367793 0.124458104 ... 0.305291146 0.363660634 0.376779735]
[0.249068141 0.352059752 0.123819441 ... 0.296147227 0.348434299 0.359907568]
[0.244042471 0.338200957 0.123190507 ... 0.287782818 0.33498624 0.345132589]
[0.239310354 0.325870335 0.122571081 ... 0.280092806 0.322993964 0.332051814]
[0.234844238 0.31480518 0.121960916 ... 0.272990972 0.312211543 0.320363194]
[0.230620012 0.304802209 0.121359788 ... 0.266405731 0.302447677 0.309835285]
[0.226616591 0.295701116 0.120767467 ... 0.260277182 0.293550938 0.300287217]
[0.22281535 0.287373602 0.120183729 ... 0.254554749 0.285399795 0.291575402]
[0.219199777 0.279715687 0.119608395 ... 0.249195367 0.277895272 0.283584148]
[0.215755224 0.272641927 0.119041242 ... 0.244162112 0.27095598 0.276219]
[0.212468579 0.266081452 0.118482098 ... 0.239423141 0.264514148 0.269402057]
[0.209328115 0.259974867 0.117930762 ... 0.234950811 0.258512884 0.263068348]
[0.206323296 0.254272 0.117387071 ... 0.230720937 0.252904058 0.257163167]
[0.203444585 0.248930186 0.116850831 ... 0.226712331 0.247646555 0.251640201]
[0.20068343 0.243912742 0.116321884 ... 0.222906306 0.242705107 0.246459827]
[0.198032036 0.239188075 0.11580006 ... 0.219286352 0.238049179 0.241587952]
[0.195483267 0.234728679 0.115285195 ... 0.215837747 0.233652264 0.236995056]
[0.193030685 0.230510592 0.11477714 ... 0.212547362 0.22949113 0.232655436]
[0.190668374 0.22651279 0.114275753 ... 0.20940344 0.225545421 0.228546619]
[0.18839094 0.222716689 0.113780893 ... 0.206395403 0.221797109 0.224648744]
[0.186193377 0.21910584 0.113292404 ... 0.203513727 0.218230233 0.220944375]
[0.184071139 0.215665638 0.11281015 ... 0.20074977 0.214830607 0.217417955]
[0.182020009 0.212383032 0.112334013 ... 0.198095769 0.211585537 0.214055702]
[0.180036098 0.209246337 0.111863859 ... 0.195544571 0.208483592 0.210845172]
[0.178115815 0.20624499 0.111399569 ... 0.193089709 0.205514565 0.207775295]
[0.176255822 0.203369528 0.110941015 ... 0.190725267 0.202669233 0.2048361]
[0.174453 0.200611398 0.110488079 ... 0.188445792 0.199939176 0.202018544]
[0.172704518 0.197962806 0.110040657 ... 0.186246336 0.19731687 0.19931443]
[0.171007678 0.195416689 0.109598637 ... 0.184122294 0.19479534 0.196716368]
[0.169359967 0.192966595 0.109161898 ... 0.18206948 0.192368299 0.194217548]
[0.167759076 0.190606609 0.108730339 ... 0.18008396 0.190029979 0.19181183]
[0.166202813 0.188331351 0.10830386 ... 0.178162158 0.187775105 0.189493567]
[0.164689153 0.186135858 0.107882373 ... 0.176300719 0.185598835 0.187257543]
[0.163216189 0.184015557 0.107465766 ... 0.174496546 0.183496684 0.18509905]
[0.161782116 0.181966275 0.107053943 ... 0.172746763 0.181464508 0.183013678]
[0.160385266 0.179984108 0.106646836 ... 0.171048686 0.179498553 0.180997387]
[0.159024045 0.178065449 0.106244348 ... 0.169399813 0.177595273 0.179046452]
[0.157696947 0.176207 0.10584639 ... 0.167797789 0.175751403 0.177157387]
[0.156402573 0.174405679 0.105452858 ... 0.166240469 0.173963904 0.175327]
[0.15513961 0.172658607 0.105063692 ... 0.16472578 0.172229961 0.173552319]
[0.153906822 0.170963109 0.104678795 ... 0.163251832 0.170546949 0.171830565]
[0.152702987 0.169316694 0.104298107 ... 0.161816835 0.168912426 0.170159146]
[0.151527032 0.167717025 0.103921548 ... 0.160419092 0.167324096 0.168535665]
[0.150377855 0.16616194 0.103549033 ... 0.159057021 0.165779829 0.166957855]
[0.149254471 0.164649397 0.103180498 ... 0.157729104 0.164277628 0.165423632]
[0.148155943 0.163177475 0.102815881 ... 0.156433955 0.162815601 0.163931027]
[0.147081345 0.161744431 0.102455102 ... 0.155170262 0.161391988 0.162478164]
[0.146029845 0.160348549 0.102098092 ... 0.153936744 0.160005137 0.161063328]
[0.145000607 0.158988252 0.101744801 ... 0.152732223 0.158653498 0.159684896]
[0.143992841 0.157662064 0.10139516 ... 0.151555583 0.157335594 0.158341318]
[0.143005833 0.156368554 0.101049095 ... 0.15040575 0.156050041 0.157031134]
[0.142038882 0.155106425 0.100706555 ... 0.14928177 0.154795557 0.155752987]
[0.141091302 0.153874427 0.100367464 ... 0.148182631 0.15357089 0.154505596]
[0.140162453 0.152671367 0.100031786 ... 0.147107452 0.152374893 0.153287753]
[0.139251739 0.151496127 0.09969946 ... 0.146055371 0.151206434 0.152098328]
[0.138358578 0.150347665 0.09937042 ... 0.145025581 0.150064498 0.150936186]
[0.137482405 0.149224967 0.0990446135 ... 0.144017294 0.148948088 0.14980033]
[0.136622682 0.148127079 0.098722 ... 0.143029779 0.147856265 0.148689777]
[0.135778904 0.147053108 0.09840253 ... 0.142062351 0.14678815 0.147603586]
[0.134950593 0.146002188 0.0980861336 ... 0.141114324 0.145742878 0.146540895]
[0.134137273 0.144973531 0.0977727696 ... 0.140185028 0.144719645 0.145500854]
[0.133338511 0.143966332 0.0974623859 ... 0.139273882 0.143717706 0.144482672]
[0.132553861 0.14297986 0.0971549526 ... 0.138380289 0.142736316 0.143485621]
[0.131782919 0.142013431 0.0968504101 ... 0.137503698 0.141774788 0.142508969]
[0.131025285 0.141066357 0.0965487137 ... 0.136643589 0.140832454 0.141552]
[0.130280584 0.140138 0.0962498263 ... 0.135799438 0.139908686 0.140614077]
[0.12954846 0.139227778 0.0959536955 ... 0.134970754 0.139002889 0.139694586]
[0.128828555 0.138335079 0.0956602916 ... 0.134157076 0.138114482 0.138792917]
[0.128120542 0.137459353 0.0953695625 ... 0.133357972 0.137242913 0.137908503]
[0.127424076 0.136600062 0.0950814635 ... 0.132572979 0.136387661 0.137040794]
[0.126738861 0.135756716 0.0947959647 ... 0.13180171 0.135548234 0.136189297]
[0.126064584 0.134928823 0.094513014 ... 0.131043762 0.134724125 0.135353491]
[0.125400975 0.13411589 0.094232589 ... 0.130298749 0.133914873 0.134532914]
[0.124747746 0.133317515 0.0939546525 ... 0.129566327 0.133120045 0.133727089]
[0.124104619 0.132533237 0.0936791524 ... 0.128846124 0.132339224 0.132935598]
[0.123471349 0.131762654 0.0934060663 ... 0.128137812 0.131572 0.132158011]
[0.122847699 0.131005362 0.0931353569 ... 0.127441064 0.13081798 0.131393924]
[0.122233413 0.130261 0.092866987 ... 0.12675558 0.130076796 0.130642951]
[0.121628255 0.129529208 0.0926009342 ... 0.126081049 0.129348084 0.129904717]
[0.121032007 0.128809616 0.0923371464 ... 0.125417173 0.128631487 0.129178882]
[0.120444454 0.1281019 0.0920756 ... 0.124763697 0.127926692 0.128465086]
[0.11986538 0.127405748 0.0918162614 ... 0.12412034 0.127233371 0.127763018]
[0.119294584 0.126720816 0.0915591121 ... 0.123486839 0.126551211 0.127072334]
[0.118731871 0.126046836 0.091304116 ... 0.12286295 0.125879928 0.126392752]
[0.118177064 0.125383511 0.0910512358 ... 0.122248426 0.125219211 0.125723958]
[0.117629968 0.12473055 0.0908004493 ... 0.121643052 0.124568805 0.125065684]
[0.117090404 0.124087699 0.0905517191 ... 0.121046595 0.123928435 0.124417655]
[0.116558202 0.123454705 0.0903050229 ... 0.120458826 0.123297848 0.12377961]
[0.116033196 0.1228313 0.0900603384 ... 0.119879536 0.122676805 0.123151287]
[0.115515232 0.12221726 0.0898176283 ... 0.119308546 0.122065067 0.122532435]
[0.115004137 0.121612348 0.0895768702 ... 0.118745647 0.121462405 0.121922843]
[0.114499778 0.121016338 0.0893380344 ... 0.118190646 0.120868579 0.121322267]
[0.114002012 0.120429017 0.0891011059 ... 0.117643356 0.120283395 0.120730489]
[0.113510691 0.119850159 0.088866055 ... 0.117103606 0.119706623 0.120147295]
[0.113025658 0.119279578 0.0886328518 ... 0.116571225 0.119138099 0.119572476]
[0.112546802 0.118717082 0.0884014815 ... 0.116046049 0.118577592 0.119005844]
[0.112073995 0.118162483 0.0881719142 ... 0.115527913 0.118024915 0.118447199]
[0.11160709 0.117615595 0.0879441202 ... 0.115016662 0.117479913 0.117896356]
[0.111145981 0.117076233 0.0877180845 ... 0.114512131 0.116942406 0.117353126]
[0.110690549 0.116544224 0.0874937847 ... 0.114014208 0.116412207 0.116817355]
[0.110240668 0.116019413 0.0872712061 ... 0.113522716 0.115889177 0.116288856]
[0.109796233 0.115501635 0.0870503113 ... 0.113037549 0.11537312 0.115767471]
[0.109357141 0.114990734 0.0868310854 ... 0.112558544 0.114863925 0.115253046]
[0.108923271 0.114486545 0.0866135135 ... 0.112085573 0.11436139 0.114745431]
[0.108494535 0.113988958 0.0863975659 ... 0.111618526 0.113865413 0.114244461]
[0.108070821 0.113497794 0.0861832276 ... 0.111157276 0.11337585 0.11375]
[0.107652038 0.113012932 0.0859704688 ... 0.110701703 0.112892538 0.113261916]
[0.107238084 0.11253424 0.0857592896 ... 0.110251695 0.112415373 0.112780064]
[0.106828876 0.112061583 0.0855496675 ... 0.109807134 0.111944199 0.112304308]
[0.106424324 0.111594833 0.0853415579 ... 0.109367907 0.11147891 0.111834526]
[0.10602434 0.111133881 0.0851349682 ... 0.108933918 0.11101938 0.111370601]
[0.105628826 0.110678583 0.084929876 ... 0.108505055 0.110565498 0.110912412]
[0.105237715 0.110228859 0.0847262591 ... 0.108081214 0.110117123 0.110459827]
[0.104850918 0.109784573 0.0845241 ... 0.107662305 0.109674171 0.11001274]
[0.104468361 0.109345615 0.0843233839 ... 0.107248232 0.109236538 0.109571047]
[0.10408996 0.108911879 0.0841240808 ... 0.106838904 0.108804092 0.109134644]
[0.103715651 0.108483262 0.083926186 ... 0.106434241 0.108376756 0.108703405]
[0.103345349 0.108059682 0.0837296844 ... 0.106034137 0.107954413 0.108277254]
[0.102978989 0.107641019 0.0835345611 ... 0.105638526 0.107536972 0.10785608]
[0.102616489 0.107227206 0.0833407938 ... 0.105247319 0.107124351 0.107439786]
[0.102257803 0.106818117 0.0831483677 ... 0.104860418 0.106716447 0.107028268]
[0.101902857 0.106413677 0.0829572603 ... 0.104477756 0.106313154 0.106621452]
[0.101551577 0.106013812 0.0827674642 ... 0.104099244 0.105914414 0.10621924]
[0.101203911 0.105618417 0.082578972 ... 0.10372483 0.105520129 0.10582155]

<tf.Tensor: id=1841, shape=(10,), dtype=float32, numpy=
array([0.10085979, 0.10522742, 0.08239178, 0.10422876, 0.10471042,
       0.10409579, 0.08315843, 0.10335443, 0.10513023, 0.10542831],
      dtype=float32)>

 

# If you're curious you can inspect the code autograph generates.
# It feels like reading assembly language, though.

def f(x):
  while tf.reduce_sum(x) > 1:
    tf.print(x)
    x = tf.tanh(x)
  return x

print(tf.autograph.to_code(f))
from __future__ import print_function

def tf__f(x):
  try:
    with ag__.function_scope('f'):
      do_return = False
      retval_ = None

      def loop_test(x_1):
        with ag__.function_scope('loop_test'):
          return ag__.gt(ag__.converted_call('reduce_sum', tf, ag__.ConversionOptions(recursive=True, verbose=0, strip_decorators=(ag__.convert, ag__.do_not_convert, ag__.converted_call), force_conversion=False, optional_features=ag__.Feature.ALL, internal_convert_user_code=True), (x_1,), {}), 1)

      def loop_body(x_1):
        with ag__.function_scope('loop_body'):
          with ag__.utils.control_dependency_on_returns(ag__.converted_call('print', tf, ag__.ConversionOptions(recursive=True, verbose=0, strip_decorators=(ag__.convert, ag__.do_not_convert, ag__.converted_call), force_conversion=False, optional_features=ag__.Feature.ALL, internal_convert_user_code=True), (x_1,), {})):
            tf_1, x = ag__.utils.alias_tensors(tf, x_1)
            x = ag__.converted_call('tanh', tf_1, ag__.ConversionOptions(recursive=True, verbose=0, strip_decorators=(ag__.convert, ag__.do_not_convert, ag__.converted_call), force_conversion=False, optional_features=ag__.Feature.ALL, internal_convert_user_code=True), (x,), {})
            return x,
      x, = ag__.while_stmt(loop_test, loop_body, (x,), (tf, x, ag__))
      do_return = True
      retval_ = x
      return retval_
  except:
    ag__.rewrite_graph_construction_error(ag_source_map__)



tf__f.autograph_info__ = {}

autograph を制御するために、それは Python の基本的な制御フロー構成物 (if, for, while, break, etc) に影響を与えるだけであり、そしてそれは述部 (= predicates) が Tensor である場合にそれらを変更するだけであることを忘れないでください。

従って次のサンプルでは最初のループは静的に展開される一方で 2 番目のループは動的に変換されます :

@tf.function
def f(x):
  for i in range(10):  # Static python loop, we'll not convert it
    do_stuff()
  for i in tf.range(10):  # depends on a tensor, we'll convert it

同様に、print と assert が動的に発生することを保証するために、tf.print と tf.assert を使用してください :

@tf.function
def f(x):
  for i in tf.range(10):
    tf.print(i)
    tf.Assert(i < 10, ["a"])
    x += x
  return x

f(10)
0
1
2
3
4
5
6
7
8
9

<tf.Tensor: id=1904, shape=(), dtype=int32, numpy=10240>

最期に、autograph は任意の Python コードを TensorFlow グラフにコンパイルすることはできません。特に、動的に使用するデータ構造は依然として TensorFlow データ構造である必要があります。

従って、例えば、ループでデータを蓄積する最善の方法は tf.TensorArray を使用することです :

@tf.function
def f(x):
  ta = tf.TensorArray(tf.float32, size=10)
  for i in tf.range(10):
    x += x
    ta = ta.write(i, x)
  return ta.stack()

f(10.0)
<tf.Tensor: id=1973, shape=(10,), dtype=float32, numpy=
array([   20.,    40.,    80.,   160.,   320.,   640.,  1280.,  2560.,
        5120., 10240.], dtype=float32)>
 

以上






AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com