Eifrige Ausführung

Auf TensorFlow.org ansehen In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Die eifrige Ausführung von TensorFlow ist eine unverzichtbare Programmierumgebung, die Operationen sofort auswertet, ohne Graphen zu erstellen: Operationen geben konkrete Werte zurück, anstatt einen Rechengraphen für die spätere Ausführung zu erstellen. Dies erleichtert den Einstieg in TensorFlow- und Debug-Modelle und reduziert auch die Boilerplate. Um bei dieser Anleitung in einem interaktiven die Codebeispiele unten laufen python - Interpreter.

Eager Execution ist eine flexible Plattform für maschinelles Lernen für Forschung und Experimente, die Folgendes bietet:

  • Eine intuitive Benutzeroberfläche -Struktur Code natürlich und Python - Datenstrukturen verwenden. Iterieren Sie schnell mit kleinen Modellen und kleinen Daten.
  • Einfachere Debuggen -Rufen ops direkt laufen Modelle und Test Änderungen zu überprüfen. Verwenden Sie standardmäßige Python-Debugging-Tools für die sofortige Fehlerberichterstattung.
  • Natürliche Flusssteuerung -Use Python Steuerfluss statt Graph Steuerfluss, die Spezifikation von dynamischen Modellen vereinfacht wird .

Die eifrige Ausführung unterstützt die meisten TensorFlow-Operationen und die GPU-Beschleunigung.

Einrichtung und grundlegende Nutzung

import os

import tensorflow as tf

import cProfile

In Tensorflow 2.0 ist die Eager-Ausführung standardmäßig aktiviert.

tf.executing_eagerly()
True

Jetzt können Sie TensorFlow-Operationen ausführen und die Ergebnisse werden sofort zurückgegeben:

x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m))
hello, [[4.]]

Das Aktivieren von Eager Execution ändert das Verhalten von TensorFlow-Operationen – jetzt werten sie ihre Werte sofort aus und geben sie an Python zurück. tf.Tensor Objekte Werte konkreten Bezug statt symbolische Griffe zu Knoten in einem Berechnungsgraphen. Da es kein Berechnungsgraphen zu bauen ist und später in einer Sitzung ausgeführt wird , ist es einfach , Ergebnisse zu überprüfen mit print() oder einem Debugger. Das Auswerten, Drucken und Überprüfen von Tensorwerten unterbricht nicht den Fluss für die Berechnung von Gradienten.

Eager Ausführung funktioniert gut mit NumPy . NumPy Operationen akzeptieren tf.Tensor Argumente. Die TensorFlow tf.math Operationen konvertieren Python - Objekte und NumPy Arrays tf.Tensor Objekte. Die tf.Tensor.numpy Methode gibt den Wert des Objekts als NumPy ndarray .

a = tf.constant([[1, 2],
                 [3, 4]])
print(a)
tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32)
# Broadcasting support
b = tf.add(a, 1)
print(b)
tf.Tensor(
[[2 3]
 [4 5]], shape=(2, 2), dtype=int32)
# Operator overloading is supported
print(a * b)
tf.Tensor(
[[ 2  6]
 [12 20]], shape=(2, 2), dtype=int32)
# Use NumPy values
import numpy as np

c = np.multiply(a, b)
print(c)
[[ 2  6]
 [12 20]]
# Obtain numpy value from a tensor:
print(a.numpy())
# => [[1 2]
#     [3 4]]
[[1 2]
 [3 4]]

Dynamischer Kontrollfluss

Ein großer Vorteil von Eager Execution besteht darin, dass die gesamte Funktionalität der Hostsprache verfügbar ist, während Ihr Modell ausgeführt wird. So zum Beispiel, ist es einfach zu schreiben 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)
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

Dies hat Bedingungen, die von Tensorwerten abhängen, und druckt diese Werte zur Laufzeit.

Eifriges Training

Gradienten berechnen

Automatische Differenzierung ist nützlich für die Implementierung Maschine Algorithmen wie das Lernen Backpropagation für die Ausbildung neuronaler Netze. Während eifrig Ausführung verwendet tf.GradientTape Operationen zu verfolgen , für spätere Gradienten zu berechnen.

Sie können mit tf.GradientTape trainieren und / oder Rechen Gradienten in eifrig. Es ist besonders nützlich für komplizierte Trainingsschleifen.

Da bei jedem Anruf unterschiedliche Operationen auftreten können, werden alle Forward-Pass-Operationen auf einem "Band" aufgezeichnet. Um den Gradienten zu berechnen, spielen Sie das Band rückwärts ab und verwerfen Sie es dann. Ein besonderer tf.GradientTape kann nur einen Gradienten berechnen; nachfolgende Aufrufe lösen einen Laufzeitfehler aus.

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)
tf.Tensor([[2.]], shape=(1, 1), dtype=float32)

Trainiere ein Modell

