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

Acessando dados do TensorBoard como DataFrames

Visão geral

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

O TensorBoard 2.3 suporta esse caso de uso com tensorboard.data.experimental.ExperimentFromDev() . Permite acesso programático aos logs escalares do TensorBoard. Esta página demonstra o uso básico desta nova API.

Configuração

Para usar a API programática, instale pandas ao lado do 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

Carregar escalares do TensorBoard como pandas.DataFrame

Depois que um logdir do TensorBoard é carregado no TensorBoard.dev, ele se torna o que chamamos de experimento . Cada experiência tem um ID exclusivo, que pode ser encontrado no URL TensorBoard.dev da experiência. 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 logs escalares do experimento.

As colunas do DataFrame são:

  • run : cada execução corresponde a um subdiretório do logdir original. Nesta experiência, 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). Esse DataFrame contém várias execuções desse tipo, que correspondem a execuções repetidas de treinamento em diferentes tipos de otimizadores.
  • tag : descreve o significado do value na mesma linha, ou seja, qual métrica o valor representa na linha. Nesta experiência, 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, o step na verdade se refere ao número da época. Se você deseja obter os registros de data e hora, além dos valores da step , pode usar o argumento da palavra-chave include_wall_time=True ao chamar get_scalars() .
  • value : este é o valor numérico real de interesse. Conforme descrito acima, cada value nesse 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 (de 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 da 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, lembre-se 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 "valor", o DataFrame de formato amplo inclui explicitamente as duas tags (métricas) como suas colunas: 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)
 

Executando 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 de 5 execuções sob 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 sensação clara da variabilidade nessas curvas e o significado das diferenças entre os três tipos de otimizadores. Essa visualização da variabilidade ainda não é suportada na 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, em 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álises e visualizações flexíveis e poderosas que você pode fazer com os DataFrame s.