Cette page a été traduite par l'API Cloud Translation.
Switch to English

Accès aux données TensorBoard en tant que DataFrames

Aperçu

La principale caractéristique de TensorBoard est son interface graphique interactive. Cependant, les utilisateurs souhaitent parfois lire par programme les journaux de données stockés dans TensorBoard, à des fins telles que l'exécution d'analyses post-hoc et la création de visualisations personnalisées des données de journal.

TensorBoard 2.3 prend en charge ce cas d'utilisation avec tensorboard.data.experimental.ExperimentFromDev() . Il permet un accès par programme aux journaux scalaires de TensorBoard. Cette page montre l'utilisation de base de cette nouvelle API.

Installer

Afin d'utiliser l'API programmatique, assurez-vous d'installer des pandas aux côtés de tensorboard .

Nous utiliserons matplotlib et seaborn pour les tracés personnalisés dans ce guide, mais vous pouvez choisir votre outil préféré pour analyser et visualiser les DataFrame .

pip install tensorboard pandas
pip install matplotlib seaborn
 from packaging import version

import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
from scipy import stats
import tensorboard as tb
 
 major_ver, minor_ver, _ = version.parse(tb.__version__).release
assert major_ver >= 2 and minor_ver >= 3, \
    "This notebook requires TensorBoard 2.3 or later."
print("TensorBoard version: ", tb.__version__)
 
TensorBoard version:  2.3.0a20200626

Chargement des scalaires TensorBoard en tant que pandas.DataFrame

Une fois qu'un logdir TensorBoard a été téléchargé sur TensorBoard.dev, il devient ce que nous appelons une expérience . Chaque expérience a un ID unique, qui se trouve dans l'URL TensorBoard.dev de l'expérience. Pour notre démonstration ci-dessous, nous utiliserons une expérience TensorBoard.dev à l' adresse : https://tensorboard.dev/experiment/c1KCv3X3QvGwaXfgX1c4tg

 experiment_id = "c1KCv3X3QvGwaXfgX1c4tg"
experiment = tb.data.experimental.ExperimentFromDev(experiment_id)
df = experiment.get_scalars()
df
 

df est un pandas.DataFrame qui contient tous les journaux scalaires de l'expérience.

Les colonnes du DataFrame sont:

  • run : chaque run correspond à un sous-répertoire du logdir d'origine. Dans cette expérience, chaque exécution est issue d'un entraînement complet d'un réseau neuronal convolutif (CNN) sur l'ensemble de données MNIST avec un type d'optimiseur donné (un hyperparamètre d'entraînement). Ce DataFrame contient plusieurs exécutions de ce type, qui correspondent à des exécutions d'entraînement répétées sous différents types d'optimiseur.
  • tag : cela décrit ce que signifie la value de la même ligne, c'est-à-dire quelle métrique la valeur représente dans la ligne. Dans cette expérience, nous n'avons que deux balises uniques: epoch_accuracy et epoch_loss pour les métriques de précision et de perte respectivement.
  • step : il s'agit d'un nombre qui reflète l'ordre de série de la ligne correspondante dans son exécution. Ici, l' step se réfère en fait au numéro d'époque. Si vous souhaitez obtenir les horodatages en plus des valeurs de step , vous pouvez utiliser l'argument mot-clé include_wall_time=True lors de l'appel de get_scalars() .
  • value : Il s'agit de la valeur numérique réelle d'intérêt. Comme décrit ci-dessus, chaque value de ce DataFrame particulier est une perte ou une précision, selon l' tag de la ligne.
 print(df["run"].unique())
print(df["tag"].unique())
 
['adam,run_1/train' 'adam,run_1/validation' 'adam,run_2/train'
 'adam,run_2/validation' 'adam,run_3/train' 'adam,run_3/validation'
 'adam,run_4/train' 'adam,run_4/validation' 'adam,run_5/train'
 'adam,run_5/validation' 'rmsprop,run_1/train' 'rmsprop,run_1/validation'
 'rmsprop,run_2/train' 'rmsprop,run_2/validation' 'rmsprop,run_3/train'
 'rmsprop,run_3/validation' 'rmsprop,run_4/train'
 'rmsprop,run_4/validation' 'rmsprop,run_5/train'
 'rmsprop,run_5/validation' 'sgd,run_1/train' 'sgd,run_1/validation'
 'sgd,run_2/train' 'sgd,run_2/validation' 'sgd,run_3/train'
 'sgd,run_3/validation' 'sgd,run_4/train' 'sgd,run_4/validation'
 'sgd,run_5/train' 'sgd,run_5/validation']
['epoch_accuracy' 'epoch_loss']

Obtenir un DataFrame pivoté (format large)

Dans notre expérience, les deux balises ( epoch_loss et epoch_accuracy ) sont présentes au même ensemble d'étapes à chaque exécution. Cela permet d'obtenir un DataFrame "large" directement à partir de get_scalars() en utilisant l'argument de mot clé pivot=True . Le DataFrame large a toutes ses balises incluses sous forme de colonnes du DataFrame, ce qui est plus pratique à utiliser dans certains cas, y compris celui-ci.

