tensorboard
TensorBoard is the browser dashboard for machine learning runs: point it at a directory of event files and it renders loss curves, histograms, images, audio, text, embedding projections and hyperparameter tables. It installs as a standalone pip package with a CLI (tensorboard --logdir), reads data written by tf.summary, the Keras callback or PyTorch's built-in torch.utils.tensorboard.SummaryWriter, runs entirely offline with no account or server, and treats each subdirectory of your log dir as a separate run for side-by-side comparison.
Still the fastest way to see whether your loss is going down, and the only mainstream tracker that is fully local with zero setup. Beyond one person on one machine, a real experiment tracker earns its keep quickly.
Use it if
- You want free, local, offline training visualization with zero accounts, agents or infrastructure: pip install, write logs, open localhost:6006
- You train in PyTorch or Keras and want loss curves and histograms from a few lines, since the writer half already ships inside torch and the Keras callback is one argument
- You compare experiments on one machine: the subdirectory-per-run convention gives overlay charts and run toggling with no extra code
- You need the embedding projector or TensorFlow profiler views, which the hosted trackers either lack or gate behind paid tiers
- A team needs shared experiment tracking: there is no auth, no run database, no metadata search and no collaboration features, which is exactly what W&B and MLflow exist for
- You accumulate hundreds of runs: the UI gets sluggish with large logdirs and big event files, and cleanup means deleting directories by hand
- You want to log artifacts beyond metrics and media: the summary format covers scalars, tensors, images, audio and text, not config files, tables or model binaries
- You are betting on new features: the tracker carries several hundred open issues and PRs and development is largely maintenance releases synced to TensorFlow versions
Setup reality
pip install tensorboard is standalone and does not drag in TensorFlow; PyTorch users already have SummaryWriter in the core package. The routine annoyances: nothing appears until the writer flushes (call flush() or close()), every add_* call needs a global_step or your chart collapses into a single point, each run must live in its own subdirectory or curves overwrite each other, port 6006 collides with forgotten instances, and stale runs pile up until you rm the directories. Version skew between tensorboard's protobuf pins and other ML packages occasionally starts dependency resolver fights.
Patterns
Launch TensorBoard on a log directorylaunch-dashboard
tensorboard --logdir runs/
# then open http://localhost:6006
# pick another port when 6006 is taken
tensorboard --logdir runs/ --port 6007
# bind for access from another machine
tensorboard --logdir runs/ --host 0.0.0.0TensorBoard walks the logdir recursively and treats each subdirectory containing tfevents files as a run; there is no auth, so think before binding 0.0.0.0.
Log training scalars from PyTorchpytorch-log-scalars
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('runs/exp1')
for step, batch in enumerate(loader):
loss = train_step(batch)
writer.add_scalar('train/loss', loss.item(), global_step=step)
writer.close()The writer buffers; without close() or flush() the tail of your run never reaches disk. Slashes in tags group charts into sections.
Log a Keras training runkeras-callback
import tensorflow as tf
tb = tf.keras.callbacks.TensorBoard(
log_dir='logs/fit/run1',
histogram_freq=1,
)
model.fit(x_train, y_train, epochs=10,
validation_data=(x_val, y_val), callbacks=[tb])histogram_freq=1 adds weight histograms per epoch at a real training-time cost; give each fit its own log_dir or runs merge into one noisy curve.
Write summaries manually in TensorFlow 2tf2-summary-writer
import tensorflow as tf
writer = tf.summary.create_file_writer('logs/run1')
with writer.as_default():
for step in range(1000):
loss = train_step()
tf.summary.scalar('loss', loss, step=step)
writer.flush()step is mandatory in TF2 summaries; the old tf.summary.FileWriter API from TF1 shown in ancient tutorials no longer applies.
Organize the log directory to compare runscompare-runs-layout
runs/
baseline/events.out.tfevents...
lr_0.01/events.out.tfevents...
lr_0.001/events.out.tfevents...
# tensorboard --logdir runs/ overlays all threeThe directory name becomes the run name in the UI, so encode hyperparameters into it; restarted jobs can append new event files to the same run directory and TensorBoard stitches them.
Track weight and gradient distributionslog-histograms
for name, param in model.named_parameters():
writer.add_histogram(f'weights/{name}', param, global_step=epoch)
if param.grad is not None:
writer.add_histogram(f'grads/{name}', param.grad, global_step=epoch)Histograms are the quickest way to spot dead layers and exploding gradients, but they bloat event files; log per epoch, not per step.
Log image batcheslog-images
import torchvision
grid = torchvision.utils.make_grid(images[:16])
writer.add_image('inputs', grid, global_step=step)
# single HWC numpy image
writer.add_image('sample', img_hwc, global_step=step, dataformats='HWC')Default dataformats is CHW with values in [0, 1] floats or uint8; wrong layout renders as stripes rather than erroring.
Record hyperparameters with final metricslog-hparams
writer.add_hparams(
{'lr': 0.01, 'batch_size': 64, 'optimizer': 'adam'},
{'hparam/val_accuracy': 0.91, 'hparam/val_loss': 0.31},
)add_hparams writes a nested sub-run per call, which surprises people browsing the logdir; the HParams dashboard then gives a sortable table across runs.
Visualize embeddings in the projectorembedding-projector
writer.add_embedding(
features, # (N, D) tensor
metadata=labels, # N strings shown as point labels
label_img=images, # optional (N, C, H, W) thumbnails
global_step=step,
)The projector runs PCA/t-SNE/UMAP in the browser, so keep N in the low thousands; very large embeddings freeze the tab.
Run TensorBoard inside a notebooknotebook-magic
%load_ext tensorboard
%tensorboard --logdir runs/Works in Jupyter and Colab; rerunning the cell reuses the existing instance, and %reload_ext tensorboard is the fix when the panel goes blank.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| wandb | PyPI | Hosted experiment tracking with team access, artifacts and sweeps once local dashboards stop scaling |
| mlflow | PyPI | Self-hosted tracking server plus model registry when runs must be stored centrally |
| aim | PyPI | An open source local-first tracker with a faster UI when you have many runs but no appetite for a hosted service |