W&B
data visualization
machine learning
plotting
Python

Multiple lines on same plot with incremental logging - wandb

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Getting multiple lines onto the same Weights and Biases chart is less about plotting code and more about logging discipline. The key requirements are a shared step axis and stable metric names across the run. When either of those changes midstream, W&B stops seeing one evolving chart and starts seeing separate histories.

The Simplest Incremental Logging Pattern

For most training jobs, log multiple metrics at the same step and let W&B build the chart from history:

python
1import wandb
2
3wandb.init(project="demo-lines")
4
5for epoch in range(10):
6    train_loss = 1.0 / (epoch + 1)
7    val_loss = 1.2 / (epoch + 1)
8
9    wandb.log(
10        {
11            "epoch": epoch,
12            "loss/train": train_loss,
13            "loss/val": val_loss,
14        },
15        step=epoch,
16    )
17
18wandb.finish()

This is incremental logging because each call appends one more point to the same history. Both metrics share the same step value, so overlaying them makes sense.

In the W&B UI, metrics with related names such as loss/train and loss/val are easy to overlay or group.

Define a Shared Step Metric Explicitly

If you want to control the x-axis yourself, define it once:

python
1import wandb
2
3wandb.init(project="demo-lines")
4
5wandb.define_metric("epoch")
6wandb.define_metric("loss/*", step_metric="epoch")
7
8for epoch in range(10):
9    wandb.log(
10        {
11            "epoch": epoch,
12            "loss/train": 1.0 / (epoch + 1),
13            "loss/val": 1.2 / (epoch + 1),
14        }
15    )
16
17wandb.finish()

This is especially useful when W&B's internal step is not the axis you care about. A common example is plotting training and validation lines against epoch rather than against raw log-call count.

Custom Multi-Line Charts With line_series

If you want a single purpose-built chart object rather than relying on UI grouping, use wandb.plot.line_series. The trade-off is that you maintain the history arrays yourself.

python
1import wandb
2
3wandb.init(project="demo-line-series")
4
5xs = []
6train_values = []
7val_values = []
8
9for epoch in range(5):
10    xs.append(epoch)
11    train_values.append(1.0 / (epoch + 1))
12    val_values.append(1.3 / (epoch + 1))
13
14    chart = wandb.plot.line_series(
15        xs=xs,
16        ys=[train_values, val_values],
17        keys=["train", "val"],
18        title="Loss over time",
19        xname="epoch",
20    )
21
22    wandb.log({"loss_chart": chart})
23
24wandb.finish()

This approach gives you a single chart with named lines, but it is less lightweight because every update resends the accumulated arrays.

Which Approach Should You Use?

Use ordinary wandb.log with separate metric keys when:

  • training runs are long
  • you want efficient incremental logging
  • you are happy to use the dashboard's normal metric charts

Use wandb.plot.line_series when:

  • you want one explicit custom chart object
  • you need more control over labels and presentation
  • the logged history is not huge

For most training loops, plain incremental metric logging is the better default because it scales better and keeps run history simple.

Example With Training and Validation Every Epoch

Here is a practical pattern that works well in Keras or PyTorch training code:

python
1import wandb
2
3wandb.init(project="example-training")
4wandb.define_metric("epoch")
5wandb.define_metric("accuracy/*", step_metric="epoch")
6
7for epoch in range(20):
8    train_acc = 0.70 + epoch * 0.01
9    val_acc = 0.68 + epoch * 0.008
10
11    wandb.log(
12        {
13            "epoch": epoch,
14            "accuracy/train": train_acc,
15            "accuracy/val": val_acc,
16        }
17    )
18
19wandb.finish()

This gives you stable metric names, a stable x-axis, and a history that can be compared across runs.

Common Pitfalls

  • Logging related lines at different step values. Fix: make the metrics share one explicit step axis.
  • Changing metric names partway through a run. Fix: decide on naming once and keep it stable from the first log call.
  • Rebuilding a line_series chart from only the newest point. Fix: preserve accumulated history when using custom chart objects.
  • Logging too frequently. Fix: choose a cadence that is informative without bloating history.
  • Mixing custom chart logging and plain metric logging without a plan. Fix: decide whether the dashboard should group raw metrics or display one custom chart object.

Summary

  • To get multiple lines on one plot, make the metrics share the same x-axis and naming convention.
  • Plain wandb.log with stable keys is usually the simplest incremental solution.
  • Use wandb.define_metric when you want explicit control over the step metric.
  • Use wandb.plot.line_series when you need a single custom multi-line chart object.
  • Be consistent about steps and metric names, or the dashboard will fragment the plot into separate histories.

Course illustration
Course illustration

All Rights Reserved.