wandb
wandb is the Python client and CLI for Weights & Biases, the hosted experiment tracking platform for machine learning. You wrap training in wandb.init(), call run.log() with metrics, and everything (loss curves, hyperparameters, system stats, console output) streams to a shared web dashboard where runs can be compared, filtered, and turned into reports. Beyond metrics it covers artifacts for versioning datasets and models with lineage, sweeps for distributed hyperparameter search, and tables for logging predictions. Data goes to wandb.ai by default, with dedicated cloud and self-managed server options for teams that need it.
The best hosted experiment tracker for teams, with a polished UI and integrations everywhere, but you are buying into a vendor: weigh the cloud dependency and pricing against MLflow before your run history becomes the thing that locks you in.
Use it if
- A team is training models and needs shared, comparable dashboards of metrics, configs, and system utilization across hundreds of runs
- You want dataset and model versioning with lineage: artifacts record exactly which data produced which checkpoint
- You run hyperparameter sweeps across multiple machines and want a central controller handing out configs to agents
- You already use PyTorch Lightning, Keras, or Hugging Face Trainer, all of which have one-line wandb integrations
- Your metrics cannot leave your infrastructure: the default is W&B's multi-tenant cloud, and the self-managed server is a serious deployment, not a pip install
- It is a commercial product: the free tier has storage limits and team features cost money, while MLflow gives you self-hosted tracking with no vendor attached
- You are one person on one machine: TensorBoard or a CSV of metrics covers a solo project without accounts, API keys, and a background sync process
- You are tracing an LLM app rather than training models: W&B points you at its separate Weave product for that, and tools like Langfuse are purpose-built for it
Setup reality
pip install wandb brings a sizable dependency tree plus a bundled wandb-core binary, but wheels exist everywhere. First use requires an account and an API key via wandb login or WANDB_API_KEY; the key lands in ~/.netrc, which surprises people auditing credentials. In CI and tests set WANDB_MODE=offline or disabled, or every test run creates cloud runs. The client launches a background process per run; scripts that hang at exit almost always forgot run.finish() outside a with block. Large artifact uploads are slow and eat the free storage quota faster than you expect.
Patterns
Track a run and log metricsinit-and-log
import wandb
config = {"epochs": 10, "lr": 3e-4}
with wandb.init(project="my-awesome-project", config=config) as run:
for epoch in range(config["epochs"]):
loss = train_one_epoch()
run.log({"loss": loss, "epoch": epoch})The with block marks the run finished on exit and failed on exception. Without it you must call run.finish() yourself or the process can hang at exit.
Use wandb.config as the source of hyperparametersread-config
import wandb
run = wandb.init(project="my-awesome-project", config={"lr": 0.001, "batch_size": 32})
cfg = run.config
optimizer = build_optimizer(lr=cfg.lr)
loader = build_loader(batch_size=cfg.batch_size)
run.finish()Reading hyperparameters from run.config instead of local variables is what makes sweeps work: the sweep controller overrides config values.
Log images and other medialog-images-media
import wandb
with wandb.init(project="vision") as run:
run.log({
"examples": [wandb.Image(img, caption=f"pred: {p}") for img, p in samples[:8]],
})wandb.Image accepts numpy arrays, PIL images, or file paths. Log a small sample, not the whole batch, or you will burn storage quota.
Version a dataset or model as an artifactlog-artifact
import wandb
with wandb.init(project="my-awesome-project", job_type="training") as run:
artifact = wandb.Artifact("model-weights", type="model")
artifact.add_file("checkpoint.pt")
run.log_artifact(artifact)Artifacts are content-addressed: re-logging identical files creates a new version pointer without re-uploading the bytes.
Download an artifact in another runuse-artifact
import wandb
with wandb.init(project="my-awesome-project", job_type="evaluation") as run:
artifact = run.use_artifact("model-weights:latest")
path = artifact.download()
model = load_model(f"{path}/checkpoint.pt")use_artifact also records lineage, so the UI shows which training run produced the weights this evaluation consumed.
Run a hyperparameter sweephyperparameter-sweep
import wandb
sweep_config = {
"method": "bayes",
"metric": {"name": "val_loss", "goal": "minimize"},
"parameters": {
"lr": {"min": 1e-5, "max": 1e-2},
"batch_size": {"values": [16, 32, 64]},
},
}
def train():
with wandb.init() as run:
val_loss = train_with(run.config.lr, run.config.batch_size)
run.log({"val_loss": val_loss})
sweep_id = wandb.sweep(sweep_config, project="my-awesome-project")
wandb.agent(sweep_id, function=train, count=20)Run wandb.agent on as many machines as you like with the same sweep_id; the controller distributes configs to all of them.
Track a Hugging Face Trainer runhuggingface-trainer
import os
from transformers import TrainingArguments
os.environ["WANDB_PROJECT"] = "my-awesome-project"
args = TrainingArguments(
output_dir="out",
report_to="wandb",
run_name="bert-finetune-1",
logging_steps=50,
)report_to="wandb" is all it takes; the Trainer logs losses, eval metrics, and config automatically. Omit run_name and you get an auto-generated one.
Run offline and sync lateroffline-mode
import os
import wandb
os.environ["WANDB_MODE"] = "offline"
with wandb.init(project="airgapped") as run:
run.log({"loss": 0.5})
# later, from a machine with network access:
# wandb sync wandb/offline-run-*WANDB_MODE=disabled turns wandb calls into no-ops entirely, which is the right setting for unit tests.
Log a table of predictionslog-predictions-table
import wandb
with wandb.init(project="my-awesome-project") as run:
table = wandb.Table(columns=["id", "input", "prediction", "label"])
for ex in eval_samples:
table.add_data(ex.id, ex.text, ex.pred, ex.label)
run.log({"eval_predictions": table})Tables are queryable and filterable in the UI, which makes error analysis far easier than scrolling logged text.
Resume a crashed or interrupted runresume-run
import wandb
run = wandb.init(
project="my-awesome-project",
id="a1b2c3d4",
resume="must",
)
run.log({"loss": 0.42})
run.finish()You need the original run id (store it with your checkpoint). resume="must" fails loudly if the id does not exist; "allow" silently starts fresh.
Track the best value of a metric, not the lastdefine-metric-summary
import wandb
with wandb.init(project="my-awesome-project") as run:
run.define_metric("val_acc", summary="max")
for epoch in range(10):
run.log({"val_acc": evaluate()})
# run summary now shows val_acc.max instead of the final epoch valueBy default the run summary holds the last logged value, which is misleading when training overshoots; define_metric fixes the leaderboard columns.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| mlflow | PyPI | You want open source, self-hosted experiment tracking and a model registry with no commercial cloud in the loop. |
| tensorboard | PyPI | You want free local metric visualization for a solo project and do not need collaboration or artifact lineage. |
| comet-ml | PyPI | You want the closest commercial alternative and are comparing hosted tracking vendors on price and features. |