ホーム » Eager execution

Eager execution」カテゴリーアーカイブ

TensorFlow 2.4 : ガイド : 基本 – Eager 実行

TensorFlow 2.4 : ガイド : 基本 – Eager 実行 (翻訳/解説)

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

* 本ページは、TensorFlow org サイトの Guide – TensorFlow Basics の以下のページを翻訳した上で
適宜、補足説明したものです:

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

 

無料セミナー実施中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。
クラスキャットは人工知能・テレワークに関する各種サービスを提供しております :

人工知能研究開発支援 人工知能研修サービス テレワーク & オンライン授業を支援
PoC(概念実証)を失敗させないための支援 (本支援はセミナーに参加しアンケートに回答した方を対象としています。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

 

ガイド : 基本 – Eager 実行

TensorFlow の eager 実行は命令型プログラミング環境で、これはグラフを構築することなく演算を直ちに評価します : 演算は後で実行する計算グラフを構築する代わりに具体的な値を返します。これは TensorFlow を始めてモデルをデバッグすることを容易にして、そしてまたそれはボイラープレート (なコード) を削減します。このガイドに沿ってフォローするためには、対話的 python インタープリタで下のコード・サンプルを実行してください。

Eager 実行は研究と実験のための柔軟な機械学習プラットフォームで、以下を提供します :

  • 直感的なインターフェイス — 貴方のコードを自然に構造化して Python データ構造を使用します。小さなモデルと小さなデータ上で迅速に反復します。
  • より容易なデバッギング — 実行中のモデルを調査して変更をテストするために ops を直接的に呼び出します。即時のエラー報告のために標準的な Python デバッギング・ツールを使用します。
  • 自然な制御フロー — グラフ制御フローの代わりに Python 制御フローを使用し、動的モデルの仕様を単純化します。

Eager 実行は殆どの TensorFlow 演算と GPU アクセラレーションをサポートします。

Note: 幾つかのモデルでは eager 実行が有効であると増大したオーバーヘッドを経験するかもしれません。パフォーマンス改良は進行中ですが、問題を見つける場合にはバグをファイルしてベンチマークを共有してください。

 

セットアップと基本的な使用方法

import os

import tensorflow as tf

import cProfile

TensorFlow 2.0 では、eager 実行はデフォルトで有効にされています。

tf.executing_eagerly()
True

今では貴方は TensorFlow 演算を実行できて結果は直ちに返ります :

x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m))
hello, [[4.]]

eager 実行を有効にすると TensorFlow 演算がどのように動作するかを変更します — 今ではそれらは即時に評価してそれらの値を Python に返します。tf.Tensor オブジェクトは計算グラフのノードへのシンボリックなハンドルの代わりに具体的な値を参照します。セッション内で構築して後で実行する計算グラフはありませんので、print() やデバッガーを使用して結果を調査することは容易です。tensor 値の評価、プリントそして確認は勾配を計算するためのフローを壊しません。

Eager 実行は NumPy と共に素晴らしく動作します。NumPy 演算は tf.Tensor 引数を受け取ります。TensorFlow tf.math 演算は Python オブジェクトと NumPy 配列を tf.Tensor オブジェクトに変換します。tf.Tensor.numpy メソッドはオブジェクトの値を NumPy ndarray として返します。

a = tf.constant([[1, 2],
                 [3, 4]])
print(a)
tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32)
# Broadcasting support
b = tf.add(a, 1)
print(b)
tf.Tensor(
[[2 3]
 [4 5]], shape=(2, 2), dtype=int32)
# Operator overloading is supported
print(a * b)
tf.Tensor(
[[ 2  6]
 [12 20]], shape=(2, 2), dtype=int32)
# Use NumPy values
import numpy as np

c = np.multiply(a, b)
print(c)
[[ 2  6]
 [12 20]]
# Obtain numpy value from a tensor:
print(a.numpy())
# => [[1 2]
#     [3 4]]
[[1 2]
 [3 4]]

 

動的制御フロー

eager 実行の主要なメリットはモデルが実行されている間にホスト言語の総ての機能が利用可能であることです。そのため例えば、fizzbuzz を書くことが容易です :

def fizzbuzz(max_num):
  counter = tf.constant(0)
  max_num = tf.convert_to_tensor(max_num)
  for num in range(1, max_num.numpy()+1):
    num = tf.constant(num)
    if int(num % 3) == 0 and int(num % 5) == 0:
      print('FizzBuzz')
    elif int(num % 3) == 0:
      print('Fizz')
    elif int(num % 5) == 0:
      print('Buzz')
    else:
      print(num.numpy())
    counter += 1
fizzbuzz(15)
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

これは tensor 値に依拠する条件節を持ちこれらの値を実行時にプリントします。

 

Eager 訓練

勾配を計算する

自動微分 はニューラルネットワークを訓練するための バックプロパゲーション のような機械学習アルゴリズムを実装するために有用です。eager 実行の間は、後で勾配を計算するための演算を追跡するために tf.GradientTape を利用します。

eager で訓練する、そして/あるいは勾配を計算するために tf.GradientTape を使用できます。それは複雑な訓練ループのために特に有用です。

異なる演算が各呼び出しの間に発生しますので、総ての forward パス演算は「テープ」に記録されます。勾配を計算するために、テープを逆向きに再生してから破棄します。特定の tf.GradientTape は 1 つの勾配を計算するだけです ; 続く呼び出しはランタイム・エラーを投げます。

w = tf.Variable([[1.0]])
with tf.GradientTape() as tape:
  loss = w * w

grad = tape.gradient(loss, w)
print(grad)  # => tf.Tensor([[ 2.]], shape=(1, 1), dtype=float32)
tf.Tensor([[2.]], shape=(1, 1), dtype=float32)

 

モデルを訓練する

次のサンプルは標準的な MNIST 手書き数字を分類する多層モデルを作成します。eager 実行環境で訓練可能なグラフを構築する optimizer と層 API を実演します。

# Fetch and format the mnist data
(mnist_images, mnist_labels), _ = tf.keras.datasets.mnist.load_data()

dataset = tf.data.Dataset.from_tensor_slices(
  (tf.cast(mnist_images[...,tf.newaxis]/255, tf.float32),
   tf.cast(mnist_labels,tf.int64)))
dataset = dataset.shuffle(1000).batch(32)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
# Build the model
mnist_model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(16,[3,3], activation='relu',
                         input_shape=(None, None, 1)),
  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(10)
])

訓練なしでさえも、eager 実行ではモデルを呼び出して出力を調べてください :

for images,labels in dataset.take(1):
  print("Logits: ", mnist_model(images[0:1]).numpy())
Logits:  [[ 0.02873135  0.01367911 -0.00195673  0.01703224  0.0010254   0.02827391
   0.02236997  0.038897    0.03638642  0.06484333]]

keras モデルが (fit メソッドを使用した) 組み込み訓練ループを持つ一方、時には更なるカスタマイズが必要でしょう。ここに、eager で実装された訓練ループのサンプルがあります :

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

loss_history = []

Note: 条件が成り立つかを確認するために tf.debugging の assert 関数を使用してください。これは eager とグラフ実行で動作します。

def train_step(images, labels):
  with tf.GradientTape() as tape:
    logits = mnist_model(images, training=True)

    # Add asserts to check the shape of the output.
    tf.debugging.assert_equal(logits.shape, (32, 10))

    loss_value = loss_object(labels, logits)

  loss_history.append(loss_value.numpy().mean())
  grads = tape.gradient(loss_value, mnist_model.trainable_variables)
  optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables))
def train(epochs):
  for epoch in range(epochs):
    for (batch, (images, labels)) in enumerate(dataset):
      train_step(images, labels)
    print ('Epoch {} finished'.format(epoch))
train(epochs = 3)
Epoch 0 finished
Epoch 1 finished
Epoch 2 finished
import matplotlib.pyplot as plt

plt.plot(loss_history)
plt.xlabel('Batch #')
plt.ylabel('Loss [entropy]')
Text(0, 0.5, 'Loss [entropy]')

 

Variable と optimizer

tf.Variable オブジェクトは自動微分をより容易にするために訓練の間にアクセスされるミュータブルな tf.Tensor ライクな値をストアします。

変数のコレクションは、それらの上で動作するメソッドとともに層やモデルにカプセル化できます。詳細は カスタム Keras 層とモデル を見てください。層とモデルの主要な違いはモデルは Model.fit, Model.evaluate と Model.save のようなメソッドを追加することです。

例えば、上の自動微分サンプルは次のように書き換えることができます :

class Linear(tf.keras.Model):
  def __init__(self):
    super(Linear, self).__init__()
    self.W = tf.Variable(5., name='weight')
    self.B = tf.Variable(10., name='bias')
  def call(self, inputs):
    return inputs * self.W + self.B
# A toy dataset of points around 3 * x + 2
NUM_EXAMPLES = 2000
training_inputs = tf.random.normal([NUM_EXAMPLES])
noise = tf.random.normal([NUM_EXAMPLES])
training_outputs = training_inputs * 3 + 2 + noise

# The loss function to be optimized
def loss(model, inputs, targets):
  error = model(inputs) - targets
  return tf.reduce_mean(tf.square(error))

def grad(model, inputs, targets):
  with tf.GradientTape() as tape:
    loss_value = loss(model, inputs, targets)
  return tape.gradient(loss_value, [model.W, model.B])

Next:

  1. モデルを作成する。
  2. モデルパラメータに関する損失関数の導関数。
  3. 導関数に基づいて変数を更新するためのストラテジー。
model = Linear()
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)

print("Initial loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))

steps = 300
for i in range(steps):
  grads = grad(model, training_inputs, training_outputs)
  optimizer.apply_gradients(zip(grads, [model.W, model.B]))
  if i % 20 == 0:
    print("Loss at step {:03d}: {:.3f}".format(i, loss(model, training_inputs, training_outputs)))
Initial loss: 69.507
Loss at step 000: 66.748
Loss at step 020: 29.894
Loss at step 040: 13.695
Loss at step 060: 6.571
Loss at step 080: 3.437
Loss at step 100: 2.058
Loss at step 120: 1.450
Loss at step 140: 1.183
Loss at step 160: 1.065
Loss at step 180: 1.013
Loss at step 200: 0.990
Loss at step 220: 0.979
Loss at step 240: 0.975
Loss at step 260: 0.973
Loss at step 280: 0.972
print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
Final loss: 0.972
print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
W = 2.998509407043457, B = 2.0533735752105713

Not: 変数は python オブジェクトへの最後の参照が除去されるまで存続し、それから変数が削除されます。

 

オブジェクト・ベースのセービング

tf.keras.Model はチェックポイントを容易に作成することを可能にする便利な save_weights メソッドを含みます :

model.save_weights('weights')
status = model.load_weights('weights')

tf.train.Checkpoint を使用してこのプロセスを完全に制御できます。

このセクションは 訓練チェックポイントへのガイド の短縮バージョンです。

x = tf.Variable(10.)
checkpoint = tf.train.Checkpoint(x=x)
x.assign(2.)   # Assign a new value to the variables and save.
checkpoint_path = './ckpt/'
checkpoint.save('./ckpt/')
'./ckpt/-1'
x.assign(11.)  # Change the variable after saving.

# Restore values from the checkpoint
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))

print(x)  # => 2.0
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=2.0>

モデルをセーブしてロードするためには、隠れ変数を必要とすることなく、tf.train.Checkpoint はオブジェクトの内部状態をストアします。モデル、optimizer そしてグローバルステップの状態を記録するためには、それらを tf.train.Checkpoint に渡します :

model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(10)
])
optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
checkpoint_dir = 'path/to/model_dir'
if not os.path.exists(checkpoint_dir):
  os.makedirs(checkpoint_dir)
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
root = tf.train.Checkpoint(optimizer=optimizer,
                           model=model)

root.save(checkpoint_prefix)
root.restore(tf.train.latest_checkpoint(checkpoint_dir))
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7fe7eb90b080>

Note: 多くの訓練ループで、変数は tf.train.Checkpoint.restore が呼び出された後で作成されます。これらの変数はそれらが作成されるとすぐにリストアされて、チェックポイントが完全にロードされたかを確かなものにするために assertion が利用可能です。詳細は 訓練チェックポイントへのガイド を見てください。

 

オブジェクト指向メトリクス

tf.keras.metrics はオブジェクトとしてストアされます。新しいデータを callable に渡すことでメトリクスを更新して、そして tf.keras.metrics.result メソッドを使用して結果を取得します、例えば :

m = tf.keras.metrics.Mean("loss")
m(0)
m(5)
m.result()  # => 2.5
m([8, 9])
m.result()  # => 5.5
<tf.Tensor: shape=(), dtype=float32, numpy=5.5>

 

要約と TensorBoard

TensorBoard はモデル訓練プロセスを理解し、デバッグして最適化するための可視化ツールです。それはプログラムを実行する間に書かれる要約イベントを利用します。

eager 実行で変数の要約を記録するために tf.summary を使用できます。例えば、100 訓練ステップ毎に一度損失の要約を記録するには :

logdir = "./tb/"
writer = tf.summary.create_file_writer(logdir)

steps = 1000
with writer.as_default():  # or call writer.set_as_default() before the loop.
  for i in range(steps):
    step = i + 1
    # Calculate loss with your real train function.
    loss = 1 - 0.001 * step
    if step % 100 == 0:
      tf.summary.scalar('loss', loss, step=step)
ls tb/
events.out.tfevents.1608312504.eb7bff84630e.75.636510.v2

 

上級者のための自動微分トピック

動的モデル

tf.GradientTape はまた動的モデルでも利用できます。バックトラックする直線探索 (= line search) アルゴリズムのためのこのサンプルは、複雑な制御フローにもかかわらず、(勾配があり微分可能であることを除けば) 普通の NumPy コードのように見えます :

def line_search_step(fn, init_x, rate=1.0):
  with tf.GradientTape() as tape:
    # Variables are automatically tracked.
    # But to calculate a gradient from a tensor, you must `watch` it.
    tape.watch(init_x)
    value = fn(init_x)
  grad = tape.gradient(value, init_x)
  grad_norm = tf.reduce_sum(grad * grad)
  init_value = value
  while value > init_value - rate * grad_norm:
    x = init_x - rate * grad
    value = fn(x)
    rate /= 2.0
  return x, value

 

カスタム勾配

カスタム勾配は勾配を override するための簡単な方法です。forward 関数内で、入力、出力、または中間結果に関する勾配を定義します。例えば、backward パスで勾配のノルムをクリップするための容易な方法がここにあります :

@tf.custom_gradient
def clip_gradient_by_norm(x, norm):
  y = tf.identity(x)
  def grad_fn(dresult):
    return [tf.clip_by_norm(dresult, norm), None]
  return y, grad_fn

カスタム勾配は演算のシークエンスのための数値的安定な勾配を提供するために一般に使用されます :

def log1pexp(x):
  return tf.math.log(1 + tf.exp(x))

def grad_log1pexp(x):
  with tf.GradientTape() as tape:
    tape.watch(x)
    value = log1pexp(x)
  return tape.gradient(value, x)
# The gradient computation works fine at x = 0.
grad_log1pexp(tf.constant(0.)).numpy()
0.5
# However, x = 100 fails because of numerical instability.
grad_log1pexp(tf.constant(100.)).numpy()
nan

ここで、log1pexp 関数はカスタム勾配で解析的に単純化できます。下の実装は forward パスの間に計算された tf.exp(x) のための値を再利用しています — 冗長な計算を除去することでそれをより効率的にしています :

@tf.custom_gradient
def log1pexp(x):
  e = tf.exp(x)
  def grad(dy):
    return dy * (1 - 1 / (1 + e))
  return tf.math.log(1 + e), grad

def grad_log1pexp(x):
  with tf.GradientTape() as tape:
    tape.watch(x)
    value = log1pexp(x)
  return tape.gradient(value, x)
# As before, the gradient computation works fine at x = 0.
grad_log1pexp(tf.constant(0.)).numpy()
0.5
# And the gradient computation also works at x = 100.
grad_log1pexp(tf.constant(100.)).numpy()
1.0

 

パフォーマンス

eager 実行の間は計算は GPU へと自動的にオフロードされます。計算がどこで実行されるかについて制御を望む場合にはそれを tf.device(‘/gpu:0’) ブロック (または CPU の同値のもの) で囲むことができます :

import time

def measure(x, steps):
  # TensorFlow initializes a GPU the first time it's used, exclude from timing.
  tf.matmul(x, x)
  start = time.time()
  for i in range(steps):
    x = tf.matmul(x, x)
  # tf.matmul can return before completing the matrix multiplication
  # (e.g., can return after enqueing the operation on a CUDA stream).
  # The x.numpy() call below will ensure that all enqueued operations
  # have completed (and will also copy the result to host memory,
  # so we're including a little more than just the matmul operation
  # time).
  _ = x.numpy()
  end = time.time()
  return end - start

shape = (1000, 1000)
steps = 200
print("Time to multiply a {} matrix by itself {} times:".format(shape, steps))

# Run on CPU:
with tf.device("/cpu:0"):
  print("CPU: {} secs".format(measure(tf.random.normal(shape), steps)))

# Run on GPU, if available:
if tf.config.experimental.list_physical_devices("GPU"):
  with tf.device("/gpu:0"):
    print("GPU: {} secs".format(measure(tf.random.normal(shape), steps)))
else:
  print("GPU: not found")
Time to multiply a (1000, 1000) matrix by itself 200 times:
CPU: 5.130153179168701 secs
GPU: 0.16303229331970215 secs

tf.Tensor オブジェクトはその演算を実行するために異なるデバイスへとコピーできます :

if tf.config.experimental.list_physical_devices("GPU"):
  x = tf.random.normal([10, 10])

  x_gpu0 = x.gpu()
  x_cpu = x.cpu()

  _ = tf.matmul(x_cpu, x_cpu)    # Runs on CPU
  _ = tf.matmul(x_gpu0, x_gpu0)  # Runs on GPU:0
