Migration de l'utilisation de tf.summary vers TF 2.x

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier
import tensorflow as tf

Tensorflow 2.x inclut des changements importants à la tf.summary API utilisée pour les données de synthèse d'écriture pour la visualisation en TensorBoard.

Qu'est-ce qui a changé

Il est utile de penser à la tf.summary API comme deux sous-API:

  • Un ensemble d'opérations pour des résumés individuels d' enregistrement - summary.scalar() , summary.histogram() , summary.image() , summary.audio() , et summary.text() - qui sont appelés en ligne à partir de votre code modèle.
  • Logique d'écriture qui collecte ces résumés individuels et les écrit dans un fichier journal spécialement formaté (que TensorBoard lit ensuite pour générer des visualisations).

Dans TF 1.x

Les deux moitiés devaient être câblées ensemble manuellement - en allant chercher les sorties op sommaire via Session.run() et appeler FileWriter.add_summary(output, step) . Le v1.summary.merge_all() op a fait cela plus facile en utilisant une collection de graphique pour regrouper toutes les sorties op sommaire, mais cette approche a encore mal à l' exécution avide et flux de contrôle, ce qui rend particulièrement mal adapté pour TF 2.x.

Dans TF 2.X

Les deux moitiés sont étroitement intégrées, et maintenant chaque tf.summary ops écrivent leurs données immédiatement lors de l' exécution. L'utilisation de l'API à partir de votre code de modèle devrait toujours sembler familière, mais elle est désormais conviviale pour une exécution rapide tout en restant compatible avec le mode graphique. L' intégration des deux moitiés des moyens API la summary.FileWriter fait maintenant partie du contexte d'exécution de tensorflow et s'accéder directement tf.summary ops, donc la configuration des écrivains est la partie principale qui est différent.

Exemple d'utilisation avec exécution rapide, la valeur par défaut dans TF 2.x :

writer = tf.summary.create_file_writer("/tmp/mylogs/eager")

with writer.as_default():
  for step in range(100):
    # other model code would go here
    tf.summary.scalar("my_metric", 0.5, step=step)
    writer.flush()
ls /tmp/mylogs/eager
events.out.tfevents.1633086727.kokoro-gcp-ubuntu-prod-1386032077.31590.0.v2

Exemple d'utilisation avec l'exécution du graphe tf.function :

writer = tf.summary.create_file_writer("/tmp/mylogs/tf_function")

@tf.function
def my_func(step):
  with writer.as_default():
    # other model code would go here
    tf.summary.scalar("my_metric", 0.5, step=step)

for step in tf.range(100, dtype=tf.int64):
  my_func(step)
  writer.flush()
ls /tmp/mylogs/tf_function
events.out.tfevents.1633086728.kokoro-gcp-ubuntu-prod-1386032077.31590.1.v2

Exemple d'utilisation avec l'exécution du graphique TF 1.x hérité :

g = tf.compat.v1.Graph()
with g.as_default():
  step = tf.Variable(0, dtype=tf.int64)
  step_update = step.assign_add(1)
  writer = tf.summary.create_file_writer("/tmp/mylogs/session")
  with writer.as_default():
    tf.summary.scalar("my_metric", 0.5, step=step)
  all_summary_ops = tf.compat.v1.summary.all_v2_summary_ops()
  writer_flush = writer.flush()


with tf.compat.v1.Session(graph=g) as sess:
  sess.run([writer.init(), step.initializer])

  for i in range(100):
    sess.run(all_summary_ops)
    sess.run(step_update)
    sess.run(writer_flush)
ls /tmp/mylogs/session
events.out.tfevents.1633086728.kokoro-gcp-ubuntu-prod-1386032077.31590.2.v2

Conversion de votre code

