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アクセラレーションをサポートします。 Eager Executionの実行例については、以下を参照してください。 tensorflow/contrib/eager/python/examples.
Note: いくつかのモデルはEager Executionを有効化することでオーバヘッドが増える可能性があります。 パフォーマンス改善を行っていますが、もしも問題を発見したら、バグ報告してベンチマークを共有してください。
Eager Executionをはじめるためには、プログラムやコンソールセッションの最初に、tf.enable_eager_execution()
を追加してください。
プログラムが呼び出すほかのモジュールにこのオペレーションを追加しないでください。
In [ ]:
import tensorflow.compat.v1 as tf
これでTensorFlowのオペレーションを実行してみましょう。結果はすぐに返されます。
In [ ]:
tf.executing_eagerly()
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]]
tf.contrib.eager
モジュールは、Eager ExecutionとGraph Executionの両方の環境で利用可能なシンボルが含まれており、Graph Execution方式での記述に便利です:
In [ ]:
tfe = tf.contrib.eager
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)
この関数はテンソル値に依存する条件式を持ち、実行時にこれらの値を表示します。
In [ ]:
class MySimpleLayer(tf.keras.layers.Layer):
def __init__(self, output_units):
super(MySimpleLayer, self).__init__()
self.output_units = output_units
def build(self, input_shape):
# buildメソッドは、レイヤーが初めて使われたときに呼ばれます
# build()で変数を作成すると、それらのshapeを入力のshapeに依存させることができ、
# ユーザがshapeを完全に指定する必要はありません。
# 既に完全なshapeが決まっている場合は、__init__()の中で変数を作成することもできます。
self.kernel = self.add_variable(
"kernel", [input_shape[-1], self.output_units])
def call(self, input):
# __call__の代わりにcall()を上書きします。
return tf.matmul(input, self.kernel)
MySimpleLayer
の代わりに、その機能のスーパーセットを持っているtf.keras.layers.Dense
レイヤーを使用してください
(このレイヤーはバイアスを加えることもできるもできます)。
レイヤーをモデルに組み立てるとき、レイヤーの線形スタックである
モデルを表すために tf.keras.Sequential
を使うことができます。この書き方は基本的なモデルを扱いやすいです。
In [ ]:
model = tf.keras.Sequential([
tf.keras.layers.Dense(10, input_shape=(784,)), # 入力のshapeを指定する必要がある
tf.keras.layers.Dense(10)
])
もしくは、 tf.keras.Model
を継承してモデルをクラスにまとめます。
これはレイヤー自身であるレイヤーのコンテナで、 tf.keras.Model
オブジェクトが他のtf.keras.Model
オブジェクトを含むことを可能にします。
Alternatively, organize models in classes by inheriting from tf.keras.Model
.
This is a container for layers that is a layer itself, allowing tf.keras.Model
objects to contain other tf.keras.Model
objects.
In [ ]:
class MNISTModel(tf.keras.Model):
def __init__(self):
super(MNISTModel, self).__init__()
self.dense1 = tf.keras.layers.Dense(units=10)
self.dense2 = tf.keras.layers.Dense(units=10)
def call(self, input):
"""Run the model."""
result = self.dense1(input)
result = self.dense2(result)
result = self.dense2(result) # dense2レイヤーを再利用します reuse variables from dense2 layer
return result
model = MNISTModel()
入力のshapeは最初のレイヤーに初めて入力データを渡すときにセットされるため、
モデル構築時にtf.keras.Model
クラスに設定する必要はありません。
tf.keras.layers
クラスは独自のモデル変数を作成し、包含します。このモデル変数は、それを含むレイヤーオブジェクトのライフタイムにひもづきます。レイヤー変数を共有するには、それらのオブジェクトを共有します。
自動微分はニューラルネットワークの学習で利用されるバックプロパゲーションなどの機械学習アルゴリズムの実装を行う上で便利です。
Eager Executionでは、勾配計算をあとで行うためのオペレーションをトレースするためにtf.GradientTape
を利用します。
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'),
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.train.AdamOptimizer()
loss_history = []
In [ ]:
for (batch, (images, labels)) in enumerate(dataset.take(400)):
if batch % 10 == 0:
print('.', end='')
with tf.GradientTape() as tape:
logits = mnist_model(images, training=True)
loss_value = tf.losses.sparse_softmax_cross_entropy(labels, logits)
loss_history.append(loss_value.numpy())
grads = tape.gradient(loss_value, mnist_model.trainable_variables)
optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables),
global_step=tf.train.get_or_create_global_step())
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 * 2 + 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.train.GradientDescentOptimizer(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]),
global_step=tf.train.get_or_create_global_step())
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
import tempfile
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
tf.keras.layers.GlobalAveragePooling2D(),
tf.keras.layers.Dense(10)
])
optimizer = tf.train.AdamOptimizer(learning_rate=0.001)
checkpoint_dir = tempfile.mkdtemp()
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
root = tf.train.Checkpoint(optimizer=optimizer,
model=model,
optimizer_step=tf.train.get_or_create_global_step())
root.save(checkpoint_prefix)
root.restore(tf.train.latest_checkpoint(checkpoint_dir))
In [ ]:
m = tfe.metrics.Mean("loss")
m(0)
m(5)
m.result() # => 2.5
m([8, 9])
m.result() # => 5.5
TensorBoard はモデルの学習プロセスを理解、デバッグ、最適化するための可視化ツールです。プログラムの実行中に書き込まれるサマリイベントを使用します。
tf.contrib.summary
はEager ExecutionとGraph Executionの両方の環境と互換性があります。
tf.contrib.summary.scalar
のようなサマリオペレーションはモデル構築の間に挿入されます。
たとえば、100のグローバルステップごとにサマリを記録するには、次のようにします。
In [ ]:
global_step = tf.train.get_or_create_global_step()
logdir = "./tb/"
writer = tf.contrib.summary.create_file_writer(logdir)
writer.set_as_default()
for _ in range(10):
global_step.assign_add(1)
# record_summariesメソッドをincludeする必要がある
with tf.contrib.summary.record_summaries_every_n_global_steps(100):
# ここにモデルのコードを記述する
tf.contrib.summary.scalar('global_step', global_step)
In [ ]:
!ls tb/
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
tf.GradientTape
は強力な勾配計算インタフェースですが、
自動微分に利用できる別のAutogradスタイルのAPIもあります。
これらの関数はテンソルと勾配関数のみを使って、tf.variables
を使わずに数式コードを書く場合に便利です:
tfe.gradients_function
—引数をとり、入力関数パラメータの導関数を計算する関数を返します。
入力パラメータはスカラ値を返さなければなりません。返された関数が
されると、 tf.Tensor
オブジェクトのリストを返します:入力関数のそれぞれの
引数に対して一つの要素。重要なものすべてを関数パラメータとして渡さなければならないので、
多くのtrainableパラメータに依存している場合、これは扱いにくくなります。tfe.value_and_gradients_function
—tfe.gradients_function
に似ていますが、
返された関数が呼び出されると、その引数に関する入力関数の導関数のリストに加えて、入力関数からの値を返します。次の例では、 tfe.gradients_function
は引数としてsquare
関数を取り、その入力に関して square
の偏微分
導関数を計算する関数を返します。 3
におけるsquare
の微分を計算するために、 grad(3.0)
は 6
を返します。
In [ ]:
def square(x):
return tf.multiply(x, x)
grad = tfe.gradients_function(square)
In [ ]:
square(3.).numpy()
In [ ]:
grad(3.)[0].numpy()
In [ ]:
# 平方の二次導関数:
gradgrad = tfe.gradients_function(lambda x: grad(x)[0])
gradgrad(3.)[0].numpy()
In [ ]:
# 3次導関数はNoneになる:
gradgradgrad = tfe.gradients_function(lambda x: gradgrad(x)[0])
gradgradgrad(3.)
In [ ]:
# フロー制御:
def abs(x):
return x if x > 0. else -x
grad = tfe.gradients_function(abs)
In [ ]:
grad(3.)[0].numpy()
In [ ]:
grad(-3.)[0].numpy()
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.log(1 + tf.exp(x))
grad_log1pexp = tfe.gradients_function(log1pexp)
In [ ]:
# 勾配計算はx = 0のときにはうまくいきます。
grad_log1pexp(0.)[0].numpy()
In [ ]:
# しかし、x = 100のときは数値的不安定により失敗します。
grad_log1pexp(100.)[0].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.log(1 + e), grad
grad_log1pexp = tfe.gradients_function(log1pexp)
In [ ]:
# 上と同様に、勾配計算はx = 0のときにはうまくいきます。
grad_log1pexp(0.)[0].numpy()
In [ ]:
# また、勾配計算はx = 100でも機能します。
grad_log1pexp(100.)[0].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 tfe.num_gpus() > 0:
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上で実行するとき
if tfe.num_gpus() > 1:
x_gpu1 = x.gpu(1)
_ = tf.matmul(x_gpu1, x_gpu1) # GPU:1で実行するとき
GPUでの ResNet50 の学習のような、計算量の多いモデルの場合は、Eager ExecutionのパフォーマンスはGraph Executionのパフォーマンスに匹敵します。 しかし、この2つの環境下のパフォーマンスの違いは計算量の少ないモデルではより大きくなり、小さなたくさんのオペレーションからなるモデルでホットコードパスを最適化するためにやるべきことがあります。
Eager Executionは開発とデバッグをより対話的にしますが、 TensorFlowのGraph Executionは分散学習、パフォーマンスの最適化、そしてプロダクション環境へのデプロイの観点で利点があります。 しかし、Graph Executionのコードの記述方法、標準的なのPythonコードの書き方と異なり、デバッグがより難しく感じるかもしれません。
Graph Execution形式のモデルの構築と学習のために、Pythonプログラムは最初に計算グラフを構築し、
それからC++ベースのランタイムで実行するためにSession.run
を呼び出し、グラフを渡します。この機能の特徴は以下のとおりです:
Eager Executionのコードは、Graph Executionのコードよりもデプロイが難しいです:モデルから 計算グラフを生成するか、またはサーバ上で直接Pythonランタイムからコードを実行する必要があります。
Eager Execution環境で記述されたコードは、Eager Executionが有効になっていない新しいPythonセッションでおなじコードを実行するだけで おなじコードのままGraph Executionで実行することができます。
ほとんどのTensorFlowオペレーションはEager Executionで動作しますが、注意すべき点がいくつかあります:
tf.data
を使います。この方法はより高速で簡単です。tf.keras.layers
やtf.keras.Model
のような、オブジェクト指向のレイヤーAPIを使用します—これらのAPIは変数のための明示的なストレージを持っているためです。tf.enable_eager_execution
によってEager Executionが有効化されると、それを無効化することはできません。
Graph Executionに戻すには、新しいPythonセッションを開始する必要があります。以上が、Eager Execution と Graph Executionの両方のためのコードを書くためのベストプラクティスです。これによって、 Eager Executionによる対話的な実験とデバッガビリティを享受することができ、かつGraph Executionによる分散パフォーマンスの恩恵を受けることができます。
Eager Executionを用いてコードを記述、デバッグ、実験を繰り返したのちにプロダクションへのデプロイのためにモデルパスをimportします。
モデル変数を保存および復元するには tf.train.Checkpoint
を使います。これはEager ExecutionとGraph Executionの両環境の互換性を担保します。
以下にEager Executionのサンプル集があります:
tensorflow/contrib/eager/python/examples
In [ ]:
def my_py_func(x):
x = tf.matmul(x, x) # tfオペレーションを使用することができる
print(x) # しかしEager Executionで実行される!
return x
with tf.Session() as sess:
x = tf.placeholder(dtype=tf.float32)
# Graph Execution環境でEager Executionを呼び出す
pf = tfe.py_func(my_py_func, [x], tf.float32)
sess.run(pf, feed_dict={x: [[2.0]]}) # [[4.0]]