WARNING:tensorflow:From :4: _EagerTensorBase.gpu (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.identity instead.
WARNING:tensorflow:From :5: _EagerTensorBase.cpu (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.identity instead.

 

ベンチマーク

GPU 上の ResNet50 訓練のような計算が重いモデルについては、eager 実行パフォーマンスは tf.function 実行に匹敵します。しかしこの隔たりはより少ない計算を持つモデルのためにはより大きくなり、多くの小さい演算を持つモデルのためにホットコード・パスを最適化するために行われなければならない作業があります。

 

Work with functions

eager 実行が開発とデバッグをより対話的にする一方で、TensorFlow 1.x スタイルのグラフ実行は分散訓練、パフォーマンス最適化、そしてプロダクション配備のために優位点を持ちます。この隔たりを埋めるために、TensorFlow 2.0 は tf.function API を通した関数を導入します。より多くの情報は、tf.function ガイドを見てください。

 

以上



TensorFlow 2.0 : 上級 Tutorials : カスタマイズ :- tf.function でパフォーマンスの改善

TensorFlow 2.0 : 上級 Tutorials : カスタマイズ :- tf.function でパフォーマンスの改善 (翻訳/解説)

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

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

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

カスタマイズ :- tf.function でパフォーマンスの改善

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

最高点のパフォーマンスを得て貴方のモデルをどこでも配備可能にするために、貴方のプログラムからグラフを作成するために tf.function を使用します。AutoGraph のおかげで、Python コードの驚くべき総量が tf.function で単に動作しますが、依然として注意すべき落とし穴があります。

主な重要な点と推奨は :

  • オブジェクト mutation やリスト append のような Python 副作用に依拠しないでください。
  • tf.function は NumPy ops や Python プリミティブよりも TensorFlow ops で最善に動作します。
  • 疑わしいときには、”for x in y” イディオムを使用してください。
from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf
import contextlib

# Some helper code to demonstrate the kinds of errors you might encounter.
@contextlib.contextmanager
def assert_raises(error_class):
  try:
    yield
  except error_class as e:
    print('Caught expected exception \n  {}: {}'.format(error_class, e))
  except Exception as e:
    print('Got unexpected exception \n  {}: {}'.format(type(e), e))
  else:
    raise Exception('Expected {} to be raised but no error was raised!'.format(
        error_class))

貴方が定義する tf.function はちょうどコア TensorFlow 演算のようなものです : それを eagerly (逐次実行的) に実行できます; それをグラフで使用できます; それは勾配を持ちます; 等々。

# A function is like an 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=14, shape=(2, 2), dtype=float32, numpy=
array([[2., 2.],
       [2., 2.]], dtype=float32)>
# Functions have gradients

@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=38, shape=(), dtype=float32, numpy=1.0>
# You can use functions inside functions

@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=64, shape=(3, 2), dtype=float32, numpy=
array([[3., 3.],
       [3., 3.],
       [3., 3.]], dtype=float32)>

 

Tracing とポリモーフィズム

Python の動的型付けは関数を様々な引数型で呼び出すことができて、そして Python は各シナリオで異なる何かを行なうことを意味します。

その一方で、TensorFlow グラフは静的な dtype と shape 次元を要求します。tf.function は正しいグラフを生成するために必要なとき関数を辿り直す (= retrace) ことによりこの隔たりの橋渡しをします。tf.function の利用方法の微妙な点の殆どはこの再トレースの動作から生じています。

何が起きるかを見るために異なる型の引数で関数を呼び出すことができます。

# Functions are polymorphic

@tf.function
def double(a):
  print("Tracing with", a)
  return a + a

print(double(tf.constant(1)))
print()
print(double(tf.constant(1.1)))
print()
print(double(tf.constant("a")))
print()
Tracing with Tensor("a:0", shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)

Tracing with Tensor("a:0", shape=(), dtype=float32)
tf.Tensor(2.2, shape=(), dtype=float32)

Tracing with Tensor("a:0", shape=(), dtype=string)
tf.Tensor(b'aa', shape=(), dtype=string)

トレース動作を制御するには、次のテクニックを使用します :

  • 新しい tf.function を作成します。別々の tf.function オブジェクトはトレースを共有しないことが保証されます。
  • 特定のトレース を得るために get_concrete_function メソッドを使用します。
  • グラフ呼び出し毎に一度だけトレースするために tf.function を呼び出すとき input_signature を指定します。
print("Obtaining concrete trace")
double_strings = double.get_concrete_function(tf.TensorSpec(shape=None, dtype=tf.string))
print("Executing traced function")
print(double_strings(tf.constant("a")))
print(double_strings(a=tf.constant("b")))
print("Using a concrete trace with incompatible types will throw an error")
with assert_raises(tf.errors.InvalidArgumentError):
  double_strings(tf.constant(1))
Obtaining concrete trace
Tracing with Tensor("a:0", dtype=string)
Executing traced function
tf.Tensor(b'aa', shape=(), dtype=string)
tf.Tensor(b'bb', shape=(), dtype=string)
Using a concrete trace with incompatible types will throw an error
Caught expected exception 
  <class 'tensorflow.python.framework.errors_impl.InvalidArgumentError'>: cannot compute __inference_double_91 as input #0(zero-based) was expected to be a string tensor but is a int32 tensor [Op:__inference_double_91]
@tf.function(input_signature=(tf.TensorSpec(shape=[None], dtype=tf.int32),))
def next_collatz(x):
  print("Tracing with", x)
  return tf.where(x % 2 == 0, x // 2, 3 * x + 1)

print(next_collatz(tf.constant([1, 2])))
# We specified a 1-D tensor in the input signature, so this should fail.
with assert_raises(ValueError):
  next_collatz(tf.constant([[1, 2], [3, 4]]))
Tracing with Tensor("x:0", shape=(None,), dtype=int32)
tf.Tensor([4 1], shape=(2,), dtype=int32)
Caught expected exception 
  <class 'ValueError'>: Python inputs incompatible with input_signature:
  inputs: (
    tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32))
  input_signature: (
    TensorSpec(shape=(None,), dtype=tf.int32, name=None))

 

いつ再トレースするか?

多態的な (= polymorphic) tf.function はトレースにより生成された具体的な関数のキャッシュを保持します。キャッシュ・キーは効果的に関数 argsと kwargs から生成されたキーのタプルです。tf.Tensor 引数のために生成されたキーはその shape と型です。Python プリミティブのために生成されたキーはその値です。総ての他の Python 型については、キーはオブジェクト id() に基づきその結果メソッドはクラスの各インスタンスのために独立的にトレースされます。将来的には、TensorFlow は Python オブジェクトのためにより洗練されたキャッシングを追加するかもしれません、それは tensor に安全に変換できます。

 

Python あるいは Tensor args?

しばしば、Python 引数はハイパーパラメータとグラフ構築を制御するために使用されます – 例えば、num_layers=10 や training=True や nonlinearity=’relu’ です。そのため Python 引数が変わる場合、グラフを再トレースしなければならないことは意味があります。

けれども、Python 引数がグラフ構築を制御するために使用されていないこともあり得ます。これらのケースでは、Python 値の変更は必要のない再トレースを引き起こす可能性があります。例えばこの訓練ループを取れば、AutoGraph はそれを動的に展開するでしょう。複数のトレースにもかかわらず、生成されたグラフは実際には同一です、従ってこれは少し非効率的です。

def train_one_step():
  pass

@tf.function
def train(num_steps):
  print("Tracing with num_steps = {}".format(num_steps))
  for _ in tf.range(num_steps):
    train_one_step()

train(num_steps=10)
train(num_steps=20)
Tracing with num_steps = 10
Tracing with num_steps = 20

ここでの最も単純な回避方法は、それら (引数) が生成されたグラフの shape に影響しないのであれば、引数を Tensor にキャストすることです。

train(num_steps=tf.constant(10))
train(num_steps=tf.constant(20))
Tracing with num_steps = Tensor("num_steps:0", shape=(), dtype=int32)

 

tf.function の副作用

一般に、(プリントやオブジェクトの mutating のような) Python 副作用はトレースの間だけ発生します。それでは tf.function からどのように副作用を確実に引き起こせるのでしょう?

一般的な経験則はトレースをデバッグするために Python 副作用を使用するだけです。そうでないなら、 tf.Variable.assign, tf.printtf.summary のような TensorFlow ops は各呼び出しに伴う TensorFlow ランタイムにより貴方のコードがトレースされて実行されることを確かなものにする最善の方法です。一般に functional スタイルの使用は最善の結果を生じるでしょう。

@tf.function
def f(x):
  print("Traced with", x)
  tf.print("Executed with", x)

f(1)
f(1)
f(2)
Traced with 1
Executed with 1
Executed with 1
Traced with 2
Executed with 2

tf.function の各起動中に Python コードを実行することを望むのであれば、tf.py_function が脱出ハッチ (= exit hatch) です。tf.py_function の欠点はそれは可搬ではなく特に非効率的で、分散セットアップ (マルチ GPU, TPU) では上手く動作しません。また、tf.py_function はグラフに配線 (= wire) されなければならず、それは総ての入力/出力を tensor にキャストします。

external_list = []

def side_effect(x):
  print('Python side effect')
  external_list.append(x)

@tf.function
def f(x):
  tf.py_function(side_effect, inp=[x], Tout=[])

f(1)
f(1)
f(1)
assert len(external_list) == 3
# .numpy() call required because py_function casts 1 to tf.constant(1)
assert external_list[0].numpy() == 1
Python side effect
Python side effect
Python side effect

 

Python 状態に注意する

generator と iterator のような、多くの Python 特徴は状態を追跡するために Python ランタイムに依拠します。一般に、これらの構成物が Eager モードで期待どおりに動作する一方で、多くの予期せぬことがトレース動作により tf.function 内で起きるかもしれません。

一つのサンプルを与えるために、iterator 状態を前に進めることは Python 副作用で従ってトレースの間だけに発生します。

external_var = tf.Variable(0)
@tf.function
def buggy_consume_next(iterator):
  external_var.assign_add(next(iterator))
  tf.print("Value of external_var:", external_var)

iterator = iter([0, 1, 2, 3])
buggy_consume_next(iterator)
# This reuses the first value from the iterator, rather than consuming the next value.
buggy_consume_next(iterator)
buggy_consume_next(iterator)
Value of external_var: 0
Value of external_var: 0
Value of external_var: 0

iterator が完全に tf.function 内で生成されて消費される場合には、それは正しく動作します。けれども、iterator 全体がトレースされると多分、これは巨大グラフ (= giant graph) に繋がるかもしれません。これは貴方が望むものかもしれません。しかし貴方が Python リストで表わされる巨大な in-memory データセット上で訓練している場合、これは非常に巨大なグラフを生成するかもしれず、そして tf.function はスピードアップを生み出さないでしょう。

Python データに渡り iterate することを望む場合、最も安全な方法はそれを tf.data.Dataset でラップして “for x in y” イディオムを使用することです。AutoGraph は y が tensor か tf.data.Dataset のときループのための安全に変換するための特別なサポートを持ちます。

def measure_graph_size(f, *args):
  g = f.get_concrete_function(*args).graph
  print("{}({}) contains {} nodes in its graph".format(
      f.__name__, ', '.join(map(str, args)), len(g.as_graph_def().node)))

@tf.function
def train(dataset):
  loss = tf.constant(0)
  for x, y in dataset:
    loss += tf.abs(y - x) # Some dummy computation.
  return loss

small_data = [(1, 1)] * 2
big_data = [(1, 1)] * 10
measure_graph_size(train, small_data)
measure_graph_size(train, big_data)

measure_graph_size(train, tf.data.Dataset.from_generator(
    lambda: small_data, (tf.int32, tf.int32)))
measure_graph_size(train, tf.data.Dataset.from_generator(
    lambda: big_data, (tf.int32, tf.int32)))
train([(1, 1), (1, 1)]) contains 8 nodes in its graph
train([(1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1)]) contains 32 nodes in its graph
train(, ), types: (tf.int32, tf.int32)>) contains 5 nodes in its graph
train(, ), types: (tf.int32, tf.int32)>) contains 5 nodes in its graph

Python/Numpy データを Dataset にラッピングするとき、tf.data.Dataset.from_generator versus tf.data.Dataset.from_tensors に留意してください。前者はデータを Python 内で保持して tf.py_function を通してそれを取得します、これはパフォーマンスと密接な関係を持つかもしれず、その一方で後者はデータのコピーをグラフの一つの巨大な tf.constant() ノードとして束ねます、これはメモリとの密接な関係を持つかもしれません。

TFRecordDataset/CsvDataset/etc. を通してファイルからデータを読むのがデータを消費する最も効果的な方法です、何故ならばそれで TensorFlow 自身がデータの非同期ロードと先読みを管理できるからです、Python を巻き込まなければならないことなしに。

 

自動制御依存性

functions のプログラミングモデルとしての非常に魅力的な特質は、一般的なデータフロー・グラフに渡り、functions がランタイムに何がコードの意図された動作であったかについてより多くの情報を与えられることです。

例えば、同じ変数への複数の読み書きを持つコードを書くとき、データフロー・グラフは演算の元々意図された順序を自然にエンコードしないかもしれません。tf.function では、元の Python コードのステートメントの実行順序を参照して実行順序の曖昧さを解決します。このようにして、tf.function のステートフルな演算の順序は Eager モードのセマンティクスを複製します。

これは手動の制御依存性を追加する必要がないことを意味します ; 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=418, shape=(), dtype=float32, numpy=10.0>

 

Variables

tf.function で変数作成と利用を非常に容易にするためにコードの意図された実行順序を活用するのと同じアイデアを利用できます。けれども一つの非常に重要な警告があります、それは variable では eager モードと graph モードで異なる動作をするコードを書くことが可能であることです。

特に、これは各呼び出しで新しい Variable を作成するときに発生します。トレース・セマンティクスにより、tf.function は各呼び出しで同じ変数を再利用しますが、eager モードは各呼び出しで新しい変数を作成します。この間違いから守るために、 tf.function は危険な変数作成動作を検出する場合にはエラーを上げます。

@tf.function
def f(x):
  v = tf.Variable(1.0)
  v.assign_add(x)
  return v

with assert_raises(ValueError):
  f(1.0)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/resource_variable_ops.py:1781: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
Caught expected exception 
  <class 'ValueError'>: in converted code:

    <ipython-input-17-73e410646579>:3 f  *
        v = tf.Variable(1.0)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/variables.py:260 __call__
        return cls._variable_v2_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/variables.py:254 _variable_v2_call
        shape=shape)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/variables.py:65 getter
        return captured_getter(captured_previous, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/eager/def_function.py:413 invalid_creator_scope
        "tf.function-decorated function tried to create "

    ValueError: tf.function-decorated function tried to create variables on non-first call.
# Non-ambiguous code is ok though

v = tf.Variable(1.0)

@tf.function
def f(x):
  return v.assign_add(x)

print(f(1.0))  # 2.0
print(f(2.0))  # 4.0
tf.Tensor(2.0, shape=(), dtype=float32)
tf.Tensor(4.0, shape=(), dtype=float32)
# 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)

print(g(1.0))  # 2.0
print(g(2.0))  # 4.0
tf.Tensor(2.0, shape=(), dtype=float32)
tf.Tensor(4.0, shape=(), dtype=float32)
# 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]

print(fn(tf.constant(1.0)))
print(fn(tf.constant(3.0)))
tf.Tensor(12.0, shape=(), dtype=float32)
tf.Tensor(36.0, shape=(), dtype=float32)

 

AutoGraph を使用する

autograph ライブラリは tf.function と完全に統合され、そしてそれはグラフで動的に実行するための Tensor に依拠する条件節とループを書き換えます。

tf.condtf.while_loop は tf.function とともに動作し続けますが、制御フローを持つコードはしばしば命令型スタイルで書かれるとき書いて理解することが容易です。

# 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([5]))
[0.991675735 0.770152 0.265438318 0.926479578 0.270098567]
[0.758075953 0.647017837 0.259375095 0.728948355 0.263716549]
[0.639942288 0.569659 0.253710955 0.622421563 0.257768452]
[0.564860284 0.515108824 0.248403832 0.552811861 0.2522071]
[0.511574626 0.473916173 0.24341765 0.50262475 0.246992245]
[0.471171141 0.441358089 0.238721251 0.46417886 0.242089257]
[0.439145088 0.41476953 0.23428756 0.433484 0.237468168]
[0.412935585 0.392514914 0.230092898 0.408228815 0.233102903]
[0.390962422 0.373526275 0.226116508 0.386967748 0.228970662]
[0.372189641 0.357072234 0.222340047 0.368743211 0.225051373]
[0.355905473 0.342632562 0.218747273 0.352891922 0.22132732]
[0.341602385 0.32982561 0.215323746 0.33893773 0.217782795]
[0.328907162 0.318364054 0.212056547 0.326528698 0.214403793]
[0.31753847 0.30802694 0.208934113 0.315398216 0.211177796]
[0.307279497 0.298641056 0.205946043 0.305340052 0.208093569]
[0.297960132 0.290068477 0.203082964 0.296191841 0.205141023]
[0.289444745 0.282197833 0.200336367 0.287823766 0.202311009]
[0.281623662 0.274938 0.197698563 0.280130565 0.199595287]
[0.274407148 0.26821363 0.19516255 0.2730259 0.196986347]
[0.267720908 0.261961818 0.192721918 0.266438186 0.194477364]
[0.261502862 0.256129563 0.190370843 0.260307461 0.192062095]
[0.255700678 0.250671834 0.188103959 0.254583091 0.189734846]
[0.25026986 0.245550096 0.185916349 0.249221966 0.187490389]
[0.245172322 0.24073115 0.183803499 0.244187161 0.185323924]
[0.240375236 0.236186236 0.181761235 0.239446774 0.183231026]
[0.23585014 0.231890276 0.179785714 0.234973133 0.181207627]
[0.231572226 0.22782129 0.177873373 0.230742082 0.179249957]
[0.227519721 0.223959938 0.176020905 0.226732403 0.177354515]
[0.223673478 0.220289111 0.174225256 0.22292541 0.175518081]
[0.220016524 0.216793597 0.172483563 0.219304562 0.173737645]

<tf.Tensor: id=675, shape=(5,), dtype=float32, numpy=
array([0.21653381, 0.21345986, 0.17079319, 0.21585512, 0.17201042],
      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))
def tf__f(x):
  do_return = False
  retval_ = ag__.UndefinedReturnValue()
  with ag__.FunctionScope('f', 'f_scope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as f_scope:

    def get_state():
      return ()

    def set_state(_):
      pass

    def loop_body(x):
      ag__.converted_call(tf.print, f_scope.callopts, (x,), None, f_scope)
      x = ag__.converted_call(tf.tanh, f_scope.callopts, (x,), None, f_scope)
      return x,

    def loop_test(x):
      return ag__.converted_call(tf.reduce_sum, f_scope.callopts, (x,), None, f_scope) > 1
    x, = ag__.while_stmt(loop_test, loop_body, get_state, set_state, (x,), ('x',), ())
    do_return = True
    retval_ = f_scope.mark_return_value(x)
  do_return,
  return ag__.retval(retval_)

 

AutoGraph: 条件節

AutoGraph は if ステートメントを等値な tf.cond 呼び出しに変換します。

この置き換えは条件が Tensor である場合に行われます。そうでないなら、条件節はトレースの間に実行されます。

def test_tf_cond(f, *args):
  g = f.get_concrete_function(*args).graph
  if any(node.name == 'cond' for node in g.as_graph_def().node):
    print("{}({}) uses tf.cond.".format(
        f.__name__, ', '.join(map(str, args))))
  else:
    print("{}({}) executes normally.".format(
        f.__name__, ', '.join(map(str, args))))
@tf.function
def hyperparam_cond(x, training=True):
  if training:
    x = tf.nn.dropout(x, rate=0.5)
  return x

@tf.function
def maybe_tensor_cond(x):
  if x < 0:
    x = -x
  return x

test_tf_cond(hyperparam_cond, tf.ones([1], dtype=tf.float32))
test_tf_cond(maybe_tensor_cond, tf.constant(-1))
test_tf_cond(maybe_tensor_cond, -1)
hyperparam_cond(tf.Tensor([1.], shape=(1,), dtype=float32)) executes normally.
maybe_tensor_cond(tf.Tensor(-1, shape=(), dtype=int32)) uses tf.cond.
maybe_tensor_cond(-1) executes normally.

tf.cond は多くの微妙な点を持ちます。- それは条件節の両側をトレースし、それから条件に依拠して、ランタイムに適切な分岐を選択することにより動作します。両側のトレースは Python コードの予期せぬ実行という結果になるかもしれません - 一つの分岐が downstream で使用される tensor を作成する場合、他の分岐もまた tensor を作成しなければならないことを必要とします。

@tf.function
def f():
  x = tf.constant(0)
  if tf.constant(True):
    x = x + 1
    print("Tracing `then` branch")
  else:
    x = x - 1
    print("Tracing `else` branch")
  return x

f()
Tracing `then` branch
Tracing `else` branch

<tf.Tensor: id=747, shape=(), dtype=int32, numpy=1>
@tf.function
def f():
  if tf.constant(True):
    x = tf.ones([3, 3])
  return x

# Throws an error because both branches need to define `x`.
with assert_raises(ValueError):
  f()
Caught expected exception 
  <class 'ValueError'>: in converted code:

    <ipython-input-26-3ae6a92ff50d>:3 f  *
        if tf.constant(True):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:893 if_stmt
        basic_symbol_names, composite_symbol_names)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:931 tf_if_stmt
        error_checking_orelse)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/util/deprecation.py:507 new_func
        return func(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/control_flow_ops.py:1174 cond
        return cond_v2.cond_v2(pred, true_fn, false_fn, name)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/cond_v2.py:91 cond_v2
        op_return_value=pred)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/framework/func_graph.py:915 func_graph_from_py_func
        func_outputs = python_func(*func_args, **func_kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:924 error_checking_orelse
        result[orelse_branch] = orelse()
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:962 wrapper
        new_vars = func()
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:988 wrapper
        tuple(s.symbol_name for s in undefined)))

    ValueError: The following symbols must also be initialized in the else branch: ('x',). Alternatively, you may initialize them before the if statement.

 

AutoGraph とループ

AutoGraph はループを変換するための 2, 3 の単純なルールを持ちます。

  • for: iterable が tensor である場合に変換します。
  • while: while 条件が tensor に依拠する場合に変換されます。

ループが変換される場合、それは tf.while_loop で動的に変換され、あるいは "for x in tf.data.Dataset" の特別なケースでは、tf.data.Dataset.reduce に変換されます。

ループが変換されない場合には、それは静的に展開されます。

def test_dynamically_unrolled(f, *args):
  g = f.get_concrete_function(*args).graph
  if any(node.name == 'while' for node in g.as_graph_def().node):
    print("{}({}) uses tf.while_loop.".format(
        f.__name__, ', '.join(map(str, args))))
  elif any(node.name == 'ReduceDataset' for node in g.as_graph_def().node):
    print("{}({}) uses tf.data.Dataset.reduce.".format(
        f.__name__, ', '.join(map(str, args))))
  else:
    print("{}({}) gets unrolled.".format(
        f.__name__, ', '.join(map(str, args))))
@tf.function
def for_in_range():
  x = 0
  for i in range(5):
    x += i
  return x

test_dynamically_unrolled(for_in_range)
for_in_range() gets unrolled.
@tf.function
def for_in_tfrange():
  x = tf.constant(0, dtype=tf.int32)
  for i in tf.range(5):
    x += i
  return x

test_dynamically_unrolled(for_in_tfrange)
for_in_tfrange() uses tf.while_loop.
@tf.function
def for_in_tfdataset():
  x = tf.constant(0, dtype=tf.int64)
  for i in tf.data.Dataset.range(5):
    x += i
  return x

test_dynamically_unrolled(for_in_tfdataset)
for_in_tfdataset() uses tf.data.Dataset.reduce.
@tf.function
def while_py_cond():
  x = 5
  while x > 0:
    x -= 1
  return x

test_dynamically_unrolled(while_py_cond)
while_py_cond() gets unrolled.
@tf.function
def while_tf_cond():
  x = tf.constant(5)
  while x > 0:
    x -= 1
  return x


test_dynamically_unrolled(while_tf_cond)
while_tf_cond() uses tf.while_loop.

tensor に依拠する break か早期の return 節を持つのであれば、top-level 条件や iterable もまた tensor であるべきです。

次のサンプルを比較してください :

@tf.function
def while_py_true_py_break(x):
  while True:  # py true
    if x == 0: # py break
      break
    x -= 1
  return x

test_dynamically_unrolled(while_py_true_py_break, 5)
while_py_true_py_break(5) gets unrolled.
@tf.function
def buggy_while_py_true_tf_break(x):
  while True:   # py true
    if tf.equal(x, 0): # tf break
      break
    x -= 1
  return x

with assert_raises(TypeError):
  test_dynamically_unrolled(buggy_while_py_true_tf_break, 5)
Caught expected exception 
  <class 'TypeError'>: in converted code:

    <ipython-input-34-453240ea98e6>:3 buggy_while_py_true_tf_break  *
        while True:   # py true
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:730 while_stmt
        return _py_while_stmt(test, body, get_state, set_state, init_vars, opts)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:845 _py_while_stmt
        while test(*loop_vars):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/framework/ops.py:765 __bool__
        self._disallow_bool_casting()
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/framework/ops.py:531 _disallow_bool_casting
        "using a `tf.Tensor` as a Python `bool`")
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/framework/ops.py:518 _disallow_when_autograph_enabled
        " decorating it directly with @tf.function.".format(task))

    OperatorNotAllowedInGraphError: using a `tf.Tensor` as a Python `bool` is not allowed: AutoGraph did not convert this function. Try decorating it directly with @tf.function.
@tf.function
def while_tf_true_tf_break(x):
  while tf.constant(True): # tf true
    if x == 0:  # py break
      break
    x -= 1
  return x

test_dynamically_unrolled(while_tf_true_tf_break, 5)
while_tf_true_tf_break(5) uses tf.while_loop.
@tf.function
def buggy_py_for_tf_break():
  x = 0
  for i in range(5):  # py for
    if tf.equal(i, 3): # tf break
      break
    x += i
  return x

