Eine Frage haben? Verbinden Sie sich mit der Community im TensorFlow Forum Visit Forum

Einführung in Graphen und tf.function

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

Überblick

Dieser Leitfaden geht unter die Oberfläche von TensorFlow und Keras, um zu demonstrieren, wie TensorFlow funktioniert. Wenn Sie stattdessen sofort mit Keras beginnen möchten, sehen Sie sich die Sammlung von Keras-Leitfäden an .

In diesem Handbuch erfahren Sie, wie Sie mit TensorFlow einfache Änderungen an Ihrem Code vornehmen können, um Diagramme zu erhalten, wie Diagramme gespeichert und dargestellt werden und wie Sie damit Ihre Modelle beschleunigen können.

Dies ist eine umfassende Übersicht, die zeigt, wie Sie mit tf.function von der eifrigen Ausführung zur Diagrammausführung wechseln können. Eine ausführlichere Spezifikation von tf.function finden Sie in der tf.function Anleitung .

Was sind Grafiken?

In den vorherigen drei Anleitungen haben Sie TensorFlow eifrig ausgeführt . Dies bedeutet, dass TensorFlow-Operationen von Python, Operation für Operation, ausgeführt werden und die Ergebnisse an Python zurückgeben.

Während Eifer Execution mehrere einzigartige Vorteile bietet, ermöglicht die Graphenausführung die Portabilität außerhalb von Python und bietet tendenziell eine bessere Leistung. Graphenausführung bedeutet, dass Tensorberechnungen als TensorFlow-Graph ausgeführt werden , manchmal auch als tf.Graph oder einfach als "Graph" bezeichnet.

Graphen sind Datenstrukturen, die einen Satz 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 TensorFlow-Diagramm aus, das ein zweischichtiges neuronales Netzwerk darstellt, wenn es in TensorBoard visualisiert wird.

Ein einfaches TensorFlow-Diagramm

Die Vorteile von Grafiken

Mit einer Grafik haben Sie eine große Flexibilität. Sie können Ihr TensorFlow-Diagramm in Umgebungen verwenden, die keinen Python-Interpreter haben, wie mobile Anwendungen, eingebettete Geräte und Back-End-Server. TensorFlow verwendet Grafiken als Format für gespeicherte Modelle, wenn es diese aus Python exportiert.

Diagramme lassen sich auch leicht optimieren, sodass der Compiler Transformationen wie folgt durchführen kann:

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

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

Kurz gesagt, Diagramme sind äußerst nützlich und lassen Ihren TensorFlow schnell laufen, parallel laufen und effizient auf mehreren Geräten laufen.

Sie möchten jedoch Ihre Machine-Learning-Modelle (oder andere Berechnungen) der Einfachheit halber immer noch in Python definieren und dann bei Bedarf automatisch Diagramme erstellen.

Nutzen von Grafiken

Sie erstellen und führen ein Diagramm in TensorFlow aus, indem Sie tf.function , entweder als direkten Aufruf oder als Dekorator. tf.function nimmt eine reguläre Funktion als Eingabe und gibt eine Function . Eine Function ist ein Python-Aufruf, der TensorFlow-Graphen aus der Python-Funktion erstellt. Sie verwenden eine Function auf die gleiche Weise wie ihr Python-Äquivalent.

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

# `a_function_that_uses_a_graph` is a TensorFlow `Function`.
a_function_that_uses_a_graph = tf.function(a_regular_function)

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

orig_value = a_regular_function(x1, y1, b1).numpy()
# Call a `Function` like a Python function.
tf_function_value = a_function_that_uses_a_graph(x1, y1, b1).numpy()
assert(orig_value == tf_function_value)

Äußerlich sieht eine Function wie eine normale Funktion aus, die Sie mit TensorFlow-Operationen schreiben. Darunter ist es jedoch ganz anders . Eine Function kapselt mehrere tf.Graph s hinter einer API . Auf diese Weise bietet Ihnen Function die Vorteile der Graphausführung , wie Geschwindigkeit und Bereitstellung.

tf.function gilt für eine Funktion und alle anderen Funktionen, die sie aufruft :

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

# Use the decorator to make `outer_function` a `Function`.
@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.Session .

Konvertieren von Python-Funktionen in Graphen

Jede Funktion, die Sie mit TensorFlow schreiben, enthält eine Mischung aus integrierten TF-Operationen und Python-Logik, wie if-then Klauseln, Schleifen, break , return , continue und mehr. Während TensorFlow-Operationen leicht von einem tf.Graph erfasst werden tf.Graph , muss Python-spezifische Logik einen zusätzlichen Schritt durchlaufen, um Teil des Graphen zu werden. tf.function verwendet eine Bibliothek namens AutoGraph ( tf.autograph ), um Python-Code in tf.autograph Code umzuwandeln.

