Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Einführung in Grafiken und Funktionen

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

Einführung in Graphen und tf.function

Diese Anleitung befindet sich unter der Oberfläche von TensorFlow und Keras, um zu sehen, wie TensorFlow funktioniert. Wenn Sie stattdessen sofort mit Keras beginnen möchten, lesen Sie bitte unsere Sammlung von Keras-Handbüchern .

In diesem Handbuch erfahren Sie, wie Sie mit TensorFlow einfache Änderungen an Ihrem Code vornehmen können, um Diagramme abzurufen, wie diese gespeichert und dargestellt werden und wie Sie sie zum Beschleunigen und Exportieren Ihrer Modelle verwenden können.

Dies ist eine kurze Einführung. Eine vollständige Einführung in diese Konzepte finden Sie im tf.function .

Was sind Grafiken?

In den vorherigen drei Handbüchern haben Sie TensorFlow eifrig laufen sehen. Dies bedeutet, dass TensorFlow-Operationen von Python ausgeführt werden, Operation für Operation und Ergebnisse an Python zurückgegeben werden. Eager TensorFlow nutzt GPUs und ermöglicht es Ihnen, Variablen, Tensoren und sogar Operationen auf GPUs und TPUs zu platzieren. Es ist auch leicht zu debuggen.

Für einige Benutzer benötigen oder möchten Sie Python möglicherweise nie verlassen.

Das Ausführen von TensorFlow op-by-op in Python verhindert jedoch eine Vielzahl von sonst verfügbaren Beschleunigungen. Wenn Sie Tensorberechnungen aus Python extrahieren können, können Sie sie in ein Diagramm umwandeln .

Diagramme sind Datenstrukturen, die eine Reihe von tf.Operation Objekten enthalten, die Recheneinheiten darstellen. und tf.Tensor Objekte, die die Dateneinheiten darstellen, die zwischen Operationen fließen. Sie werden in einem tf.Graph Kontext definiert. Da es sich bei diesen Diagrammen um Datenstrukturen handelt, können sie ohne den ursprünglichen Python-Code gespeichert, ausgeführt und wiederhergestellt werden.

So sieht ein einfaches zweischichtiges Diagramm aus, wenn es in TensorBoard visualisiert wird.

ein zweischichtiges Tensorflow-Diagramm

Die Vorteile von Grafiken

Mit einem Diagramm haben Sie viel Flexibilität. Sie können Ihr TensorFlow-Diagramm in Umgebungen verwenden, in denen kein Python-Interpreter vorhanden ist, z. B. in mobilen Anwendungen, eingebetteten Geräten und Back-End-Servern. TensorFlow verwendet Diagramme als Format für gespeicherte Modelle, wenn sie aus Python exportiert werden.

Diagramme können auch leicht optimiert werden, sodass der Compiler Transformationen durchführen kann wie:

  • Leiten Sie den Wert von Tensoren statisch ab, indem Sie konstante Knoten in Ihrer Berechnung falten ("konstante Faltung") .
  • Trennen Sie unabhängige Teile einer Berechnung und teilen Sie sie auf Threads oder Geräte auf.
  • Vereinfachen Sie arithmetische Operationen, indem Sie gängige Unterausdrücke eliminieren.

Es gibt ein ganzes Optimierungssystem, Grappler , um diese und andere Beschleunigungen durchzuführen.

Kurz gesagt, Diagramme sind äußerst nützlich und ermöglichen es Ihrem TensorFlow, schnell , parallel und effizient auf mehreren Geräten zu laufen.

Sie möchten jedoch weiterhin unsere Modelle für maschinelles Lernen (oder andere Berechnungen) in Python definieren und dann bei Bedarf automatisch Diagramme erstellen.

Diagramme verfolgen

Sie erstellen ein Diagramm in TensorFlow, tf.function entweder als direkten Aufruf oder als Dekorateur verwenden.

