In [ ]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳はベストエフォートであるため、この翻訳が正確であることや英語の公式ドキュメントの 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリtensorflow/docsにプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 docs-ja@tensorflow.org メーリングリストにご連絡ください。
TensorFlow の Eager Execution は、計算グラフの作成と評価を同時におこなう命令的なプログラミングを行うための環境です:
オペレーションはあとで実行するための計算グラフでなく、具体的な計算結果の値を返します。
この方法を用いることにより、初心者にとって TensorFlow を始めやすくなり、またモデルのデバッグも行いやすくなります。
さらにコードの記述量も削減されます。
このガイドの内容を実行するためには、対話的インタープリタ python
を起動し、以下のコードサンプルを実行してください。
Eager Execution は研究や実験のための柔軟な機械学習環境として、以下を提供します。
Eager Execution は TensorFlow のほとんどのオペレーションとGPUアクセラレーションをサポートします。
Note: いくつかのモデルは Eager Execution を有効化することでオーバヘッドが増える可能性があります。 パフォーマンス改善を行っていますが、もしも問題を発見したら、バグ報告してベンチマークを共有してください。
In [ ]:
import tensorflow as tf
import cProfile
TensorFlow 2.0 では、 Eager Execution はデフォルトで有効化されます。
In [ ]:
tf.executing_eagerly()
これで TensorFlow のオペレーションを実行してみましょう。結果はすぐに返されます。
In [ ]:
x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m))
Eager Execution を有効化することで、 TensorFlow の挙動は変わります—TensorFlowは即座に式を評価して結果をPythonに返すようになります。
tf.Tensor
オブジェクトは計算グラフのノードへのシンボリックハンドルの代わりに具体的な値を参照します。
セッションの中で構築して実行する計算グラフが存在しないため、print()
やデバッガを使って容易に結果を調べることができます。
勾配計算を遮ることなくテンソル値を評価、出力、およびチェックすることができます。
Eager Execution は、NumPyと一緒に使うことができます。
NumPy のオペレーションは、tf.Tensor
を引数として受け取ることができます。
TensorFlow math operations はPython オブジェクトと Numpy array を tf.Tensor
に変換します。
tf.Tensor.numpy
メソッドはオブジェクトの値を NumPy の ndarray
形式で返します。
In [ ]:
a = tf.constant([[1, 2],
[3, 4]])
print(a)
In [ ]:
# ブロードキャストのサポート
b = tf.add(a, 1)
print(b)
In [ ]:
# オペレータのオーバーロードがサポートされている
print(a * b)
In [ ]:
# NumPy valueの使用
import numpy as np
c = np.multiply(a, b)
print(c)
In [ ]:
# Tensor から numpy の値を得る
print(a.numpy())
# => [[1 2]
# [3 4]]
Eager Execution の主要なメリットは、モデルを実行する際にホスト言語のすべての機能性が利用できることです。 たとえば、fizzbuzzが簡単に書けます:
In [ ]:
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
In [ ]:
fizzbuzz(15)
この関数はテンソル値に依存する条件式を持ち、実行時にこれらの値を表示します。
自動微分はニューラルネットワークの学習で利用されるバックプロパゲーションなどの機械学習アルゴリズムの実装を行う上で便利です。
Eager Executionでは、勾配計算をあとで行うためのオペレーションをトレースするためにtf.GradientTape
を利用します。
Eager Execution では、学習や勾配計算に, tf.GradientTape
を利用できます。これは複雑な学習ループを実行するときに特に役立ちます。
各呼び出し中に異なるオペレーションが発生する可能性があるため、すべての forward-pass オペレーションは一つの「テープ」に記録されます。勾配を計算するには、テープを逆方向に再生してから破棄します。特定の tf.GradientTape
は一つのグラデーションしか計算できません。後続の呼び出しは実行時エラーをスローします。
In [ ]:
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)
In [ ]:
# mnist データのを取得し、フォーマットする
(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)
In [ ]:
# モデルを構築する
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 Execution により、出力を検査することができます:
In [ ]:
for images,labels in dataset.take(1):
print("Logits: ", mnist_model(images[0:1]).numpy())
keras モデルは組み込みで学習のループを回すメソッド fit
がありますが、よりカスタマイズが必要な場合もあるでしょう。 Eager Executionを用いて実装された学習ループのサンプルを以下に示します:
In [ ]:
optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
loss_history = []
Note: モデルの状況を確認したいときは、 tf.debugging
にある assert 機能を利用してください。この機能は Eager Execution と Graph Execution のどちらでも利用できます。
In [ ]:
def train_step(images, labels):
with tf.GradientTape() as tape:
logits = mnist_model(images, training=True)
# assertを入れて出力の型をチェックする。
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))
In [ ]:
def train():
for epoch in range(3):
for (batch, (images, labels)) in enumerate(dataset):
train_step(images, labels)
print ('Epoch {} finished'.format(epoch))
In [ ]:
train()
In [ ]:
import matplotlib.pyplot as plt
plt.plot(loss_history)
plt.xlabel('Batch #')
plt.ylabel('Loss [entropy]')
In [ ]:
class Model(tf.keras.Model):
def __init__(self):
super(Model, 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
# 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
# 最適化対象のloss関数
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])
# 定義:
# 1. モデル
# 2. モデルパラメータに関する損失関数の導関数
# 3. 導関数に基づいて変数を更新するストラテジ。
model = Model()
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
print("Initial loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
# 学習ループ
for i in range(300):
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)))
print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
In [ ]:
if tf.test.is_gpu_available():
with tf.device("gpu:0"):
v = tf.Variable(tf.random.normal([1000, 1000]))
v = None # v は GPU メモリを利用しなくなる
In [ ]:
x = tf.Variable(10.)
checkpoint = tf.train.Checkpoint(x=x)
In [ ]:
x.assign(2.) # 新しい値を変数に代入して保存する。
checkpoint_path = './ckpt/'
checkpoint.save('./ckpt/')
In [ ]:
x.assign(11.) # 保存後に変数の値を変える。
# チェックポイントから変数を復元する
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))
print(x) # => 2.0
モデルを保存して読み込むために、 tf.train.Checkpoint
は隠れ変数なしにオブジェクトの内部状態を保存します。 モデル
、 オプティマイザ
、そしてグローバルステップの状態を記録するには、それらを tf.train.Checkpoint
に渡します。
In [ ]:
import os
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))
多くの学習ループでは、変数は tf.train..Checkpoint.restore
が呼ばれたあとに作成されます。これらの変数は作成されてすぐに復元され、チェックポイントがすべてロードされたことを確認するためのアサーションが利用可能になります。詳しくは、 guide to training checkpoints を見てください。
In [ ]:
m = tf.keras.metrics.Mean("loss")
m(0)
m(5)
m.result() # => 2.5
m([8, 9])
m.result() # => 5.5
tf.GradientTape
は動的モデルでも使うことができます。
以下の バックトラックライン検索
アルゴリズムの例は、複雑な制御フローにもかかわらず
勾配があり、微分可能であることを除いて、通常の NumPy コードのように見えます:
In [ ]:
def line_search_step(fn, init_x, rate=1.0):
with tf.GradientTape() as tape:
# 変数は自動的に記録されるが、Tensorは手動でウォッチする
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
In [ ]:
@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
カスタム勾配は、一連の演算に対して数値的に安定した勾配を提供するために共通的に使用されます。:
In [ ]:
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)
In [ ]:
# 勾配計算は x = 0 のときはうまくいく。
grad_log1pexp(tf.constant(0.)).numpy()
In [ ]:
# しかし、x = 100のときは数値的不安定により失敗する。
grad_log1pexp(tf.constant(100.)).numpy()
ここで、 log1pexp
関数はカスタム勾配を用いて解析的に単純化することができます。
以下の実装は、フォワードパスの間に計算された tf.exp(x)
の値を
再利用します—冗長な計算を排除することでより効率的になります:
In [ ]:
@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)
In [ ]:
# 上と同様に、勾配計算はx = 0のときにはうまくいきます。
grad_log1pexp(tf.constant(0.)).numpy()
In [ ]:
# また、勾配計算はx = 100でも機能します。
grad_log1pexp(tf.constant(100.)).numpy()
In [ ]:
import time
def measure(x, steps):
# TensorFlowはGPUを初めて使用するときに初期化するため、時間計測対象からは除外する。
tf.matmul(x, x)
start = time.time()
for i in range(steps):
x = tf.matmul(x, x)
# tf.matmulは、行列乗算が完了する前に戻ることができる。
# (たとえば、CUDAストリームにオペレーションをエンキューした後に戻すことができる)。
# 以下のx.numpy()呼び出しは、すべてのキューに入れられたオペレーションが完了したことを確認する。
# (そして結果をホストメモリにコピーするため、計算時間は単純なmatmulオペレーションよりも多くのことを含む時間になる。)
_ = 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))
# CPU上で実行するとき:
with tf.device("/cpu:0"):
print("CPU: {} secs".format(measure(tf.random.normal(shape), steps)))
# GPU上で実行するとき(GPUが利用できれば):
if tf.test.is_gpu_available():
with tf.device("/gpu:0"):
print("GPU: {} secs".format(measure(tf.random.normal(shape), steps)))
else:
print("GPU: not found")
tf.Tensor
オブジェクトはそのオブジェクトに対するオペレーションを実行するために別のデバイスにコピーすることができます:
In [ ]:
if tf.test.is_gpu_available():
x = tf.random.normal([10, 10])
x_gpu0 = x.gpu()
x_cpu = x.cpu()
_ = tf.matmul(x_cpu, x_cpu) # CPU上で実行するとき
_ = tf.matmul(x_gpu0, x_gpu0) # GPU:0上で実行するとき
GPUでの
ResNet50
の学習のような、計算量の多いモデルの場合は、Eager Executionのパフォーマンスは tf.function
のパフォーマンスに匹敵します。
しかし、この2つの環境下のパフォーマンスの違いは計算量の少ないモデルではより大きくなり、小さなたくさんのオペレーションからなるモデルでホットコードパスを最適化するためにやるべきことがあります。
Eager Execution は開発とデバッグをより対話的にしますが、 TensorFlow 1.x スタイルの Graph Execution は分散学習、パフォーマンスの最適化、そしてプロダクション環境へのデプロイの観点で利点があります。
2つの手法のギャップを埋めるために、 TensorFlow 2.0 は tf.function
という機能を導入しています。
詳しくは、 Autograph のガイドを見てください。