ホーム » TensorFlow 2.0 » TensorFlow 2.0 Beta : ガイド : TensorFlow 2.0 の tf.function と AutoGraph

TensorFlow 2.0 Beta : ガイド : TensorFlow 2.0 の tf.function と AutoGraph

TensorFlow 2.0 Beta : ガイド : TensorFlow 2.0 の tf.function と AutoGraph (翻訳/解説)

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

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

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

 

ガイド : TensorFlow 2.0 の tf.function と AutoGraph

TF 2.0 は eager execution の容易さと TF 1.0 のパワーを一つにまとめています。この融合の中心には tf.function があります、これは Python シンタクスのサブセットを可搬な、高パフォーマンスの TensorFlow グラフに変換することを可能にします。

tf.function のクールな新しい特徴は AutoGraph です、これは貴方に自然な Python シンタクスを使用してグラフコードを書かせます。AutoGraph で使用できる Python 特徴のリストについては、AutoGraph Capabilities and Limitations を見てください。tf.function についてのより多くの詳細は、RFC TF 2.0: Functions, not Sessions を見てください。AutoGraph についてのより詳細は、tf.autograph を見てください。

このチュートリアルは tf.function と AutoGraph の基本的な特徴を貴方にウォークスルーしてもらいます。

 

セットアップ

TensorFlow 2.0 Preview Nightly をインポートして TF 2.0 モードを有効にします :

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

 

tf.function デコレータ

関数を tf.function でアノテートするとき、依然として任意の他の関数のようにそれを呼び出すことができます。しかしそれはグラフ内にコンパイルされ、これは高速な実行の恩恵を得ることを意味し、GPU か TPU で実行されるか、SavedModel にエクスポートされます。

@tf.function
def simple_nn_layer(x, y):
  return tf.nn.relu(tf.matmul(x, y))


x = tf.random.uniform((3, 3))
y = tf.random.uniform((3, 3))

simple_nn_layer(x, y)
<tf.Tensor: id=23, shape=(3, 3), dtype=float32, numpy=
array([[0.28093395, 0.38601664, 0.1492369 ],
       [0.18872553, 0.49841696, 0.21768065],
       [0.5309121 , 0.94758576, 0.38262835]], dtype=float32)>

アノテーションの結果を調べればそれが TensorFlow ランタイムとの総ての相互作用を扱う特別な callable であることが見て取れるでしょう。

simple_nn_layer
<tensorflow.python.eager.def_function.Function at 0x7efc950bfef0>

貴方のコードが複数の関数を使用する場合、それら総てをアノテートする必要はありません – アノテートされた関数から呼び出された任意の関数もまたグラフモードで動作します。

def linear_layer(x):
  return 2 * x + 1


@tf.function
def deep_net(x):
  return tf.nn.relu(linear_layer(x))


deep_net(tf.constant((1, 2, 3)))
<tf.Tensor: id=36, shape=(3,), dtype=int32, numpy=array([3, 5, 7], dtype=int32)>

多くの小さい ops を持つグラフに対しては、Functions は eager コードよりも高速であり得ます。しかし少数の高価な ops を持つグラフに対しては、多量のスピードアップを見ないかもしれません。

import timeit
conv_layer = tf.keras.layers.Conv2D(100, 3)

@tf.function
def conv_fn(image):
  return conv_layer(image)

image = tf.zeros([1, 200, 200, 100])
# warm up
conv_layer(image); conv_fn(image)
print("Eager conv:", timeit.timeit(lambda: conv_layer(image), number=10))
print("Function conv:", timeit.timeit(lambda: conv_fn(image), number=10))
print("Note how there's not much difference in performance for convolutions")
Eager conv: 0.2274835030000304
Function conv: 0.1842926219999299
Note how there's not much difference in performance for convolutions
lstm_cell = tf.keras.layers.LSTMCell(10)

@tf.function
def lstm_fn(input, state):
  return lstm_cell(input, state)

input = tf.zeros([10, 10])
state = [tf.zeros([10, 10])] * 2
# warm up
lstm_cell(input, state); lstm_fn(input, state)
print("eager lstm:", timeit.timeit(lambda: lstm_cell(input, state), number=10))
print("function lstm:", timeit.timeit(lambda: lstm_fn(input, state), number=10))
eager lstm: 0.005612198999983775
function lstm: 0.004945082999938677

 

Python 制御フローを使用する

tf.function 内でデータ依存制御フローを使用しているとき、Python 制御フロー・ステートメントを使用することができてそして AutoGraph はそれらを適切な TensorFlow ops に変換します。例えば、if ステートメントはそれらが Tensor に依存する場合 tf.cond() に変換されるでしょう。