def simple_relu(x):
  if tf.greater(x, 0):
    return x
  else:
    return 0

# `tf_simple_relu` is a TensorFlow `Function` that wraps `simple_relu`.
tf_simple_relu = tf.function(simple_relu)

print("First branch, with graph:", tf_simple_relu(tf.constant(1)).numpy())
print("Second branch, with graph:", tf_simple_relu(tf.constant(-1)).numpy())
First branch, with graph: 1
Second branch, with graph: 0

Obwohl es unwahrscheinlich ist, dass Sie Grafiken direkt anzeigen müssen, können Sie die Ausgaben überprüfen, um die genauen Ergebnisse zu überprüfen. Diese sind nicht leicht zu lesen, also müssen Sie nicht zu genau hinschauen!

# This is the graph-generating output of AutoGraph.
print(tf.autograph.to_code(simple_relu))
def tf__simple_relu(x):
    with ag__.FunctionScope('simple_relu', '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 (do_return, retval_)

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

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

        def else_body():
            nonlocal do_return, retval_
            try:
                do_return = True
                retval_ = 0
            except:
                do_return = False
                raise
        ag__.if_stmt(ag__.converted_call(ag__.ld(tf).greater, (ag__.ld(x), 0), None, fscope), if_body, else_body, get_state, set_state, ('do_return', 'retval_'), 2)
        return fscope.ret(retval_, do_return)
# This is the graph itself.
print(tf_simple_relu.get_concrete_function(tf.constant(1)).graph.as_graph_def())
node {
  name: "x"
  op: "Placeholder"
  attr {
    key: "_user_specified_name"
    value {
      s: "x"
    }
  }
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "shape"
    value {
      shape {
      }
    }
  }
}
node {
  name: "Greater/y"
  op: "Const"
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "value"
    value {
      tensor {
        dtype: DT_INT32
        tensor_shape {
        }
        int_val: 0
      }
    }
  }
}
node {
  name: "Greater"
  op: "Greater"
  input: "x"
  input: "Greater/y"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "cond"
  op: "StatelessIf"
  input: "Greater"
  input: "x"
  attr {
    key: "Tcond"
    value {
      type: DT_BOOL
    }
  }
  attr {
    key: "Tin"
    value {
      list {
        type: DT_INT32
      }
    }
  }
  attr {
    key: "Tout"
    value {
      list {
        type: DT_BOOL
        type: DT_INT32
      }
    }
  }
  attr {
    key: "_lower_using_switch_merge"
    value {
      b: true
    }
  }
  attr {
    key: "_read_only_resource_inputs"
    value {
      list {
      }
    }
  }
  attr {
    key: "else_branch"
    value {
      func {
        name: "cond_false_34"
      }
    }
  }
  attr {
    key: "output_shapes"
    value {
      list {
        shape {
        }
        shape {
        }
      }
    }
  }
  attr {
    key: "then_branch"
    value {
      func {
        name: "cond_true_33"
      }
    }
  }
}
node {
  name: "cond/Identity"
  op: "Identity"
  input: "cond"
  attr {
    key: "T"
    value {
      type: DT_BOOL
    }
  }
}
node {
  name: "cond/Identity_1"
  op: "Identity"
  input: "cond:1"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "Identity"
  op: "Identity"
  input: "cond/Identity_1"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
library {
  function {
    signature {
      name: "cond_false_34"
      input_arg {
        name: "cond_placeholder"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_BOOL
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_INT32
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const"
      }
    }
    node_def {
      name: "cond/Const_1"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_1"
      }
    }
    node_def {
      name: "cond/Const_2"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_2"
      }
    }
    node_def {
      name: "cond/Const_3"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_3"
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const_3:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity"
      }
    }
    node_def {
      name: "cond/Const_4"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_4"
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond/Const_4:output:0"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity_1"
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    attr {
      key: "_construction_context"
      value {
        s: "kEagerRuntime"
      }
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
  function {
    signature {
      name: "cond_true_33"
      input_arg {
        name: "cond_identity_1_x"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_BOOL
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_INT32
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const"
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity"
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond_identity_1_x"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity_1"
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    attr {
      key: "_construction_context"
      value {
        s: "kEagerRuntime"
      }
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
}
versions {
  producer: 716
  min_consumer: 12
}

Meistens funktioniert tf.function ohne besondere Überlegungen. Es gibt jedoch einige Vorbehalte, und die tf.function-Anleitung kann hier helfen, ebenso wie die vollständige AutoGraph-Referenz

Polymorphismus: eine Function , viele Graphen

Ein tf.Graph ist auf einen bestimmten tf.Graph spezialisiert (zB Tensoren mit einem bestimmtendtype oder Objekte mit der gleichen id() ).

Jedes Mal, wenn Sie eine Function mit neuen dtypes und Shapes in ihren Argumenten tf.Graph , erstellt Function eine neue tf.Graph für die neuen Argumente. Die dtypes und Shapes der Eingaben eines tf.Graph werden als Eingabesignatur oder einfach als Signatur bezeichnet .

Die Function speichert die dieser Signatur entsprechende tf.Graph in einer ConcreteFunction . Eine ConcreteFunction ist ein Wrapper um einen tf.Graph .

@tf.function
def my_relu(x):
  return tf.maximum(0., x)

# `my_relu` creates new graphs as it observes more signatures.
print(my_relu(tf.constant(5.5)))
print(my_relu([1, -1]))
print(my_relu(tf.constant([3., -3.])))
tf.Tensor(5.5, shape=(), dtype=float32)
tf.Tensor([1. 0.], shape=(2,), dtype=float32)
tf.Tensor([3. 0.], shape=(2,), dtype=float32)

Wenn die Function bereits mit dieser Signatur aufgerufen wurde, erstellt Function kein neues tf.Graph .

# These two calls do *not* create new graphs.
print(my_relu(tf.constant(-2.5))) # Signature matches `tf.constant(5.5)`.
print(my_relu(tf.constant([-1., 1.]))) # Signature matches `tf.constant([3., -3.])`.
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor([0. 1.], shape=(2,), dtype=float32)

Da sie von mehreren Graphen unterstützt wird, ist eine Function polymorph . tf.Graph kann es mehr Eingabetypen unterstützen, als ein einzelner tf.Graph könnte, sowie jeden tf.Graph für eine bessere Leistung optimieren.

# There are three `ConcreteFunction`s (one for each graph) in `my_relu`.
# The `ConcreteFunction` also knows the return type and shape!
print(my_relu.pretty_printed_concrete_signatures())
my_relu(x)
  Args:
    x: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()

my_relu(x=[1, -1])
  Returns:
    float32 Tensor, shape=(2,)

my_relu(x)
  Args:
    x: float32 Tensor, shape=(2,)
  Returns:
    float32 Tensor, shape=(2,)

Verwenden der tf.function

Bisher haben Sie gelernt, wie Sie eine Python-Funktion in einen Graphen umwandeln, indem tf.function einfach tf.function als Dekorator oder Wrapper verwenden. In der Praxis kann es jedoch schwierig sein, die tf.function korrekt zum tf.function zu bringen! In den folgenden Abschnitten erfahren Sie, wie Sie Ihren Code mit tf.function wie erwartet tf.function .

Diagrammausführung vs. eifrige Ausführung

Der Code in einer Function kann sowohl eifrig als auch als Graph ausgeführt werden. Standardmäßig führt Function seinen Code als Graph aus:

@tf.function
def get_MSE(y_true, y_pred):
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)
y_true = tf.random.uniform([5], maxval=10, dtype=tf.int32)
y_pred = tf.random.uniform([5], maxval=10, dtype=tf.int32)
print(y_true)
print(y_pred)
tf.Tensor([1 1 0 0 5], shape=(5,), dtype=int32)
tf.Tensor([9 1 4 9 0], shape=(5,), dtype=int32)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=37>

Um zu überprüfen, ob der Graph Ihrer Function die gleiche Berechnung wie die entsprechende Python-Funktion durchführt, können Sie ihn mit tf.config.run_functions_eagerly(True) eifrig ausführen lassen. Dies ist ein Schalter, der die Fähigkeit von Function zum Erstellen und Ausführen von Diagrammen deaktiviert , anstatt den Code normal auszuführen.

tf.config.run_functions_eagerly(True)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=37>
# Don't forget to set it back when you are done.
tf.config.run_functions_eagerly(False)

Allerdings kann sich Function unter graphischer und eifriger Ausführung unterschiedlich verhalten. Die Python- print ist ein Beispiel dafür, wie sich diese beiden Modi unterscheiden. Sehen wir uns an, was passiert, wenn Sie eine print Anweisung in Ihre Funktion einfügen und diese wiederholt aufrufen.

@tf.function
def get_MSE(y_true, y_pred):
  print("Calculating MSE!")
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)

Beobachten Sie, was gedruckt wird:

error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!

Ist die Ausgabe überraschend? get_MSE gedruckt nur einmal , obwohl es dreimal genannt wurde.

Zur Erklärung wird die print Anweisung ausgeführt, wenn Function den Originalcode ausführt, um den Graphen in einem als "Tracing" bekannten Prozess zu erstellen. Beim Tracing werden die TensorFlow-Vorgänge in einem Diagramm erfasst, und der print wird nicht im Diagramm erfasst. Dieser Graph wird dann für alle drei Aufrufe ausgeführt, ohne dass der Python-Code jemals erneut ausgeführt wird .

Deaktivieren Sie als Plausibilitätsprüfung die Diagrammausführung zum Vergleichen:

# Now, globally set everything to run eagerly to force eager execution.
tf.config.run_functions_eagerly(True)
# Observe what is printed below.
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!
Calculating MSE!
Calculating MSE!
tf.config.run_functions_eagerly(False)

print ist ein Python-Nebeneffekt , und es gibt weitere Unterschiede , die Sie beim Konvertieren einer Funktion in eine Function beachten sollten.

tf.function Best Practicesfunction

Es kann einige Zeit dauern, sich an das Verhalten von Function zu gewöhnen. Um schnell zu beginnen, sollten @tf.function mit der Dekoration von Spielzeugfunktionen mit @tf.function , um Erfahrungen mit dem @tf.function von der eifrigen zur Diagrammausführung zu sammeln.

Das Entwerfen für tf.function möglicherweise die bestetf.function um tf.function TensorFlow-Programme zu schreiben. Hier sind einige Tipps:

Die Beschleunigung sehen

tf.function verbessert normalerweise die Leistung Ihres Codes, aber die Geschwindigkeit hängt von der Art der Berechnung ab, die Sie ausführen. Kleine Berechnungen können durch den Aufwand des Aufrufens eines Graphen dominiert werden. Sie können den Leistungsunterschied wie folgt messen:

x = tf.random.uniform(shape=[10, 10], minval=-1, maxval=2, dtype=tf.dtypes.int32)

def power(x, y):
  result = tf.eye(10, dtype=tf.dtypes.int32)
  for _ in range(y):
    result = tf.matmul(x, result)
  return result
print("Eager execution:", timeit.timeit(lambda: power(x, 100), number=1000))
Eager execution: 1.8956256799999665
power_as_graph = tf.function(power)
print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000))
Graph execution: 0.5566363650000312

tf.function wird häufig verwendet, um Trainingsschleifen zu beschleunigen. tf.function einer Trainingsschleife von Grund auf mit Keras.

Leistung und Kompromisse

Diagramme können Ihren Code beschleunigen, aber der Prozess ihrer Erstellung ist mit einigem Aufwand verbunden. Bei einigen Funktionen dauert die Erstellung des Graphen länger als die Ausführung des Graphen. Diese Investition amortisiert sich normalerweise schnell durch die Leistungssteigerung nachfolgender Ausführungen, aber es ist wichtig zu wissen, dass die ersten Schritte eines großen Modelltrainings aufgrund der Verfolgung langsamer sein können.

Egal wie groß Ihr Modell ist, Sie möchten häufiges Nachzeichnen vermeiden. Im tf.function erläutert, wie Sie Eingabespezifikationen festlegen und Tensorargumente verwenden , um ein Zurückverfolgen zu vermeiden. Wenn Sie feststellen, dass die Leistung ungewöhnlich schlecht ist, sollten Sie überprüfen, ob Sie versehentlich eine Rückverfolgung durchführen.

Wann ist ein Function Tracing?

Um herauszufinden, wann Ihre Function eine Ablaufverfolgung durchführt, fügen Sie ihrem Code eine print Anweisung hinzu. Als Faustregel gilt, dass Function die print Anweisung jedes Mal ausführt, wenn sie verfolgt wird.

@tf.function
def a_function_with_python_side_effect(x):
  print("Tracing!") # An eager-only side effect.
  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)))
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(11, shape=(), dtype=int32)
# 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)
Tracing!
tf.Tensor(11, shape=(), dtype=int32)

Neue Python-Argumente lösen immer die Erstellung eines neuen Graphen aus, daher die zusätzliche Ablaufverfolgung.

Nächste Schritte

Weitere tf.function zu tf.function auf der API-Referenzseite und im Leitfaden Bessere Leistung mit tf.function .