La conversion existante tf.summary utilisation de l'API TF 2.x ne peut pas être fiable automatisé, de sorte que le tf_upgrade_v2 scénario réécrit tout juste à tf.compat.v1.summary . Pour migrer vers TF 2.x, vous devrez adapter votre code comme suit :

  1. Un ensemble par défaut écrivain via .as_default() doit être présent à l' utilisation ops sommaires

    • Cela signifie exécuter des opérations avec empressement ou utiliser des opérations dans la construction de graphes
    • Sans un rédacteur par défaut, les opérations récapitulatives deviennent des opérations silencieuses
    • Écrivains défaut ne pas (encore) se propagent à travers le @tf.function limite d'exécution - ils ne sont détectés que lorsque la fonction est tracée - si la meilleure pratique est d'appeler writer.as_default() dans le corps de la fonction, et de faire en sorte que l'objet écrivain continue d'exister tant que le @tf.function est utilisé
  2. La valeur « étape » doit être passé dans chaque op via une l' step arguments

    • TensorBoard nécessite une valeur de pas pour afficher les données sous forme de série temporelle
    • Le passage explicite est nécessaire car l'étape globale de TF 1.x a été supprimée, donc chaque opération doit connaître la variable d'étape souhaitée à lire
    • Pour réduire boilerplate, un support expérimental pour l' enregistrement d' une valeur de pas par défaut est disponible sous forme tf.summary.experimental.set_step() , mais cela est une fonctionnalité provisoire qui peut être modifié sans préavis
  3. Les signatures de fonction des opérations de résumé individuelles ont changé

    • La valeur de retour est maintenant un booléen (indiquant si un résumé a été réellement écrit)
    • Le deuxième nom de paramètre ( le cas échéant) a changé de tensor de data
    • Les collections paramètre a été supprimé; les collections sont uniquement TF 1.x
    • La family paramètre a été supprimé; juste utilisation tf.name_scope()
  4. [Uniquement pour les utilisateurs en mode graphique hérité/exécution de session]

    • Tout d' abord initialiser l'écrivain avec v1.Session.run(writer.init())

    • Utilisez v1.summary.all_v2_summary_ops() pour obtenir toutes les opérations sommaires TF 2.x pour le graphique actuel, par exemple les exécuter via Session.run()

    • Purger l'écrivain avec v1.Session.run(writer.flush()) et de même pour close()

Si votre code 1.x TF a été au lieu d' utiliser tf.contrib.summary API, il est beaucoup plus semblable à l'API TF 2.x, donc tf_upgrade_v2 script automatiser la plupart des étapes de migration (et émettent des avertissements ou des erreurs pour toute utilisation qui ne peut pas être entièrement migré). Pour la plupart , il réécrit seulement les appels API à tf.compat.v2.summary ; si vous avez seulement besoin de compatibilité avec TF 2.x vous pouvez laisser tomber le compat.v2 et référence tout comme tf.summary .

Conseils supplémentaires

En plus des domaines critiques ci-dessus, certains aspects auxiliaires ont également changé :

  • L'enregistrement conditionnel (comme « journal toutes les 100 étapes ») a un nouveau look

    • Pour ops de contrôle et le code associé, les envelopper dans une régulière instruction if (qui fonctionne en mode avide et @tf.function via autographe ) ou un tf.cond
    • Pour contrôler seulement des résumés, utilisez la nouvelle tf.summary.record_if() gestionnaire de contexte, et transmettre la condition booléenne de votre choix
    • Ceux-ci remplacent le modèle TF 1.x :

      if condition:
        writer.add_summary()
      
  • Aucune écriture directe de tf.compat.v1.Graph - au lieu des fonctions de trace l' utilisation

  • Aucune mise en cache plus globale de l' écrivain par logdir avec tf.summary.FileWriterCache

    • Les utilisateurs doivent soit mettre en œuvre leur propre mise en cache / partage d'objets écrivain, ou tout simplement utiliser des écrivains indépendants (soutien TensorBoard pour ce dernier est en cours )
  • La représentation binaire du fichier d'événement a changé

    • TensorBoard 1.x prend déjà en charge le nouveau format ; cette différence n'affecte que les utilisateurs qui analysent manuellement les données récapitulatives des fichiers d'événements
    • Les données récapitulatives sont désormais stockées sous forme d'octets tenseurs ; vous pouvez utiliser tf.make_ndarray(event.summary.value[0].tensor) pour le convertir en numpy