下の例では、x は Tensor ですがステートメントは期待通りに動作します :

@tf.function
def square_if_positive(x):
  if x > 0:
    x = x * x
  else:
    x = 0
  return x


print('square_if_positive(2) = {}'.format(square_if_positive(tf.constant(2))))
print('square_if_positive(-2) = {}'.format(square_if_positive(tf.constant(-2))))
square_if_positive(2) = 4
square_if_positive(-2) = 0

 
Note: 前の例はスカラー値を伴う単純な条件節を使用しています。現実世界のコードでは典型的にはバッチ処理が使用されます。

AutoGraph は while, for, if, break, continue と return のような一般的な Python ステートメントをネスティングのためのサポートとともに、サポートします。それは while と if ステートメントの条件で Tensor 式を使用したり、for ループで Tensor に渡り iterate できることを意味します。

@tf.function
def sum_even(items):
  s = 0
  for c in items:
    if c % 2 > 0:
      continue
    s += c
  return s


sum_even(tf.constant([10, 12, 15, 20]))
<tf.Tensor: id=606, shape=(), dtype=int32, numpy=42>

AutoGraph はまた上級ユーザのために低位 API も提供します。例えば生成されたコードを見るためにそれを使用できます。

print(tf.autograph.to_code(sum_even.python_function))
def tf__sum_even(items):
  do_return = False
  retval_ = ag__.UndefinedReturnValue()
  s = 0

  def loop_body(loop_vars, s_2):
    c = loop_vars
    continue_ = False
    cond = c % 2 > 0

    def get_state():
      return ()

    def set_state(_):
      pass

    def if_true():
      continue_ = True
      return continue_

    def if_false():
      return continue_
    continue_ = ag__.if_stmt(cond, if_true, if_false, get_state, set_state)
    cond_1 = ag__.not_(continue_)

    def get_state_1():
      return ()

    def set_state_1(_):
      pass

    def if_true_1():
      s_1, = s_2,
      s_1 += c
      return s_1

    def if_false_1():
      return s_2
    s_2 = ag__.if_stmt(cond_1, if_true_1, if_false_1, get_state_1, set_state_1)
    return s_2,
  s, = ag__.for_stmt(items, None, loop_body, (s,))
  do_return = True
  retval_ = s
  cond_2 = ag__.is_undefined_return(retval_)

  def get_state_2():
    return ()

  def set_state_2(_):
    pass

  def if_true_2():
    retval_ = None
    return retval_

  def if_false_2():
    return retval_
  retval_ = ag__.if_stmt(cond_2, if_true_2, if_false_2, get_state_2, set_state_2)
  return retval_

ここにより複雑な制御フローのサンプルがあります :

@tf.function
def fizzbuzz(n):
  msg = tf.constant('')
  for i in tf.range(n):
    if tf.equal(i % 3, 0):
      tf.print('Fizz')
    elif tf.equal(i % 5, 0):
      tf.print('Buzz')
    else:
      tf.print(i)

fizzbuzz(tf.constant(15))
Fizz
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14

 

Keras と AutoGraph

tf.function はオブジェクトのメソッドでもまた使用できます。例えば、典型的にはモデルの call 関数をアノテートすることで貴方のカスタム Keras モデルをデコレートできます。より多くの情報については、tf.keras を見てください。

class CustomModel(tf.keras.models.Model):

  @tf.function
  def call(self, input_data):
    if tf.reduce_mean(input_data) > 0:
      return input_data
    else:
      return input_data // 2


model = CustomModel()

model(tf.constant([-2, -4]))
<tf.Tensor: id=723, shape=(2,), dtype=int32, numpy=array([-1, -2], dtype=int32)>

 

副作用

ちょうど eager モードでのように、通常は tf.function 内で tf.assign や tf.print のような、副作用を持つ演算を使用することができて、それはそれらが順番に実行されることを確実にするために必要な制御依存性を挿入します。

v = tf.Variable(5)

@tf.function
def find_next_odd():
  v.assign(v + 1)
  if tf.equal(v % 2, 0):
    v.assign(v + 1)


find_next_odd()
v
<tf.Variable 'Variable:0' shape=() dtype=int32, numpy=7>

 

例題: 単純なモデルを訓練する

AutoGraph はまたより多くの計算を TensorFlow 内に移すことを可能にします。例えば、訓練ループはちょうど制御フローですので、それは実際には TensorFlow に持ち込むことができます。

 

データをダウンロードする

def prepare_mnist_features_and_labels(x, y):
  x = tf.cast(x, tf.float32) / 255.0
  y = tf.cast(y, tf.int64)
  return x, y

