自動微分と勾配テープ

View on TensorFlow.org Run in Google Colab View source on GitHub Download notebook

これまでのチュートリアルでは、テンソルとその演算について紹介しました。このチュートリアルでは、機械学習モデルの最適化の鍵となる手法である自動微分を扱います。

設定

import tensorflow as tf

勾配テープ

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)

# 元の入力テンソル x に対する z の微分
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)

# テープを使って中間値 y に対する z の微分を計算
dz_dy = t.gradient(z, y)
assert dz_dy.numpy() == 8.0

デフォルトでは、ある GrandientTape に保持されたリソースは、GradientTape.gradient() メソッドが呼び出されると解放されます。一つの計算で複数の勾配を計算したい場合には、永続的(persistent)な勾配テープを作成します。こうすると、リソースはテープオブジェクトがガベージコレクションによって廃棄されるときに解放されるため、gradient() メソッドを複数回呼び出すことができます。

例を示します。

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  # テープへの参照を削除

制御フローの記録

勾配テープは演算を実行の都度記録するため、(たとえば ifwhile を使った)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)  # 1.0 で初期化された TensorFlow 変数を作成

with tf.GradientTape() as t:
  with tf.GradientTape() as t2:
    y = x * x * x
  # ’t’ コンテキストマネジャー内で勾配を計算
  # これは勾配計算も同様に微分可能であるということ
  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 の勾配計算を扱いました。これで、ニューラルネットワークの構築と訓練のために必要な構成要素が揃いました。