From Weights & Biases¶
W&B records what happened. WeightsLab records what happened and lets you change it while the run is still going. The port is mostly mechanical, the part that needs thought is what to do with the freedom that opens up.
Migration notes¶
Your logging calls mostly disappear. In W&B you call wandb.log() at
every point you want a number recorded. In WeightsLab you wrap the object that
produces the number once, and it logs itself from then on:
# W&B, you log, every step, by hand
loss = criterion(outputs, targets)
wandb.log({"train/loss": loss.item()}, step=step)
# WeightsLab, you wrap the criterion once, at setup
criterion = wl.watch_or_edit(nn.CrossEntropyLoss(reduction="none"),
flag="loss", signal_name="train-loss-CE", log=True)
loss_per_sample = criterion(outputs, targets, batch_ids=ids) # logs itself
Note reduction="none". That is not incidental, it is what makes the loss
per sample rather than per batch, which is what lets the studio sort your
data by loss, plot a per-sample trajectory in every grid cell, and show you
which samples produced a spike. A batch mean cannot answer that.
Config becomes editable. wandb.config is frozen after init() by
design, it describes the run. WeightsLab’s hyperparameters are live objects:
parameters = {"optimizer": {"lr": 0.001}, "batch_size": 16}
wl.watch_or_edit(parameters, flag="hyperparameters", poll_interval=1.0)
Change the learning rate in the studio and parameters changes underneath
your loop. Read hyperparameters from the dict each step rather than caching
them in locals at startup, or your loop will not see the edits.
There are no sweeps. W&B Sweeps launch many runs and compare them. WeightsLab is built around staying inside one run and steering it, edit the learning rate when the curve flattens, discard the samples that are poisoning it, keep going. If you need a sweep, keep using one; the two are not competing for the same job.
Runs are directories, not a cloud project. wandb.init(project=...)
registers a run with a server. WeightsLab’s equivalent is an experiment
directory, checkpoints, logs, and notebook.ipynb all live in it:
export WEIGHTSLAB_ROOT_LOG_DIR=~/experiments/exp1
weightslab start ~/experiments/exp1
Replaced parts¶
Weights & Biases |
WeightsLab |
|---|---|
|
|
|
|
|
A watched loss or metric that logs itself; or
|
|
|
|
|
|
|
|
|
|
|
W&B Sweeps |
No equivalent, edit hyperparameters live instead, or keep sweeping with your existing tool |
System metrics panel |
Automatic; |
Updated examples¶
Before, a typical W&B loop:
import wandb, torch.nn as nn
wandb.init(project="mnist", config={"lr": 0.001, "batch_size": 16})
config = wandb.config
model = CNN().to(device)
wandb.watch(model, log="all")
optimizer = optim.Adam(model.parameters(), lr=config.lr)
criterion = nn.CrossEntropyLoss()
for step in range(10_000):
images, targets = next(train_iter)
outputs = model(images)
loss = criterion(outputs, targets)
loss.backward(); optimizer.step(); optimizer.zero_grad()
wandb.log({"train/loss": loss.item()}, step=step)
wandb.finish()
After, the same loop on WeightsLab:
import itertools
import weightslab as wl
import torch.nn as nn
parameters = {"optimizer": {"lr": 0.001}, "batch_size": 16}
wl.watch_or_edit(parameters, flag="hyperparameters", poll_interval=1.0)
model = wl.watch_or_edit(CNN().to(device), flag="model", device=device)
optimizer = wl.watch_or_edit(
optim.Adam(model.parameters(), lr=parameters["optimizer"]["lr"]),
flag="optimizer")
train_loader = wl.watch_or_edit(
train_dataset, flag="data", loader_name="train_loader",
batch_size=parameters["batch_size"], shuffle=True, is_training=True)
criterion = wl.watch_or_edit(
nn.CrossEntropyLoss(reduction="none"), # per sample, not per batch
flag="loss", signal_name="train-loss-CE", log=True)
wl.serve(serving_grpc=True)
# Open-ended, so the studio's Pause button decides when to stop
# (see the Good Practice guide on open-ended training loops).
for step in itertools.count():
images, targets, ids = next(train_iter)
outputs = model(images)
loss_per_sample = criterion(outputs, targets, batch_ids=ids)
loss_per_sample.mean().backward(); optimizer.step(); optimizer.zero_grad()
wl.write_history(); wl.write_dataframe(); wl.keep_serving()
Three things to notice: the loop body lost its logging call, batch_ids=ids
appeared (it is what ties a loss value to the sample that produced it), and
the range became itertools.count().
Expanded UI documentation¶
In W&B you would… |
In Weights Studio |
|---|---|
Read charts on the run page |
The plots board, plus merged comparison plots and an error band showing each step’s real batch extremes |
Open a W&B Table to look at data |
The data exploration board, and you can act on what you find, not only look |
Filter a Table by a column |
Quick filters, or ask the agent in plain language |
Note a bad sample and fix it later |
Tag or discard it now; it affects the next training step |
Compare two runs side by side |
Compare within a run, merge signals onto one chart, or load weights from an earlier step straight off a plot |
Read the system metrics panel |
Resource monitoring, on the same x axis as your loss curves |