Metrics & monitoring
Log scalars with gpu_train.log() and chart them natively on the local dashboard, with optional auto-forwarding to Weights & Biases.
gpu-train gives you two ways to see training metrics:
- Native, local-first — call
gpu_train.log({...})from your script and the values chart directly on the local dashboard. Works on every provider (includinglocal), fully offline, no W&B account required. - Weights & Biases (optional overlay) — when configured, runs are tracked automatically
and the same
gpu_train.log()call also forwards towandb.log(). You can also pull a run's history back into the local dashboard on demand. The native views are complete on their own; W&B affordances only appear once a run has been tracked.
Native metrics: gpu_train.log()
Call it from inside your training script:
import gpu_train
for step in range(steps):
loss, acc = train_one_step()
gpu_train.log({"loss": loss, "acc": acc}, step=step)log(
metrics: dict[str, float],
*,
step: int | None = None,
all_ranks: bool = False,
) -> Nonemetrics— a mapping of name → number. Booleans become0/1; non-numeric values (andNaN/inf) are skipped.step— optional integer x-axis. When omitted, points are ordered by arrival.all_ranks— by default only rank 0 emits (readsRANK/LOCAL_RANK) so DDP/torchrun jobs don't duplicate points. SetTrueto log from every rank.
How it works
gpu_train.log() prints a single sentinel-prefixed JSON line to stdout. That line rides the
existing log stream back to the control plane, which parses it into a local metrics
SQLite table (kept out of the raw log view) and serves it to the dashboard, where each key is
drawn as a native SVG chart on the run detail page. No extra network path, no extra services.
The primitive has no hard dependency on W&B (or anything else). It works on the local
provider with configure(registry={}, use=[]) — great for a fully offline loop.
On SSH providers the training script needs the primitive to be importable. Either add
gpu-train to your task deps (task={"entrypoint": "train.py", "deps": {"requirements": ["gpu-train"]}}), or use the zero-dependency fallback: print the same sentinel line yourself —
print("::gpu-train:metric:: " + json.dumps({"metrics": {...}, "step": n})). The control plane
parses it identically, so charts render even where gpu-train isn't installed. See
examples/train_with_checkpoints.py.
Namespaced metrics & the grouped view
Name your metrics with a namespace/stat convention and the dashboard groups them into
collapsible sections instead of a flat wall of charts:
gpu_train.log({
"train/loss": loss,
"train/lr": lr,
"eval/acc": acc,
"perf/throughput": toks_per_s,
}, step=step)Sections are ordered train, eval, stability, performance, then the rest alphabetically
(mirroring prime-rl's curated overview). Keys without a / land in a default group.
Chart controls
The Metrics panel has controls that apply to every chart at once:
- X-axis: step vs. time — plot against the logged
stepor the wall-clock timestamp (each point carries ats). - Smoothing — an EMA slider; the raw series is drawn faint behind the smoothed line.
- Log-scale — toggle a logarithmic Y-axis for losses that span orders of magnitude.
- Hover crosshair — hover any chart for a readout of the value and step under the cursor.
These are pure client-side SVG — no extra dependency, and they work fully offline.
Weights & Biases
gpu-train integrates with Weights & Biases so every run is tracked
automatically — you don't add any wandb glue beyond the usual wandb.init() / logging.
Enable it
Add a tracking.wandb block to your registry:
configure(
registry={
"credentials": [...],
"tracking": {
"wandb": {
"secret_ref": "env://WANDB_API_KEY",
"project": "my-proj",
"entity": "my-team", # optional
"enabled": True,
}
},
},
use=[...],
)Or set WANDB_API_KEY (and optional WANDB_PROJECT / WANDB_ENTITY) in the
environment, or connect W&B from the dashboard.
What happens
When tracking is enabled, for each job the control plane:
- Mints a stable W&B run id / group for the job.
- Injects
WANDB_API_KEY,WANDB_PROJECT,WANDB_ENTITY, and the run id into the remote job's environment (secrets written to owner-only files on the box). - Records the resulting
wandb_run_idandwandb_urlon theJobRecord, so the dashboard's run detail can deep-link straight into the W&B run.
Your train.py just calls wandb.init() as usual — it picks up the injected
environment automatically and lands in the right project/run.
One call, both sinks
When W&B is configured and a run is active, gpu_train.log() also calls wandb.log()
with the same values. So a single gpu_train.log({...}) line feeds both the local dashboard
and W&B — no double bookkeeping.
Load a run's history back into the dashboard
For runs logged with plain wandb.log, the run detail page has a Load from W&B button.
It calls GET /jobs/{id}/metrics/wandb, which reads the tracked run's history via the W&B
Public API (run.history()) and overlays the
series on the local charts — so you can skim W&B metrics without leaving the local view.
The pull path lazily imports wandb; install it with pip install "gpu-train[wandb]". The
endpoint returns an actionable error if the extra is missing or the run path can't be resolved
(the job must have a wandb_run_id and the registry a W&B project).
In the dashboard
The Overview shows W&B status (connected / needs key); each run's detail view renders the
Metrics panel (native charts from gpu_train.log), a Load from W&B button, and a
deep-link into the W&B run. You can set or update the W&B key directly from the Providers page —
see the Dashboard.
W&B is entirely optional. With no tracking block (and no WANDB_API_KEY), runs simply aren't
tracked — and gpu_train.log() still charts everything locally.