with assert_raises(TypeError):
  test_dynamically_unrolled(buggy_py_for_tf_break)
Caught expected exception 
  <class 'TypeError'>: in converted code:

    <ipython-input-36-82742b0a14d0>:4 buggy_py_for_tf_break  *
        for i in range(5):  # py for
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:339 for_stmt
        return _py_for_stmt(iter_, extra_test, body, get_state, set_state, init_vars)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:348 _py_for_stmt
        if extra_test is not None and not extra_test(*state):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/framework/ops.py:765 __bool__
        self._disallow_bool_casting()
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/framework/ops.py:531 _disallow_bool_casting
        "using a `tf.Tensor` as a Python `bool`")
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/framework/ops.py:518 _disallow_when_autograph_enabled
        " decorating it directly with @tf.function.".format(task))

    OperatorNotAllowedInGraphError: using a `tf.Tensor` as a Python `bool` is not allowed: AutoGraph did not convert this function. Try decorating it directly with @tf.function.
@tf.function
def tf_for_py_break():
  x = 0
  for i in tf.range(5): # tf for
    if i == 3:  # py break
      break
    x += i
  return x

test_dynamically_unrolled(tf_for_py_break)
tf_for_py_break() uses tf.while_loop.

動的に展開されたループからの結果を累積するために、tf.TensorArray を使用することを望むでしょう。

batch_size = 2
seq_len = 3
feature_size = 4

def rnn_step(inp, state):
  return inp + state

@tf.function
def dynamic_rnn(rnn_step, input_data, initial_state):
  # [batch, time, features] -> [time, batch, features]
  input_data = tf.transpose(input_data, [1, 0, 2])
  max_seq_len = input_data.shape[0]

  states = tf.TensorArray(tf.float32, size=max_seq_len)
  state = initial_state
  for i in tf.range(max_seq_len):
    state = rnn_step(input_data[i], state)
    states = states.write(i, state)
  return tf.transpose(states.stack(), [1, 0, 2])
  
dynamic_rnn(rnn_step,
            tf.random.uniform([batch_size, seq_len, feature_size]),
            tf.zeros([batch_size, feature_size]))
<tf.Tensor: id=1254, shape=(2, 3, 4), dtype=float32, numpy=
array([[[0.20169294, 0.61629033, 0.1433531 , 0.4829756 ],
        [1.1323476 , 1.0190183 , 0.8961072 , 0.9289988 ],
        [1.8864099 , 1.3378067 , 1.4223019 , 1.3112907 ]],

       [[0.6474533 , 0.06091189, 0.8633839 , 0.65446174],
        [0.9992776 , 1.0039562 , 1.1452049 , 1.5506929 ],
        [1.4500049 , 1.0567949 , 1.5264317 , 2.1867056 ]]], dtype=float32)>

tf.cond と同様に、tf.while_loop もまた微妙な点を伴います。- ループは 0 回の実行が可能ですから、while_loop の downstream で使用される総ての tensor は上のループで初期化されなければなりません。- 総てのループ変数の shape/dtype は各 iteration と一環していなければなりません。

@tf.function
def buggy_loop_var_uninitialized():
  for i in tf.range(3):
    x = i
  return x

with assert_raises(ValueError):
  buggy_loop_var_uninitialized()
Caught expected exception 
  <class 'ValueError'>: in converted code:

    <ipython-input-39-815fd6bba8cc>:3 buggy_loop_var_uninitialized  *
        for i in tf.range(3):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:315 for_stmt
        composite_symbol_names)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:419 _tf_range_for_stmt
        _disallow_undefs_into_loop(*init_vars)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:97 _disallow_undefs_into_loop
        ' before the loop: {}'.format(tuple(s.symbol_name for s in undefined)))

    ValueError: TensorFlow requires that the following symbols must be defined before the loop: ('x',)
@tf.function
def f():
  x = tf.constant(0)
  for i in tf.range(3):
    x = i
  return x

f()
<tf.Tensor: id=1309, shape=(), dtype=int32, numpy=2>
@tf.function
def buggy_loop_type_changes():
  x = tf.constant(0, dtype=tf.float32)
  for i in tf.range(3): # Yields tensors of type tf.int32...
    x = i
  return x

with assert_raises(tf.errors.InvalidArgumentError):
  buggy_loop_type_changes()
Got unexpected exception 
  <class 'TypeError'>: in converted code:

    <ipython-input-41-46359fb065eb>:4 buggy_loop_type_changes  *
        for i in tf.range(3): # Yields tensors of type tf.int32...
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:315 for_stmt
        composite_symbol_names)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:478 _tf_range_for_stmt
        opts=opts,
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:769 _tf_while_stmt
        aug_init_vars, **opts)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/control_flow_ops.py:2675 while_loop
        back_prop=back_prop)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/while_v2.py:198 while_loop
        add_control_dependencies=add_control_dependencies)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/framework/func_graph.py:915 func_graph_from_py_func
        func_outputs = python_func(*func_args, **func_kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/while_v2.py:176 wrapped_body
        outputs = body(*_pack_sequence_as(orig_loop_vars, args))
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:759 aug_body
        composite_symbol_names)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:195 _verify_tf_loop_vars
        first_iter_var)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/util/nest.py:535 map_structure
        structure[0], [func(*x) for x in entries],
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/util/nest.py:535 
        structure[0], [func(*x) for x in entries],
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:179 _check_same_type
        first_iter_var.dtype.name,

    TypeError: "x" has dtype float32 before the loop, but dtype int32 after one iteration. TensorFlow control flow requires it stays the same.
@tf.function
def buggy_concat():
  x = tf.ones([0, 10])
  for i in tf.range(5):
    x = tf.concat([x, tf.ones([1, 10])], axis=0)
  return x

with assert_raises(ValueError):
  buggy_concat()
Caught expected exception 
  <class 'ValueError'>: in converted code:

    <ipython-input-42-bae298a1ce41>:4 buggy_concat  *
        for i in tf.range(5):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:315 for_stmt
        composite_symbol_names)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:478 _tf_range_for_stmt
        opts=opts,
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:769 _tf_while_stmt
        aug_init_vars, **opts)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/control_flow_ops.py:2675 while_loop
        back_prop=back_prop)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/while_v2.py:198 while_loop
        add_control_dependencies=add_control_dependencies)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/framework/func_graph.py:915 func_graph_from_py_func
        func_outputs = python_func(*func_args, **func_kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/ops/while_v2.py:176 wrapped_body
        outputs = body(*_pack_sequence_as(orig_loop_vars, args))
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:759 aug_body
        composite_symbol_names)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:195 _verify_tf_loop_vars
        first_iter_var)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/util/nest.py:535 map_structure
        structure[0], [func(*x) for x in entries],
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/util/nest.py:535 
        structure[0], [func(*x) for x in entries],
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow_core/python/autograph/operators/control_flow.py:191 _check_same_type
        first_iter_shape))

    ValueError: "x" has shape (0, 10) before the loop, but shape (1, 10) after one iteration. TensorFlow control flow requires it stays the same or be more specific.
@tf.function
def concat_with_padding():
  x = tf.zeros([5, 10])
  for i in tf.range(5):
    x = tf.concat([x[:i], tf.ones([1, 10]), tf.zeros([4-i, 10])], axis=0)
    x.set_shape([5, 10])
  return x

concat_with_padding()
<tf.Tensor: id=1432, shape=(5, 10), dtype=float32, numpy=
array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]], dtype=float32)>
 

以上






TensorFlow 2.0 : 上級 Tutorials : カスタマイズ :- カスタム訓練: ウォークスルー

TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- カスタム訓練: ウォークスルー (翻訳/解説)

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

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

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

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

 

カスタマイズ :- カスタム訓練: ウォークスルー

このガイドはアイリス花を種で分類するために機械学習を使用します。それは TensorFlow を次のために使用します : 1. モデルを構築する、2. サンプルデータ上でこのモデルを訓練する、そして 3. 未知のデータについて予測を行なうためにモデルを使用する。

 

TensorFlow プログラミング

このガイドはこれらの高位 TensorFlow 概念を使用します :

  • TensorFlow のデフォルト eager execution 開発環境を使用します、
  • Datasets API でデータをインポートします、
  • TensorFlow の Keras API でモデルと層を構築します。

このチュートリアルは多くの TensorFlow プログラムのように構造化されています :

  1. データセットをインポートして解析します。
  2. モデルのタイプを選択します。
  3. モデルを訓練します。
  4. モデルの有効性を評価します。
  5. 予測を行なうために訓練されたモデルを使用します。

 

プログラムのセットアップ

インポートを configure する

TensorFlow と他の必要な Python モジュールをインポートします。デフォルトでは、TensorFlow は演算を直ちに評価するために eager execution を使用し、後で実行される 計算グラフ を作成する代わりに具体的な値を返します。もし貴方が REPL か python 対話的コンソールに慣れているのであれば、これは身近に感じるでしょう。

from __future__ import absolute_import, division, print_function, unicode_literals

import os
import matplotlib.pyplot as plt
import tensorflow as tf
print("TensorFlow version: {}".format(tf.__version__))
print("Eager execution: {}".format(tf.executing_eagerly()))
TensorFlow version: 2.0.0
Eager execution: True

 

アイリス分類問題

貴方が植物学者で見つけた各アイリス花を分類するための自動化された方法を求めていると想像してください。機械学習は花を統計的に分類する多くのアルゴリズムを提供します。例えば、洗練された機械学習プログラムは写真を基にして花を分類できるでしょう。私達の野望はより控えめなものです — アイリス花をそれらのがく片 (= sepal) と花弁 (= petal) の長さと幅を基にして分類していきます。

アイリス属は約 300 種を必然的に伴いますが、私達のプログラムは以下の3つだけを分類します :

  • アイリス・セトサ
  • アイリス・バージニカ
  • アイリス・バージカラー


Figure 1. アイリス・セトサ (by Radomil, CC BY-SA 3.0)、アイリス・バージカラー (by Dlanglois, CC BY-SA 3.0)、そして アイリス・バージニカ (by Frank Mayfield, CC BY-SA 2.0) 。

 
幸いなことに、がく片と花弁の測定を持つ 120 アイリス花のデータセット を既に誰かが作成しています。これは初心者の機械学習分類問題に対してポピュラーである古典的なデータセットです。

 

訓練データセットをインポートして解析する

データセットファイルをダウンロードしてそれをこの Python プログラムで使用できる構造に変換します。

 

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

tf.keras.utils.get_file 関数を使用して訓練データセット・フアイルをダウンロードします。これはダウンロードされたファイルのファイルパスを返します :

train_dataset_url = "https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv"

train_dataset_fp = tf.keras.utils.get_file(fname=os.path.basename(train_dataset_url),
                                           origin=train_dataset_url)

print("Local copy of the dataset file: {}".format(train_dataset_fp))
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv
8192/2194 [================================================================================================================] - 0s 0us/step
Local copy of the dataset file: /home/kbuilder/.keras/datasets/iris_training.csv

 

データを調べる

このデータセット, iris_training.csv, はプレーンテキスト・ファイルでカンマ区切り値としてフォーマットされた表データ (CSV) をストアしています。最初の 5 エントリを見るために head -n5 コマンドを使用します :

!head -n5 {train_dataset_fp}
120,4,setosa,versicolor,virginica
6.4,2.8,5.6,2.2,2
5.0,2.3,3.3,1.0,1
4.9,2.5,4.5,1.7,2
4.9,3.1,1.5,0.1,0

データセットのこのビューから、次のことが分かります :

  1. 最初の行はデータセットについての情報を含むヘッダです :
    • 120 総計サンプルがあります。各サンプルは4つの特徴と3つの可能なラベル名の1つを持ちます。
  2. 続く行はデータレコードで、行毎に1つの サンプル で、そこでは :
    • 最初の4つのフィールドは 特徴 です : これらはサンプルの特質です。ここでは、フィールドは花の測定を表わす浮動小数点数を保持しています。
    • 最後のカラムは ラベル です : これは予測することを望む値です。このデータセットのためには、それは花の名前に対応する 0, 1, または 2 の整数値です。

それをコードに書き出しましょう :

# column order in CSV file
column_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']

feature_names = column_names[:-1]
label_name = column_names[-1]

print("Features: {}".format(feature_names))
print("Label: {}".format(label_name))
Features: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
Label: species

各ラベルは文字列名 (例えば、”setosa”) に関連付けられますが、機械学習は典型的には数値に依拠します。ラベルの数字は命名された表現にマップされます、次のように :

  • 0: アイリス・セトサ
  • 1: アイリス・バージカラー
  • 2: アイリス・バージニカ

特徴とラベルについてのより多くの情報については、ML Terminology section of the Machine Learning Crash Course を見てください。

class_names = ['Iris setosa', 'Iris versicolor', 'Iris virginica']

 

tf.data.Dataset を作成する

TensorFlow の Dataset API はデータをモデルにロードするための多くの一般的なケースを処理します。これはデータを読みそしてそれを訓練のために使用される形式に変形するための高位 API です。より多くの情報のためには Dataset クイックスタート・ガイド を見てください。

データセットは CSV 形式のテキストファイルですので、データを適切なフォーマットにパースするために make_csv_dataset 関数を使用します。この関数は訓練モデルのためのデータを生成しますので、デフォルトの動作はデータをシャッフルして (shuffle=True, shuffle_buffer_size=10000)、データセットを永久に繰り返します (num_epochs=None)。batch_size パラメータもまた設定します。

batch_size = 32