import tensorflow as tf
import timeit
from datetime import datetime
# Define a Python function
def function_to_get_faster(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# Create a `Function` object that contains a graph
a_function_that_uses_a_graph = tf.function(function_to_get_faster)

# Make some tensors
x1 = tf.constant([[1.0, 2.0]])
y1 = tf.constant([[2.0], [3.0]])
b1 = tf.constant(4.0)

# It just works!
a_function_that_uses_a_graph(x1, y1, b1).numpy()
array([[12.]], dtype=float32)

tf.function Funktionen sind Python- tf.function genauso funktionieren wie ihre Python-Äquivalente. Sie haben eine bestimmte Klasse ( python.eager.def_function.Function ), aber für Sie fungieren sie nur als nicht verfolgte Version.

tf.function rekursiv jede von ihm tf.function Python-Funktion.

def inner_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# Use the decorator
@tf.function
def outer_function(x):
  y = tf.constant([[2.0], [3.0]])
  b = tf.constant(4.0)

  return inner_function(x, y, b)

# Note that the callable will create a graph that
# includes inner_function() as well as outer_function()
outer_function(tf.constant([[1.0, 2.0]])).numpy()
array([[12.]], dtype=float32)

Wenn Sie TensorFlow 1.x verwendet haben, werden Sie feststellen, dass Sie zu keinem Zeitpunkt einen Placeholder oder eine Placeholder definieren tf.Sesssion .

Flusskontrolle und Nebenwirkungen

tf.autograph und Schleifen werden standardmäßig über tf.autograph in TensorFlow tf.autograph . Autograph verwendet eine Kombination von Methoden, einschließlich Standardisierung von Schleifenkonstrukten, Abrollen und AST- Manipulation.

def my_function(x):
  if tf.reduce_sum(x) <= 1:
    return x * x
  else:
    return x-1

a_function = tf.function(my_function)

print("First branch, with graph:", a_function(tf.constant(1.0)).numpy())
print("Second branch, with graph:", a_function(tf.constant([5.0, 5.0])).numpy())
First branch, with graph: 1.0
Second branch, with graph: [4. 4.]

Sie können die Autograph-Konvertierung direkt aufrufen, um zu sehen, wie Python in TensorFlow-Operationen konvertiert wird. Dies ist meistens nicht lesbar, aber Sie können die Transformation sehen.

# Don't read the output too carefully.
print(tf.autograph.to_code(my_function))
def tf__my_function(x):
    with ag__.FunctionScope('my_function', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()

        def get_state():
            return (retval_, do_return)

        def set_state(vars_):
            nonlocal retval_, do_return
            (retval_, do_return) = vars_

        def if_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = (ag__.ld(x) * ag__.ld(x))
            except:
                do_return = False
                raise

        def else_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = (ag__.ld(x) - 1)
            except:
                do_return = False
                raise
        ag__.if_stmt((ag__.converted_call(ag__.ld(tf).reduce_sum, (ag__.ld(x),), None, fscope) <= 1), if_body, else_body, get_state, set_state, ('retval_', 'do_return'), 2)
        return fscope.ret(retval_, do_return)


Autograph konvertiert automatisch if-then Then if-then Klauseln, Schleifen, break , return , continue und mehr.

Meistens funktioniert Autograph ohne besondere Überlegungen. Es gibt jedoch einige Einschränkungen, und die tf.Funktionsanleitung kann hier helfen, ebenso wie die vollständige Autogrammreferenz

Die Geschwindigkeit sehen

Das tf.function einer Tensor-using-Funktion in tf.function beschleunigt Ihren Code nicht automatisch. Bei kleinen Funktionen, die einige Male auf einem einzelnen Computer aufgerufen werden, kann der Aufwand für das Aufrufen eines Diagramms oder eines Diagrammfragments die Laufzeit dominieren. Wenn der größte Teil der Berechnung bereits auf einem Beschleuniger ausgeführt wurde, z. B. auf Stapeln von GPU-starken Windungen, ist die Beschleunigung des Diagramms nicht groß.

Für komplizierte Berechnungen können Diagramme eine erhebliche Beschleunigung bieten. Dies liegt daran, dass Diagramme die Kommunikation zwischen Python und Gerät verringern und einige Beschleunigungen durchführen.

Dieser Code läuft einige Male auf einigen kleinen dichten Schichten.

# Create an oveerride model to classify pictures
class SequentialModel(tf.keras.Model):
  def __init__(self, **kwargs):
    super(SequentialModel, self).__init__(**kwargs)
    self.flatten = tf.keras.layers.Flatten(input_shape=(28, 28))
    self.dense_1 = tf.keras.layers.Dense(128, activation="relu")
    self.dropout = tf.keras.layers.Dropout(0.2)
    self.dense_2 = tf.keras.layers.Dense(10)

  def call(self, x):
    x = self.flatten(x)
    x = self.dense_1(x)
    x = self.dropout(x)
    x = self.dense_2(x)
    return x

input_data = tf.random.uniform([60, 28, 28])

eager_model = SequentialModel()
graph_model = tf.function(eager_model)

print("Eager time:", timeit.timeit(lambda: eager_model(input_data), number=10000))
print("Graph time:", timeit.timeit(lambda: graph_model(input_data), number=10000))

Eager time: 5.302399797999897
Graph time: 2.3688509589999285

Polymorphe Funktionen

Wenn Sie eine Funktion verfolgen, erstellen Sie ein Function , das polymorph ist . Eine polymorphe Funktion ist eine aufrufbare Python-Funktion, die mehrere konkrete Funktionsgraphen hinter einer API kapselt.

Sie können diese Function für alle Arten von d- dtypes und Formen verwenden. Jedes Mal, wenn Sie es mit einer neuen Argumentensignatur aufrufen, wird die ursprüngliche Funktion mit den neuen Argumenten zurückverfolgt. Die Function speichert dann den diesem tf.Graph entsprechenden tf.Graph in einer concrete_function . Wenn die Funktion bereits mit dieser Art von Argument verfolgt wurde, erhalten Sie nur Ihr vorverfolgtes Diagramm.

Konzeptionell also:

  • Ein tf.Graph ist die tragbare Rohdatenstruktur, die eine Berechnung beschreibt
  • Eine Function ist ein Caching-, Tracing- und Dispatcher über ConcreteFunctions
  • Eine ConcreteFunction ist ein eifrig kompatibler Wrapper um ein Diagramm, mit dem Sie das Diagramm in Python ausführen können

Untersuchung polymorpher Funktionen

Sie können a_function überprüfen, was das Ergebnis des Aufrufs von tf.function für die Python-Funktion my_function . In diesem Beispiel führt der Aufruf von a_function mit drei Arten von Argumenten zu drei verschiedenen konkreten Funktionen.

print(a_function)

print("Calling a `Function`:")
print("Int:", a_function(tf.constant(2)))
print("Float:", a_function(tf.constant(2.0)))
print("Rank-1 tensor of floats", a_function(tf.constant([2.0, 2.0, 2.0])))
<tensorflow.python.eager.def_function.Function object at 0x7fe28ce18cf8>
Calling a `Function`:
Int: tf.Tensor(1, shape=(), dtype=int32)
Float: tf.Tensor(1.0, shape=(), dtype=float32)
Rank-1 tensor of floats tf.Tensor([1. 1. 1.], shape=(3,), dtype=float32)

# Get the concrete function that works on floats
print("Inspecting concrete functions")
print("Concrete function for float:")
print(a_function.get_concrete_function(tf.TensorSpec(shape=[], dtype=tf.float32)))
print("Concrete function for tensor of floats:")
print(a_function.get_concrete_function(tf.constant([2.0, 2.0, 2.0])))

Inspecting concrete functions
Concrete function for float:
ConcreteFunction my_function(x)
  Args:
    x: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()
Concrete function for tensor of floats:
ConcreteFunction my_function(x)
  Args:
    x: float32 Tensor, shape=(3,)
  Returns:
    float32 Tensor, shape=(3,)

# Concrete functions are callable
# Note: You won't normally do this, but instead just call the containing `Function`
cf = a_function.get_concrete_function(tf.constant(2))
print("Directly calling a concrete function:", cf(tf.constant(2)))
Directly calling a concrete function: tf.Tensor(1, shape=(), dtype=int32)

In diesem Beispiel sehen Sie ziemlich weit im Stapel. Sofern Sie die Ablaufverfolgung nicht speziell verwalten, müssen Sie normalerweise keine konkreten Funktionen direkt aufrufen, wie hier gezeigt.

Zurück zur eifrigen Ausführung

Möglicherweise sehen Sie sich lange Stapelspuren an, insbesondere solche, die sich auf tf.Graph oder with tf.Graph().as_default() beziehen with tf.Graph().as_default() . Dies bedeutet, dass Sie wahrscheinlich in einem Diagrammkontext ausgeführt werden. model.fit() in TensorFlow verwenden Diagrammkontexte wie Keras ' model.fit() .

Es ist oft viel einfacher, eifrige Ausführung zu debuggen. Stapelspuren sollten relativ kurz und leicht verständlich sein.

In Situationen, in denen das Debuggen aufgrund des Diagramms schwierig ist, können Sie zum Debuggen wieder die eifrige Ausführung verwenden.

Hier sind Möglichkeiten, wie Sie sicherstellen können, dass Sie eifrig laufen:

Verwenden von run_eagerly=True

# Define an identity layer with an eager side effect
class EagerLayer(tf.keras.layers.Layer):
  def __init__(self, **kwargs):
    super(EagerLayer, self).__init__(**kwargs)
    # Do some kind of initialization here

  def call(self, inputs):
    print("\nCurrently running eagerly", str(datetime.now()))
    return inputs
# Create an override model to classify pictures, adding the custom layer
class SequentialModel(tf.keras.Model):
  def __init__(self):
    super(SequentialModel, self).__init__()
    self.flatten = tf.keras.layers.Flatten(input_shape=(28, 28))
    self.dense_1 = tf.keras.layers.Dense(128, activation="relu")
    self.dropout = tf.keras.layers.Dropout(0.2)
    self.dense_2 = tf.keras.layers.Dense(10)
    self.eager = EagerLayer()

  def call(self, x):
    x = self.flatten(x)
    x = self.dense_1(x)
    x = self.dropout(x)
    x = self.dense_2(x)
    return self.eager(x)

# Create an instance of this model
model = SequentialModel()

# Generate some nonsense pictures and labels
input_data = tf.random.uniform([60, 28, 28])
labels = tf.random.uniform([60])

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

Kompilieren Sie zunächst das Modell ohne Eifer. Beachten Sie, dass das Modell nicht verfolgt wird. Trotz seines Namens richtet compile nur Verlustfunktionen, Optimierung und andere Trainingsparameter ein.

model.compile(run_eagerly=False, loss=loss_fn)

Rufen Sie nun fit und stellen Sie sicher, dass die Funktion (zweimal) verfolgt wird und der eifrige Effekt dann nie wieder ausgeführt wird.

model.fit(input_data, labels, epochs=3)
Epoch 1/3

Currently running eagerly 2020-09-30 01:22:01.861126

Currently running eagerly 2020-09-30 01:22:01.982367
2/2 [==============================] - 0s 2ms/step - loss: 1.5409
Epoch 2/3
2/2 [==============================] - 0s 1ms/step - loss: 0.0021
Epoch 3/3
2/2 [==============================] - 0s 1ms/step - loss: 0.0013

<tensorflow.python.keras.callbacks.History at 0x7fe20c051940>

Wenn Sie jedoch auch nur eine Epoche eifrig ausführen, können Sie die eifrige Nebenwirkung zweimal sehen.

print("Running eagerly")
# When compiling the model, set it to run eagerly
model.compile(run_eagerly=True, loss=loss_fn)

model.fit(input_data, labels, epochs=1)

Running eagerly

Currently running eagerly 2020-09-30 01:22:02.196963
1/2 [==============>...............] - ETA: 0s - loss: 9.7674e-04
Currently running eagerly 2020-09-30 01:22:02.220018
2/2 [==============================] - 0s 6ms/step - loss: 5.2117e-04

<tensorflow.python.keras.callbacks.History at 0x7fe200201278>

run_functions_eagerly

Sie können auch global festlegen, dass alles eifrig ausgeführt wird. Dies ist ein Schalter, der die verfolgten Funktionen der polymorphen Funktion umgeht und die ursprüngliche Funktion direkt aufruft. Sie können dies zum Debuggen verwenden.

# Now, globally set everything to run eagerly
tf.config.run_functions_eagerly(True)
print("Run all functions eagerly.")

# Create a polymorphic function
polymorphic_function = tf.function(model)

print("Tracing")
# This does, in fact, trace the function
print(polymorphic_function.get_concrete_function(input_data))

print("\nCalling twice eagerly")
# When you run the function again, you will see the side effect
# twice, as the function is running eagerly.
result = polymorphic_function(input_data)
result = polymorphic_function(input_data)
Run all functions eagerly.
Tracing

Currently running eagerly 2020-09-30 01:22:02.249703
ConcreteFunction function(self)
  Args:
    self: float32 Tensor, shape=(60, 28, 28)
  Returns:
    float32 Tensor, shape=(60, 10)

Calling twice eagerly

Currently running eagerly 2020-09-30 01:22:02.254759

Currently running eagerly 2020-09-30 01:22:02.256224

# Don't forget to set it back when you are done
tf.config.experimental_run_functions_eagerly(False)
WARNING:tensorflow:From <ipython-input-1-782fe9ce7b18>:2: experimental_run_functions_eagerly (from tensorflow.python.eager.def_function) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.config.run_functions_eagerly` instead of the experimental version.

Rückverfolgung und Leistung

Die Rückverfolgung kostet etwas Overhead. Obwohl das Nachverfolgen kleiner Funktionen schnell ist, kann das Nachverfolgen großer Modelle eine bemerkenswerte Wanduhrzeit in Anspruch nehmen. Diese Investition zahlt sich normalerweise schnell mit einer Leistungssteigerung aus. Es ist jedoch wichtig zu wissen, dass die ersten Epochen eines großen Modelltrainings aufgrund der Rückverfolgung langsamer sein können.

Unabhängig davon, wie groß Ihr Modell ist, möchten Sie eine häufige Nachverfolgung vermeiden. In diesem Abschnitt des tf.function-Handbuchs wird erläutert, wie Sie Eingabespezifikationen festlegen und Tensorargumente verwenden, um ein Zurückverfolgen zu vermeiden. Wenn Sie feststellen, dass Sie eine ungewöhnlich schlechte Leistung erzielen, sollten Sie überprüfen, ob Sie versehentlich zurückverfolgen.

Sie können einen eifrigen Nebeneffekt hinzufügen (z. B. das Drucken eines Python-Arguments), damit Sie sehen können, wann die Funktion verfolgt wird. Hier sehen Sie zusätzliches Retracing, da neue Python-Argumente immer das Retracing auslösen.

# Use @tf.function decorator
@tf.function
def a_function_with_python_side_effect(x):
  print("Tracing!")  # This eager
  return x * x + tf.constant(2)

# This is traced the first time
print(a_function_with_python_side_effect(tf.constant(2)))
# The second time through, you won't see the side effect
print(a_function_with_python_side_effect(tf.constant(3)))

# This retraces each time the Python argument changes,
# as a Python argument could be an epoch count or other
# hyperparameter
print(a_function_with_python_side_effect(2))
print(a_function_with_python_side_effect(3))

Tracing!
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(11, shape=(), dtype=int32)
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
Tracing!
tf.Tensor(11, shape=(), dtype=int32)

Nächste Schritte

Eine ausführlichere Diskussion finden Sie sowohl auf der Referenzseite der tf.function API als auch im Handbuch .