Im folgenden Beispiel wird ein mehrschichtiges Modell erstellt, das die standardmäßigen handgeschriebenen MNIST-Ziffern klassifiziert. Es demonstriert die Optimierungs- und Layer-APIs, um trainierbare Diagramme in einer eifrigen Ausführungsumgebung zu erstellen.

# 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)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 1s 0us/step
11501568/11490434 [==============================] - 1s 0us/step
# 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)
])

Rufen Sie das Modell auch ohne Training auf und überprüfen Sie die Ausgabe in der eifrigen Ausführung:

for images,labels in dataset.take(1):
  print("Logits: ", mnist_model(images[0:1]).numpy())
Logits:  [[-0.01776254  0.03069138 -0.01169398  0.04716408  0.00278952 -0.0160556
   0.00397673 -0.02701374  0.01099346 -0.01109808]]

Während keras Modelle eine eingebaute Trainingsschleife (mit dem haben fit - Methode), manchmal braucht man mehr Anpassung. Hier ist ein Beispiel für eine Trainingsschleife, die mit Eager implementiert wurde:

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

loss_history = []
def train_step(images, labels):
  with tf.GradientTape() as tape:
    logits = mnist_model(images, training=True)

    # Add asserts to check the shape of the output.
    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(epochs):
  for epoch in range(epochs):
    for (batch, (images, labels)) in enumerate(dataset):
      train_step(images, labels)
    print ('Epoch {} finished'.format(epoch))
train(epochs = 3)
Epoch 0 finished
Epoch 1 finished
Epoch 2 finished
import matplotlib.pyplot as plt

plt.plot(loss_history)
plt.xlabel('Batch #')
plt.ylabel('Loss [entropy]')
Text(0, 0.5, 'Loss [entropy]')

png

Variablen und Optimierer

tf.Variable Objekte speichern wandelbar tf.Tensor -ähnlichen Werte während des Trainings zugegriffen einfacher automatische Unterscheidung zu machen.

Die Sammlungen von Variablen können in Schichten oder Modellen gekapselt werden, zusammen mit Methoden, die mit ihnen arbeiten. Siehe Benutzerdefinierte Keras Schichten und Modelle für weitere Einzelheiten. Der Hauptunterschied zwischen den Schichten und Modellen ist , dass Modelle hinzuzufügen Methoden wie Model.fit , Model.evaluate und Model.save .

Zum Beispiel kann das obige automatische Unterscheidungsbeispiel umgeschrieben werden:

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
# A toy dataset of points around 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

# The loss function to be optimized
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])

Nächste:

  1. Erstellen Sie das Modell.
  2. Die Ableitungen einer Verlustfunktion in Bezug auf Modellparameter.
  3. Eine Strategie zum Aktualisieren der Variablen basierend auf den Derivaten.
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)))
Initial loss: 67.455
Loss at step 000: 64.856
Loss at step 020: 29.757
Loss at step 040: 13.947
Loss at step 060: 6.824
Loss at step 080: 3.613
Loss at step 100: 2.165
Loss at step 120: 1.512
Loss at step 140: 1.218
Loss at step 160: 1.085
Loss at step 180: 1.025
Loss at step 200: 0.998
Loss at step 220: 0.985
Loss at step 240: 0.980
Loss at step 260: 0.977
Loss at step 280: 0.976
print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
Final loss: 0.976
print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
W = 2.9953086376190186, B = 2.0608654022216797

Objektbasiertes Speichern

Ein tf.keras.Model enthält eine praktische save_weights Methode erlaubt es Ihnen , einen Kontrollpunkt zu erstellen:

model.save_weights('weights')
status = model.load_weights('weights')

Mit tf.train.Checkpoint können Sie die volle Kontrolle über diesen Prozess nehmen.

Dieser Abschnitt ist eine gekürzte Version der Anleitung zur Ausbildung Checkpoints .

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)
'./ckpt/-1'
x.assign(11.)  # Change the variable after saving.

# Restore values from the checkpoint
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))

print(x)  # => 2.0
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=2.0>

Zu Speichern und Laden von Modellen, tf.train.Checkpoint speichert den internen Zustand der Objekte, ohne versteckte Variablen zu erfordern. Um den Status eines Datensatzes model , ein optimizer und einem globalen Schritt, geben sie nicht an einem tf.train.Checkpoint :

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))
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f0d4c6a38d0>

Objektorientierte Metriken

tf.keras.metrics werden als Objekte gespeichert. Aktualisieren einer Metrik durch die neuen Daten an den aufrufbaren Geben, und Abrufen das Ergebnis der Verwendung von tf.keras.metrics.result Verfahren, zum Beispiel:

m = tf.keras.metrics.Mean("loss")
m(0)
m(5)
m.result()  # => 2.5
m([8, 9])
m.result()  # => 5.5
<tf.Tensor: shape=(), dtype=float32, numpy=5.5>

Zusammenfassungen und TensorBoard

