web-dev-qa-db-fra.com

TensorBoard - Tracer les pertes de formation et de validation sur le même graphique?

Existe-t-il un moyen de tracer à la fois les pertes d'entraînement et les pertes de validation sur le graphique même?

Il est facile d'avoir deux résumés scalaires distincts pour chacun d'eux individuellement, mais cela les place sur des graphiques distincts. Si les deux sont affichés dans le même graphique, il est beaucoup plus facile de voir l'écart entre eux et s'ils ont commencé à diverger en raison d'un sur-ajustement.

Existe-t-il un moyen intégré de le faire? Sinon, une solution de contournement? Merci beaucoup!

32
golmschenk

La solution de contournement que j'ai faite consiste à utiliser deux SummaryWriter avec des répertoires de journaux différents pour l'ensemble de formation et l'ensemble de validation croisée respectivement. Et vous verrez quelque chose comme ça:

enter image description here

23
Lifu Huang

Plutôt que d'afficher les deux lignes séparément, vous pouvez plutôt tracer la différence entre les pertes de validation et d'entraînement comme son propre résumé scalaire pour suivre la divergence.

Cela ne donne pas autant d'informations sur un seul tracé (par rapport à l'ajout de deux résumés), mais cela permet de comparer plusieurs exécutions (et de ne pas ajouter plusieurs résumés par exécution).

9
golmschenk

Par souci d'exhaustivité, depuis tensorboard 1.5.0, cela est désormais possible.

Vous pouvez utiliser le plugin scalaires personnalisé. Pour cela, vous devez d'abord faire la configuration de la disposition du tensorboard et l'écrire dans le fichier d'événements. De l'exemple de tensorboard:

import tensorflow as tf
from tensorboard import summary
from tensorboard.plugins.custom_scalar import layout_pb2

# The layout has to be specified and written only once, not at every step

layout_summary = summary.custom_scalar_pb(layout_pb2.Layout(
  category=[
    layout_pb2.Category(
      title='losses',
      chart=[
          layout_pb2.Chart(
              title='losses',
              multiline=layout_pb2.MultilineChartContent(
                tag=[r'loss.*'],
              )),
          layout_pb2.Chart(
              title='baz',
              margin=layout_pb2.MarginChartContent(
                series=[
                  layout_pb2.MarginChartContent.Series(
                    value='loss/baz/scalar_summary',
                    lower='baz_lower/baz/scalar_summary',
                    upper='baz_upper/baz/scalar_summary'),
                ],
              )), 
      ]),
    layout_pb2.Category(
      title='trig functions',
      chart=[
          layout_pb2.Chart(
              title='wave trig functions',
              multiline=layout_pb2.MultilineChartContent(
                tag=[r'trigFunctions/cosine', r'trigFunctions/sine'],
              )),
          # The range of tangent is different. Let's give it its own chart.
          layout_pb2.Chart(
              title='tan',
              multiline=layout_pb2.MultilineChartContent(
                tag=[r'trigFunctions/tangent'],
              )),
      ],
      # This category we care less about. Let's make it initially closed.
      closed=True),
  ]))

writer = tf.summary.FileWriter(".")
writer.add_summary(layout_summary)
# ...
# Add any summary data you want to the file
# ...
writer.close()

