Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/ja/guide/eager.ipynb
25115 views
Kernel: Python 3
#@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.

Eager execution

TensorFlow の Eager Execution は、計算グラフの作成と評価を同時におこなう命令的なプログラミングを行うための環境です: オペレーションはあとで実行するための計算グラフでなく、具体的な計算結果の値を返します。 この方法を用いることにより、初心者にとって TensorFlow を始めやすくなり、またモデルのデバッグも行いやすくなります。 さらにコードの記述量も削減されます。 このガイドの内容を実行するためには、対話的インタープリタ python を起動し、以下のコードサンプルを実行してください。

Eager execution は研究や実験のための柔軟な機械学習環境として、以下を提供します。

  • 直感的なインタフェース— Python のデータ構造を使用して、コードを自然に記述することができます。小規模なモデルとデータに対してすばやく実験を繰り返すことができます。

  • より簡単なデバッグ— ops を直接呼び出すことで、実行中のモデルを調査したり、変更をテストすることができます。 Python 標準のデバッグツールを用いて即座にエラーのレポーティングができます。

  • 自然な制御フロー— TensorFlow のグラフ制御フローの代わりに Python の制御フローを利用するため、動的なモデルの作成をシンプルに行うことができます。

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

注意: いくつかのモデルは Eager execution を有効化することでオーバヘッドが増える可能性があります。 パフォーマンス改善を行っていますが、問題が見つかった場合はバグを報告し、ベンチマークを提供してください。

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

import tensorflow as tf import cProfile

TensorFlow 2.0 では、 Eager Execution はデフォルトで有効化されます。

tf.executing_eagerly()

これで TensorFlow の演算を実行してみましょう。結果はすぐに返されます。

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 の tf.math 演算は Python オブジェクトと Numpy 配列を tf.Tensor に変換します。tf.Tensor.numpy メソッドはオブジェクトの値を NumPy の ndarray 形式で返します。

a = tf.constant([[1, 2], [3, 4]]) print(a)
# ブロードキャストのサポート b = tf.add(a, 1) print(b)
# オペレータのオーバーロードがサポートされている print(a * b)
# NumPy valueの使用 import numpy as np c = np.multiply(a, b) print(c)
# Obtain numpy value from a tensor: print(a.numpy()) # => [[1 2] # [3 4]]

動的な制御フロー

Eager Execution の主要なメリットは、モデルを実行する際にホスト言語のすべての機能性が利用できることです。 たとえば、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)

この関数はテンソル値に依存する条件式を持ち、実行時にこれらの値を表示します。

Eager Execution による学習

勾配の計算

自動微分はニューラルネットワークの学習で利用されるバックプロパゲーションなどの機械学習アルゴリズムの実装を行う上で便利です。 Eager Executionでは、勾配計算をあとで行うためのオペレーションをトレースするためにtf.GradientTape を利用します。

Eager execution では、トレーニングや勾配計算に、tf.GradientTape を利用できます。これは特に、複雑なトレーニングループを実行するときに役立ちます。

各呼び出し中に異なるオペレーションが発生する可能性があるため、すべての forward-pass オペレーションは一つの「テープ」に記録されます。勾配を計算するには、テープを逆方向に再生してから破棄します。特定の tf.GradientTapeは一つのグラデーションしか計算できません。後続の呼び出しは実行時エラーをスローします。

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)

モデルをトレーニングする

以下の example は標準的な MNIST の手書き数字を分類するマルチレイヤーモデルを作成します。Eager execution 環境にトレーニング対象のグラフを構築するオプティマイザとレイヤー 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)
# 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 execution により、モデルを呼び出して出力を検査することができます。

for images,labels in dataset.take(1): print("Logits: ", mnist_model(images[0:1]).numpy())

keras モデルは組み込みで学習のループを回すメソッド fit がありますが、よりカスタマイズが必要な場合もあるでしょう。 Eager Executionを用いて実装された学習ループのサンプルを以下に示します:

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

Note: モデルの状況を確認したいときは、 tf.debugging にある assert 機能を利用してください。この機能は Eager Execution と Graph Execution のどちらでも利用できます。

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))
def train(): for epoch in range(3): for (batch, (images, labels)) in enumerate(dataset): train_step(images, labels) print ('Epoch {} finished'.format(epoch))
train(epochs = 3)
import matplotlib.pyplot as plt plt.plot(loss_history) plt.xlabel('Batch #') plt.ylabel('Loss [entropy]')

