Differenziazione automatica e gradient tape

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza il sorgente su GitHub Scarica il notebook

Nel tutorial precedente abbiamo introdotto i Tensori e le loro operazioni. In questo tutorial copriremo la differenziazione automatica, una tecnica importante per ottimizare i modelli di machine learning.

Setup

import tensorflow as tf

Gradient tapes

TensorFlow fornisce l'API tf.GradientTape per la differenziazione automatica che calcola il gradiente di una computazione rispetto alle sue variabili in input. Tensorflow "registra" tutte le operazioni eseguite dentro il contesto di un tf.GradientTape su un "tape". Tensorflow quindi usa quel "tape" e i gradienti associati con ogni operazione registrata per calcolare il gradiente di una computazione "registrata" utilizzando l'accumulazione inversa.

Per esempio:

x = tf.ones((2, 2))

with tf.GradientTape() as t:
  t.watch(x)
  y = tf.reduce_sum(x)
  z = tf.multiply(y, y)

# Derivative of z with respect to the original input tensor x
dz_dx = t.gradient(z, x)
for i in [0, 1]:
  for j in [0, 1]:
    assert dz_dx[i][j].numpy() == 8.0

Puoi anche richiedere i gradienti dell'output rispetto ai valori intermedi calcolati in un contesto "registrato" di 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)

# Use the tape to compute the derivative of z with respect to the
# intermediate value y.
dz_dy = t.gradient(z, y)
assert dz_dy.numpy() == 8.0

Di default, le risorse tenute da un GradientTape sono rilasciate non appena il metodo GradientTape.gradient() è chiamato. Per calcolare multipli gradienti sullo stesso calcolo, crea un persistent gradient tape. Questo da la possibilità di fare chiamate multiple del metodo gradient() non appena le risorse sono rilasciate quando l'oggetto tape è liberato dal garbage collector. Per esempio:

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  # Drop the reference to the tape

Flusso di controllo della registrazione

Poichè i "tape" registrano operazione nel momento in cui le eseugono, il flusso di controllo Python (usando if e while per esempio) è gestito naturalmente:

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

Gradienti di ordine superiore

Le operazioni dentro il gestore di contesto di GradientTape sono registrati per la differenziazione automatica. Se i gradienti sono calcolati nello stesso contesto, allora anche il calcolo del gradiente è registrato. Come risultato, la stessa API funziona per gradienti di ordine superiore. Per esempio:

x = tf.Variable(1.0)  # Create a Tensorflow variable initialized to 1.0

with tf.GradientTape() as t:
  with tf.GradientTape() as t2:
    y = x * x * x
  # Compute the gradient inside the 't' context manager
  # which means the gradient computation is differentiable as well.
  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

Passi successivi

In questo tutorial abbiamo coperto il calcolo di gradienti in TensorFlow. Con questo abbiamo abbastanza primitive richieste per costruire e addestrare reti neurali.