train_dataset = tf.data.experimental.make_csv_dataset(
    train_dataset_fp,
    batch_size,
    column_names=column_names,
    label_name=label_name,
    num_epochs=1)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/data/experimental/ops/readers.py:521: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_determinstic`.

make_csv_dataset 関数は (features, label) ペアの tf.data.Dataset を返します、ここで features は辞書 : {‘feature_name’: 値} です。

これらの Dataset オブジェクトは iterable です。features のバッチを見てみましょう :

features, labels = next(iter(train_dataset))

print(features)
OrderedDict([('sepal_length', <tf.Tensor: id=68, shape=(32,), dtype=float32, numpy=
array([5. , 5.7, 5.9, 5. , 4.4, 4.4, 6.4, 4.6, 4.6, 6.1, 5. , 6.9, 6.3,
       5.8, 6.1, 7.6, 5. , 4.6, 5.7, 6.5, 5.4, 4.7, 6. , 6.1, 6.3, 5.8,
       6.1, 6.7, 7.7, 4.8, 6.3, 6.7], dtype=float32)>), ('sepal_width', <tf.Tensor: id=69, shape=(32,), dtype=float32, numpy=
array([3.6, 2.8, 3.2, 3.2, 3.2, 2.9, 3.1, 3.1, 3.2, 3. , 2. , 3.1, 3.4,
       2.8, 2.6, 3. , 2.3, 3.6, 3.8, 3. , 3. , 3.2, 2.2, 2.8, 2.7, 2.7,
       2.8, 3.3, 3.8, 3.4, 2.5, 3.1], dtype=float32)>), ('petal_length', <tf.Tensor: id=66, shape=(32,), dtype=float32, numpy=
array([1.4, 4.1, 4.8, 1.2, 1.3, 1.4, 5.5, 1.5, 1.4, 4.9, 3.5, 5.1, 5.6,
       5.1, 5.6, 6.6, 3.3, 1. , 1.7, 5.8, 4.5, 1.6, 5. , 4.7, 4.9, 5.1,
       4. , 5.7, 6.7, 1.6, 5. , 4.4], dtype=float32)>), ('petal_width', <tf.Tensor: id=67, shape=(32,), dtype=float32, numpy=
array([0.2, 1.3, 1.8, 0.2, 0.2, 0.2, 1.8, 0.2, 0.2, 1.8, 1. , 2.3, 2.4,
       2.4, 1.4, 2.1, 1. , 0.2, 0.3, 2.2, 1.5, 0.2, 1.5, 1.2, 1.8,

同様な特徴は一緒にグループ化、あるいはバッチ化されることが分かるでしょう。各サンプルの行のフィールドは対応する特徴配列に付加されます。これらの特徴配列にストアされるサンプル数を設定するためには batch_size を変更します。

バッチから幾つかの特徴をプロットすることで幾つかのクラスタを見ることから始めることができます :

plt.scatter(features['petal_length'],
            features['sepal_length'],
            c=labels,
            cmap='viridis')

plt.xlabel("Petal length")
plt.ylabel("Sepal length")
plt.show()

モデル構築ステップを単純化するために、特徴辞書を shape: (batch_size, num_features) を持つシングル配列に再パッケージする関数を作成します。

この関数は tf.stack メソッドを使用します、これは tensor のリストからの値を取得して指定された次元で結合された tensor を作成します。

def pack_features_vector(features, labels):
  """Pack the features into a single array."""
  features = tf.stack(list(features.values()), axis=1)
  return features, labels

それから各 (features,label) ペアの特徴を訓練データセットにパックするために tf.data.Dataset.map メソッドを使用します :

train_dataset = train_dataset.map(pack_features_vector)

Dataset の特徴要素は今では shape (batch_size, num_features) を持つ配列です。最初の幾つかのサンプルを見てみましょう :

features, labels = next(iter(train_dataset))

print(features[:5])
tf.Tensor(
[[5.1 3.7 1.5 0.4]
 [5.5 2.6 4.4 1.2]
 [4.6 3.4 1.4 0.3]
 [5.5 2.4 3.7 1. ]
 [5.6 2.5 3.9 1.1]], shape=(5, 4), dtype=float32)

 

モデルのタイプを選択する

何故モデルなのでしょう?

モデル は特徴とラベルの間の関係性です。アイリス分類問題については、モデルはがく片と花弁の測定と予測されるアイリス種の間の関係性を定義します。ある単純なモデルは代数の 2, 3 行で記述できますが、複雑な機械学習モデルは要約することが難しい巨大な数のパラメータを持ちます。

貴方は機械学習を使用することなしに4つの特徴とアイリス種の間の関係性を決定できるでしょうか?つまり、貴方はモデルを作成するために伝統的なプログラミング技術 (例えば、多くの条件ステートメント) を利用できますか?多分 (可能でしょう) — もし貴方がデータセットを特定の種へのがく片と花弁の測定の間の関係性を決定するために十分に長く解析したのであれば。そしてこれはより複雑なデータセット上では難しく — 多分不可能に — なるでしょう。良い機械学習アプローチは貴方のためにモデルを決定します。もし十分な代表的なサンプルを正しい機械学習モデル・タイプに供給すれば、プログラムはその関係性を貴方のために解くでしょう。

 

モデルを選択する

私達は訓練するモデルの種類を選択する必要があります。多くのモデルのタイプがあり良い一つを選択するには経験が必要です。このチュートリアルではアイリス分類問題を解くためにニューラルネットワークを使用します。ニューラルネットワーク は特徴とラベル間の複雑な関係性を見つけることができます。それは高度に構造化されたグラフで、一つかそれ以上の 隠れ層 へと体系化されます。各隠れ層は一つかそれ以上の ニューロン から成ります。ニューラルネットワークの幾つかのカテゴリがあり、このプログラムは dense、あるいは 完全結合ニューラルネットワーク を使用します : 一つの層のニューロンは前の層の総てのニューロンからの入力接続を受け取ります。例えば、Figure 2 は入力層、2つの隠れ層、そして出力層から成る dense ニューラルネットワークを示します :


Figure 2. 特徴、隠れ層、そして予測を持つニューラルネットワーク

Figure 2 からのモデルが訓練されてラベル付けされていないサンプルが供給されたとき、それは3つの予測を生成します : この花が与えられたアイリス種である尤度です。この予測は 推論 と呼ばれます。このサンプルについて、出力予測の合計は 1.0 です。Figure 2 では、この予測は次のように分解されます : アイリス・セトサのために 0.02, アイリス・バージカラーのために 0.95, そしてアイリス・バージニカのために 0.03 です。これはモデルは — 95% の確率で — ラベル付けされていないサンプル花がアイリス・バージカラーであると予測していることを意味します。

 

Keras を使用してモデルを作成する

TensorFlow tf.keras API はモデルと層を作成するための好ましい方法です。これがモデルと実験の構築を容易にする一方で Keras は総てを一緒に接続する複雑さを処理します。

tf.keras.Sequential モデルは層の線形スタックです。そのコンストラクタは層インスタンスのリストを取り、この場合では、それぞれ 10 ノードを持つ2つの Dense 層、そしてラベル予測を表わす3つのノードを持つ出力層です。最初の層の input_shape パラメータは dataset からの特徴の数に相当し、そしてこれは必要です :

model = tf.keras.Sequential([
  tf.keras.layers.Dense(10, activation=tf.nn.relu, input_shape=(4,)),  # input shape required
  tf.keras.layers.Dense(10, activation=tf.nn.relu),
  tf.keras.layers.Dense(3)
])

活性化関数 は層の各ノードの出力 shape を決定します。これらの非線形性は重要です — それらなしではモデルはシングル層と同じでしょう。多くの 利用可能な活性 がありますが、隠れ層のためには ReLU が一般的です。

隠れ層とニューロンの理想的な数は問題とデータセットに依拠します。機械学習の多くの様相のように、ニューラルネットワークの最善の shape の選択は知識と実験の混合を必要とします。おおよそのところは、隠れ層とニューロンの数を増加させると典型的にはよりパワフルなモデルを作成し、これは効果的に訓練するためにより多くのデータを必要とします。

 

モデルを使用する

このモデルが特徴のバッチに何を遂行するかを素早く見てみましょう :

predictions = model(features)
predictions[:5]
<tf.Tensor: id=231, shape=(5, 3), dtype=float32, numpy=
array([[-0.543846  , -0.52378184, -0.10649145],
       [-0.02903301, -0.12047299, -0.14458351],
       [-0.5125149 , -0.5001465 , -0.08259603],
       [-0.18031298, -0.22815299, -0.10972097],
       [-0.1382576 , -0.19937445, -0.12546575]], dtype=float32)>

ここで、各サンプルは各クラスのために ロジット を返します。

これらのロジットを各クラスのための確率に変換するために、softmax 関数を使用します :

tf.nn.softmax(predictions[:5])
<tf.Tensor: id=236, shape=(5, 3), dtype=float32, numpy=
array([[0.2802006 , 0.28587934, 0.43392003],
       [0.35669807, 0.32552838, 0.31777358],
       [0.28172362, 0.28522974, 0.43304664],
       [0.33042237, 0.31498712, 0.35459054],
       [0.33857134, 0.31849852, 0.3429301 ]], dtype=float32)>

クラスに渡る tf.argmax を取れば予測されたクラス・インデックスを与えます。しかし、モデルはまだ訓練されていませんので、これらは良い予測ではありません :

print("Prediction: {}".format(tf.argmax(predictions, axis=1)))
print("    Labels: {}".format(labels))
Prediction: [2 0 2 2 2 0 0 2 0 0 0 0 0 2 0 0 2 0 0 0 0 2 0 2 2 0 2 0 0 0 0 2]
    Labels: [0 1 0 1 1 1 1 0 1 2 2 1 2 0 2 2 1 1 1 2 1 1 2 0 0 1 0 2 2 2 2 0]

 

モデルを訓練する

訓練 はモデルが徐々に最適化される、あるいはモデルがデータセットを学習する際の機械学習のステージです。目標は、初見のデータについて予測を行なうために訓練データセットの構造について十分に学習することです。もし訓練データセットについて学習し過ぎる場合には、予測はそれが見たデータに対して動作するのみで一般化できないでしょう。この問題は overfitting と呼ばれます — それは問題をどのように解くかを理解する代わりに答えを覚えるようなものです。

アイリス分類問題は 教師あり機械学習 の例です : モデルはラベルを含むサンプルから訓練されます。教師なし機械学習 では、サンプルはラベルを含みません。代わりに、モデルは典型的には特徴内のパターンを見つけます。

 

損失と勾配関数を定義する

訓練と評価ステージの両者はモデルの 損失 を計算する必要があります。これはモデルの予測が望まれるラベルからどのくらい離れているかを測定します、換言すれば、モデルがどのくらい悪く遂行しているかです。私達はこの値を最小化、あるいは最適化することを望みます。

私達のモデルは tf.keras.losses.SparseCategoricalCrossentropy 関数を使用してその損失を計算します、これはモデルのクラス確率予測と望まれるラベルを取り、サンプルに渡る平均損失を返します。

loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
def loss(model, x, y):
  y_ = model(x)

  return loss_object(y_true=y, y_pred=y_)


l = loss(model, features, labels)
print("Loss test: {}".format(l))
Loss test: 1.2178399562835693

モデルを最適化するために使用される 勾配 を計算するために tf.GradientTape コンテキストを使用します :

def grad(model, inputs, targets):
  with tf.GradientTape() as tape:
    loss_value = loss(model, inputs, targets)
  return loss_value, tape.gradient(loss_value, model.trainable_variables)

 

optimizer を作成する

optimizer は loss 関数を最小化するために計算された勾配をモデルの変数に適用します。貴方は損失関数を曲面 (Figure 3 参照) として考えることができて私達は歩き回ることによりその最も低いポイントを見つけることを望みます。勾配は上りの最も険しい方向をポイントします — 従って私達は反対の道に移動して進み丘を降ります。各バッチに対する損失と勾配を反復的に計算することにより、訓練の間にモデルを調整するでしょう。徐々に、モデルは損失を最小化するための重みとバイアスの最善の組み合わせを見つけるでしょう。そして損失がより小さくなれば、モデルの予測はより良くなります。


Figure 3. 3D 空間における時間に渡り可視化された最適化アルゴリズム。
(ソース: Stanford class CS231n, MIT License, Image credit: Alec Radford)

TensorFlow は訓練のために利用可能な多くの 最適化アルゴリズム を持ちます。このモデルは 確率的勾配降下 (SGD, stochastic gradient descent) アルゴリズムを実装する tf.train.GradientDescentOptimizer を使用します。learning_rate は各反復のための丘を降りるためのステップサイズを設定します。これはより良い結果を得るために一般に調整するハイパーパラメータです。

optimizer をセットアップしましょう :

optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)

単一の最適化ステップを計算するためにこれを使用します :

loss_value, grads = grad(model, features, labels)

print("Step: {}, Initial Loss: {}".format(optimizer.iterations.numpy(),
                                          loss_value.numpy()))

optimizer.apply_gradients(zip(grads, model.trainable_variables))

print("Step: {},         Loss: {}".format(optimizer.iterations.numpy(),
                                          loss(model, features, labels).numpy()))
Step: 0, Initial Loss: 1.2178399562835693
Step: 1,         Loss: 1.144298791885376

 

訓練ループ

総てのピースが所定の位置にあり、モデルは訓練のための準備ができました! 訓練ループは (モデルが) より良い予測を行なうことを手助けするためにデータセットのサンプルをモデルに供給します。次のコードブロックはこれらの訓練ステップをセットアップします :

  1. 各エポックを反復します。エポックはデータセットを通した一つのパスです。
  2. (一つの) エポック内では、特徴 (x) とラベル (y) を取り込んでいる訓練 Dataset の各サンプルに渡り反復します。
  3. サンプルの特徴を使用して、予測を行ないそれをラベルと比較します。予測の不正確さを測定してモデルの損失と勾配を計算するためにそれを使用します。
  4. モデル変数を更新するために optimizer を使用します。
  5. 可視化のために幾つかの統計情報を追跡します。
  6. 各エポックのために反復します。

num_epochs 変数はデータセット・コレクションに渡りループする回数です。直感に反して、モデルをより長く訓練することはより良いモデルを保証しません。num_epochs は貴方が調整可能な ハイパーパラメータ です。正しい数を選択することは通常は経験と実験の両者が必要です :

## Note: Rerunning this cell uses the same model variables

# Keep results for plotting
train_loss_results = []
train_accuracy_results = []

num_epochs = 201

for epoch in range(num_epochs):
  epoch_loss_avg = tf.keras.metrics.Mean()
  epoch_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()

  # Training loop - using batches of 32
  for x, y in train_dataset:
    # Optimize the model
    loss_value, grads = grad(model, x, y)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))

    # Track progress
    epoch_loss_avg(loss_value)  # Add current batch loss
    # Compare predicted label to actual label
    epoch_accuracy(y, model(x))

  # End epoch
  train_loss_results.append(epoch_loss_avg.result())
  train_accuracy_results.append(epoch_accuracy.result())

  if epoch % 50 == 0:
    print("Epoch {:03d}: Loss: {:.3f}, Accuracy: {:.3%}".format(epoch,
                                                                epoch_loss_avg.result(),
                                                                epoch_accuracy.result()))
Epoch 000: Loss: 1.072, Accuracy: 57.500%
Epoch 050: Loss: 0.092, Accuracy: 98.333%
Epoch 100: Loss: 0.067, Accuracy: 98.333%
Epoch 150: Loss: 0.052, Accuracy: 98.333%
Epoch 200: Loss: 0.092, Accuracy: 95.833%

 

損失関数を時間に渡り可視化する

モデルの訓練進捗を表示出力することは有用である一方で、この進捗を見ることはしばしばより有用です。TensorBoard は TensorFlow とともにパッケージ化されている良い可視化ツールですが、matplotlib モジュールを使用して基本的なチャートを作成することができます。

これらのチャートを解釈することは何某かの経験が必要ですが、貴方は損失が下がり精度が上がることを見ることを実際に望むでしょう :

fig, axes = plt.subplots(2, sharex=True, figsize=(12, 8))
fig.suptitle('Training Metrics')

axes[0].set_ylabel("Loss", fontsize=14)
axes[0].plot(train_loss_results)

axes[1].set_ylabel("Accuracy", fontsize=14)
axes[1].set_xlabel("Epoch", fontsize=14)
axes[1].plot(train_accuracy_results)
plt.show()

 

モデルの有効性を評価する

モデルが訓練された今、そのパフォーマンスについて何某かの統計情報を得ることができます。

評価するとはモデルがどのくらい効果的に予測を行なうかを決めることを意味します。アイリス分類でのモデルの有効性を決定するために、幾つかのがく片と花弁の測定をモデルに渡してそれらがどのアイリス種を表わすかを予測するためにモデルに尋ねます。それから実際のラベルに対してモデルの予測を比較します。例えば、入力サンプルの半分の上で正しい種を選択したモデルは 0.5 の 精度 を持ちます。Figure 4 は少しばかりより効果的なモデルを示し、80 % 精度で 5 つの予測から 4 つを正しく得ます :

サンプル特徴 ラベル モデル予測
5.9 3.0 4.3 1.5 1 1
6.9 3.1 5.4 2.1 2 2
5.1 3.3 1.7 0.5 0 0
6.0 3.4 4.5 1.6 1 2
5.5 2.5 4.0 1.3 1 1


Figure 4. 80% 精度のアイリス分類器

 

テストデータセットをセットアップする

モデルを評価することはモデルを訓練することに類似しています。最大の違いはサンプルが訓練セットではなく別の テストセット に由来することです。モデルの有効性を公正に評価するためには、モデルを評価するために使用されるサンプルはモデルを訓練するために使用されたサンプルとは異なっていなければなりません。

テスト Dataset のセットアップは訓練 Dataset のセットアップと同様です。CSV テキストファイルをダウンロードしてその値をパースして、それからそれを少しばかりシャッフルします :

test_url = "https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv"

test_fp = tf.keras.utils.get_file(fname=os.path.basename(test_url),
                                  origin=test_url)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv
8192/573 [============================================================================================================================================================================================================================================================================================================================================================================================================================================] - 0s 0us/step
test_dataset = tf.data.experimental.make_csv_dataset(
    test_fp,
    batch_size,
    column_names=column_names,
    label_name='species',
    num_epochs=1,
    shuffle=False)

test_dataset = test_dataset.map(pack_features_vector)

 

テストデータセット上でモデルを評価する

訓練ステージとは違い、モデルはテストデータの単一の エポック を評価するだけです。次のコードセルでは、テストセットの各サンプルに渡り反復してモデルの予測を実際のラベルに対して比較しています。これはテストセット全体に渡るモデルの精度を測定するために使用されます :

test_accuracy = tf.keras.metrics.Accuracy()

for (x, y) in test_dataset:
  logits = model(x)
  prediction = tf.argmax(logits, axis=1, output_type=tf.int32)
  test_accuracy(prediction, y)

print("Test set accuracy: {:.3%}".format(test_accuracy.result()))
Test set accuracy: 93.333%

例えば最後のバッチ上で、モデルは通常は正しいことを見て取れます :

tf.stack([y,prediction],axis=1)
<tf.Tensor: id=115075, shape=(30, 2), dtype=int32, numpy=
array([[1, 1],
       [2, 2],
       [0, 0],
       [1, 1],
       [1, 1],
       [1, 1],
       [0, 0],
       [2, 2],
       [1, 1],
       [2, 2],
       [2, 2],
       [0, 0],
       [2, 2],
       [1, 1],
       [1, 1],
       [0, 0],
       [1, 1],
       [0, 0],
       [0, 0],
       [2, 2],
       [0, 0],
       [1, 2],
       [2, 2],
       [1, 2],
       [1, 1],
       [1, 1],
       [0, 0],
       [1, 1],
       [2, 2],
       [1, 1]], dtype=int32)>

 

予測を行なうために訓練されたモデルを使用する

私達はモデルを訓練してそれが良いことを「証明」しました — しかし完全ではありません — アイリス種の分類において。今は ラベル付けされていないサンプル 上で幾つか予測を行なうために訓練されたモデルを使用しましょう ; つまり、特徴を含むけれどもラベルを含まないサンプル上です。

現実世界では、ラベル付けされていないサンプルはアプリケーション、CSV ファイル、そしてデータ供給を含む多くの異なるソースに由来するでしょう。当面は、ラベルを予測するために3つのラベル付けされていないサンプルを手動で提供します。思い出してください、次のようにラベル数字は名前付けられた表現にマップされています :

  • 0: アイリス・セトサ
  • 1: アイリス・バージカラー
  • 2: アイリス・バージニカ
predict_dataset = tf.convert_to_tensor([
    [5.1, 3.3, 1.7, 0.5,],
    [5.9, 3.0, 4.2, 1.5,],
    [6.9, 3.1, 5.4, 2.1]
])

predictions = model(predict_dataset)

for i, logits in enumerate(predictions):
  class_idx = tf.argmax(logits).numpy()
  p = tf.nn.softmax(logits)[class_idx]
  name = class_names[class_idx]
  print("Example {} prediction: {} ({:4.1f}%)".format(i, name, 100*p))
Example 0 prediction: Iris setosa (100.0%)
Example 1 prediction: Iris versicolor (97.7%)
Example 2 prediction: Iris virginica (99.9%)
 

以上






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

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

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

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – 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 > 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, unicode_literals

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

import numpy as np
import os
import time

 

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}')
{
  'Z' :  38,
  'n' :  52,
  'x' :  62,
  'd' :  42,
  'k' :  49,
  'i' :  47,
  'z' :  64,
  'W' :  35,
  'Y' :  37,
  'V' :  34,
  'Q' :  29,
  'X' :  36,
  ';' :  11,
  'K' :  23,
  'D' :  16,
  'E' :  17,
  'b' :  40,
  '.' :   8,
  'P' :  28,
  'u' :  59,
  ...
}
# 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     
_________________________________________________________________
lstm (LSTM)                  (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([45, 37,  3, 51, 29,  6, 42, 49, 16,  0,  8, 37, 10, 32, 15, 28, 13,
       24, 11, 59, 35, 59, 38, 36, 21, 63, 34, 56, 48, 23, 28,  8, 30, 28,
       25,  3, 47, 34, 54,  7, 39, 15, 40, 46, 40, 16, 13,  8, 34, 61, 59,
       20, 27, 25,  7, 43, 17, 51, 41, 33, 10, 60,  2, 21, 63, 48, 24, 56,
       43, 39,  9, 46, 14, 38, 55, 40, 20, 32, 38, 29, 46, 12,  4, 60, 11,
       24, 23, 55, 10, 59, 47, 33, 40, 13, 43,  7, 34, 17, 31, 28])

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

print("Input: \n", repr("".join(idx2char[input_example_batch[0]])))
print()
print("Next Char Predictions: \n", repr("".join(idx2char[sampled_indices ])))
Input: 
 'ilent judgment tried it,\nWithout more overture.\n\nLEONTES:\nHow could that be?\nEither thou art most ig'

Next Char Predictions: 
 'gY$mQ,dkD\n.Y:TCPAL;uWuZXIyVrjKP.RPM$iVp-aCbhbDA.VwuHOM-eEmcU:v!IyjLrea3hBZqbHTZQh?&v;LKq:uiUbAe-VESP'

 

モデルを訓練する

この時点で問題は標準的な分類問題として扱うことができます。前の 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.174535

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)

 

訓練を実行する

訓練時間を合理的なものに保持するために、モデルを訓練するために 10 エポックを使用します。

EPOCHS=10
history = model.fit(dataset, epochs=EPOCHS, callbacks=[checkpoint_callback])
Epoch 1/10
172/172 [==============================] - 8s 47ms/step - loss: 2.5719
Epoch 2/10
172/172 [==============================] - 7s 39ms/step - loss: 1.8664
Epoch 3/10
172/172 [==============================] - 7s 43ms/step - loss: 1.6230
Epoch 4/10
172/172 [==============================] - 7s 43ms/step - loss: 1.4920
Epoch 5/10
172/172 [==============================] - 7s 40ms/step - loss: 1.4106
Epoch 6/10
172/172 [==============================] - 7s 40ms/step - loss: 1.3502
Epoch 7/10
172/172 [==============================] - 7s 40ms/step - loss: 1.2989
Epoch 8/10
172/172 [==============================] - 7s 40ms/step - loss: 1.2519
Epoch 9/10
172/172 [==============================] - 7s 41ms/step - loss: 1.2077
Epoch 10/10
172/172 [==============================] - 7s 40ms/step - loss: 1.1659

 

テキストを生成する

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

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

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

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

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]))
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (1, None, 256)            16640     
_________________________________________________________________
lstm_1 (LSTM)                (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 的な著述語彙を模倣するのを見るでしょう。小さい数の訓練エポックでは、それは首尾一貫したセンテンスを形成することをまだ学習していません。

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: where he skill
That ever we fear their looks.

LEONTES:
S stooph of ournd
The dukes o' the selferur is it of her;
Forses thereof willingly no wealth!

TRANIO:
Mine ears, sir, to them speak. whth ever young with one there her to
do than thy heart? There is a fie,
The reverse honoural gives my dather's death
That waves we'll sovereignty of thy aughts,
Your keels with us, save axhousinous: gentle brifferent, for an intent
I did forbilling ougly age on my sin,
With such man est in thy father's father's sky,
As tells me, Yorknishall and Prittend Henry, and her part withan this place
I'll speak of soldier; then I was; to report on
how horse spokes ready the nie, I knew thee,
Pray you, indeed. You were not 'quirence will wear-fallentut and frazed.

YORK:
Is no beg-of this to the Tower.

KATHARINA:
Here is a war.

Third Citizen:
You have professed them and crse trunk blow his chlician.

QUEEN ELIZABETH:
So sovereour a gentlemen, 'tis a book-so sort,
Amen, unwilling,
your love and wars their pa

結果を改善するために貴方ができる最も容易なことはそれをより長く訓練することです (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)
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, from_logits=True))
  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()

  # initializing the hidden state at the start of every epoch
  # initally hidden is None
  hidden = model.reset_states()

  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))
WARNING: Logging before flag parsing goes to stderr.
W0628 06:41:29.934798 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer
W0628 06:41:29.936547 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer.iter
W0628 06:41:29.937477 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer.beta_1
W0628 06:41:29.938759 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer.beta_2
W0628 06:41:29.939390 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer.decay
W0628 06:41:29.940099 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer.learning_rate
W0628 06:41:29.940868 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'm' for (root).layer_with_weights-0.embeddings
W0628 06:41:29.941582 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'm' for (root).layer_with_weights-2.kernel
W0628 06:41:29.942270 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'm' for (root).layer_with_weights-2.bias
W0628 06:41:29.944056 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'm' for (root).layer_with_weights-1.cell.kernel
W0628 06:41:29.944905 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'm' for (root).layer_with_weights-1.cell.recurrent_kernel
W0628 06:41:29.945559 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'm' for (root).layer_with_weights-1.cell.bias
W0628 06:41:29.946222 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'v' for (root).layer_with_weights-0.embeddings
W0628 06:41:29.947816 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'v' for (root).layer_with_weights-2.kernel
W0628 06:41:29.948982 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'v' for (root).layer_with_weights-2.bias
W0628 06:41:29.949806 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'v' for (root).layer_with_weights-1.cell.kernel
W0628 06:41:29.950479 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'v' for (root).layer_with_weights-1.cell.recurrent_kernel
W0628 06:41:29.951171 140162012227328 util.py:244] Unresolved object in checkpoint: (root).optimizer's state 'v' for (root).layer_with_weights-1.cell.bias
W0628 06:41:29.952606 140162012227328 util.py:252] A checkpoint was restored (e.g. tf.train.Checkpoint.restore or tf.keras.Model.load_weights) but not all checkpointed values were used. See above for specific issues. Use expect_partial() on the load status object, e.g. tf.train.Checkpoint.restore(...).expect_partial(), to silence these warnings, or use assert_consumed() to make the check explicit. See https://www.tensorflow.org/alpha/guide/checkpoints#loading_mechanics for details.

Epoch 1 Batch 0 Loss 4.172821998596191
Epoch 1 Batch 100 Loss 2.391324758529663
Epoch 1 Loss 2.0715
Time taken for 1 epoch 9.32474422454834 sec

Epoch 2 Batch 0 Loss 2.1118874549865723
Epoch 2 Batch 100 Loss 1.9127529859542847
Epoch 2 Loss 1.7389
Time taken for 1 epoch 6.282292127609253 sec

Epoch 3 Batch 0 Loss 1.7284945249557495
Epoch 3 Batch 100 Loss 1.6665358543395996
Epoch 3 Loss 1.5530
Time taken for 1 epoch 6.350098133087158 sec

Epoch 4 Batch 0 Loss 1.5307190418243408
Epoch 4 Batch 100 Loss 1.528407335281372
Epoch 4 Loss 1.4468
Time taken for 1 epoch 6.355969429016113 sec

Epoch 5 Batch 0 Loss 1.4143530130386353
Epoch 5 Batch 100 Loss 1.451310396194458
Epoch 5 Loss 1.3683
Time taken for 1 epoch 6.345163583755493 sec

Epoch 6 Batch 0 Loss 1.3415405750274658
Epoch 6 Batch 100 Loss 1.3894364833831787
Epoch 6 Loss 1.3122
Time taken for 1 epoch 6.310795068740845 sec

Epoch 7 Batch 0 Loss 1.2863143682479858
Epoch 7 Batch 100 Loss 1.3341155052185059
Epoch 7 Loss 1.2637
Time taken for 1 epoch 6.189915895462036 sec

Epoch 8 Batch 0 Loss 1.2400826215744019
Epoch 8 Batch 100 Loss 1.2844866514205933
Epoch 8 Loss 1.2265
Time taken for 1 epoch 6.186691999435425 sec

Epoch 9 Batch 0 Loss 1.2004377841949463
Epoch 9 Batch 100 Loss 1.2391895055770874
Epoch 9 Loss 1.1848
Time taken for 1 epoch 6.293030500411987 sec

Epoch 10 Batch 0 Loss 1.1650713682174683
Epoch 10 Batch 100 Loss 1.19230055809021
Epoch 10 Loss 1.1492
Time taken for 1 epoch 6.376950740814209 sec
 

以上






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

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

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

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

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

 

カスタマイズ :- tf.function

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

最高点のパフォーマンスを得て貴方のモデルをどこでも配備可能にするために、貴方のプログラムからグラフを作成するために tf.function を使用します。AutoGraph のおかげで、Python コードの驚くべき総量が tf.function で単に動作しますが、依然として注意すべき落とし穴があります。

主な重要な点と推奨は :

  • オブジェクト mutation やリスト append のような Python 副作用に依拠しないでください。
  • tf.function は NumPy ops や Python プリミティブよりも TensorFlow ops で最善に動作します。
  • 疑わしいときには、”for x in y” イディオムを使用してください。
from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf
import contextlib

# Some helper code to demonstrate the kinds of errors you might encounter.
@contextlib.contextmanager
def assert_raises(error_class):
  try:
    yield
  except error_class as e:
    print('Caught expected exception \n  {}: {}'.format(error_class, e))
  except Exception as e:
    print('Got unexpected exception \n  {}: {}'.format(type(e), e))
  else:
    raise Exception('Expected {} to be raised but no error was raised!'.format(
        error_class))

貴方が定義する tf.function はちょうどコア TensorFlow 演算のようなものです : それを eagerly に実行できます; それをグラフで使用できます; それは勾配を持ちます; 等々。

# A function is like an 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=14, shape=(2, 2), dtype=float32, numpy=
array([[2., 2.],
       [2., 2.]], dtype=float32)>
# Functions have gradients

@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=40, shape=(), dtype=float32, numpy=1.0>
# You can use functions inside functions

@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=67, shape=(3, 2), dtype=float32, numpy=
array([[3., 3.],
       [3., 3.],
       [3., 3.]], dtype=float32)>

 

Tracing とポリモーフィズム

Python の動的型付けは関数を様々な引数型で呼び出すことができて、そして Python は各シナリオで異なる何かを行なうことを意味します。

その一方で、TensorFlow グラフは静的 dtype と shape 次元を要求します。tf.function は正しいグラフを生成するために必要なとき関数を辿り直す (= retrace) ことによりこの隔たりの橋渡しをします。tf.function の利用方法の微妙な点の殆どはこの retracing 動作から生じています。

何が起きるかを見るために異なる型の引数で関数を呼び出せます。

# Functions are polymorphic

@tf.function
def double(a):
  print("Tracing with", a)
  return a + a

print(double(tf.constant(1)))
print()
print(double(tf.constant(1.1)))
print()
print(double(tf.constant("a")))
print()
Tracing with Tensor("a:0", shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)

Tracing with Tensor("a:0", shape=(), dtype=float32)
tf.Tensor(2.2, shape=(), dtype=float32)

Tracing with Tensor("a:0", shape=(), dtype=string)
tf.Tensor(b'aa', shape=(), dtype=string)

tracing 動作を制御するには、次のテクニックを使用します :

  • 新しい tf.function を作成します。別々の tf.function オブジェクトは trace を共有しないことが保証されます。
  • 特定の trace を得るために get_concrete_function メソッドを使用します。
  • グラフ呼び出し毎に一度だけ trace するために tf.function を呼び出すとき input_signature を指定します。
print("Obtaining concrete trace")
double_strings = double.get_concrete_function(tf.TensorSpec(shape=None, dtype=tf.string))
print("Executing traced function")
print(double_strings(tf.constant("a")))
print(double_strings(a=tf.constant("b")))
print("Using a concrete trace with incompatible types will throw an error")
with assert_raises(tf.errors.InvalidArgumentError):
  double_strings(tf.constant(1))
Obtaining concrete trace
Tracing with Tensor("a:0", dtype=string)
Executing traced function
tf.Tensor(b'aa', shape=(), dtype=string)
tf.Tensor(b'bb', shape=(), dtype=string)
Using a concrete trace with incompatible types will throw an error
Caught expected exception 
  <class 'tensorflow.python.framework.errors_impl.InvalidArgumentError'>: cannot compute __inference_double_98 as input #0(zero-based) was expected to be a string tensor but is a int32 tensor [Op:__inference_double_98]
@tf.function(input_signature=(tf.TensorSpec(shape=[None], dtype=tf.int32),))
def next_collatz(x):
  print("Tracing with", x)
  return tf.where(tf.equal(x % 2, 0), x // 2, 3 * x + 1)

print(next_collatz(tf.constant([1, 2])))
# We specified a 1-D tensor in the input signature, so this should fail.
with assert_raises(ValueError):
  next_collatz(tf.constant([[1, 2], [3, 4]]))
Tracing with Tensor("x:0", shape=(None,), dtype=int32)
tf.Tensor([4 1], shape=(2,), dtype=int32)
Caught expected exception 
  <class 'ValueError'>: Python inputs incompatible with input_signature: inputs ((<tf.Tensor: id=125, shape=(2, 2), dtype=int32, numpy=
array([[1, 2],
       [3, 4]], dtype=int32)>,)), input_signature ((TensorSpec(shape=(None,), dtype=tf.int32, name=None),))

 

いつ retrace するか?

多態的な (= polymorphic) tf.function は tracing により生成された具体的な関数のキャッシュを保持します。キャッシュキーは効果的に関数引数と kwargs から生成されたキーのタプルです。tf.Tensor 引数のために生成されたキーはその shape と型です。Python プリミティブのために生成されたキーはその値です。総ての他の Python 型については、キーはオブジェクト id() に基づきその結果メソッドはクラスの各インスタンスのために独立的に trace されます。将来的には、TensorFlow は Python オブジェクトのためにより洗練されたキャッシングを追加するかもしれません、それは tensor に安全に変換できます。

 

Python あるいは Tensor args?

しばしば、Python 引数はハイパーパラメータとグラフ構築を制御するために使用されます – 例えば、num_layers=10 や training=True や nonlinearity=’relu’ です。そのため Python 引数が変わる場合、グラフを retrace しなければならないことは意味をなします。

けれども、Python 引数がグラフ構築を制御するために使用されていないこともあり得ます。これらのケースでは、Python 値の変更は必要のない retrace を引き起こす可能性があります。例えばこの訓練ループを取れば、AutoGraph はそれを動的に展開するでしょう。複数の trace にもかかわらず、生成されたグラフは実際には同一です、従ってこれは少し非効率的です。

def train_one_step():
  pass

@tf.function
def train(num_steps):
  print("Tracing with num_steps = {}".format(num_steps))
  for _ in tf.range(num_steps):
    train_one_step()

train(num_steps=10)
train(num_steps=20)
Tracing with num_steps = 10
Tracing with num_steps = 20

ここでの最も単純な回避方法は、それら (引数) が生成されたグラフの shape に影響しない場合に引数を Tensor にキャストすることです。

train(num_steps=tf.constant(10))
train(num_steps=tf.constant(20))
Tracing with num_steps = Tensor("num_steps:0", shape=(), dtype=int32)

 

tf.function の副作用

一般に、(プリントや mutating オブジェクトのような) Python 副作用は tracing の間だけ発生します。それでは tf.function からどのように副作用を確実に引き起こせるのでしょう?

一般的な経験則は trace をデバッグするために Python 副作用だけを使用することです。さもなければ、 tf.Variable.assign, tf.printtf.summary のような TensorFlow ops は各呼び出しに伴う TensorFlow ランタイムにより貴方のコードが trace されて実行されることを確かなものにする最善の方法です。一般に functional スタイルの使用は最善の結果を生じるでしょう。

@tf.function
def f(x):
  print("Traced with", x)
  tf.print("Executed with", x)

f(1)
f(1)
f(2)
Traced with 1
Executed with 1
Executed with 1
Traced with 2
Executed with 2

tf.function の各起動中に Python コードを実行することを望むのであれば、tf.py_function が脱出ハッチ (= exit hatch) です。tf.py_function の欠点はそれは可搬ではなく特に非効率的で、分散セットアップ (マルチ GPU, TPU) では上手く動作しません。また、tf.py_function はグラフに配線 (= wire) されなければならず、それは総ての入力/出力を tensor にキャストします。

external_list = []

def side_effect(x):
  print('Python side effect')
  external_list.append(x)

@tf.function
def f(x):
  tf.py_function(side_effect, inp=[x], Tout=[])

f(1)
f(1)
f(1)
assert len(external_list) == 3
# .numpy() call required because py_function casts 1 to tf.constant(1)
assert external_list[0].numpy() == 1
WARNING: Logging before flag parsing goes to stderr.
W0629 01:08:56.691862 140487172290304 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.int32
W0629 01:08:56.694054 140487155504896 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.int32
W0629 01:08:56.695917 140487172290304 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.int32

Python side effect
Python side effect
Python side effect

 

Python 状態に注意する

generator と iterator のような、多くの Python 特徴は状態を追跡するために Python ランタイムに依拠します。一般に、これらの構成物が Eager モードで期待どおりに動作する一方で、多くの予期せぬことが tracing 動作により tf.function 内で起きるかもしれません。

一つのサンプルを与えるために、進んだ iterator 状態は Python 副作用で従って tracing の間だけに発生します。

external_var = tf.Variable(0)
@tf.function
def buggy_consume_next(iterator):
  external_var.assign_add(next(iterator))
  tf.print("Value of external_var:", external_var)

iterator = iter([0, 1, 2, 3])
buggy_consume_next(iterator)
# This reuses the first value from the iterator, rather than consuming the next value.
buggy_consume_next(iterator)
buggy_consume_next(iterator)
Value of external_var: 0
Value of external_var: 0
Value of external_var: 0

iterator が完全に tf.function 内で生成されて消費される場合には、それは正しく動作します。けれども、全体の iterator が多分 trace され、これは巨大グラフ (= giant graph) に繋がるかもしれません。これは貴方が望むものかもしれません。しかし貴方が Python リストで表わされる巨大な in-memory データセット上で訓練している場合、これは非常に巨大なグラフを生成するかもしれず、そして tf.function はスピードアップを生み出さないでしょう。

Python データに渡り iterate することを望む場合、最も安全な方法はそれを tf.data.Dataset でラップして “for x in y” イディオムを使用することです。AutoGraph は y が tensor か tf.data.Dataset のときループのための安全に変換するための特別なサポートを持ちます。

def measure_graph_size(f, *args):
  g = f.get_concrete_function(*args).graph
  print("{}({}) contains {} nodes in its graph".format(
      f.__name__, ', '.join(map(str, args)), len(g.as_graph_def().node)))

@tf.function
def train(dataset):
  loss = tf.constant(0)
  for x, y in dataset:
    loss += tf.abs(y - x) # Some dummy computation.
  return loss

small_data = [(1, 1)] * 2
big_data = [(1, 1)] * 10
measure_graph_size(train, small_data)
measure_graph_size(train, big_data)

measure_graph_size(train, tf.data.Dataset.from_generator(
    lambda: small_data, (tf.int32, tf.int32)))
measure_graph_size(train, tf.data.Dataset.from_generator(
    lambda: big_data, (tf.int32, tf.int32)))
W0629 01:08:57.038732 140490414335744 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/data/ops/dataset_ops.py:505: py_func (from tensorflow.python.ops.script_ops) is deprecated and will be removed in a future version.
Instructions for updating:
tf.py_func is deprecated in TF V2. Instead, there are two
    options available in V2.
    - tf.py_function takes a python function which manipulates tf eager
    tensors instead of numpy arrays. It's easy to convert a tf eager tensor to
    an ndarray (just call tensor.numpy()) but having access to eager tensors
    means <a href="../../../versions/r2.0/api_docs/python/tf/py_function"><code>tf.py_function</code></a>s can use accelerators such as GPUs as well as
    being differentiable using a gradient tape.
    - tf.numpy_function maintains the semantics of the deprecated tf.py_func
    (it is not differentiable, and manipulates numpy arrays). It drops the
    stateful argument making all functions stateful.
    

train([(1, 1), (1, 1)]) contains 8 nodes in its graph
train([(1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1)]) contains 32 nodes in its graph
train(<DatasetV1Adapter shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 4 nodes in its graph
train(<DatasetV1Adapter shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 4 nodes in its graph

Python/Numpy データを Dataset にラッピングするとき、tf.data.Dataset.from_generator versus tf.data.Dataset.from_tensors に留意してください。前者はデータを Python で保持して tf.py_function を通してそれを取得します、これはパフォーマンスと密接な関係を持つかもしれず、その一方で後者はデータのコピーをグラフの一つの巨大な tf.constant() ノードとして束ねます、これはメモリとの密接な関係を持つかもしれません。

TFRecordDataset/CsvDataset/etc. を通してファイルからデータを読むのがデータを消費する最も効果的な方法です、何故ならば TensorFlow 自身がデータの非同期ロードと先読みを管理できるからです、Python を巻き込まなければならないことなしに。

 

自動制御依存性

functions のプログラミングモデルとしての非常に魅力的な特質は、一般的なデータフロー・グラフに渡り、functions がランタイムに何がコードの意図された動作であったかについてより多くの情報を与えられることです。

例えば、同じ変数への複数の読み書きを持つコードを書くとき、データフロー・グラフは演算の元々意図された順序を自然にエンコードしないかもしれません。tf.function では、元の Python コードのステートメントの実行順序を参照して実行順序の曖昧さを解決します。このようにして、tf.function のステートフルな演算の順序は Eager モードのセマンティクスをレプリケートします。

これは手動の制御依存性を追加する必要がないことを意味します ; 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=460, shape=(), dtype=float32, numpy=10.0>

 

Variables

tf.function で変数作成と利用を非常に容易にするためにコードの意図された実行順序を活用するのと同じアイデアを利用できます。けれども一つの非常に重要な警告があります、それは variable では eager モードと graph モードで異なる動作をするコードを書くことが可能であることです。

特に、これは各呼び出しで新しい Variable を作成するときに発生します。tracing セマンティクスにより、tf.function は各呼び出しで同じ変数を再利用しますが、eager モードは各呼び出しで新しい変数を作成します。この間違いから守るために、危険な変数作成動作を検出する場合には tf.function はエラーを上げます。

@tf.function
def f(x):
  v = tf.Variable(1.0)
  v.assign_add(x)
  return v

with assert_raises(ValueError):
  f(1.0)
Caught expected exception 
  <class 'ValueError'>: in converted code:

    <ipython-input-17-73e410646579>:3 f  *
        v = tf.Variable(1.0)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/variables.py:262 __call__
        return cls._variable_v2_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/variables.py:256 _variable_v2_call
        shape=shape)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/variables.py:60 getter
        return captured_getter(captured_previous, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/eager/def_function.py:364 invalid_creator_scope
        "tf.function-decorated function tried to create "

    ValueError: tf.function-decorated function tried to create variables on non-first call.
# Non-ambiguous code is ok though

v = tf.Variable(1.0)

@tf.function
def f(x):
  return v.assign_add(x)

print(f(1.0))  # 2.0
print(f(2.0))  # 4.0
tf.Tensor(2.0, shape=(), dtype=float32)
tf.Tensor(4.0, shape=(), dtype=float32)
# 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)

print(g(1.0))  # 2.0
print(g(2.0))  # 4.0
tf.Tensor(2.0, shape=(), dtype=float32)
tf.Tensor(4.0, shape=(), dtype=float32)
# 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]

print(fn(tf.constant(1.0)))
print(fn(tf.constant(3.0)))
tf.Tensor(12.0, shape=(), dtype=float32)
tf.Tensor(36.0, shape=(), dtype=float32)

 

AutoGraph を使用する

autograph ライブラリは tf.function と完全に統合され、そしてそれはグラフで動的に実行するための Tensor に依拠する条件節とループを書き換えます。

tf.condtf.while_loop は tf.function とともに動作し続けますが、制御フローを持つコードはしばしば命令型スタイルで書かれるとき書いて理解することが容易です。

# 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([5]))
[0.604465961 0.235631824 0.292372584 0.0933123827 0.8393749]
[0.540219843 0.231365576 0.284317046 0.093042478 0.685477853]
[0.493154347 0.227323771 0.276895881 0.0927749 0.595069051]
[0.456716418 0.223487303 0.270029694 0.0925096273 0.533531547]
[0.427404225 0.219839349 0.263652474 0.0922466218 0.488075942]
[0.403149694 0.21636492 0.257708609 0.0919858441 0.452688]
[0.382640719 0.213050663 0.252151072 0.0917272717 0.424106032]
[0.364998549 0.209884614 0.2469396 0.0914708674 0.40038386]
[0.349609256 0.206856042 0.242039666 0.091216594 0.380277365]
[0.336028934 0.203955248 0.237421349 0.0909644365 0.362948328]
[0.323927581 0.201173469 0.233058617 0.0907143652 0.347808361]
[0.313053906 0.198502809 0.228928685 0.0904663429 0.334430456]
[0.303212792 0.195936009 0.225011513 0.0902203396 0.322496086]
[0.294249982 0.193466514 0.221289411 0.0899763331 0.311762124]
[0.286041766 0.191088319 0.21774666 0.0897343 0.302039325]
[0.278487593 0.188795939 0.214369327 0.0894942135 0.293177754]
[0.27150473 0.186584324 0.21114485 0.0892560408 0.285056978]
[0.265024424 0.184448823 0.208062038 0.0890197605 0.27757892]
[0.258989 0.182385176 0.205110788 0.0887853503 0.270662814]

<tf.Tensor: id=727, shape=(5,), dtype=float32, numpy=
array([0.25334966, 0.1803894 , 0.202282  , 0.08855279, 0.26424146],
      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))
def tf__f(x):
  do_return = False
  retval_ = ag__.UndefinedReturnValue()

  def loop_test(x_1):
    return ag__.converted_call('reduce_sum', tf, ag__.ConversionOptions(recursive=True, force_conversion=False, optional_features=(), internal_convert_user_code=True), (x_1,), None) > 1

  def loop_body(x_1):
    ag__.converted_call('print', tf, ag__.ConversionOptions(recursive=True, force_conversion=False, optional_features=(), internal_convert_user_code=True), (x_1,), None)
    x_1 = ag__.converted_call('tanh', tf, ag__.ConversionOptions(recursive=True, force_conversion=False, optional_features=(), internal_convert_user_code=True), (x_1,), None)
    return x_1,
  x, = ag__.while_stmt(loop_test, loop_body, (x,))
  do_return = True
  retval_ = x
  cond = ag__.is_undefined_return(retval_)

  def get_state():
    return ()

  def set_state(_):
    pass

  def if_true():
    retval_ = None
    return retval_

  def if_false():
    return retval_
  retval_ = ag__.if_stmt(cond, if_true, if_false, get_state, set_state)
  return retval_

 

AutoGraph: 条件節

AutoGraph は if ステートメントを等値の tf.cond 呼び出しに変換します。

この置き換えは条件が Tensor である場合に行われます。さもなければ、条件節は tracing の間に実行されます。

def test_tf_cond(f, *args):
  g = f.get_concrete_function(*args).graph
  if any(node.name == 'cond' for node in g.as_graph_def().node):
    print("{}({}) uses tf.cond.".format(
        f.__name__, ', '.join(map(str, args))))
  else:
    print("{}({}) executes normally.".format(
        f.__name__, ', '.join(map(str, args))))
@tf.function
def hyperparam_cond(x, training=True):
  if training:
    x = tf.nn.dropout(x, rate=0.5)
  return x

@tf.function
def maybe_tensor_cond(x):
  if x < 0:
    x = -x
  return x

test_tf_cond(hyperparam_cond, tf.ones([1], dtype=tf.float32))
test_tf_cond(maybe_tensor_cond, tf.constant(-1))
test_tf_cond(maybe_tensor_cond, -1)
hyperparam_cond(tf.Tensor([1.], shape=(1,), dtype=float32)) executes normally.
maybe_tensor_cond(tf.Tensor(-1, shape=(), dtype=int32)) uses tf.cond.
maybe_tensor_cond(-1) executes normally.

tf.cond は多くの微妙な点を持ちます。- それは条件節の両側を trace し、それから条件に依拠して、ランタイムに適切な分岐を選択することにより動作します。両側の trace は Python コードの予期せぬ実行という結果になるかもしれません - 一つの分岐が downstream で使用される tensor を作成する場合、他の分岐もまた tensor を作成しなければならないことを必要とします。

@tf.function
def f():
  x = tf.constant(0)
  if tf.constant(True):
    x = x + 1
    print("Tracing `then` branch")
  else:
    x = x - 1
    print("Tracing `else` branch")
  return x

f()
Tracing `then` branch
Tracing `else` branch

<tf.Tensor: id=802, shape=(), dtype=int32, numpy=1>
@tf.function
def f():
  if tf.constant(True):
    x = tf.ones([3, 3])
  return x

# Throws an error because both branches need to define `x`.
with assert_raises(ValueError):
  f()
Caught expected exception 
  <class 'ValueError'>: in converted code:

    <ipython-input-26-3ae6a92ff50d>:3 f  *
        if tf.constant(True):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:439 if_stmt
        return tf_if_stmt(cond, body, orelse, get_state, set_state)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:456 tf_if_stmt
        outputs, final_state = control_flow_ops.cond(cond, body, orelse)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/util/deprecation.py:507 new_func
        return func(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/control_flow_ops.py:1147 cond
        return cond_v2.cond_v2(pred, true_fn, false_fn, name)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/cond_v2.py:86 cond_v2
        op_return_value=pred)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/framework/func_graph.py:716 func_graph_from_py_func
        func_outputs = python_func(*func_args, **func_kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:486 wrapper
        outputs = func()
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:512 wrapper
        tuple(s.symbol_name for s in undefined)))

    ValueError: The following symbols must also be initialized in the else branch: ('x',). Alternatively, you may initialize them before the if statement.

 

AutoGraph とループ

AutoGraph はループを変換するための 2, 3 の単純なルールを持ちます。

  • for: iterable が tensor である場合に変換します。
  • while: while 条件が tensor に依拠する場合に変換されます。

ループが変換される場合、それは tf.while_loop で動的に変換され、あるいは "for x in tf.data.Dataset" の特別なケースでは、tf.data.Dataset.reduce に変換されます。

ループが変換されない場合には、それは静的に展開されます。

def test_dynamically_unrolled(f, *args):
  g = f.get_concrete_function(*args).graph
  if any(node.name == 'while' for node in g.as_graph_def().node):
    print("{}({}) uses tf.while_loop.".format(
        f.__name__, ', '.join(map(str, args))))
  elif any(node.name == 'ReduceDataset' for node in g.as_graph_def().node):
    print("{}({}) uses tf.data.Dataset.reduce.".format(
        f.__name__, ', '.join(map(str, args))))
  else:
    print("{}({}) gets unrolled.".format(
        f.__name__, ', '.join(map(str, args))))
@tf.function
def for_in_range():
  x = 0
  for i in range(5):
    x += i
  return x

@tf.function
def for_in_tfrange():
  x = tf.constant(0, dtype=tf.int32)
  for i in tf.range(5):
    x += i
  return x

@tf.function
def for_in_tfdataset():
  x = tf.constant(0, dtype=tf.int64)
  for i in tf.data.Dataset.range(5):
    x += i
  return x

test_dynamically_unrolled(for_in_range)
test_dynamically_unrolled(for_in_tfrange)
test_dynamically_unrolled(for_in_tfdataset)
for_in_range() gets unrolled.
for_in_tfrange() uses tf.while_loop.
for_in_tfdataset() uses tf.data.Dataset.reduce.
@tf.function
def while_py_cond():
  x = 5
  while x > 0:
    x -= 1
  return x

@tf.function
def while_tf_cond():
  x = tf.constant(5)
  while x > 0:
    x -= 1
  return x

test_dynamically_unrolled(while_py_cond)
test_dynamically_unrolled(while_tf_cond)
while_py_cond() gets unrolled.
while_tf_cond() uses tf.while_loop.

tensor に依拠する break か早期の return 節を持つのであれば、top-level 条件や iterable もまた tensor であるべきです。

@tf.function
def buggy_while_py_true_tf_break(x):
  while True:
    if tf.equal(x, 0):
      break
    x -= 1
  return x

@tf.function
def while_tf_true_tf_break(x):
  while tf.constant(True):
    if tf.equal(x, 0):
      break
    x -= 1
  return x

with assert_raises(TypeError):
  test_dynamically_unrolled(buggy_while_py_true_tf_break, 5)
test_dynamically_unrolled(while_tf_true_tf_break, 5)

@tf.function
def buggy_py_for_tf_break():
  x = 0
  for i in range(5):
    if tf.equal(i, 3):
      break
    x += i
  return x

@tf.function
def tf_for_tf_break():
  x = 0
  for i in tf.range(5):
    if tf.equal(i, 3):
      break
    x += i
  return x

with assert_raises(TypeError):
  test_dynamically_unrolled(buggy_py_for_tf_break)
test_dynamically_unrolled(tf_for_tf_break)
Caught expected exception 
  <class 'TypeError'>: in converted code:

    <ipython-input-30-220fba6e1df8>:3 buggy_while_py_true_tf_break  *
        while True:
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:313 while_stmt
        return _py_while_stmt(test, body, init_state, opts)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:401 _py_while_stmt
        while test(*state):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/framework/ops.py:698 __bool__
        raise TypeError("Using a `tf.Tensor` as a Python `bool` is not allowed. "

    TypeError: Using a `tf.Tensor` as a Python `bool` is not allowed. Use `if t is not None:` instead of `if t:` to test if a tensor is defined, and use TensorFlow ops such as tf.cond to execute subgraphs conditioned on the value of a tensor.

while_tf_true_tf_break(5) uses tf.while_loop.
Caught expected exception 
  <class 'TypeError'>: in converted code:

    <ipython-input-30-220fba6e1df8>:24 buggy_py_for_tf_break  *
        for i in range(5):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:110 for_stmt
        return _py_for_stmt(iter_, extra_test, body, init_state)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:117 _py_for_stmt
        if extra_test is not None and not extra_test(*state):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/framework/ops.py:698 __bool__
        raise TypeError("Using a `tf.Tensor` as a Python `bool` is not allowed. "

    TypeError: Using a `tf.Tensor` as a Python `bool` is not allowed. Use `if t is not None:` instead of `if t:` to test if a tensor is defined, and use TensorFlow ops such as tf.cond to execute subgraphs conditioned on the value of a tensor.

tf_for_tf_break() uses tf.while_loop.

動的に展開されたループからの結果を累積するために、tf.TensorArray を使用することを望むでしょう。

batch_size = 2
seq_len = 3
feature_size = 4

def rnn_step(inp, state):
  return inp + state

@tf.function
def dynamic_rnn(rnn_step, input_data, initial_state):
  # [batch, time, features] -> [time, batch, features]
  input_data = tf.transpose(input_data, [1, 0, 2])
  max_seq_len = input_data.shape[0]

  states = tf.TensorArray(tf.float32, size=max_seq_len)
  state = initial_state
  for i in tf.range(max_seq_len):
    state = rnn_step(input_data[i], state)
    states = states.write(i, state)
  return tf.transpose(states.stack(), [1, 0, 2])
  
dynamic_rnn(rnn_step,
            tf.random.uniform([batch_size, seq_len, feature_size]),
            tf.zeros([batch_size, feature_size]))
<tf.Tensor: id=1307, shape=(2, 3, 4), dtype=float32, numpy=
array([[[0.2641269 , 0.32148373, 0.27760386, 0.871022  ],
        [0.36447   , 0.82768893, 1.1970165 , 1.2398282 ],
        [1.1957974 , 1.242557  , 1.2163385 , 1.7522211 ]],

       [[0.11496973, 0.8242916 , 0.5828695 , 0.01092482],
        [0.64337647, 1.7383248 , 1.182523  , 0.94805145],
        [1.2105927 , 1.9715753 , 1.2999045 , 1.6146696 ]]], dtype=float32)>

tf.cond と同様に、tf.while_loop もまた微妙な点を伴います。- ループは 0 回の実行が可能ですから、while_loop の downstream で使用される総ての tensor は上のループで初期化されなければなりません。- 総てのループ変数の shape/dtype は各 iteration と一環していなければなりません。

@tf.function
def buggy_loop_var_uninitialized():
  for i in tf.range(3):
    x = i
  return x

@tf.function
def f():
  x = tf.constant(0)
  for i in tf.range(3):
    x = i
  return x

with assert_raises(ValueError):
  buggy_loop_var_uninitialized()
f()
Caught expected exception 
  <class 'ValueError'>: in converted code:

    <ipython-input-32-4012abce963a>:3 buggy_loop_var_uninitialized  *
        for i in tf.range(3):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:95 for_stmt
        return _known_len_tf_for_stmt(iter_, extra_test, body, init_state)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:125 _known_len_tf_for_stmt
        _disallow_undefs_into_loop(*init_state)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:50 _disallow_undefs_into_loop
        tuple(s.symbol_name for s in undefined)))

    ValueError: TensorFlow requires that the following symbols must be defined before the loop: ('x',)


<tf.Tensor: id=1371, shape=(), dtype=int32, numpy=2>
@tf.function
def buggy_loop_type_changes():
  x = tf.constant(0, dtype=tf.float32)
  for i in tf.range(3): # Yields tensors of type tf.int32...
    x = i
  return x

with assert_raises(tf.errors.InvalidArgumentError):
  buggy_loop_type_changes()
Caught expected exception 
  <class 'tensorflow.python.framework.errors_impl.InvalidArgumentError'>: Input 1 of node while/merge/_10 was passed int32 from while/next_iteration/_28:0 incompatible with expected float. [Op:__inference_buggy_loop_type_changes_1428]
@tf.function
def buggy_concat():
  x = tf.ones([0, 10])
  for i in tf.range(5):
    x = tf.concat([x, tf.ones([1, 10])], axis=0)
  return x

with assert_raises(ValueError):
  buggy_concat()
  
@tf.function
def concat_with_padding():
  x = tf.zeros([5, 10])
  for i in tf.range(5):
    x = tf.concat([x[:i], tf.ones([1, 10]), tf.zeros([4-i, 10])], axis=0)
    x.set_shape([5, 10])
  return x

concat_with_padding()
Caught expected exception 
  <class 'ValueError'>: in converted code:

    <ipython-input-34-d212bdeeeb5e>:4 buggy_concat  *
        for i in tf.range(5):
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:95 for_stmt
        return _known_len_tf_for_stmt(iter_, extra_test, body, init_state)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:156 _known_len_tf_for_stmt
        opts=dict(maximum_iterations=n))
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/autograph/operators/control_flow.py:327 _tf_while_stmt
        retval = control_flow_ops.while_loop(test, body, init_state, **opts)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/control_flow_ops.py:2646 while_loop
        return_same_structure=return_same_structure)
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/while_v2.py:213 while_loop
        len_orig_loop_vars], expand_composites=True))
    /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/while_v2.py:869 _check_shapes_compat
        "specify a less-specific shape." % (input_t.name, shape, t.shape))

    ValueError: Input tensor 'ones:0' enters the loop with shape (0, 10), but has shape (1, 10) after one iteration. To allow the shape to vary across iterations, use the `shape_invariants` argument of tf.while_loop to specify a less-specific shape.


<tf.Tensor: id=1549, shape=(5, 10), dtype=float32, numpy=
array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]], dtype=float32)>
 

以上






TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- カスタム訓練: ウォークスルー

TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- カスタム訓練: ウォークスルー (翻訳/解説)

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

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

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

 

 

カスタマイズ :- カスタム訓練: ウォークスルー

このガイドはアイリス花を種で分類するために機械学習を使用します。それは TensorFlow を次のために使用します : 1. モデルを構築する, 2. サンプルデータ上でこのモデルを訓練する, そして 3. 未知のデータについて予測を行なうためにモデルを使用する。

 

TensorFlow プログラミング

このガイドはこれらの高位 TensorFlow 概念を使用します :

  • TensorFlow のデフォルト eager execution 開発環境を使用します、
  • Datasets API でデータをインポートします、
  • TensorFlow の Keras API でモデルと層を構築します。

このチュートリアルは多くの TensorFlow プログラムのように構造化されています :

  1. データセットをインポートして解析します。
  2. モデルのタイプを選択します。
  3. モデルを訓練します。
  4. モデルの有効性を評価します。
  5. 予測を行なうために訓練されたモデルを使用します。

 

プログラムのセットアップ

インポートを configure する

TensorFlow と他の必要な Python モジュールをインポートします。デフォルトでは、TensorFlow は演算を直ちに評価するために eager execution を使用し、後で実行される 計算グラフ を作成する代わりに具体的な値を返します。もし貴方が REPL か python 対話的コンソールに慣れているのであれば、これは馴染むでしょう。

from __future__ import absolute_import, division, print_function, unicode_literals

import os
import matplotlib.pyplot as plt
!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf
print("TensorFlow version: {}".format(tf.__version__))
print("Eager execution: {}".format(tf.executing_eagerly()))
TensorFlow version: 2.0.0-beta1
Eager execution: True

 

アイリス分類問題

貴方が植物学者で見つけたアイリス花の各々を分類するための自動化された方法を求めていると想像してください。機械学習は花を統計的に分類する多くのアルゴリズムを提供します。例えば、洗練された機械学習プログラムは写真を基にして花を分類できるでしょう。私達の野望はより控えめなものです — アイリス花をそれらのがく片 (= sepal) と花弁 (= petal) の長さと幅を基にして分類していきます。

アイリス属は約 300 種を必然的に伴いますが、私達のプログラムは以下の3つだけを分類します :

  • アイリス・セトサ
  • アイリス・バージニカ
  • アイリス・バージカラー

Figure 1. アイリス・セトサ (by Radomil, CC BY-SA 3.0)、アイリス・バージカラー (by Dlanglois, CC BY-SA 3.0)、そして アイリス・バージニカ (by Frank Mayfield, CC BY-SA 2.0) 。

幸いなことに、がく片と花弁の計測を持つ 120 アイリス花のデータセット を既に誰かが作成しています。これは初心者の機械学習分類問題に対してポピュラーである古典的なデータセットです。

 

訓練データセットをインポートして解析する

データセットファイルをダウンロードしてそれをこの Python プログラムで使用できる構造に変換します。

 

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

tf.keras.utils.get_file 関数を使用して訓練データセット・フアイルをダウンロードします。これはダウンロードされたファイルのファイルパスを返します。

train_dataset_url = "https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv"

train_dataset_fp = tf.keras.utils.get_file(fname=os.path.basename(train_dataset_url),
                                           origin=train_dataset_url)

print("Local copy of the dataset file: {}".format(train_dataset_fp))
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv
8192/2194 [================================================================================================================] - 0s 0us/step
Local copy of the dataset file: /home/kbuilder/.keras/datasets/iris_training.csv

 

データを調べる

このデータセット, iris_training.csv, はプレーンテキスト・ファイルでカンマ区切り値としてフォーマットされた表データ (CSV) をストアしています。最初の 5 エントリを見るために head -n5 コマンドを使用します :

!head -n5 {train_dataset_fp}
120,4,setosa,versicolor,virginica
6.4,2.8,5.6,2.2,2
5.0,2.3,3.3,1.0,1
4.9,2.5,4.5,1.7,2
4.9,3.1,1.5,0.1,0

データセットのこのビューから、次のことが分かります :

  1. 最初の行はデータセットについての情報を含むヘッダです :
    • 120 総計サンプルがあります。各サンプルは4つの特徴と3つの可能なラベル名の1つを持ちます。
  2. 続く行はデータレコードで、行毎に1つの サンプル で、そこでは :
    • 最初の4つのフィールドは 特徴 です : これらはサンプルの特質です。ここで、フィールドは花の計測を表わす浮動小数点数を保持しています。
    • 最後のカラムは ラベル です : これは予測することを望む値です。このデータセットのためには、それは花の名前に相当する 0, 1, または 2 の整数値です。

それをコードに書き出しましょう :

# column order in CSV file
column_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']

feature_names = column_names[:-1]
label_name = column_names[-1]

print("Features: {}".format(feature_names))
print("Label: {}".format(label_name))
Features: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
Label: species

各ラベルは文字列名 (例えば、”setosa”) に関係しますが、機械学習は典型的には数値に依拠します。ラベルの数字は命名された表現にマップされます、次のように :

  • 0: アイリス・セトサ
  • 1: アイリス・バージカラー
  • 2: アイリス・バージニカ

特徴とラベルについてのより多くの情報については、ML Terminology section of the Machine Learning Crash Course を見てください。

class_names = ['Iris setosa', 'Iris versicolor', 'Iris virginica']

 

tf.data.Dataset を作成する

TensorFlow の Dataset API はデータをモデルにロードするための多くの一般的なケースを処理します。これはデータを読みそしてそれを訓練のために使用される形式に変形するための高位 API です。より多くの情報のためには Dataset クイックスタート・ガイド を見てください。

データセットは CSV フォーマットのテキストファイルですので、データを適切なフォーマットに解析するために make_csv_dataset 関数を使用します。この関数は訓練モデルのためのデータを生成しますので、デフォルトの挙動はデータをシャッフルして (shuffle=True, shuffle_buffer_size=10000)、データセットを永久に繰り返します (num_epochs=None)。batch_size パラメータもまた設定します。

batch_size = 32

train_dataset = tf.data.experimental.make_csv_dataset(
    train_dataset_fp,
    batch_size,
    column_names=column_names,
    label_name=label_name,
    num_epochs=1)
WARNING: Logging before flag parsing goes to stderr.
W0628 01:13:44.830377 140601493292800 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/data/experimental/ops/readers.py:498: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use <a href="../../../versions/r2.0/api_docs/python/tf/data/Dataset#interleave"><code>tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)</code></a> instead. If sloppy execution is desired, use `tf.data.Options.experimental_determinstic`.

make_csv_dataset 関数は (features, label) ペアの tf.data.Dataset を返します、ここで features は辞書 : {‘feature_name’: 値} です。

これらの Dataset オブジェクトは iterable です。features のバッチを見てみましょう :

features, labels = next(iter(train_dataset))

print(features)
OrderedDict([('sepal_length', <tf.Tensor: id=65, shape=(32,), dtype=float32, numpy=
array([7.2, 6.7, 5.8, 6.8, 5.9, 6. , 5.7, 4.4, 5.1, 6.8, 7.7, 7.2, 5.5,
       6.3, 7.4, 5.8, 6.2, 6.3, 5. , 5.5, 5.4, 4.6, 5.2, 4.6, 6.3, 4.6,
       6.3, 7.3, 6.2, 7. , 5.1, 6.1], dtype=float32)>), ('sepal_width', <tf.Tensor: id=66, shape=(32,), dtype=float32, numpy=
array([3.2, 3.1, 2.7, 3. , 3.2, 2.2, 2.8, 2.9, 3.8, 3.2, 2.6, 3. , 2.6,
       3.4, 2.8, 2.6, 2.2, 3.3, 3.5, 3.5, 3.7, 3.4, 2.7, 3.6, 3.3, 3.1,
       2.5, 2.9, 3.4, 3.2, 3.5, 2.8], dtype=float32)>), ('petal_length', <tf.Tensor: id=63, shape=(32,), dtype=float32, numpy=
array([6. , 5.6, 4.1, 5.5, 4.8, 5. , 4.5, 1.4, 1.9, 5.9, 6.9, 5.8, 4.4,
       5.6, 6.1, 4. , 4.5, 6. , 1.3, 1.3, 1.5, 1.4, 3.9, 1. , 4.7, 1.5,
       5. , 6.3, 5.4, 4.7, 1.4, 4. ], dtype=float32)>), ('petal_width', <tf.Tensor: id=64, shape=(32,), dtype=float32, numpy=
array([1.8, 2.4, 1. , 2.1, 1.8, 1.5, 1.3, 0.2, 0.4, 2.3, 2.3, 1.6, 1.2,
       2.4, 1.9, 1.2, 1.5, 2.5, 0.3, 0.2, 0.2, 0.3, 1.4, 0.2, 1.6, 0.2,
       1.9, 1.8, 2.3, 1.4, 0.3, 1.3], dtype=float32)>)])

同様な特徴は一緒にグループ化、あるいはバッチ化されることが分かるでしょう。各サンプルの行のフィールドは対応する特徴配列に付加されます。これらの特徴配列にストアされるサンプルの数を設定するためには batch_size を変更します。

バッチからの幾つかの特徴をプロットすることで幾つかのクラスタを見ることから始めることができます :

plt.scatter(features['petal_length'],
            features['sepal_length'],
            c=labels,
            cmap='viridis')

plt.xlabel("Petal length")
plt.ylabel("Sepal length")
plt.show()

モデル構築ステップを単純化するために、特徴辞書を shape: (batch_size, num_features) を持つシングル配列に再パッケージする関数を作成します。

この関数は tf.stack メソッドを使用し、これは tensor のリストからの値を取得して指定された次元で結合された tensor を作成します。

def pack_features_vector(features, labels):
  """Pack the features into a single array."""
  features = tf.stack(list(features.values()), axis=1)
  return features, labels

それから各 (features,label) ペアの特徴を訓練データセットにパックするために tf.data.Dataset.map メソッドを使用します :

train_dataset = train_dataset.map(pack_features_vector)

Dataset の特徴要素は今では shape (batch_size, num_features) を持つ配列です。最初の幾つかのサンプルを見てみましょう :

features, labels = next(iter(train_dataset))

print(features[:5])
tf.Tensor(
[[7.2 3.2 6.  1.8]
 [6.7 3.1 5.6 2.4]
 [5.8 2.7 4.1 1. ]
 [6.8 3.  5.5 2.1]
 [5.9 3.2 4.8 1.8]], shape=(5, 4), dtype=float32)

 

モデルのタイプを選択する

何故モデルなのでしょう?

モデル は特徴とラベルの間の関係性です。アイリス分類問題については、モデルはがく片と花弁の計測と予測されるアイリス種の間の関係性を定義します。ある単純なモデルは代数の 2, 3 行で記述できますが、複雑な機械学習モデルは要約することが難しい巨大な数のパラメータを持ちます。

貴方は機械学習を使用することなしに4つの特徴とアイリス種の間の関係性を決定できるでしょうか?つまり、貴方はモデルを作成するために伝統的なプログラミング技術 (例えば、多くの条件ステートメント) を利用できますか?多分 (可能でしょう) — もし貴方がデータセットをがく片と花弁の計測と特定の種間の関係性を決定するために十分に長く解析したのであれば。そしてこれはより複雑なデータセット上では難しく — 多分不可能に — なるでしょう。良い機械学習アプローチは貴方のためにモデルを決定します。もし十分な代表的なサンプルを正しい機械学習モデル・タイプに供給すれば、プログラムはその関係性を貴方のために解くでしょう。

 

モデルを選択する

私達は訓練するモデルの種類を選択する必要があります。多くのモデルのタイプがあり良い一つを選択するには経験が必要です。このチュートリアルではアイリス分類問題を解くためにニューラルネットワークを使用します。ニューラルネットワーク は特徴とラベル間の複雑な関係性を見つけることができます。それは高度に構造化されたグラフで、一つかそれ以上の 隠れ層 へと体系化されます。各隠れ層は一つかそれ以上の ニューロン から成ります。ニューラルネットワークの幾つかのカテゴリがあり、このプログラムは dense、あるいは 完全結合ニューラルネットワーク を使用します : 一つの層のニューロンは前の層の総てのニューロンからの入力接続を受け取ります。例えば、Figure 2 は入力層、2つの隠れ層、そして出力層から成る dense ニューラルネットワークを示します :


Figure 2. 特徴、隠れ層、そして予測を持つニューラルネットワーク

Figure 2 からのモデルが訓練されてラベル付けされていないサンプルが供給されたとき、それは3つの予測を生成します : この花が与えられたアイリス種である尤度です。この予測は 推論 と呼ばれます。このサンプルについて、出力予測の合計は 1.0 です。Figure 2 では、この予測は次のように分解されます : アイリス・セトサのために 0.02, アイリス・バージカラーのために 0.95, そしてアイリス・バージニカのために 0.03 です。これはモデルは — 95% の確率で — ラベル付けされていないサンプル花がアイリス・バージカラーであると予測していることを意味します。

 

Keras を使用してモデルを作成する

TensorFlow tf.keras API はモデルと層を作成するための好ましい方法です。これがモデルと実験の構築を容易にする一方で Keras は総てを一緒に接続する複雑さを処理します。

tf.keras.Sequential モデルは層の線形スタックです。そのコンストラクタは層インスタンスのリストを取り、この場合、それぞれ 10 ノードを持つ2つの Dense 層、そしてラベル予測を表わす3つのノードを持つ出力層です。最初の層の input_shape パラメータは dataset からの特徴の数に対応し、これは必要です。

model = tf.keras.Sequential([
  tf.keras.layers.Dense(10, activation=tf.nn.relu, input_shape=(4,)),  # input shape required
  tf.keras.layers.Dense(10, activation=tf.nn.relu),
  tf.keras.layers.Dense(3)
])

活性化関数 は層の各ノードの出力 shape を決定します。これらの非線形性は重要です — それらなしではモデルはシングル層と同じでしょう。多くの 利用可能な活性 がありますが、隠れ層のためには ReLU が一般的です。

隠れ層とニューロンの理想的な数は問題とデータセットに依拠します。機械学習の多くの様相のように、ニューラルネットワークの最善の shape の選択は知識と実験の混合を必要とします。おおよそのところは、隠れ層とニューロンの数を増加させると典型的にはよりパワフルなモデルを作成し、これは効果的に訓練するためにより多くのデータを必要とします。

 

モデルを使用する

このモデルが特徴のバッチに何を遂行するかを素早く見てみましょう :

predictions = model(features)
predictions[:5]
<tf.Tensor: id=231, shape=(5, 3), dtype=float32, numpy=
array([[1.1745981 , 0.15444025, 0.9319882 ],
       [1.2357749 , 0.07391439, 0.818767  ],
       [0.9062968 , 0.20347528, 0.91987234],
       [1.176329  , 0.10138635, 0.8369183 ],
       [1.1326532 , 0.09312753, 0.87961143]], dtype=float32)>

ここで、各サンプルは各クラスのために ロジット を返します。

これらのロジットを各クラスのための確率に変換するために、softmax 関数を使用します :

tf.nn.softmax(predictions[:5])
<tf.Tensor: id=237, shape=(5, 3), dtype=float32, numpy=
array([[0.46617538, 0.16807395, 0.3657507 ],
       [0.50712013, 0.15867966, 0.33420017],
       [0.3985883 , 0.19737542, 0.40403625],
       [0.48697177, 0.16621189, 0.34681636],
       [0.46947083, 0.16601537, 0.36451378]], dtype=float32)>

クラスに渡る tf.argmax を取れば予測されたクラス・インデックスを与えます。しかし、モデルはまだ訓練されていませんので、これらは良い予測ではありません。

print("Prediction: {}".format(tf.argmax(predictions, axis=1)))
print("    Labels: {}".format(labels))
Prediction: [0 0 2 0 0 0 0 2 2 0 0 0 0 0 0 0 0 0 2 2 2 2 0 2 0 2 0 0 0 0 2 0]
    Labels: [2 2 1 2 1 2 1 0 0 2 2 2 1 2 2 1 1 2 0 0 0 0 1 0 1 0 2 2 2 1 0 1]

 

モデルを訓練する

訓練 はモデルが徐々に最適化される、あるいはモデルがデータセットを学習する際の機械学習の段階です。目標は、初見のデータについて予測を行なうために訓練データセットの構造について十分に学習することです。もし訓練データセットについて学習し過ぎる場合には、予測はそれが見たデータに対して動作するのみで一般化できないでしょう。この問題は overfitting と呼ばれます — それは問題をどのように解くかを理解する代わりに答えを覚えるようなものです。

アイリス分類問題は 教師あり機械学習 の例です : モデルはラベルを含むサンプルから訓練されます。教師なし機械学習 では、サンプルはラベルを含みません。代わりに、モデルは典型的には特徴内のパターンを見つけます。

 

損失と勾配関数を定義する

訓練と評価ステージの両者はモデルの 損失 を計算する必要があります。これはモデルの予測が望まれるラベルからどのくらい離れているかを測定します、換言すれば、モデルがどのくらい悪く遂行しているかです。私達はこの値を最小化、あるいは最適化することを望みます。

私達のモデルは tf.keras.losses.SparseCategoricalCrossentropy 関数を使用してその損失を計算します、これはモデルのクラス確率予測と望まれるラベルを取り、サンプルに渡る平均損失を返します。

loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
def loss(model, x, y):
  y_ = model(x)

  return loss_object(y_true=y, y_pred=y_)


l = loss(model, features, labels)
print("Loss test: {}".format(l))
Loss test: 1.393274188041687

モデルを最適化するために使用される 勾配 を計算するために tf.GradientTape コンテキストを使用します。

def grad(model, inputs, targets):
  with tf.GradientTape() as tape:
    loss_value = loss(model, inputs, targets)
  return loss_value, tape.gradient(loss_value, model.trainable_variables)

 

optimizer を作成する

optimizer は loss 関数を最小化するために計算された勾配をモデルの変数に適用します。貴方は損失関数を曲面 (Figure 3 参照) として考えることができて私達は歩き回ることによりその最も低いポイントを見つけることを望みます。勾配は上りの最も険しい方向をポイントします — 従って私達は反対の道へ動いて進み丘を降ります。各バッチに対する損失と勾配を反復的に計算することにより、訓練の間にモデルを調整するでしょう。徐々に、モデルは損失を最小化するための重みとバイアスの最善の組み合わせを見つけるでしょう。そして損失がより小さくなれば、モデルの予測はより良くなります。


Figure 3. 3D 空間における時間に渡り可視化された最適化アルゴリズム。
(ソース: Stanford class CS231n, MIT License, Image credit: Alec Radford)

TensorFlow は訓練のために利用可能な多くの 最適化アルゴリズム を持ちます。このモデルは stochastic gradient descent (SGD) アルゴリズムを実装する tf.train.GradientDescentOptimizer を使用します。learning_rate は各反復のための丘を降りるためのステップサイズを設定します。これはより良い結果を得るために一般に調整するハイパーパラメータです。

optimizer をセットアップしましょう :

optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)

単一の最適化ステップを計算するためにこれを使用します :

loss_value, grads = grad(model, features, labels)

print("Step: {}, Initial Loss: {}".format(optimizer.iterations.numpy(),
                                          loss_value.numpy()))

optimizer.apply_gradients(zip(grads, model.trainable_variables))

print("Step: {},         Loss: {}".format(optimizer.iterations.numpy(),
                                          loss(model, features, labels).numpy()))
Step: 0, Initial Loss: 1.393274188041687
Step: 1,         Loss: 1.2710542678833008

 

訓練ループ

総てのピースが所定の位置にあり、モデルは訓練のための準備ができました! 訓練ループは (モデルが) より良い予測を行なうことを手助けするためにデータセットのサンプルをモデルに供給します。次のコードブロックはこれらの訓練ステップをセットアップします :

  1. 各エポックを反復します。エポックはデータセットを通す一つのパスです。
  2. (一つの) エポック内では、特徴 (x) とラベル (y) を取り込んでいる訓練 Dataset の各サンプルに渡り反復します。
  3. サンプルの特徴を使用して、予測を行ないそれをラベルと比較します。予測の不正確さを測定してモデルの損失と勾配を計算するためにそれを使用します。
  4. モデル変数を更新するために optimizer を使用します。
  5. 可視化のために幾つかのスタッツを追跡します。
  6. 各エポックのために反復します。

num_epochs 変数はデータセット・コレクションに渡りループする回数です。直感に反して、モデルをより長く訓練することはより良いモデルを保証しません。num_epochs は貴方が調整可能な ハイパーパラメータ です。正しい数を選択することは通常は経験と実験の両者が必要です。

## Note: Rerunning this cell uses the same model variables

# keep results for plotting
train_loss_results = []
train_accuracy_results = []

num_epochs = 201

for epoch in range(num_epochs):
  epoch_loss_avg = tf.keras.metrics.Mean()
  epoch_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()

  # Training loop - using batches of 32
  for x, y in train_dataset:
    # Optimize the model
    loss_value, grads = grad(model, x, y)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))

    # Track progress
    epoch_loss_avg(loss_value)  # add current batch loss
    # compare predicted label to actual label
    epoch_accuracy(y, model(x))

  # end epoch
  train_loss_results.append(epoch_loss_avg.result())
  train_accuracy_results.append(epoch_accuracy.result())

  if epoch % 50 == 0:
    print("Epoch {:03d}: Loss: {:.3f}, Accuracy: {:.3%}".format(epoch,
                                                                epoch_loss_avg.result(),
                                                                epoch_accuracy.result()))
Epoch 000: Loss: 1.174, Accuracy: 49.167%
Epoch 050: Loss: 0.070, Accuracy: 98.333%
Epoch 100: Loss: 0.056, Accuracy: 99.167%
Epoch 150: Loss: 0.049, Accuracy: 99.167%
Epoch 200: Loss: 0.045, Accuracy: 99.167%

 

損失関数を時間に渡り可視化する

モデルの訓練進捗を表示出力することは有用である一方で、この進捗を見ることはしばしばより有用です。TensorBoard は TensorFlow とともにパッケージ化されている素晴らしい可視化ツールですが、matplotlib モジュールを使用して基本的なチャートを作成することができます。

これらのチャートを解釈することは何某かの経験が必要ですが、貴方は損失が下がり精度が上がることを見ることを実際に望むでしょう。

fig, axes = plt.subplots(2, sharex=True, figsize=(12, 8))
fig.suptitle('Training Metrics')

axes[0].set_ylabel("Loss", fontsize=14)
axes[0].plot(train_loss_results)

axes[1].set_ylabel("Accuracy", fontsize=14)
axes[1].set_xlabel("Epoch", fontsize=14)
axes[1].plot(train_accuracy_results)
plt.show()

 

モデルの有効性を評価する

モデルが訓練された今、そのパフォーマンスについて何某かの統計情報を得ることができます。

評価するとはモデルがどのくらい効果的に予測を行なうかを決めることを意味します。アイリス分類でのモデルの有効性を決定するために幾つかのがく片と花弁の測定をモデルに渡してそれらがどのアイリス種を表わすかを予測するためにモデルに尋ねます。それから実際のラベルに対してモデルの予測を比較します。例えば、入力サンプルの半分の上で正しい種を選択したモデルは 0.5 の 精度 を持ちます。Figure 4 は少しばかりより効果的なモデルを示し、80 % 精度で 5 つの予測から 4 つを正しく得ます :

サンプル特徴 ラベル モデル予測
5.9 3.0 4.3 1.5 1 1
6.9 3.1 5.4 2.1 2 2
5.1 3.3 1.7 0.5 0 0
6.0 3.4 4.5 1.6 1 2
5.5 2.5 4.0 1.3 1 1


Figure 4. 80% 精度のアイリス分類器

 

テストデータセットをセットアップする

モデルを評価することはモデルを訓練することに類似しています。最大の違いはサンプルが訓練セットではなく別の テストセット に由来することです。モデルの有効性を公正に評価するためには、モデルを評価するために使用されるサンプルはモデルを訓練するために使用されたサンプルとは異なっていなければなりません。

テスト Dataset のセットアップは訓練 Dataset のセットアップと同様です。CSV テキストファイルをダウンロードしてその値を解析して、それを少々シャッフルします :

test_url = "https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv"

test_fp = tf.keras.utils.get_file(fname=os.path.basename(test_url),
                                  origin=test_url)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv
8192/573 [============================================================================================================================================================================================================================================================================================================================================================================================================================================] - 0s 0us/step
test_dataset = tf.data.experimental.make_csv_dataset(
    test_fp,
    batch_size,
    column_names=column_names,
    label_name='species',
    num_epochs=1,
    shuffle=False)

test_dataset = test_dataset.map(pack_features_vector)

 

テストデータセット上でモデルを評価する

訓練ステージとは違い、モデルはテストデータの単一の エポック を評価するだけです。次のコードセルでは、テストセットの各サンプルに渡り反復してモデルの予測を実際のラベルに対して比較しています。これはテストセット全体に渡るモデルの精度を測定するために使用されます。

test_accuracy = tf.keras.metrics.Accuracy()

for (x, y) in test_dataset:
  logits = model(x)
  prediction = tf.argmax(logits, axis=1, output_type=tf.int32)
  test_accuracy(prediction, y)

print("Test set accuracy: {:.3%}".format(test_accuracy.result()))
Test set accuracy: 96.667%

例えば最後のバッチ上で、モデルは通常は正しいことを見て取れます :

tf.stack([y,prediction],axis=1)
<tf.Tensor: id=157753, shape=(30, 2), dtype=int32, numpy=
array([[1, 1],
       [2, 2],
       [0, 0],
       [1, 1],
       [1, 1],
       [1, 1],
       [0, 0],
       [2, 1],
       [1, 1],
       [2, 2],
       [2, 2],
       [0, 0],
       [2, 2],
       [1, 1],
       [1, 1],
       [0, 0],
       [1, 1],
       [0, 0],
       [0, 0],
       [2, 2],
       [0, 0],
       [1, 1],
       [2, 2],
       [1, 1],
       [1, 1],
       [1, 1],
       [0, 0],
       [1, 1],
       [2, 2],
       [1, 1]], dtype=int32)>

 

予測を行なうために訓練されたモデルを使用する

私達はモデルを訓練してそれが良いことを「証明」しました — しかし完全ではありません — アイリス種の分類において。さて ラベル付けされていないサンプル 上で幾つか予測を行なうために訓練されたモデルを使用しましょう ; つまり、特徴を含むけれどもラベルを含まないサンプル上です。

現実世界では、ラベル付けされていないサンプルはアプリケーション、CSV ファイル、そしてデータ供給を含む多くの異なるソースに由来するでしょう。当面は、ラベルを予測するために3つのラベル付けされていないサンプルを手動で提供します。思い出してください、次のようにラベル数字は名前付けられた表現にマップされます :

  • 0: アイリス・セトサ
  • 1: アイリス・バージカラー
  • 2: アイリス・バージニカ
predict_dataset = tf.convert_to_tensor([
    [5.1, 3.3, 1.7, 0.5,],
    [5.9, 3.0, 4.2, 1.5,],
    [6.9, 3.1, 5.4, 2.1]
])

predictions = model(predict_dataset)

for i, logits in enumerate(predictions):
  class_idx = tf.argmax(logits).numpy()
  p = tf.nn.softmax(logits)[class_idx]
  name = class_names[class_idx]
  print("Example {} prediction: {} ({:4.1f}%)".format(i, name, 100*p))
Example 0 prediction: Iris setosa (99.9%)
Example 1 prediction: Iris versicolor (100.0%)
Example 2 prediction: Iris virginica (99.8%)
 

以上






TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- カスタム訓練: 基本

TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- カスタム訓練: 基本 (翻訳/解説)

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

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

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

 

カスタマイズ :- カスタム訓練: 基本

前のチュートリアルでは自動微分のための TensorFlow API をカバーしました、機械学習のための基本的なビルディングブロックです。このチュートリアルでは前のチュートリアルで紹介された TensorFlow プリミティブを使用してある単純な機械学習を行ないます。

TensorFlow はまた高位ニューラルネットワーク API (tf.keras) を含み、これはボイラープレートを削減するための有用な抽象を提供します。私達はニューラルネットワークで作業する人々のためにこれらの高位 API を強く推奨します。けれども、この短いチュートリアルでは最初の原理から強固な基礎を築くまでのニューラルネットワーク訓練をカバーします。

 

Setup

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf

 

Variable

TensorFlow の Tensor はイミュータブルでステートレスなオブジェクトです。けれども機械学習モデルは変化する状態を持つ必要があります : モデルの訓練につれて、予測を計算する同じコードが時間とともに異なる挙動をするはずです (できればより小さい損失で!)。計算の進行に渡り変化する必要があるこの状態を表わすために、Python はステートフルなプログラミング言語であるという事実に頼ることを選択できます :

# Using python state
x = tf.zeros([10, 10])
x += 2  # This is equivalent to x = x + 2, which does not mutate the original
        # value of x
print(x)
tf.Tensor(
[[2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]
 [2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]], shape=(10, 10), dtype=float32)

けれども TensorFlow はステートフルな組み込み演算を持ち、これらは状態の下位 Python 表現よりもしばしば使用するに快適です。例えばモデルの重みを表現するには、TensorFlow variable を使用するのがしばしば便利で効率的です。

Variable は値をストアするオブジェクトで、TensorFlow 計算で使用されるときはこのストアされた値から暗黙的に読みます。TensorFlow variable にストアされた値を操作する演算 (tf.assign_sub, tf.scatter_update, etc) があります。

v = tf.Variable(1.0)
assert v.numpy() == 1.0

# Re-assign the value
v.assign(3.0)
assert v.numpy() == 3.0

# Use `v` in a TensorFlow operation like tf.square() and reassign
v.assign(tf.square(v))
assert v.numpy() == 9.0

Variable を使用する計算は勾配を計算するとき自動的に追跡されます。埋め込みを表わす Variable については TensorFlow はデフォルトでスパース更新を行ない、これはより多くの計算がかかりメモリ効率的です。

Variable の使用はまた貴方のコードの読み手に状態のこのピースがミュータブルであることを速やかに知らせる方法でもあります。

 

例題: 線形モデルをフィットさせる

さてここまでに学習した幾つかの概念を使用しましょう — Tensor, GradientTape, Variable — 単純なモデルを構築して訓練するためにです。これは典型的には幾つかのステップを伴います :

  1. モデルを定義する。
  2. 損失関数を定義する。
  3. 訓練データを取得する。
  4. 訓練データを通して実行してデータにフィットするように variable を調整するために “optimizer” を使用します。

このチュートリアルでは、単純な線形モデル: f(x) = x * W + b (これは 2 つの variable – W と b を持ちます) の自明な例題をウォークスルーします。更に、良く訓練されたモデルが W = 3.0 と b = 2.0 を持つようなデータを合成します。

 

モデルを定義する

variable と計算をカプセル化するために単純なクラスを定義しましょう。

class Model(object):
  def __init__(self):
    # Initialize variable to (5.0, 0.0)
    # In practice, these should be initialized to random values.
    self.W = tf.Variable(5.0)
    self.b = tf.Variable(0.0)

  def __call__(self, x):
    return self.W * x + self.b

model = Model()

assert model(3.0).numpy() == 15.0

 

損失関数を定義する

損失関数は与えられた入力に対するモデルの出力がどのくらい上手く望ましい出力に適合しているかを測定します。標準的な L2 損失を使用しましょう。

def loss(predicted_y, desired_y):
  return tf.reduce_mean(tf.square(predicted_y - desired_y))

 

訓練データを得る

何某かのノイズを持つ訓練データを合成しましょう。

TRUE_W = 3.0
TRUE_b = 2.0
NUM_EXAMPLES = 1000

inputs  = tf.random.normal(shape=[NUM_EXAMPLES])
noise   = tf.random.normal(shape=[NUM_EXAMPLES])
outputs = inputs * TRUE_W + TRUE_b + noise

モデルを訓練する前に現時点でモデルがどこに在るかを可視化しましょう。モデルの予測を赤色で訓練データを青色でプロットします。

import matplotlib.pyplot as plt

plt.scatter(inputs, outputs, c='b')
plt.scatter(inputs, model(inputs), c='r')
plt.show()

print('Current loss: '),
print(loss(model(inputs), outputs).numpy())
<Figure size 640x480 with 1 Axes>
Current loss: 
8.4688225

 

訓練ループを定義する

今、ネットワークと訓練データを持ちました。それを訓練しましょう、i.e. 勾配降下 を使用して損失が下がるようにモデルの variable (W と b) を更新するために訓練データを使用します。tf.train.Optimizer 実装で取り入れられた勾配降下スキームの多くの変種があります。それらの実装の使用を強く推奨しますが、最初の原理から構築するという精神で、この特定の例では基本的な数学を私達自身で実装します。

def train(model, inputs, outputs, learning_rate):
  with tf.GradientTape() as t:
    current_loss = loss(model(inputs), outputs)
  dW, db = t.gradient(current_loss, [model.W, model.b])
  model.W.assign_sub(learning_rate * dW)
  model.b.assign_sub(learning_rate * db)

最後に、訓練データを通して繰り返し実行して W と b がどのように展開するか見てみましょう。

model = Model()

# Collect the history of W-values and b-values to plot later
Ws, bs = [], []
epochs = range(10)
for epoch in epochs:
  Ws.append(model.W.numpy())
  bs.append(model.b.numpy())
  current_loss = loss(model(inputs), outputs)

  train(model, inputs, outputs, learning_rate=0.1)
  print('Epoch %2d: W=%1.2f b=%1.2f, loss=%2.5f' %
        (epoch, Ws[-1], bs[-1], current_loss))

# Let's plot it all
plt.plot(epochs, Ws, 'r',
         epochs, bs, 'b')
plt.plot([TRUE_W] * len(epochs), 'r--',
         [TRUE_b] * len(epochs), 'b--')
plt.legend(['W', 'b', 'true W', 'true_b'])
plt.show()
Epoch  0: W=5.00 b=0.00, loss=8.46882
Epoch  1: W=4.63 b=0.38, loss=5.90502
Epoch  2: W=4.33 b=0.69, loss=4.21623
Epoch  3: W=4.08 b=0.94, loss=3.10372
Epoch  4: W=3.88 b=1.15, loss=2.37080
Epoch  5: W=3.72 b=1.31, loss=1.88791
Epoch  6: W=3.59 b=1.44, loss=1.56974
Epoch  7: W=3.48 b=1.55, loss=1.36008
Epoch  8: W=3.39 b=1.63, loss=1.22192
Epoch  9: W=3.32 b=1.70, loss=1.13087

 

以上






TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- 自動微分と gradient tape

TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- 自動微分と gradient tape (翻訳/解説)

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

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

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

 

カスタマイズ :- 自動微分と gradient tape

前のチュートリアルで Tensor とその上の演算を紹介しました。このチュートリアルでは 自動微分 をカバーします、機械学習モデルを最適化するための主要テクニックです。

 

Setup

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf

 

Gradient tape

TensorFlow は自動微分のための tf.GradientTape API を提供します – その入力変数に関する計算の勾配を計算します。TensorFlow は tf.GradientTape のコンテキスト内で実行された総ての演算を「テープ」上に「記録」します。それから TensorFlow は リバースモード微分 を使用して「記録」された計算の勾配を計算するためにそのテープと各記録された演算に関連する勾配を使用します。

例えば :

x = tf.ones((2, 2))

with tf.GradientTape() as t:
  t.watch(x)
  y = tf.reduce_sum(x)
  z = tf.multiply(y, y)

# Derivative of z with respect to the original input tensor x
dz_dx = t.gradient(z, x)
for i in [0, 1]:
  for j in [0, 1]:
    assert dz_dx[i][j].numpy() == 8.0

「記録」された tf.GradientTape コンテキストの間に計算された中間値に関する出力の勾配を要求することもできます。

x = tf.ones((2, 2))

with tf.GradientTape() as t:
  t.watch(x)
  y = tf.reduce_sum(x)
  z = tf.multiply(y, y)

# Use the tape to compute the derivative of z with respect to the
# intermediate value y.
dz_dy = t.gradient(z, y)
assert dz_dy.numpy() == 8.0

デフォルトでは、GradientTape により保持されたリソースは GradientTape.gradient() メソッドが呼び出されるとすぐに解放されます。同じ計算に渡る複数の勾配を計算するためには、persistent gradient tape を作成します。これは gradient() メソッドへの複数の呼び出しを可能にします。何故ならば tape オブジェクトがガベージコレクションされるときにリソースは解放されますので。例えば :

x = tf.constant(3.0)
with tf.GradientTape(persistent=True) as t:
  t.watch(x)
  y = x * x
  z = y * y
dz_dx = t.gradient(z, x)  # 108.0 (4*x^3 at x = 3)
dy_dx = t.gradient(y, x)  # 6.0
del t  # Drop the reference to the tape

 

制御フローを記録する

テープは演算をそれらが実行されるときに記録しますので、(例えば if と while を使用する) Python 制御フローは自然に処理されます :

def f(x, y):
  output = 1.0
  for i in range(y):
    if i > 1 and i < 5:
      output = tf.multiply(output, x)
  return output

def grad(x, y):
  with tf.GradientTape() as t:
    t.watch(x)
    out = f(x, y)
  return t.gradient(out, x)

x = tf.convert_to_tensor(2.0)

assert grad(x, 6).numpy() == 12.0
assert grad(x, 5).numpy() == 12.0
assert grad(x, 4).numpy() == 4.0

 

高位勾配

GradientTape コンテキスト・マネージャ内の演算は自動微分のために記録されます。そのコンテキストで勾配が計算される場合、勾配計算もまた記録されます。その結果、正確に同じ API が高位勾配のためにもまた動作します。例えば :

x = tf.Variable(1.0)  # Create a Tensorflow variable initialized to 1.0

with tf.GradientTape() as t:
  with tf.GradientTape() as t2:
    y = x * x * x
  # Compute the gradient inside the 't' context manager
  # which means the gradient computation is differentiable as well.
  dy_dx = t2.gradient(y, x)
d2y_dx2 = t.gradient(dy_dx, x)

assert dy_dx.numpy() == 3.0
assert d2y_dx2.numpy() == 6.0
 

以上






TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- カスタム層

TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- カスタム層 (翻訳/解説)

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

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

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

 

カスタマイズ :- カスタム層

ニューラルネットワークを構築するために tf.keras を高位 API として使用することを推奨します。とは言うものの、殆どの TensorFlow API は eagar execution とともに利用可能です。

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

 

層: 有用な演算の一般的なセット

機械学習モデルのためのコードを書く時の殆どで個々の演算と個々の変数の操作よりも抽象度の高いレベルで操作することを望むでしょう。

多くの機械学習モデルは比較的単純な層の組み合わせとスタックとして表現可能で、そして TensorFlow は多くの一般的な層のセットに加えて貴方自身のアプリケーション固有の層をスクラッチからあるいは既存の層の組み合わせとして書くための容易な方法の両者を提供します。

TensorFlow は tf.keras パッケージで完全な Keras API を含み、そして Keras 層は貴方自身のモデルを構築するとき非常に有用です。

# In the tf.keras.layers package, layers are objects. To construct a layer,
# simply construct the object. Most layers take as a first argument the number
# of output dimensions / channels.
layer = tf.keras.layers.Dense(100)
# The number of input dimensions is often unnecessary, as it can be inferred
# the first time the layer is used, but it can be provided if you want to
# specify it manually, which is useful in some complex models.
layer = tf.keras.layers.Dense(10, input_shape=(None, 5))

事前の既存の層の完全なリストは ドキュメント で見ることができます。それは Dense (完全結合層), Conv2D, LSTM, BatchNormalization, Dropout そしてその他多くを含みます。

# To use a layer, simply call it.
layer(tf.zeros([10, 5]))
<tf.Tensor: id=29, shape=(10, 10), dtype=float32, numpy=
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)>
# Layers have many useful methods. For example, you can inspect all variables
# in a layer using `layer.variables` and trainable variables using
# `layer.trainable_variables`. In this case a fully-connected layer
# will have variables for weights and biases.
layer.variables
[<tf.Variable 'dense_1/kernel:0' shape=(5, 10) dtype=float32, numpy=
 array([[-0.45203224, -0.49100596, -0.49586484, -0.03597105, -0.02791589,
          0.59075147, -0.10046268, -0.3485075 ,  0.52843136,  0.37997633],
        [ 0.24188435, -0.11822426,  0.5947488 ,  0.28323162,  0.6153639 ,
         -0.54930526,  0.13033777, -0.04597318,  0.09440243, -0.08491254],
        [ 0.07092369, -0.4460328 ,  0.43974787, -0.12983793, -0.5321488 ,
         -0.53699183,  0.21715474,  0.49850827, -0.06530118, -0.45353276],
        [-0.36072668,  0.31528533,  0.3017155 , -0.5406943 , -0.4158225 ,
         -0.09183133, -0.0244289 ,  0.04464054, -0.5192156 ,  0.20999378],
        [ 0.36308336, -0.5447986 ,  0.32299954, -0.33683974,  0.07472193,
         -0.57537097, -0.066365  , -0.19609043,  0.36212164,  0.42469734]],
       dtype=float32)>,
 <tf.Variable 'dense_1/bias:0' shape=(10,) dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)>]
# The variables are also accessible through nice accessors
layer.kernel, layer.bias
(<tf.Variable 'dense_1/kernel:0' shape=(5, 10) dtype=float32, numpy=
 array([[-0.45203224, -0.49100596, -0.49586484, -0.03597105, -0.02791589,
          0.59075147, -0.10046268, -0.3485075 ,  0.52843136,  0.37997633],
        [ 0.24188435, -0.11822426,  0.5947488 ,  0.28323162,  0.6153639 ,
         -0.54930526,  0.13033777, -0.04597318,  0.09440243, -0.08491254],
        [ 0.07092369, -0.4460328 ,  0.43974787, -0.12983793, -0.5321488 ,
         -0.53699183,  0.21715474,  0.49850827, -0.06530118, -0.45353276],
        [-0.36072668,  0.31528533,  0.3017155 , -0.5406943 , -0.4158225 ,
         -0.09183133, -0.0244289 ,  0.04464054, -0.5192156 ,  0.20999378],
        [ 0.36308336, -0.5447986 ,  0.32299954, -0.33683974,  0.07472193,
         -0.57537097, -0.066365  , -0.19609043,  0.36212164,  0.42469734]],
       dtype=float32)>,
 <tf.Variable 'dense_1/bias:0' shape=(10,) dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)>)

 

カスタム層を実装する

貴方自身の層を実装する最善の方法は tf.keras.Layer クラスを拡張して以下を実装することです : * __init__ , そこでは総ての入力独立な初期化を行なうことができます * build, そこでは入力 tensor の shape を知って初期化の残りを行なうことができます * call, そこでは forward 計算を行ないます。

貴方の変数を作成するために build が呼び出されるまで待たなくても構わないことに注意してください、それらを __init__ で作成することもできます。けれども、それらを build で作成する優位点は (層がその上で操作する) 入力の shape を基にそれが遅延変数作成を可能にすることです。他方、__init__ で変数を作成することは変数を作成するために必要な shape が明示的に指定される必要があることを意味します。

class MyDenseLayer(tf.keras.layers.Layer):
  def __init__(self, num_outputs):
    super(MyDenseLayer, self).__init__()
    self.num_outputs = num_outputs

  def build(self, input_shape):
    self.kernel = self.add_variable("kernel",
                                    shape=[int(input_shape[-1]),
                                           self.num_outputs])

  def call(self, input):
    return tf.matmul(input, self.kernel)

layer = MyDenseLayer(10)
print(layer(tf.zeros([10, 5])))
print(layer.trainable_variables)
tf.Tensor(
[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]], shape=(10, 10), dtype=float32)
[<tf.Variable 'my_dense_layer/kernel:0' shape=(5, 10) dtype=float32, numpy=
array([[-0.13745713,  0.20385987,  0.3308882 , -0.55763197, -0.2896679 ,
         0.5872863 , -0.22654337,  0.43126732, -0.09286654,  0.59604806],
       [ 0.53581923, -0.56217843, -0.14932257,  0.00366616, -0.0120728 ,
        -0.30010343,  0.4884537 ,  0.12585086, -0.14010605,  0.25820726],
       [ 0.4151489 ,  0.19842285, -0.09367836, -0.28914934, -0.3939644 ,
         0.33803612, -0.57224464, -0.37771535, -0.21020746, -0.01422739],
       [ 0.00673819,  0.5517891 ,  0.25531226,  0.17221254,  0.60322064,
        -0.3257994 ,  0.04672939, -0.3532266 ,  0.30166608, -0.06154853],
       [-0.29644936, -0.5298993 ,  0.36587477,  0.03407788,  0.32965267,
        -0.5066039 ,  0.3986141 , -0.544722  ,  0.13702917,  0.57767636]],
      dtype=float32)>]

全体的なコードが可能なときには標準的な層を使用すれば、他の読み手は標準的な層の挙動に馴染みがありますので、読みやすく維持しやすくなります、tf.keras.layers にない層を使用することを望む場合、github issue を提出するか、更に良いことは pull リクエストを送ることです!

 

モデル: 層を組み合わせる

機械学習モデルの多くの興味深い層-like なものは既存の層を組み合わせることにより実装されます。例えば、resnet の各残差ブロックは畳み込み、バッチ正規化とショートカットの組み合わせです。

他の層を含む層-like なものを作成するときに使用される主なクラスは tf.keras.Model です。一つを実装することは tf.keras.Model から継承することにより成されます。

class ResnetIdentityBlock(tf.keras.Model):
  def __init__(self, kernel_size, filters):
    super(ResnetIdentityBlock, self).__init__(name='')
    filters1, filters2, filters3 = filters

    self.conv2a = tf.keras.layers.Conv2D(filters1, (1, 1))
    self.bn2a = tf.keras.layers.BatchNormalization()

    self.conv2b = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same')
    self.bn2b = tf.keras.layers.BatchNormalization()

    self.conv2c = tf.keras.layers.Conv2D(filters3, (1, 1))
    self.bn2c = tf.keras.layers.BatchNormalization()

  def call(self, input_tensor, training=False):
    x = self.conv2a(input_tensor)
    x = self.bn2a(x, training=training)
    x = tf.nn.relu(x)

    x = self.conv2b(x)
    x = self.bn2b(x, training=training)
    x = tf.nn.relu(x)

    x = self.conv2c(x)
    x = self.bn2c(x, training=training)

    x += input_tensor
    return tf.nn.relu(x)


block = ResnetIdentityBlock(1, [1, 2, 3])
print(block(tf.zeros([1, 2, 3, 3])))
print([x.name for x in block.trainable_variables])
tf.Tensor(
[[[[0. 0. 0.]
   [0. 0. 0.]
   [0. 0. 0.]]

  [[0. 0. 0.]
   [0. 0. 0.]
   [0. 0. 0.]]]], shape=(1, 2, 3, 3), dtype=float32)
['resnet_identity_block/conv2d/kernel:0', 'resnet_identity_block/conv2d/bias:0', 'resnet_identity_block/batch_normalization/gamma:0', 'resnet_identity_block/batch_normalization/beta:0', 'resnet_identity_block/conv2d_1/kernel:0', 'resnet_identity_block/conv2d_1/bias:0', 'resnet_identity_block/batch_normalization_1/gamma:0', 'resnet_identity_block/batch_normalization_1/beta:0', 'resnet_identity_block/conv2d_2/kernel:0', 'resnet_identity_block/conv2d_2/bias:0', 'resnet_identity_block/batch_normalization_2/gamma:0', 'resnet_identity_block/batch_normalization_2/beta:0']

けれども大抵の時は、多くの層を構成するモデルは単純に他の (層の) 後に一つの層を呼び出します。これは tf.keras.Sequential を使用して非常に小さいなコードから成されます。

my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1),
                                                    input_shape=(
                                                        None, None, 3)),
                             tf.keras.layers.BatchNormalization(),
                             tf.keras.layers.Conv2D(2, 1,
                                                    padding='same'),
                             tf.keras.layers.BatchNormalization(),
                             tf.keras.layers.Conv2D(3, (1, 1)),
                             tf.keras.layers.BatchNormalization()])
my_seq(tf.zeros([1, 2, 3, 3]))
<tf.Tensor: id=749, shape=(1, 2, 3, 3), dtype=float32, numpy=
array([[[[0., 0., 0.],
         [0., 0., 0.],
         [0., 0., 0.]],

        [[0., 0., 0.],
         [0., 0., 0.],
         [0., 0., 0.]]]], dtype=float32)>
 

以上






TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- テンソルと演算

TensorFlow 2.0 Beta : 上級 Tutorials : カスタマイズ :- テンソルと演算 (翻訳/解説)

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

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

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

 

カスタマイズ :- テンソルと演算

これは初歩的な TensorFlow チュートリアルでどのように以下を行なうかを示します :

  • 必要なパッケージをインポートする
  • tensor を作成して使用する
  • GPU アクセラレーションを使用する
  • tf.data.Dataset を実演する
from __future__ import absolute_import, division, print_function

!pip install -q tensorflow-gpu==2.0.0-beta1

 

Import TensorFlow

始めるために、tensorflow モジュールをインポートします。TensorFlow 2.0 の時点では、eager execution がデフォルトで有効です。これは TensorFlow へのより対話的なフロントエンドを可能にします、その詳細は後で更に議論します。

import tensorflow as tf

 

Tensors

Tensor は多次元配列です。NumPy ndarray オブジェクトと同様に、tf.Tensor オブジェクトはデータ型と shape を持ちます。更に、tf.Tensor は (GPU のような) アクセラレータ・メモリに常駐できます。TensorFlow は tf.Tensor を消費して生成する演算 (tf.add, tf.matmul, tf.linalg.inv etc.) の豊富なライブラリを提供します。これらの演算は native Python 型を自動的に変換します、例えば :

(訳注: 参考リンク tf.math.add, tf.linalg.matmul, tf.linalg.inv)

print(tf.add(1, 2))
print(tf.add([1, 2], [3, 4]))
print(tf.square(5))
print(tf.reduce_sum([1, 2, 3]))

# Operator overloading is also supported
print(tf.square(2) + tf.square(3))
tf.Tensor(3, shape=(), dtype=int32)
tf.Tensor([4 6], shape=(2,), dtype=int32)
tf.Tensor(25, shape=(), dtype=int32)
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(13, shape=(), dtype=int32)

各 tf.Tensor は shape とデータ型を持ちます :

x = tf.matmul([[1]], [[2, 3]])
print(x)
print(x.shape)
print(x.dtype)
tf.Tensor([[2 3]], shape=(1, 2), dtype=int32)
(1, 2)
<dtype: 'int32'>

Numpy 配列と tf.Tensor 間の最も明白な違いは :

  1. Tensor は (GPU, TPU のような) アクセラレータメモリにより支援されます。
  2. Tensor は immutable (不変) です。

 

NumPy 互換性

TensorFlow tf.Tensor と NumPy ndarray 間の変換は容易です :

  • TensorFlow 演算は NumPy ndarray を Tensor に自動的に変換します。
  • NumPy 演算は Tensor を Numpy ndarray に自動的に変換します。

Tensor は .numpy() メソッドを使用して明示的に NumPy ndarray に変換されます。これらの変換は典型的には安価です、何故ならば配列と tf.Tensor は基礎となるメモリ表現を可能であれば、共有するからです。けれども、基礎的な表現の共有は常に可能ではありません、何故ならば tf.Tensor は GPU メモリにホストされるかもしれない一方で NumPy 配列は常にホストメモリにより支援されるからで、変換は GPU からホストメモリへのコピーを伴います。

import numpy as np

ndarray = np.ones([3, 3])

print("TensorFlow operations convert numpy arrays to Tensors automatically")
tensor = tf.multiply(ndarray, 42)
print(tensor)


print("And NumPy operations convert Tensors to numpy arrays automatically")
print(np.add(tensor, 1))

print("The .numpy() method explicitly converts a Tensor to a numpy array")
print(tensor.numpy())
TensorFlow operations convert numpy arrays to Tensors automatically
tf.Tensor(
[[42. 42. 42.]
 [42. 42. 42.]
 [42. 42. 42.]], shape=(3, 3), dtype=float64)
And NumPy operations convert Tensors to numpy arrays automatically
[[43. 43. 43.]
 [43. 43. 43.]
 [43. 43. 43.]]
The .numpy() method explicitly converts a Tensor to a numpy array
[[42. 42. 42.]
 [42. 42. 42.]
 [42. 42. 42.]]

 

GPU アクセラレーション

多くの TensorFlow 演算は計算のために GPU を使用して高速化されます。どのようなアノテーションがなくても、演算のために GPU か CPU を使用するかを TensorFlow は自動的に決定します – 必要であれば CPU と GPU メモリの間で tensor をコピーします。演算で生成された tensor は典型的には (その上で) 演算が実行されるデバイスのメモリにより支援されます、例えば :

x = tf.random.uniform([3, 3])

print("Is there a GPU available: "),
print(tf.test.is_gpu_available())

print("Is the Tensor on GPU #0:  "),
print(x.device.endswith('GPU:0'))
Is there a GPU available: 
True
Is the Tensor on GPU #0:  
True

 

デバイス名

Tensor.device プロパティは tensor の内容をホストするデバイスの完全修飾 (= fully qualified) 文字列名を提供します。この名前は (その上でプログラムが実行されている) ホストのネットワークアドレスやそのホスト内のデバイスの識別子のような、多くの詳細をエンコードします。これは TensorFlow プログラムの分散実行のために必要です。tensor がホストの N-th GPU 上に置かれるのであれば文字列は GPU:<N> で終わります

 

明示的なデバイス配置

TensorFlow では、配置は個々の演算が実行のためのデバイスにどのように割り当てられるか (置かれるか) を参照します。言及したように、提供される明示的なガイダンスがないときは、TensorFlow はどのデバイスで演算を実行するかを自動的に決定して必要であれば、tensor をそのデバイスにコピーします。けれども、TensorFlow 演算は tf.device コンテキスト・マネージャを使用して特定のデバイス上に明示的に置くことができます、例えば :

import time

def time_matmul(x):
  start = time.time()
  for loop in range(10):
    tf.matmul(x, x)

  result = time.time()-start

  print("10 loops: {:0.2f}ms".format(1000*result))

# Force execution on CPU
print("On CPU:")
with tf.device("CPU:0"):
  x = tf.random.uniform([1000, 1000])
  assert x.device.endswith("CPU:0")
  time_matmul(x)

# Force execution on GPU #0 if available
if tf.test.is_gpu_available():
  print("On GPU:")
  with tf.device("GPU:0"): # Or GPU:1 for the 2nd GPU, GPU:2 for the 3rd etc.
    x = tf.random.uniform([1000, 1000])
    assert x.device.endswith("GPU:0")
    time_matmul(x)
On CPU:
10 loops: 105.17ms
On GPU:
10 loops: 336.23ms

 

データセット

このセクションはデータを貴方のモデルに供給するためにパイプラインを構築するために tf.data.Dataset API を使用します。tf.data.Dataset API はモデルの訓練や評価ループに供給する、単純で再利用可能なピースから高パフォーマンスで複雑なパイプラインを構築するために使用されます。

 

ソース Dataset を作成する

Dataset.from_tensors, Dataset.from_tensor_slices のようなファクトリ関数の一つを使用するか、TextLineDatasetTFRecordDataset のようなファイルから読むオブジェクトを使用してソース・データセットを作成します。より多くの情報については TensorFlow Dataset ガイド を見てください。

ds_tensors = tf.data.Dataset.from_tensor_slices([1, 2, 3, 4, 5, 6])

# Create a CSV file
import tempfile
_, filename = tempfile.mkstemp()

with open(filename, 'w') as f:
  f.write("""Line 1
Line 2
Line 3
  """)

ds_file = tf.data.TextLineDataset(filename)

 

transformation を適用する

データセット・レコードに変換を適用するためには map, batchshuffle のような transformation 関数を使用します。

ds_tensors = ds_tensors.map(tf.square).shuffle(2).batch(2)

ds_file = ds_file.batch(2)

 

Iterate

tf.data.Dataset オブジェクトはレコードに渡りループするために iteration をサポートします :

print('Elements of ds_tensors:')
for x in ds_tensors:
  print(x)

print('\nElements in ds_file:')
for x in ds_file:
  print(x)
Elements of ds_tensors:
tf.Tensor([1 9], shape=(2,), dtype=int32)
tf.Tensor([16  4], shape=(2,), dtype=int32)
tf.Tensor([36 25], shape=(2,), dtype=int32)

Elements in ds_file:
tf.Tensor([b'Line 1' b'Line 2'], shape=(2,), dtype=string)
tf.Tensor([b'Line 3' b'  '], shape=(2,), dtype=string)
 

以上






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