Esta página foi traduzida pela API Cloud Translation.
Switch to English

Acessando dados do TensorBoard como DataFrames

visão global

O principal recurso do TensorBoard é sua GUI interativa. No entanto, às vezes os usuários desejam ler programaticamente os registros de dados armazenados no TensorBoard, para fins como realizar análises post-hoc e criar visualizações personalizadas dos dados de registro.

O TensorBoard 2.3 oferece suporte a este caso de uso com tensorboard.data.experimental.ExperimentFromDev() . Ele permite acesso programático aos registros escalares do TensorBoard. Esta página demonstra o uso básico desta nova API.

Configuração

Para usar a API programática, certifique-se de instalar o pandas junto com o tensorboard .

Usaremos matplotlib e seaborn para plotagens personalizadas neste guia, mas você pode escolher sua ferramenta preferida para analisar e visualizar DataFrame s.

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

Carregando escalares do TensorBoard como um pandas.DataFrame

Depois que um logdir do TensorBoard é carregado para TensorBoard.dev, ele se torna o que chamamos de experimento . Cada experimento tem um ID exclusivo, que pode ser encontrado no URL TensorBoard.dev do experimento. Para nossa demonstração abaixo, usaremos um experimento TensorBoard.dev em: https://tensorboard.dev/experiment/c1KCv3X3QvGwaXfgX1c4tg

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

df é um pandas.DataFrame que contém todos os registros escalares do experimento.

As colunas do DataFrame são:

  • run : cada run corresponde a um subdiretório do logdir original. Neste experimento, cada execução é de um treinamento completo de uma rede neural convolucional (CNN) no conjunto de dados MNIST com um determinado tipo de otimizador (um hiperparâmetro de treinamento). Este DataFrame contém várias dessas execuções, que correspondem a execuções de treinamento repetidas em diferentes tipos de otimizador.
  • tag : descreve o que significa o value na mesma linha, ou seja, qual métrica o valor representa na linha. Neste experimento, temos apenas duas tags exclusivas: epoch_accuracy e epoch_loss para as métricas de precisão e perda, respectivamente.
  • step : Este é um número que reflete a ordem serial da linha correspondente em sua execução. Aqui, a step realmente se refere ao número da época. Se desejar obter os carimbos de data / hora além dos valores da step , você pode usar o argumento de palavra-chave include_wall_time=True ao chamar get_scalars() .
  • value : este é o valor numérico real de interesse. Conforme descrito acima, cada value neste DataFrame específico é uma perda ou uma precisão, dependendo da tag da linha.
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']

Obtendo um DataFrame dinâmico (formato amplo)

Em nosso experimento, as duas tags ( epoch_loss e epoch_accuracy ) estão presentes no mesmo conjunto de etapas em cada execução. Isso torna possível obter um DataFrame "formato amplo" diretamente de get_scalars() usando o argumento de palavra-chave pivot=True . O DataFrame formato DataFrame tem todas as suas tags incluídas como colunas do DataFrame, o que é mais conveniente para trabalhar em alguns casos, incluindo este.

No entanto, esteja ciente de que se a condição de ter conjuntos uniformes de valores de etapas em todas as tags em todas as execuções não for atendida, o uso de pivot=True resultará em erro.

dfw = experiment.get_scalars(pivot=True) 
dfw

Observe que, em vez de uma única coluna de "valor", o DataFrame de formato largo inclui as duas tags (métricas) como suas colunas explicitamente: epoch_accuracy e epoch_loss .

Salvando o DataFrame como CSV

pandas.DataFrame tem boa interoperabilidade com CSV . Você pode armazená-lo como um arquivo CSV local e carregá-lo novamente mais tarde. Por exemplo:

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)

Execução de visualização personalizada e análise estatística

# 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

Os gráficos acima mostram os cursos de tempo de precisão e perda de validação. Cada curva mostra a média em 5 execuções em um tipo de otimizador. Graças a um recurso seaborn.lineplot() de seaborn.lineplot() , cada curva também exibe ± 1 desvio padrão em torno da média, o que nos dá uma noção clara da variabilidade nessas curvas e o significado das diferenças entre os três tipos de otimizador. Essa visualização da variabilidade ainda não é compatível com a GUI do TensorBoard.

Queremos estudar a hipótese de que a perda mínima de validação difere significativamente entre os otimizadores "adam", "rmsprop" e "sgd". Portanto, extraímos um DataFrame para a perda mínima de validação em cada um dos otimizadores.

Em seguida, fazemos um boxplot para visualizar a diferença nas perdas mínimas de validação.

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

Portanto, a um nível de significância de 0,05, nossa análise confirma nossa hipótese de que a perda mínima de validação é significativamente maior (ou seja, pior) no otimizador rmsprop em comparação com os outros dois otimizadores incluídos em nosso experimento.

Em resumo, este tutorial fornece um exemplo de como acessar dados escalares como panda.DataFrame s do TensorBoard.dev. Ele demonstra o tipo de análise e visualização flexível e poderosa que você pode fazer com os DataFrame s.