Un Category est un groupe de Charts. Chaque Chart correspond à un seul tracé qui affiche plusieurs scalaires ensemble. Chart peut tracer des scalaires simples (MultilineChartContent) ou des zones remplies (MarginChartContent, par exemple lorsque vous voulez tracer la déviation d'une certaine valeur). Le membre tag de MultilineChartContent doit être une liste d'expressions régulières qui correspondent aux tag des scalaires que vous souhaitez regrouper dans le graphique. Pour plus de détails, vérifiez les définitions de proto des objets dans https://github.com/tensorflow/tensorboard/blob/master/tensorboard/plugins/custom_scalar/layout.proto . Notez que si plusieurs FileWriter écrivent dans le même répertoire, vous devez écrire la disposition dans un seul des fichiers. L'écrire dans un fichier séparé fonctionne également.

Pour afficher les données dans TensorBoard, vous devez ouvrir l'onglet Scalars personnalisés. Voici un exemple d'image de ce qui vous attend https://user-images.githubusercontent.com/4221553/32865784-840edf52-ca19-11e7-88bc-1806b1243e0d.png

4
niko

Un grand merci à niko pour l'astuce sur les scalaires personnalisés.

J'ai été confus par le fonctionnaire custom_scalar_demo.py parce qu'il se passe tellement de choses, et j'ai dû l'étudier pendant un bon moment avant de comprendre comment cela fonctionnait.

Pour montrer exactement ce qui doit être fait pour créer un graphique scalaire personnalisé pour un modèle existant, j'ai rassemblé l'exemple complet suivant:

# + <
# We need these to make a custom protocol buffer to display custom scalars.
# See https://developers.google.com/protocol-buffers/
from tensorboard.plugins.custom_scalar import layout_pb2
from tensorboard.summary.v1 import custom_scalar_pb
#   > 
import tensorflow as tf
from time import time
import re

# Initial values
(x0, y0) = (-1, 1)

# This is useful only when re-running code (e.g. Jupyter).
tf.reset_default_graph()  

# Set up variables.
x = tf.Variable(x0, name="X", dtype=tf.float64)
y = tf.Variable(y0, name="Y", dtype=tf.float64)

# Define loss function and give it a name.
loss = tf.square(x - 3*y) + tf.square(x+y)
loss = tf.identity(loss, name='my_loss')

# Define the op for performing gradient descent.
minimize_step_op = tf.train.GradientDescentOptimizer(0.092).minimize(loss)

# List quantities to summarize in a dictionary 
# with (key, value) = (name, Tensor).
to_summarize = dict(
    X = x,
    Y_plus_2 = y + 2,
)

# Build scalar summaries corresponding to to_summarize.
# This should be done in a separate name scope to avoid name collisions
# between summaries and their respective tensors. The name scope also
# gives a title to a group of scalars in TensorBoard.
with tf.name_scope('scalar_summaries'):
    my_var_summary_op = tf.summary.merge(
        [tf.summary.scalar(name, var) 
            for name, var in to_summarize.items()
        ]
    )

# + <
# This constructs the layout for the custom scalar, and specifies
# which scalars to plot.
layout_summary = custom_scalar_pb(
    layout_pb2.Layout(category=[
        layout_pb2.Category(
            title='Custom scalar summary group',
            chart=[
                layout_pb2.Chart(
                    title='Custom scalar summary chart',
                    multiline=layout_pb2.MultilineChartContent(
                        # regex to select only summaries which 
                        # are in "scalar_summaries" name scope:
                        tag=[r'^scalar_summaries\/']
                    )
                )
            ])
    ])
)
#   >

# Create session.
with tf.Session() as sess:

    # Initialize session.
    sess.run(tf.global_variables_initializer())

    # Create writer.
    with tf.summary.FileWriter(f'./logs/session_{int(time())}') as writer:

        # Write the session graph.
        writer.add_graph(sess.graph) # (not necessary for scalars)

# + <
        # Define the layout for creating custom scalars in terms
        # of the scalars.
        writer.add_summary(layout_summary)
#   >

        # Main iteration loop.
        for i in range(50):
            current_summary = sess.run(my_var_summary_op)
            writer.add_summary(current_summary, global_step=i)
            writer.flush()
            sess.run(minimize_step_op)   

Ce qui précède consiste en un "modèle original" augmenté de trois blocs de code indiqués par

# + <
        [code to add custom scalars goes here]
#   >

Mon "modèle original" a ces scalaires:

enter image description here

et ce graphique:

enter image description here

Mon modèle modifié a les mêmes scalaires et graphique, ainsi que le scalaire personnalisé suivant:

enter image description here

Ce graphique scalaire personnalisé est simplement une mise en page qui combine les deux graphiques scalaires d'origine.

Malheureusement, le graphique résultant est difficile à lire car les deux valeurs ont la même couleur. (Ils ne se distinguent que par un marqueur.) Ceci est cependant conforme à la convention de TensorBoard d'avoir une couleur par log.

Explication

L'idée est la suivante. Vous avez un groupe de variables que vous souhaitez tracer dans un seul graphique. Comme condition préalable, TensorBoard doit tracer chaque variable individuellement sous le titre "SCALARS". (Ceci est accompli en créant un résumé scalaire pour chaque variable, puis en écrivant ces résumés dans le journal. Rien de nouveau ici.)

Pour tracer plusieurs variables dans le même graphique, nous indiquons à TensorBoard lequel de ces résumés regrouper. Les résumés spécifiés sont ensuite combinés en un seul graphique sous la rubrique "SCALAIRES PERSONNALISÉS". Nous accomplissons cela en écrivant une "mise en page" une fois au début du journal. Une fois que TensorBoard reçoit la mise en page, il produit automatiquement un graphique combiné sous "SCALARS PERSONNALISÉS" lorsque les "SCALARS" ordinaires sont mis à jour.

En supposant que votre "modèle d'origine" envoie déjà vos variables (sous forme de résumés scalaires) à TensorBoard, la seule modification nécessaire est d'injecter la disposition avant le début de votre boucle d'itération principale. Chaque graphique scalaire personnalisé sélectionne les résumés à tracer au moyen d'une expression régulière. Ainsi, pour chaque groupe de variables à tracer ensemble, il peut être utile de placer les résumés respectifs des variables dans une portée de nom distincte. (De cette façon, votre expression régulière peut simplement sélectionner tous les résumés sous cette portée de nom.)

Remarque importante: L'opération qui génère le résumé d'une variable est distincte de la variable elle-même. Par exemple, si j'ai une variable ns1/my_var, Je peux créer un résumé ns2/summary_op_for_myvar. La disposition du graphique scalaire personnalisé ne se soucie que de l'opération de résumé, pas le nom ou la portée de la variable d'origine.

4
Ben Mares

Voici un exemple, en créant deux tf.summary.FileWriters qui partagent le même répertoire racine. Créer un tf.summary.scalar partagé par les deux tf.summary.FileWriters. À chaque pas de temps, obtenez le summary et mettez à jour chaque tf.summary.FileWriter.

import os

import tqdm
import tensorflow as tf


def tb_test():
    sess = tf.Session()

    x = tf.placeholder(dtype=tf.float32)
    summary = tf.summary.scalar('Values', x)
    merged = tf.summary.merge_all()

    sess.run(tf.global_variables_initializer())

    writer_1 = tf.summary.FileWriter(os.path.join('tb_summary', 'train'))
    writer_2 = tf.summary.FileWriter(os.path.join('tb_summary', 'eval'))

    for i in tqdm.tqdm(range(200)):
        # train
        summary_1 = sess.run(merged, feed_dict={x: i-10})
        writer_1.add_summary(summary_1, i)
        # eval
        summary_2 = sess.run(merged, feed_dict={x: i+10})            
        writer_2.add_summary(summary_2, i)

    writer_1.close()
    writer_2.close()


if __name__ == '__main__':
    tb_test()

Voici le résultat:

enter image description here

La ligne orange montre le résultat de l'étape d'évaluation et, en conséquence, la ligne bleue illustre les données de l'étape de formation.

En outre, il y a un très utile post par l'équipe TF auquel vous pouvez vous référer.

1
GoingMyWay

Tensorboard est vraiment un bel outil mais de par sa nature déclarative, il peut être difficile de le faire faire exactement ce que vous voulez.

Je vous recommande de vérifier Losswise ( https://losswise.com ) pour tracer et suivre les fonctions de perte comme alternative à Tensorboard. Avec Losswise, vous spécifiez exactement ce qui doit être représenté graphiquement ensemble:

import losswise

losswise.set_api_key("project api key")
session = losswise.Session(tag='my_special_lstm', max_iter=10)
loss_graph = session.graph('loss', kind='min')

# train an iteration of your model...
loss_graph.append(x, {'train_loss': train_loss, 'validation_loss': validation_loss})
# keep training model...

session.done()

Et puis vous obtenez quelque chose qui ressemble à:

Training and test loss on the same graph

Remarquez comment les données sont introduites explicitement dans un graphique particulier via l'appel loss_graph.append, Dont les données apparaissent ensuite dans le tableau de bord de votre projet.

De plus, pour l'exemple ci-dessus, Losswise générerait automatiquement un tableau avec des colonnes pour min(training_loss) et min(validation_loss) afin que vous puissiez facilement comparer les statistiques récapitulatives à travers vos expériences. Très utile pour comparer les résultats d'un grand nombre d'expériences.

1
nicodjimenez

Veuillez me laisser contribuer avec un exemple de code dans la réponse donnée par @ Lifu Huang . Téléchargez d'abord le loger.py de ici puis:

from logger import Logger
def train_model(parameters...):
    N_EPOCHS = 15
    # Set the logger
    train_logger = Logger('./summaries/train_logs')
    test_logger = Logger('./summaries/test_logs')
    for Epoch in range(N_EPOCHS):

        # Code to get train_loss and test_loss

        # ============ TensorBoard logging ============#
        # Log the scalar values
        train_info = {
            'loss': train_loss,
        }
        test_info = {
            'loss': test_loss,
        }

        for tag, value in train_info.items():
            train_logger.scalar_summary(tag, value, step=Epoch)
        for tag, value in test_info.items():
            test_logger.scalar_summary(tag, value, step=Epoch)

Enfin, vous exécutez tensorboard --logdir=summaries/ --port=6006et vous obtenez:

enter image description here

0
Darkmoor