TensorBoard ist ein Visualisierungs - Tool für das Verständnis, das Debuggen und das Modell Trainingsprozess zu optimieren. Es verwendet zusammenfassende Ereignisse, die während der Ausführung des Programms geschrieben werden.

Sie können verwenden tf.summary zum Datensatz Zusammenfassungen der Variablen in eifrig Ausführung. Zum Beispiel Zusammenfassungen von aufzeichnen loss einmal alle 100 Trainingsschritte:

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/
events.out.tfevents.1630113988.kokoro-gcp-ubuntu-prod-1335170857.27045.0.v2

Fortgeschrittene Themen zur automatischen Differenzierung

Dynamische Modelle

tf.GradientTape kann auch in dynamischen Modellen verwendet werden. Dieses Beispiel für einen Rückzieher Linie suchen Algorithmus sieht aus wie normale NumPy Code, mit Ausnahme gibt es Steigungen und differenzierbar ist trotz der komplexen Steuerungsablauf:

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

Benutzerdefinierte Farbverläufe

Benutzerdefinierte Farbverläufe sind eine einfache Möglichkeit, Farbverläufe zu überschreiben. Definieren Sie innerhalb der Vorwärtsfunktion den Gradienten in Bezug auf die Eingaben, Ausgaben oder Zwischenergebnisse. Hier ist beispielsweise eine einfache Möglichkeit, die Norm der Farbverläufe im Rückwärtsdurchlauf zu beschneiden:

@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

Benutzerdefinierte Gradienten werden häufig verwendet, um einen numerisch stabilen Gradienten für eine Folge von Operationen bereitzustellen:

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)
# The gradient computation works fine at x = 0.
grad_log1pexp(tf.constant(0.)).numpy()
0.5
# However, x = 100 fails because of numerical instability.
grad_log1pexp(tf.constant(100.)).numpy()
nan

Hier wird die log1pexp kann analytisch Funktion mit einem benutzerdefinierten Gradienten vereinfacht werden. Die Implementierung unter Wiederverwendung der Wert für tf.exp(x) , die während des Vorwärtsdurch damit es effizienter durch Eliminieren redundante Berechnungen berechnet wird:

@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()
0.5
# And the gradient computation also works at x = 100.
grad_log1pexp(tf.constant(100.)).numpy()
1.0

Leistung

Die Berechnung wird während der Eifer-Ausführung automatisch auf die GPUs ausgelagert. Wenn Sie wollen die Kontrolle über , wo eine Berechnung läuft man es in einem umschließen kann tf.device('/gpu:0') Block (oder die CPU - Äquivalent):

import time

def measure(x, steps):
  # TensorFlow initializes a GPU the first time it's used, exclude from timing.
  tf.matmul(x, x)
  start = time.time()
  for i in range(steps):
    x = tf.matmul(x, x)
  # tf.matmul can return before completing the matrix multiplication
  # (e.g., can return after enqueing the operation on a CUDA stream).
  # The x.numpy() call below will ensure that all enqueued operations
  # have completed (and will also copy the result to host memory,
  # so we're including a little more than just the matmul operation
  # time).
  _ = 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))

# Run on CPU:
with tf.device("/cpu:0"):
  print("CPU: {} secs".format(measure(tf.random.normal(shape), steps)))

# Run on GPU, if available:
if tf.config.list_physical_devices("GPU"):
  with tf.device("/gpu:0"):
    print("GPU: {} secs".format(measure(tf.random.normal(shape), steps)))
else:
  print("GPU: not found")
Time to multiply a (1000, 1000) matrix by itself 200 times:
CPU: 0.8828294277191162 secs
GPU: 0.04031491279602051 secs

Ein tf.Tensor Objekt kann auf ein anderes Gerät kopiert werden , um seine Operationen auszuführen:

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
WARNING:tensorflow:From /tmp/ipykernel_27045/406964202.py:4: _EagerTensorBase.gpu (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.identity instead.
WARNING:tensorflow:From /tmp/ipykernel_27045/406964202.py:5: _EagerTensorBase.cpu (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.identity instead.

Benchmarks

Für rechen schwere Modelle, wie ResNet50 Training auf einer GPU, ist eifrig Ausführungsleistung vergleichbar tf.function Ausführung. Bei Modellen mit weniger Rechenaufwand wird diese Lücke jedoch größer, und es gibt noch einiges zu tun, um Hot-Code-Pfade für Modelle mit vielen kleinen Operationen zu optimieren.

Mit Funktionen arbeiten

Während die eifrige Ausführung die Entwicklung und das Debugging interaktiver macht, bietet die Diagrammausführung im TensorFlow 1.x-Stil Vorteile für verteiltes Training, Leistungsoptimierungen und Produktionsbereitstellung. Um diese Lücke zu überbrücken, TensorFlow 2.0 führt function s über die tf.function API. Weitere Informationen finden Sie in der tf.function Führung.