web-dev-qa-db-fra.com

keras tensorboard: train de parcelles et scalaires de validation dans une même figure

Donc, je me sers de tensorboard dans keras. Dans tensorflow, on pourrait utiliser deux summarywriters différents pour les scalaires de train et de validation afin que tensorboard puisse les tracer de la même manière. Quelque chose comme la figure dans

TensorBoard - Entraînement au tracé et pertes de validation sur le même graphique?

Y a-t-il un moyen de faire cela à keras?

Merci.

32
Fangzhou Zhai

Pour gérer les journaux de validation avec un graveur distinct, vous pouvez écrire un rappel personnalisé qui enveloppe les méthodes TensorBoard d'origine.

import os
import tensorflow as tf
from keras.callbacks import TensorBoard

class TrainValTensorBoard(TensorBoard):
    def __init__(self, log_dir='./logs', **kwargs):
        # Make the original `TensorBoard` log to a subdirectory 'training'
        training_log_dir = os.path.join(log_dir, 'training')
        super(TrainValTensorBoard, self).__init__(training_log_dir, **kwargs)

        # Log the validation metrics to a separate subdirectory
        self.val_log_dir = os.path.join(log_dir, 'validation')

    def set_model(self, model):
        # Setup writer for validation metrics
        self.val_writer = tf.summary.FileWriter(self.val_log_dir)
        super(TrainValTensorBoard, self).set_model(model)

    def on_Epoch_end(self, Epoch, logs=None):
        # Pop the validation logs and handle them separately with
        # `self.val_writer`. Also rename the keys so that they can
        # be plotted on the same figure with the training metrics
        logs = logs or {}
        val_logs = {k.replace('val_', ''): v for k, v in logs.items() if k.startswith('val_')}
        for name, value in val_logs.items():
            summary = tf.Summary()
            summary_value = summary.value.add()
            summary_value.simple_value = value.item()
            summary_value.tag = name
            self.val_writer.add_summary(summary, Epoch)
        self.val_writer.flush()

        # Pass the remaining logs to `TensorBoard.on_Epoch_end`
        logs = {k: v for k, v in logs.items() if not k.startswith('val_')}
        super(TrainValTensorBoard, self).on_Epoch_end(Epoch, logs)

    def on_train_end(self, logs=None):
        super(TrainValTensorBoard, self).on_train_end(logs)
        self.val_writer.close()
  • Dans __init__, deux sous-répertoires sont configurés pour les journaux de formation et de validation
  • Dans set_model, un écrivain self.val_writer est créé pour les journaux de validation
  • Dans on_Epoch_end, les journaux de validation sont séparés des journaux d’entraînement et écrits dans un fichier avec self.val_writer

En utilisant le jeu de données MNIST comme exemple:

from keras.models import Sequential
from keras.layers import Dense
from keras.datasets import mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

model = Sequential()
model.add(Dense(64, activation='relu', input_shape=(784,)))
model.add(Dense(10, activation='softmax'))
model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit(x_train, y_train, epochs=10,
          validation_data=(x_test, y_test),
          callbacks=[TrainValTensorBoard(write_graph=False)])

Vous pouvez ensuite visualiser les deux courbes sur une même figure dans TensorBoard.

Screenshot


EDIT: J'ai légèrement modifié la classe afin qu'elle puisse être utilisée avec une exécution rapide.

Le plus gros changement est que j'utilise tf.keras dans le code suivant. Il semble que le callback TensorBoard dans Keras autonome ne supporte pas encore le mode passionné.

import os
import tensorflow as tf
from tensorflow.keras.callbacks import TensorBoard
from tensorflow.python.eager import context

class TrainValTensorBoard(TensorBoard):
    def __init__(self, log_dir='./logs', **kwargs):
        self.val_log_dir = os.path.join(log_dir, 'validation')
        training_log_dir = os.path.join(log_dir, 'training')
        super(TrainValTensorBoard, self).__init__(training_log_dir, **kwargs)

    def set_model(self, model):
        if context.executing_eagerly():
            self.val_writer = tf.contrib.summary.create_file_writer(self.val_log_dir)
        else:
            self.val_writer = tf.summary.FileWriter(self.val_log_dir)
        super(TrainValTensorBoard, self).set_model(model)

    def _write_custom_summaries(self, step, logs=None):
        logs = logs or {}
        val_logs = {k.replace('val_', ''): v for k, v in logs.items() if 'val_' in k}
        if context.executing_eagerly():
            with self.val_writer.as_default(), tf.contrib.summary.always_record_summaries():
                for name, value in val_logs.items():
                    tf.contrib.summary.scalar(name, value.item(), step=step)
        else:
            for name, value in val_logs.items():
                summary = tf.Summary()
                summary_value = summary.value.add()
                summary_value.simple_value = value.item()
                summary_value.tag = name
                self.val_writer.add_summary(summary, step)
        self.val_writer.flush()

        logs = {k: v for k, v in logs.items() if not 'val_' in k}
        super(TrainValTensorBoard, self)._write_custom_summaries(step, logs)

    def on_train_end(self, logs=None):
        super(TrainValTensorBoard, self).on_train_end(logs)
        self.val_writer.close()

L'idée est la même -

  • Vérifiez le code source de TensorBoard callback
  • Voir ce qu'il fait pour mettre en place l'écrivain
  • Faites la même chose dans ce rappel personnalisé

Encore une fois, vous pouvez utiliser les données MNIST pour le tester,

from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.train import AdamOptimizer

tf.enable_eager_execution()

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
y_train = y_train.astype(int)
y_test = y_test.astype(int)

model = Sequential()
model.add(Dense(64, activation='relu', input_shape=(784,)))
model.add(Dense(10, activation='softmax'))
model.compile(loss='sparse_categorical_crossentropy', optimizer=AdamOptimizer(), metrics=['accuracy'])

model.fit(x_train, y_train, epochs=10,
          validation_data=(x_test, y_test),
          callbacks=[TrainValTensorBoard(write_graph=False)])
64
Yu-Yang

Si vous utilisez TensorFlow 2.0, vous l'obtenez maintenant par défaut à l'aide du rappel Keras TensorBoard. (Lorsque vous utilisez TensorFlow avec Keras, assurez-vous que vous utilisez tensorflow.keras.)

Voir ce tutoriel:

https://www.tensorflow.org/tensorboard/r2/scalars_and_keras

6
Mani Varadarajan