Effektiver TensorFlow 2

Es gibt mehrere Änderungen in TensorFlow 2.0, um TensorFlow-Benutzer produktiver zu machen. TensorFlow 2.0 entfernt APIs redundante , macht APIs konsistentere ( Unified - RNNs , Unified - Optimierer ) und besser integriert mit der Python - Laufzeit mit Eager Ausführung .

Viele RFCs haben die Änderungen erläutert , die in die Herstellung TensorFlow 2.0 gegangen sind. Dieser Leitfaden präsentiert eine Vision, wie die Entwicklung in TensorFlow 2.0 aussehen sollte. Es wird davon ausgegangen, dass Sie mit TensorFlow 1.x vertraut sind.

Eine kurze Zusammenfassung der wichtigsten Änderungen

API-Bereinigung

Viele APIs sind entweder verschwunden oder bewegt in TF 2.0. Einige der wichtigsten Änderungen gehören das Entfernen tf.app , tf.flags und tf.logging zugunsten des jetzt Open-Source - ABSL-py , Projekte private Unterbringung , die in gelebt tf.contrib , und die Haupt Reinigung tf.* Namespace bewegen weniger verwendete Funktionen in Subpackages wie tf.math . Einige APIs wurden mit ihren 2,0 Äquivalente ersetzt - tf.summary , tf.keras.metrics und tf.keras.optimizers . Der einfachste Weg , um automatisch diese Umbenennungen anzuwenden ist , die verwenden v2 Upgrade - Skript .

Eifrige Ausführung

TensorFlow 1.x erfordert Benutzer manuell zusammen eine Aufnähen abstrakte Syntaxbaum (der Graph) , indem sie tf.* API - Aufrufe. Es erfordert dann Anwender manuell den abstrakten Syntaxbaum zusammenstellen, indem eine Reihe von Ausgang Tensoren und Eingang Tensoren an einen vorbei session.run Anruf. TensorFlow 2.0 wird eifrig ausgeführt (wie Python normalerweise) und in 2.0 sollten sich Diagramme und Sitzungen wie Implementierungsdetails anfühlen.

Ein bemerkenswertes Nebenprodukt der eifrigen Ausführung ist , dass tf.control_dependencies nicht mehr erforderlich ist , da alle Zeilen Code ausführen , um (innerhalb eines tf.function , Code mit Nebenwirkungen führen eine in der Reihenfolge geschrieben).

Keine Globals mehr

TensorFlow 1.x stützte sich stark auf implizit globale Namespaces. Wenn Sie angerufen tf.Variable , wäre es in den Standarddiagramm gesetzt werden, und es würde dort bleiben, auch wenn Sie den Überblick über die Python - Variable verloren auf sie zeigen. Sie könnten dann diese wiederherstellen tf.Variable , aber nur , wenn Sie den Namen wusste , dass es mit erstellt worden war. Dies war schwierig, wenn Sie die Erstellung der Variablen nicht kontrollieren konnten. Als Ergebnis stark vermehrt alle Arten von Mechanismen , um die Benutzer helfen , zu versuchen , ihre Variablen wieder zu finden, und für Gerüste vom Benutzer erstellten Variablen zu finden: Variable Bereiche, globale Sammlungen, Hilfsmethoden wie tf.get_global_step , tf.global_variables_initializer , Optimizern implizit Berechnung Gradienten über alle trainierbaren Variablen und so weiter. TensorFlow 2.0 beseitigt alle diese Mechanismen ( Variablen 2.0 RFC ) für den Standard - Mechanismus: Behalten Sie Ihre Variablen! Wenn Sie Spur eines verlieren tf.Variable , es wird Müll gesammelt.

Das Erfordernis, Variablen zu verfolgen, verursacht einige zusätzliche Arbeit für den Benutzer, aber mit Keras-Objekten (siehe unten) wird der Aufwand minimiert.

Funktionen, keine Sitzungen