変数とオプティマイザ

tf.Variable オブジェクトは、トレーニング中にアクセスされるミュータブルな tf.Tensor のような値を格納し、自動微分を簡単化しています。

tf.GradientTape と共に tf.Variable を使うことでモデルパラメータはよりカプセル化されます。たとえば、上の の自動微分の例は以下のように書き換えることができます:

たとえば、上記の自動微分の例は次のように書き直すことができます。

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
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()))

次に、以下を行います。

  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)))
print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))

注意: 変数は、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(checkpoint_path)
x.assign(11.) # 保存後に変数の値を変える。 # チェックポイントから変数を復元する checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path)) print(x) # => 2.0

モデルを保存して読み込むために、 tf.train.Checkpoint は隠れ変数なしにオブジェクトの内部状態を保存します。 モデルオプティマイザ 、そしてグローバルステップの状態を記録するには、それらを tf.train.Checkpoint に渡します。

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 が呼び出された後に作成されます。これらの変数は作成されてすぐに復元され、チェックポイントが完全に読み込まれたことを確認するためのアサーションが利用可能になります。詳しくは、トレーニングチェックポイントに関するガイドをご覧ください。

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

tfe.keras.metrics はオブジェクトとして保存されます。新しいデータをコーラブルに渡してメトリクスを更新し、 tfe.keras.metrics.result メソッドを使って結果を取得します。次に例を示します:

m = tf.keras.metrics.Mean("loss") m(0) m(5) m.result() # => 2.5 m([8, 9]) m.result() # => 5.5

サマリーと TensorBoard

TensorBoard はモデルのトレーニングプロセスを理解、デバッグ、および最適化するための視覚化ツールです。プログラムを実行する間に書き込まれるサマリーイベントを使用します。

Eager execution で変数のサマリーを記録するには、tf.summary を使用できます。たとえば、100 トレーニングステップごとに loss のサマリーを 1 回記録するには、次のように記述できます。

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/

高度な自動微分トピック

動的なモデル

tf.GradientTape は動的モデルでも使うことができます。 以下の バックトラックライン検索 アルゴリズムの例は、複雑な制御フローにもかかわらず 勾配があり、微分可能であることを除いて、通常の 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

カスタム勾配

カスタム勾配は、勾配を上書きする簡単な方法です。 フォワード関数では、 入力、出力、または中間結果に関する勾配を定義します。たとえば、逆方向パスにおいて勾配のノルムを制限する簡単な方法は次のとおりです:

@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)
# 勾配計算は x = 0 のときはうまくいく。 grad_log1pexp(tf.constant(0.)).numpy()
# However, x = 100 fails because of numerical instability. grad_log1pexp(tf.constant(100.)).numpy()

ここで、 log1pexp 関数はカスタム勾配を用いて解析的に単純化することができます。 以下の実装は、フォワードパスの間に計算された 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()
# また、勾配計算はx = 100でも機能します。 grad_log1pexp(tf.constant(100.)).numpy()

パフォーマンス

Eager execution の間、計算は自動的に GPU にオフロードされます。計算を実行する場所を指定する場合は、tf.device('/gpu:0') ブロック(または CPU を指定するブロック)に含めることができます。

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 オブジェクトはそのオブジェクトに対するオペレーションを実行するために別のデバイスにコピーすることができます:

if tf.config.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

ベンチマーク

GPUでの ResNet50 の学習のような、計算量の多いモデルの場合は、Eager Executionのパフォーマンスは tf.function のパフォーマンスに匹敵します。 しかし、この2つの環境下のパフォーマンスの違いは計算量の少ないモデルではより大きくなり、小さなたくさんのオペレーションからなるモデルでホットコードパスを最適化するためにやるべきことがあります。

function の使用

Eager Execution は開発とデバッグをより対話的にしますが、 TensorFlow 1.x スタイルの Graph Execution は分散学習、パフォーマンスの最適化、そしてプロダクション環境へのデプロイの観点で利点があります。