Quick Reference · experiment tracking & the ML record

wandb cheat sheet

A run is a record, and everything hangs off it. wandb.init() opens one and hands back a Run object that owns five buckets: config (the inputs), history (one row per log call), summary (one final number per metric), files & media, and artifacts. A sweep is just a controller that writes a different config into each run.

install · CLI · env the Run object config & settings logging media · artifacts · API sweeps gotcha / removed ★ most common

Built against the official docs: docs.wandb.ai (quickstart · the W&B SDK Python coding cheat sheet · init() & Run reference · global functions · log media · log tables · sweep config keys · sweeps walkthrough · environment variables · Keras integration) · the full SDK release notes v0.13–v0.28 · github.com/wandb/wandb. Verified 2026-08-27 against wandb 0.28.2.

One run end to end · and the five buckets it owns
A · THE LIFE OF A RUN wandb.login() once per machine or WANDB_API_KEY wandb.init() project= · config= returns a Run run.log() one row per call called in the loop log_artifact() versioned output async — .wait() run.finish() flush & close free with `with` every step a `with` block also marks the run FAILED on exception B · WHAT A RUN HOLDS Run one record config the inputs · written once history one row per log() call summary one number per metric files & media images · tables · code artifacts versioned · carry lineage sweep controller picks the next config a sweep = many runs, one config each history is append-only and step never goes backwards
A whole tracked experiment in fifteen lines
import wandb

with wandb.init(project="demo",
                config={"lr": 3e-4, "epochs": 10}) as run:   # closes cleanly; marks FAILED on exception

    cfg = run.config                          # read hyperparameters from here — a sweep overwrites them
    run.watch(model, log="all", log_freq=100)   # gradients + parameters

    for epoch in range(cfg.epochs):
        loss, acc = train_one_epoch(model, cfg.lr)
        run.log({"epoch": epoch, "train/loss": loss, "val/acc": acc})   # one history row

    run.summary["best_acc"] = best        # summary defaults to the LAST value, not the best

    art = wandb.Artifact("resnet", type="model")
    art.add_file("ckpt.pt")
    run.log_artifact(art, aliases=["best"]).wait()   # log_artifact is async since 0.18.3
Part I

Setup & the Run object

get authenticated, then open a run and know what it gives you
01Install & authenticateonce per machine
02The CLIwandb --help
03Environment variablesfor CI & containers
04Start a run★init returns a Run
05wandb.init() parameters★the ones that matter
06The Run objectwhat init hands back
07Resume, fork, rewindthree different things
08Organising many runsbefore you have 500
Part II

Config & logging

the inputs, the history rows, and the one number you are judged on
09Config — the inputs★write once, read often
10run.log() — the core call★one row per call
11Summary & define_metricthe headline number
12Images, video, audiowrap, then log
13Tablesrows you can query
14Custom chartswandb.plot
15System metrics, logs, offlinewandb.Settings
Part III

Artifacts, sweeps & the wider platform

versioned data, automated search, and what the tutorials get wrong
16Artifacts — create & log★versioned outputs
17Artifacts — use & downloadand the lineage
18Sweeps — the config★YAML or dict
19Sweeps — running themcontroller + agents
20Framework hooksintegrations
21Public API — querying backwandb.Api()
22Beyond experiment trackingthe rest of the platform
23What the tutorials still teach★all removed
24When something goes wrongthe usual suspects

Four things worth seeing

Where your numbers actually land, and the defaults that quietly decide what you see.

Where a number lives

The same metric can sit in three places with three different meanings. The runs table sorts on summary — which defaults to your last value, not your best.

CONFIG — the inputs, written once at init() lr = 3e-4 batch_size = 32 arch = "resnet50" one row per run · this is what a sweep overwrites HISTORY — one row per run.log() call step 0 step 1 step 2 … append-only best 0.94 last 0.81 val/acc over steps SUMMARY — one number per metric, and what the table sorts on default behaviour val/acc = 0.81 ← the last value define_metric("val/acc", summary="max") val/acc = 0.94 ← the best

The step counter, three ways

Every log() call advances the step. Call it twice per iteration and you get two half-empty rows — the most common reason charts look wrong.

TWO log() CALLS PER ITERATION — what goes wrong step train/loss val/acc 0 0.62 — 1 — 0.71 each series is drawn on alternate points — charts look sparse and jagged run.log({"train/loss": l}) run.log({"val/acc": a}) FIX 1 — commit=False accumulates into the current row step train/loss val/acc 0 0.62 0.71 run.log(a, commit=False) run.log(b) ← commits both one full row per iteration — or just build one dict and log it once FIX 2 — plot against your own x-axis run.log({"epoch": e, "val/acc": a}) run.define_metric("*", step_metric="epoch") x-axis becomes "epoch" instead of the internal step declare define_metric BEFORE the loop — it does not apply retroactively step is monotonic: you can skip forward, never back. That is what rewind is for.

What lineage actually records

use_artifact draws an input edge, log_artifact an output edge. Those two calls are the whole graph — which is why skipping use_artifact costs you the trail.

raw-data:v1 artifact prep-run run cifar:v3 artifact use log use_artifact train-run run resnet:v7 artifact log :latest :best :production aliases point at a version Registry collection link_artifact — org-wide promotion aliases move. :v7 is forever. pin the version for reproducibility

Anatomy of a sweep

The controller lives on W&B and hands each agent a config. With hyperband, runs are judged at exponentially spaced brackets and the losers are killed early.

sweep.yaml method · metric controller runs on W&B, not on you wandb.sweep() agent → run 1 agent → run 2 agent → run 3 a different config each early_terminate: hyperband, min_iter=3, eta=3 val loss, lower is better 3 9 27 iters survives killed at 3 killed at 9 grid needs `values`; combine it with min/max and the sweep never terminates. Always set run_cap.

Worth memorizing

run.log()not wandb.log() — logging lives on the Run object
with wandb.init()closes cleanly and marks the run FAILED on exception
run.configread every hyperparameter here or sweeps cannot reach it
summary = lastnot best — the runs table sorts on it
define_metricsummary="max" is the fix; declare it before the loop
stepmonotonic — skip forward, never back
one slasha/b/c still groups under section a
commit=Falsemerge several calls into one history row
.wait()log_artifact is async since 0.18.3
:v7 vs :latestversions are permanent, aliases move
use_artifactthe input edge — skip it and you lose the lineage
"domain":"pixel"boxes are fractional without it
media caps<50 images and <100 audio clips per step
Table.MAX_ROWS10000 by default; extra rows are dropped
sweep fntakes NO arguments — the agent injects the config
sweep projectmust match the run's project
grid + min/maxnever terminates; grid needs values
learning rateswant log_uniform_values, not uniform
WandbCallbackremoved in 0.27.1 — the Keras docs still show it
require("core")unnecessary since 0.18.0; "legacy-service" now raises