Ein session.run Anruf ist fast wie ein Funktionsaufruf: Sie können die Eingaben angeben und die Funktion aufgerufen werden, und Sie eine Reihe von Ausgaben zurück. In TensorFlow 2.0 können Sie eine Python - Funktion dekorieren tf.function es für JIT - Kompilierung zu markieren , so dass TensorFlow es als einzelnes Graphen (läuft Funktionen 2.0 RFC ). Dieser Mechanismus ermöglicht es TensorFlow 2.0, alle Vorteile des Grafikmodus zu nutzen:

  • Performance: Die Funktion kann optimiert werden (Node Pruning, Kernel Fusion, etc.)
  • Portabilität: Die Funktion exportiert werden kann / reimportiert ( SavedModel 2.0 RFC ), so dass Anwender modular TensorFlow Funktionen wieder zu verwenden und zu teilen.
# TensorFlow 1.X
outputs = session.run(f(placeholder), feed_dict={placeholder: input})
# TensorFlow 2.0
outputs = f(input)

Mit der Möglichkeit, Python- und TensorFlow-Code frei einzufügen, können Benutzer die Ausdruckskraft von Python nutzen. Aber portables TensorFlow wird in Kontexten ohne Python-Interpreter ausgeführt, z. B. mobile, C++ und JavaScript. Um Benutzern ersparen , ihren Code neu zu schreiben , wenn das Hinzufügen @tf.function , Autograph wandelt eine Teilmenge von Python - Konstrukte in ihre TensorFlow Äquivalente:

  • for / while -> tf.while_loop ( break und continue unterstützt)
  • if -> tf.cond
  • for _ in dataset -> dataset.reduce

AutoGraph unterstützt beliebige Verschachtelungen des Kontrollflusses, wodurch viele komplexe ML-Programme wie Sequenzmodelle, Reinforcement Learning, benutzerdefinierte Trainingsschleifen und mehr performant und prägnant implementiert werden können.

Empfehlungen für idiomatischen TensorFlow 2.0

Refaktorieren Sie Ihren Code in kleinere Funktionen

Ein gemeinsames Nutzungsmuster in TensorFlow 1.x war die „kitchen sink“ Strategie, wo die Vereinigung aller möglichen Berechnungen präventiv angelegt wurde, und dann ausgewählt Tensoren wurde über ausgewertet session.run . In TensorFlow 2.0 sollten Benutzer ihren Code in kleinere Funktionen umgestalten, die nach Bedarf aufgerufen werden. Im Allgemeinen ist es nicht notwendig , jede dieser kleineren Funktionen mit dekorieren tf.function ; verwenden nur tf.function High-Level - Berechnungen dekorieren - zum Beispiel, einen Schritt der Ausbildung oder dem Vorwärtsdurchlauf des Modells.

Verwenden Sie Keras-Layer und -Modelle, um Variablen zu verwalten

Keras Modelle und Schichten bieten die bequemen variables und trainable_variables Eigenschaften, die rekursiv alle abhängigen Variablen sammeln. Dies macht es einfach, Variablen lokal dort zu verwalten, wo sie verwendet werden.

Kontrast:

def dense(x, W, b):
  return tf.nn.sigmoid(tf.matmul(x, W) + b)

@tf.function
def multilayer_perceptron(x, w0, b0, w1, b1, w2, b2 ...):
  x = dense(x, w0, b0)
  x = dense(x, w1, b1)
  x = dense(x, w2, b2)
  ...

# You still have to manage w_i and b_i, and their shapes are defined far away from the code.

mit der Keras-Version:

# Each layer can be called, with a signature equivalent to linear(x)
layers = [tf.keras.layers.Dense(hidden_size, activation=tf.nn.sigmoid) for _ in range(n)]
perceptron = tf.keras.Sequential(layers)

# layers[3].trainable_variables => returns [w3, b3]
# perceptron.trainable_variables => returns [w0, b0, ...]

Keras Schichten / Modelle erben von tf.train.Checkpointable und sind mit integrierten @tf.function , die es ermöglicht , direkt einen Checkpoint oder Export SavedModels von Keras Objekten. Sie müssen nicht unbedingt Keras der Verwendung Model.fit API Vorteil dieser Integrationen zu nehmen.

Hier ist ein Transfer-Learning-Beispiel, das zeigt, wie Keras es einfach macht, eine Teilmenge relevanter Variablen zu erfassen. Angenommen, Sie trainieren ein mehrköpfiges Modell mit einem gemeinsamen Stamm:

trunk = tf.keras.Sequential([...])
head1 = tf.keras.Sequential([...])
head2 = tf.keras.Sequential([...])

path1 = tf.keras.Sequential([trunk, head1])
path2 = tf.keras.Sequential([trunk, head2])

# Train on primary dataset
for x, y in main_dataset:
  with tf.GradientTape() as tape:
    # training=True is only needed if there are layers with different
    # behavior during training versus inference (e.g. Dropout).
    prediction = path1(x, training=True)
    loss = loss_fn_head1(prediction, y)
  # Simultaneously optimize trunk and head1 weights.
  gradients = tape.gradient(loss, path1.trainable_variables)
  optimizer.apply_gradients(zip(gradients, path1.trainable_variables))

# Fine-tune second head, reusing the trunk
for x, y in small_dataset:
  with tf.GradientTape() as tape:
    # training=True is only needed if there are layers with different
    # behavior during training versus inference (e.g. Dropout).
    prediction = path2(x, training=True)
    loss = loss_fn_head2(prediction, y)
  # Only optimize head2 weights, not trunk weights
  gradients = tape.gradient(loss, head2.trainable_variables)
  optimizer.apply_gradients(zip(gradients, head2.trainable_variables))

# You can publish just the trunk computation for other people to reuse.
tf.saved_model.save(trunk, output_path)

Kombinieren Sie tf.data.Datasets und @tf.function

Wenn Sie über Trainingsdaten iterieren, die in den Speicher passen, können Sie die reguläre Python-Iteration verwenden. Ansonsten tf.data.Dataset ist der beste Weg , Trainingsdaten von der Festplatte zu streamen. Datensammlungen sind Iterables (nicht Iteratoren) , und die Arbeit ebenso wie andere Python Iterables in Eager - Modus. Sie können Daten - Set voll nutzen async Prefetching / Funktionen Streaming von Ihrem Code in Einwickeln tf.function , die Python - Iteration mit den entsprechenden Graph Operationen mit Autograph ersetzt.

@tf.function
def train(model, dataset, optimizer):
  for x, y in dataset:
    with tf.GradientTape() as tape:
      # training=True is only needed if there are layers with different
      # behavior during training versus inference (e.g. Dropout).
      prediction = model(x, training=True)
      loss = loss_fn(prediction, y)
    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))

Wenn Sie die Keras verwenden Model.fit API, werden Sie nicht über Daten - Set Iteration zu kümmern.

model.compile(optimizer=optimizer, loss=loss_fn)
model.fit(dataset)

Nutzen Sie die Vorteile von AutoGraph mit Python-Kontrollfluss

Autograph bietet eine Möglichkeit , datenabhängige Steuerfluss in graphenModus Äquivalente wie zu konvertieren tf.cond und tf.while_loop .

Ein häufiger Ort, an dem datenabhängiger Kontrollfluss auftritt, sind Sequenzmodelle. tf.keras.layers.RNN wickelt eine RNN Zelle, so dass Sie entweder statisch oder dynamisch entrollen die Wiederholung. Zur Veranschaulichung können Sie die dynamische Abwicklung wie folgt neu implementieren:

class DynamicRNN(tf.keras.Model):

  def __init__(self, rnn_cell):
    super(DynamicRNN, self).__init__(self)
    self.cell = rnn_cell

  def call(self, input_data):
    # [batch, time, features] -> [time, batch, features]
    input_data = tf.transpose(input_data, [1, 0, 2])
    outputs = tf.TensorArray(tf.float32, input_data.shape[0])
    state = self.cell.zero_state(input_data.shape[1], dtype=tf.float32)
    for i in tf.range(input_data.shape[0]):
      output, state = self.cell(input_data[i], state)
      outputs = outputs.write(i, output)
    return tf.transpose(outputs.stack(), [1, 0, 2]), state

Für eine detailliertere Übersicht über Autograph-Funktionen finden Sie die Anleitung .

tf.metrics aggregiert Daten und tf.summary protokolliert sie