def mnist_dataset():
  (x, y), _ = tf.keras.datasets.mnist.load_data()
  ds = tf.data.Dataset.from_tensor_slices((x, y))
  ds = ds.map(prepare_mnist_features_and_labels)
  ds = ds.take(20000).shuffle(20000).batch(100)
  return ds

train_dataset = mnist_dataset()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

 

モデルを定義する

model = tf.keras.Sequential((
    tf.keras.layers.Reshape(target_shape=(28 * 28,), input_shape=(28, 28)),
    tf.keras.layers.Dense(100, activation='relu'),
    tf.keras.layers.Dense(100, activation='relu'),
    tf.keras.layers.Dense(10)))
model.build()
optimizer = tf.keras.optimizers.Adam()

 

訓練ループを定義する

compute_loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

compute_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()


def train_one_step(model, optimizer, x, y):
  with tf.GradientTape() as tape:
    logits = model(x)
    loss = compute_loss(y, logits)

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

  compute_accuracy(y, logits)
  return loss


@tf.function
def train(model, optimizer):
  train_ds = mnist_dataset()
  step = 0
  loss = 0.0
  accuracy = 0.0
  for x, y in train_ds:
    step += 1
    loss = train_one_step(model, optimizer, x, y)
    if tf.equal(step % 10, 0):
      tf.print('Step', step, ': loss', loss, '; accuracy', compute_accuracy.result())
  return step, loss, accuracy

step, loss, accuracy = train(model, optimizer)
print('Final step', step, ': loss', loss, '; accuracy', compute_accuracy.result())
Step 10 : loss 1.83085191 ; accuracy 0.336
Step 20 : loss 1.03369474 ; accuracy 0.532
Step 30 : loss 0.636070907 ; accuracy 0.627
Step 40 : loss 0.49779284 ; accuracy 0.6745
Step 50 : loss 0.485161096 ; accuracy 0.7116
Step 60 : loss 0.574727654 ; accuracy 0.7365
Step 70 : loss 0.307435066 ; accuracy 0.756714284
Step 80 : loss 0.495543599 ; accuracy 0.7725
Step 90 : loss 0.34826988 ; accuracy 0.785
Step 100 : loss 0.334337503 ; accuracy 0.7955
Step 110 : loss 0.290210605 ; accuracy 0.805090904
Step 120 : loss 0.320541888 ; accuracy 0.813166678
Step 130 : loss 0.307672709 ; accuracy 0.821230769
Step 140 : loss 0.235306799 ; accuracy 0.826928556
Step 150 : loss 0.252060503 ; accuracy 0.833266675
Step 160 : loss 0.177218169 ; accuracy 0.8395
Step 170 : loss 0.292944938 ; accuracy 0.844470561
Step 180 : loss 0.249169499 ; accuracy 0.848555565
Step 190 : loss 0.43364051 ; accuracy 0.852210522
Step 200 : loss 0.25526756 ; accuracy 0.85565
Final step tf.Tensor(200, shape=(), dtype=int32) : loss tf.Tensor(0.25526756, shape=(), dtype=float32) ; accuracy tf.Tensor(0.85565, shape=(), dtype=float32)

 

バッチ処理

実際のアプリケーションではバッチ処理はパフォーマンスのために重要です。AutoGraph に変換する最善のコードはそこでは制御フローがバッチレベルで決定されるようなコードです。個々のサンプルレベルで決定を行なう場合には、パフォーマンスを維持するために batch API を利用してみてください。

例えば、Python で次のようなコードを持つ場合 :

def square_if_positive(x):
  return [i ** 2 if i > 0 else i for i in x]


square_if_positive(range(-5, 5))
[-5, -4, -3, -2, -1, 0, 1, 4, 9, 16]

TensorFlow で次のようにそれを書く誘惑にかられるかもしれません (そしてこれは動作します!) :

@tf.function
def square_if_positive_naive(x):
  result = tf.TensorArray(tf.int32, size=x.shape[0])
  for i in tf.range(x.shape[0]):
    if x[i] > 0:
      result = result.write(i, x[i] ** 2)
    else:
      result = result.write(i, x[i])
  return result.stack()


square_if_positive_naive(tf.range(-5, 5))
<tf.Tensor: id=1834, shape=(10,), dtype=int32, numpy=array([-5, -4, -3, -2, -1,  0,  1,  4,  9, 16], dtype=int32)>

しかしこの場合、次を書くことができることが判明します :

def square_if_positive_vectorized(x):
  return tf.where(x > 0, x ** 2, x)


square_if_positive_vectorized(tf.range(-5, 5))
<tf.Tensor: id=1844, shape=(10,), dtype=int32, numpy=array([-5, -4, -3, -2, -1,  0,  1,  4,  9, 16], dtype=int32)>
 

以上



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