Cependant, sachez que si la condition d'avoir des ensembles uniformes de valeurs de pas sur toutes les balises de toutes les exécutions n'est pas remplie, l'utilisation de pivot=True entraînera une erreur.

 dfw = experiment.get_scalars(pivot=True) 
dfw
 

Notez qu'au lieu d'une seule colonne "valeur", le DataFrame au format large inclut les deux balises (métriques) comme colonnes explicitement: epoch_accuracy et epoch_loss .

Enregistrement du DataFrame au format CSV

pandas.DataFrame a une bonne interopérabilité avec CSV . Vous pouvez le stocker en tant que fichier CSV local et le recharger plus tard. Par exemple:

 csv_path = '/tmp/tb_experiment_1.csv'
dfw.to_csv(csv_path, index=False)
dfw_roundtrip = pd.read_csv(csv_path)
pd.testing.assert_frame_equal(dfw_roundtrip, dfw)
 

Effectuer une visualisation personnalisée et une analyse statistique

 # Filter the DataFrame to only validation data, which is what the subsequent
# analyses and visualization will be focused on.
dfw_validation = dfw[dfw.run.str.endswith("/validation")]
# Get the optimizer value for each row of the validation DataFrame.
optimizer_validation = dfw_validation.run.apply(lambda run: run.split(",")[0])

plt.figure(figsize=(16, 6))
plt.subplot(1, 2, 1)
sns.lineplot(data=dfw_validation, x="step", y="epoch_accuracy",
             hue=optimizer_validation).set_title("accuracy")
plt.subplot(1, 2, 2)
sns.lineplot(data=dfw_validation, x="step", y="epoch_loss",
             hue=optimizer_validation).set_title("loss")
 
Text(0.5, 1.0, 'loss')

png

Les graphiques ci-dessus montrent les chronologies de l'exactitude de la validation et de la perte de validation. Chaque courbe montre la moyenne sur 5 exécutions sous un type d'optimiseur. Grâce à une fonction intégrée de seaborn.lineplot() , chaque courbe affiche également ± 1 écart-type autour de la moyenne, ce qui nous donne une idée claire de la variabilité de ces courbes et de l'importance des différences entre les trois types d'optimiseur. Cette visualisation de la variabilité n'est pas encore prise en charge dans l'interface graphique de TensorBoard.

Nous souhaitons étudier l'hypothèse que la perte de validation minimale diffère significativement entre les optimiseurs "adam", "rmsprop" et "sgd". Nous extrayons donc un DataFrame pour la perte de validation minimale sous chacun des optimiseurs.

Ensuite, nous faisons un boxplot pour visualiser la différence des pertes de validation minimales.

 adam_min_val_loss = dfw_validation.loc[optimizer_validation=="adam", :].groupby(
    "run", as_index=False).agg({"epoch_loss": "min"})
rmsprop_min_val_loss = dfw_validation.loc[optimizer_validation=="rmsprop", :].groupby(
    "run", as_index=False).agg({"epoch_loss": "min"})
sgd_min_val_loss = dfw_validation.loc[optimizer_validation=="sgd", :].groupby(
    "run", as_index=False).agg({"epoch_loss": "min"})
min_val_loss = pd.concat([adam_min_val_loss, rmsprop_min_val_loss, sgd_min_val_loss])

sns.boxplot(data=min_val_loss, y="epoch_loss",
            x=min_val_loss.run.apply(lambda run: run.split(",")[0]))
 
<matplotlib.axes._subplots.AxesSubplot at 0x7f5e017c8150>

png

 # Perform pairwise comparisons between the minimum validation losses
# from the three optimizers.
_, p_adam_vs_rmsprop = stats.ttest_ind(
    adam_min_val_loss["epoch_loss"],
    rmsprop_min_val_loss["epoch_loss"]) 
_, p_adam_vs_sgd = stats.ttest_ind(
    adam_min_val_loss["epoch_loss"],
    sgd_min_val_loss["epoch_loss"]) 
_, p_rmsprop_vs_sgd = stats.ttest_ind(
    rmsprop_min_val_loss["epoch_loss"],
    sgd_min_val_loss["epoch_loss"]) 
print("adam vs. rmsprop: p = %.4f" % p_adam_vs_rmsprop)
print("adam vs. sgd: p = %.4f" % p_adam_vs_sgd)
print("rmsprop vs. sgd: p = %.4f" % p_rmsprop_vs_sgd)
 
adam vs. rmsprop: p = 0.0244
adam vs. sgd: p = 0.9749
rmsprop vs. sgd: p = 0.0135

Par conséquent, à un niveau de signification de 0,05, notre analyse confirme notre hypothèse selon laquelle la perte de validation minimale est significativement plus élevée (c'est-à-dire pire) dans l'optimiseur rmsprop par rapport aux deux autres optimiseurs inclus dans notre expérience.

En résumé, ce didacticiel fournit un exemple d'accès aux données scalaires en tant que panda.DataFrame partir de TensorBoard.dev. Il montre le type d'analyses et de visualisation flexibles et puissantes que vous pouvez effectuer avec les DataFrame .