Um sich einzuloggen Zusammenfassungen, Verwendung tf.summary.(scalar|histogram|...) Und leiten sie an einen Schreiber einen Kontext - Manager. (Wenn Sie den Kontextmanager weglassen, passiert nichts.) Im Gegensatz zu TF 1.x werden die Zusammenfassungen direkt an den Writer ausgegeben; es gibt keinen separaten „merge“ op und keinen separater add_summary Anruf, was bedeutet , dass der step muss bei der callsite vorgesehen sein.

summary_writer = tf.summary.create_file_writer('/tmp/summaries')
with summary_writer.as_default():
  tf.summary.scalar('loss', 0.1, step=42)

Um aggregierte Daten , bevor sie als Zusammenfassungen, Verwendung Protokollierung tf.metrics . Metrics sind Stateful: Sie reichern sie Werte und geben ein kumulatives Ergebnis , wenn Sie die Aufruf result Methode (wie Mean.result ). Klare akkumulierten Werte mit Model.reset_states .

def train(model, optimizer, dataset, log_freq=10):
  avg_loss = tf.keras.metrics.Mean(name='loss', dtype=tf.float32)
  for images, labels in dataset:
    loss = train_step(model, optimizer, images, labels)
    avg_loss.update_state(loss)
    if tf.equal(optimizer.iterations % log_freq, 0):
      tf.summary.scalar('loss', avg_loss.result(), step=optimizer.iterations)
      avg_loss.reset_states()

def test(model, test_x, test_y, step_num):
  # training=False is only needed if there are layers with different
  # behavior during training versus inference (e.g. Dropout).
  loss = loss_fn(model(test_x, training=False), test_y)
  tf.summary.scalar('loss', loss, step=step_num)

train_summary_writer = tf.summary.create_file_writer('/tmp/summaries/train')
test_summary_writer = tf.summary.create_file_writer('/tmp/summaries/test')

with train_summary_writer.as_default():
  train(model, optimizer, dataset)

with test_summary_writer.as_default():
  test(model, test_x, test_y, optimizer.iterations)

Visualisieren Sie die generierten Zusammenfassungen, indem Sie TensorBoard auf das Zusammenfassungsprotokollverzeichnis zeigen:

tensorboard --logdir /tmp/summaries

Verwenden Sie tf.config.run_functions_eagerly beim Debuggen

In TensorFlow 2.0 können Sie mit der Eager-Ausführung den Code Schritt für Schritt ausführen, um Formen, Datentypen und Werte zu überprüfen. Bestimmte APIs, wie tf.function , tf.keras usw. sind so ausgelegt , Graph Ausführung zu verwenden, für die Leistung und Portabilität. Beim Debuggen, Verwendung tf.config.run_functions_eagerly(True) in diesem Code Eager Ausführung zu verwenden.

Beispielsweise:

@tf.function
def f(x):
  if x > 0:
    import pdb
    pdb.set_trace()
    x = x + 1
  return x

tf.config.run_functions_eagerly(True)
f(tf.constant(1))
f()
-> x = x + 1
(Pdb) l
  6     @tf.function
  7     def f(x):
  8       if x > 0:
  9         import pdb
 10         pdb.set_trace()
 11  ->     x = x + 1
 12       return x
 13
 14     tf.config.run_functions_eagerly(True)
 15     f(tf.constant(1))
[EOF]

Dies funktioniert auch in Keras-Modellen und anderen APIs, die die Eager-Ausführung unterstützen:

class CustomModel(tf.keras.models.Model):

  @tf.function
  def call(self, input_data):
    if tf.reduce_mean(input_data) > 0:
      return input_data
    else:
      import pdb
      pdb.set_trace()
      return input_data // 2


tf.config.run_functions_eagerly(True)
model = CustomModel()
model(tf.constant([-2, -4]))
call()
-> return input_data // 2
(Pdb) l
 10         if tf.reduce_mean(input_data) > 0:
 11           return input_data
 12         else:
 13           import pdb
 14           pdb.set_trace()
 15  ->       return input_data // 2
 16
 17
 18     tf.config.run_functions_eagerly(True)
 19     model = CustomModel()
 20     model(tf.constant([-2, -4]))