Training loops are easier to compare when loss and accuracy are written as event data instead of disappearing into terminal output. PyTorch sends scalar metrics to TensorBoard through SummaryWriter, so a run directory can show both the metric names and the values recorded at each training step.

SummaryWriter writes TensorBoard event files under its log_dir and flushes entries asynchronously while training continues. Giving each run its own directory, such as runs/tensorboard-demo or runs/experiment-name, keeps the Scalars dashboard organized and prevents unrelated runs from sharing one event file.

Close or flush the writer before inspecting a short smoke run so pending metric points reach disk. The same logging calls can then move into the real training loop, where global_step should come from the epoch, batch index, or optimizer step that the metric represents.

Steps to log PyTorch training metrics to TensorBoard:

  1. Install TensorBoard in the active PyTorch environment.
    $ python -m pip install tensorboard

    Run this inside the same virtual environment or Conda environment that imports torch.
    Related: How to install PyTorch with pip

  2. Create a SummaryWriter smoke script.
    tensorboard_train_metrics.py
    from pathlib import Path
    import shutil
     
    import torch
    from torch import nn
    from torch.utils.tensorboard import SummaryWriter
    from tensorboard.backend.event_processing import event_accumulator
     
     
    torch.manual_seed(19)
    log_dir = Path("runs/tensorboard-demo")
    if log_dir.exists():
        shutil.rmtree(log_dir)
     
    features = torch.tensor(
        [
            [-1.0, 0.2, 0.1],
            [-0.5, 0.5, 0.4],
            [0.0, -0.3, 0.9],
            [0.5, 0.7, -0.4],
            [1.0, -0.6, -0.1],
            [1.5, 0.4, 0.2],
        ],
        dtype=torch.float32,
    )
    targets = torch.tensor([0, 0, 1, 1, 1, 0])
     
    model = nn.Sequential(
        nn.Linear(3, 6),
        nn.ReLU(),
        nn.Linear(6, 2),
    )
    loss_fn = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.15)
    writer = SummaryWriter(log_dir=log_dir)
     
    for epoch in range(5):
        optimizer.zero_grad(set_to_none=True)
        logits = model(features)
        loss = loss_fn(logits, targets)
        loss.backward()
        optimizer.step()
     
        with torch.no_grad():
            predictions = model(features).argmax(dim=1)
            accuracy = (predictions == targets).float().mean().item()
     
        writer.add_scalar("Loss/train", loss.item(), epoch)
        writer.add_scalar("Accuracy/train", accuracy, epoch)
        print(f"epoch={epoch} loss={loss.item():.4f} accuracy={accuracy:.4f}")
     
    writer.flush()
    writer.close()
     
    events = sorted(log_dir.glob("events.out.tfevents.*"))
    reader = event_accumulator.EventAccumulator(str(log_dir))
    reader.Reload()
    scalar_tags = sorted(reader.Tags()["scalars"])
    loss_points = reader.Scalars("Loss/train")
    accuracy_points = reader.Scalars("Accuracy/train")
     
    print(f"torch_version={torch.__version__}")
    print(f"log_dir={log_dir}")
    print(f"event_files={len(events)}")
    for path in events:
        print(f"event_file={path.name}")
    print(f"scalar_tags={','.join(scalar_tags)}")
    print(f"loss_points={len(loss_points)}")
    print(f"accuracy_points={len(accuracy_points)}")
    print(f"last_loss={loss_points[-1].value:.4f}")
    print(f"last_accuracy={accuracy_points[-1].value:.4f}")

    EventAccumulator is used only for the terminal readback. Training code only needs SummaryWriter and the add_scalar() calls.

  3. Run the smoke script.
    $ python tensorboard_train_metrics.py
    epoch=0 loss=0.7052 accuracy=0.5000
    epoch=1 loss=0.6980 accuracy=0.5000
    epoch=2 loss=0.6919 accuracy=0.5000
    epoch=3 loss=0.6870 accuracy=0.5000
    epoch=4 loss=0.6823 accuracy=0.5000
    torch_version=2.12.1+cpu
    log_dir=runs/tensorboard-demo
    event_files=1
    event_file=events.out.tfevents.1783379917.training-host.2508.0
    scalar_tags=Accuracy/train,Loss/train
    loss_points=5
    accuracy_points=5
    last_loss=0.6823
    last_accuracy=0.5000

    event_files=1 confirms that SummaryWriter created an event file. scalar_tags confirms that both metric names were written.

  4. Verify the event directory with TensorBoard.
    $ tensorboard --inspect --logdir runs/tensorboard-demo
    TensorFlow installation not found - running with reduced feature set.
    ======================================================================
    Processing event files... (this can take a few minutes)
    ======================================================================
    
    Found event files in:
    runs/tensorboard-demo
    
    These tags are in runs/tensorboard-demo:
    audio -
    histograms -
    images -
    scalars
       Accuracy/train
       Loss/train
    tensor -
    ======================================================================
    
    Event statistics for runs/tensorboard-demo:
    audio -
    graph -
    histograms -
    images -
    scalars
       first_step           0
       last_step            4
       max_step             4
       min_step             0
       num_steps            5
       outoforder_steps     []
    sessionlog:checkpoint -
    sessionlog:start -
    sessionlog:stop -
    tensor -
    ======================================================================

    The reduced feature message is normal when TensorBoard is installed without TensorFlow. The scalars section and num_steps count are the event readback signals.

  5. Add the same writer pattern to the project training loop.
    writer = SummaryWriter(log_dir=f"runs/{run_name}")
     
    for global_step, (inputs, targets) in enumerate(train_loader):
        loss, accuracy = train_one_batch(model, inputs, targets)
        writer.add_scalar("Loss/train", loss, global_step)
        writer.add_scalar("Accuracy/train", accuracy, global_step)
     
    writer.close()

    Log Python floats or scalar tensors after each value is computed. Keep tag names stable across runs so TensorBoard can overlay comparable curves.

  6. Start TensorBoard with the parent run directory.
    $ tensorboard --logdir runs

    Open the local URL printed by TensorBoard and choose the tensorboard-demo run in the Scalars dashboard. Point --logdir at the parent directory when several run subdirectories should be compared.

  7. Remove the smoke script and demo run directory after moving the logging calls into the project.
    $ rm -r tensorboard_train_metrics.py runs/tensorboard-demo