Skip to content

πŸ“Š Experiment Trackingβš“οΈŽ

The History class handles the full experiment tracking lifecycle: recording metrics, computing distributed statistics, dispatching to external backends (Weights & Biases, CSV, or custom), and producing plots and reports at the end of a run.

Recent changes

Tracker integration (v0.11): History now owns a Tracker internally. Pass backends= directly to History() instead of managing wandb separately:

# Before (deprecated)
import ezpz
ezpz.setup_wandb(project_name="my-project")
history = History()
history.update({"loss": 0.42}, use_wandb=True)

# After
history = History(project_name="my-project", backends="wandb,csv")
history.update({"loss": 0.42}, step=step)
  • use_wandb parameter on update() is deprecated β€” use backends="wandb" in the constructor
  • If an active wandb.run is detected without backends= being set, History will use it automatically with a deprecation warning
  • Backend errors are isolated β€” a failing backend logs a warning but never crashes your training run

Quick Startβš“οΈŽ

import ezpz

rank = ezpz.setup_torch()

history = ezpz.History(
    project_name="my-project",
    backends="wandb,csv",
    outdir="./outputs",
    config={"lr": 1e-4, "batch_size": 32},
)

for step in range(100):
    loss = train_step(...)
    summary = history.update({"loss": loss.item()}, step=step)
    logger.info(summary)  # "loss=0.420000"

if rank == 0:
    history.finalize(outdir="./outputs")

History() with no backend arguments works identically for local-only tracking β€” metrics are still accumulated, plotted, and saved to JSONL, but nothing is dispatched externally.

Constructorβš“οΈŽ

history = History(
    # --- Local outputs ---
    distributed_history=True,      # aggregate stats across ranks (default: auto)
    report_dir="./outputs/history", # markdown report directory
    jsonl_path="./metrics.jsonl",  # per-step JSONL log

    # --- External backends ---
    project_name="my-project",     # passed to wandb
    backends="wandb,csv",          # comma-separated or list
    config={"lr": 1e-4},           # run-level hyperparameters
    outdir="./outputs",            # directory for file-based backends
)
Parameter Default Description
distributed_history auto Compute min/max/mean/std across ranks via all-reduce
report_dir ./outputs/history Directory for the markdown report
report_enabled True Generate a markdown report on finalize()
jsonl_path None Path for per-step JSONL log. When None, defaults to <report_dir>/<run_id>.jsonl
jsonl_overwrite False Truncate existing JSONL file
project_name None Project name for backends that support it (e.g. wandb)
backends None Comma-separated string or list of backend names
config None Run-level config dict logged via the tracker on init
outdir None Output directory for file-based backends (e.g. CSV)
tracker None Inject a pre-built Tracker instance directly

Distributed auto-detectionβš“οΈŽ

Distributed aggregation is enabled when world_size <= 384 and disabled above that threshold to avoid all-reduce overhead on very large jobs. Override with:

  • History(distributed_history=False)
  • EZPZ_NO_DISTRIBUTED_HISTORY=1 or EZPZ_LOCAL_HISTORY=1 env vars

Recording Metricsβš“οΈŽ

summary = history.update(
    {"loss": 0.42, "lr": 1e-3},
    step=42,           # forwarded to tracker backends
    precision=6,       # decimal places in summary string
)

Each call to update():

  1. Appends values to the internal history dict
  2. Computes min, max, mean, std across all ranks (when distributed)
  3. Dispatches metrics to all configured backends
  4. Writes a JSONL entry to disk
  5. Returns a compact summary string suitable for the console

Console summary formatβš“οΈŽ

update() returns a column-aligned key=value(Β±std) string designed to collapse the noisy loss=… loss/mean=… loss/max=… loss/min=… loss/std=… shape into a single scannable line:

iter=180   loss=0.078(Β± 0.026) accuracy=0.984(Β±9.9e-3) dtf=0.014(Β±7.4e-4) lr=0.000059 memory=0.01/0.01GiB (0%)

Format rules (all handled by ezpz.utils.format_compact_summary):

  • For each base metric X with a sibling X/std (auto-computed when distributed history is on), the std is appended inline as X=value(Β±std). The /mean, /min, /max, /avg companions are dropped from the console β€” trackers still receive them.
  • Std values are right-aligned in a 6-char column so a row with (Β±0.070) lines up under one with (Β±5.1e-4) or (Β± 0.12).
  • Counter-like keys (iter, step, epoch, batch, idx) are bare (no (Β±std)) and left-edge padded so the next field aligns across rows: iter=8 loss=… lines up under iter=180 loss=….
  • Replicated hyperparameters (lr, momentum, weight_decay, beta1, beta2, eps, clip_grad, warmup_steps, …) are bare β€” their per-step std is always 0 across ranks, so no parenthetical and no padding gap is emitted.
  • Std formatting: 2 sig figs, fixed-point for magnitudes β‰₯ 0.01 (0.16, 0.020, 1.3), scientific for magnitudes < 0.01 with the leading exponent zero stripped (5.1e-4, not 5.1e-04). Bounded width keeps columns stable across runs.

Device memory trackingβš“οΈŽ

To include per-step GPU memory in the same line, merge in ezpz.get_memory_metrics before calling update():

import ezpz

metrics = {"loss": loss.item(), "dtf": t_forward}
metrics |= ezpz.get_memory_metrics(device, prefix="train/")
summary = history.update(metrics)

The helper returns 4 keys when supported (CUDA, XPU): mem_alloc, mem_peak_alloc, mem_reserved, mem_peak_reserved (units: GiB). Returns {} on CPU / MPS so the laptop smoke path stays clean. Opt out on supported devices via EZPZ_TRACK_MEMORY=0. The 4 memory keys are auto-collapsed in the console line into a single memory=alloc/reserved (peak%) token β€” full per-rank aggregations still flow to the trackers.

Distributed statisticsβš“οΈŽ

For each scalar metric "loss", distributed history creates:

Key Value
loss/mean Mean across all ranks
loss/max Maximum across all ranks
loss/min Minimum across all ranks
loss/std Standard deviation across ranks

Tracking throughput and MFU

To report TFLOPS and MFU (Model FLOPS Utilization) alongside loss, use ezpz.flops:

from ezpz.flops import compute_mfu, try_estimate

model_flops = try_estimate(model, input_shape=(batch, seq))
# ... per step:
history.update({
    "loss": loss.item(),
    "tflops": model_flops / dt / 1e12,
    "mfu": compute_mfu(model_flops, dt),
})

See the MFU Tracking recipe for the full pattern.

Backendsβš“οΈŽ

Backends control where metrics are dispatched when update() is called. Pass one or more backend names via backends= in the constructor.

Weights & Biasesβš“οΈŽ

history = History(
    project_name="my-project",
    backends="wandb",
    config={"lr": 1e-4, "batch_size": 32},
)

The wandb backend:

  • Rank 0 initializes a real run (online/offline per WANDB_MODE)
  • Rank != 0 gets mode="disabled" β€” no network calls, no duplicate runs
  • Resolves project name from: argument > WB_PROJECT > WANDB_PROJECT > WB_PROJECT_NAME env vars > script-derived default
  • Auto-logs system info (hostname, torch version, ezpz version)
  • Logs metrics on each update(), uploads training history table on finalize()
  • Logs matplotlib plots as image artifacts

Set WANDB_DISABLED=1 or backends="none" to disable.

MLflowβš“οΈŽ

Built-in backend that logs to an MLflow Tracking server or local filesystem.

Standalone setup

For one-call MLflow initialization outside of History, use ezpz.setup_mlflow() β€” it mirrors ezpz.setup_wandb():

run = ezpz.setup_mlflow(project_name="my-project", config={"lr": 1e-4})
# Enable MLflow tracking
EZPZ_TRACKER_BACKENDS=mlflow ezpz launch python3 -m ezpz.examples.vit

# Use alongside wandb
EZPZ_TRACKER_BACKENDS=wandb,mlflow ezpz launch python3 -m ezpz.examples.vit

Features:

  • Automatic time-series: Step counter auto-increments so MLflow shows line charts (not bar charts) by default
  • System metrics: CPU/GPU/memory usage logged automatically via mlflow.enable_system_metrics_logging() (requires psutil)
  • Environment params: Hostname, device, world size, git branch, torch version, etc. logged under ezpz.* prefix
  • User config: Logged under config.* prefix (nested dicts flattened with dot-separated keys)
  • Metric grouping: When distributed stats are present (loss/mean, /min, /max, /std), the raw per-rank value is renamed to loss/local so MLflow groups them under a collapsible loss/ section
  • Artifact uploads: On finalize(), JSONL logs, markdown reports, plots, and datasets are uploaded as run artifacts
  • Rank-aware: Only rank 0 creates a run; all other ranks are silent no-ops

Experiment name resolution (first match wins):

  1. project_name argument
  2. MLFLOW_EXPERIMENT_NAME env var
  3. WB_PROJECT / WANDB_PROJECT / WB_PROJECT_NAME env vars
  4. Auto-derived from script: ezpz.{parent}.{stem} (e.g. ezpz.examples.vit)

Authentication:

Env var Auth method
MLFLOW_TRACKING_TOKEN Bearer token (MLflow native)
AMSC_API_KEY X-API-Key header (for AMSC servers)

Credentials are loaded automatically from dotenv files:

  1. ~/.amsc.env β€” user-level credentials (loaded first)
  2. Project .env β€” project-level overrides (loaded second)

Example ~/.amsc.env:

AMSC_API_KEY=your-api-key-here
MLFLOW_TRACKING_URI=https://mlflow.american-science-cloud.org
MLFLOW_TRACKING_INSECURE_TLS=true

python-dotenv not installed?

Set the variables directly in your shell or job script. Dotenv loading is a convenience, not a requirement.

CSVβš“οΈŽ

history = History(backends="csv", outdir="./logs")

history.update({"loss": 0.5, "lr": 1e-4})
history.update({"loss": 0.3, "lr": 1e-4, "grad_norm": 0.8})  # columns auto-extend

The CSV backend writes to the outdir:

File Content
metrics.csv One row per update() call, columns auto-extend as new keys appear
config.json Merged config from the config= constructor argument
training_history.csv Written by log_table() on finalize()
  • Rank 0 only β€” non-rank-0 processes buffer rows in memory but skip all file I/O

Noneβš“οΈŽ

Pass backends="none" (or set EZPZ_TRACKER_BACKENDS=none) to disable tracking entirely. Returns a NullTracker where all methods are no-ops.

Multiple Backendsβš“οΈŽ

Combine backends to dispatch everywhere at once:

history = History(
    project_name="my-project",
    backends="wandb,mlflow,csv",
    outdir="./outputs",
    config={"lr": 1e-4, "batch_size": 32},
)

Every update() call fans out to all backends.

Error Isolationβš“οΈŽ

Backend errors are isolated β€” if one backend fails during log(), log_config(), or finish(), the others still receive the call. A warning is logged but your training run is never interrupted by a tracking failure. This means a flaky network connection to your MLflow server won't crash a multi-day training job.

Custom Backendsβš“οΈŽ

Register your own backend by subclassing TrackerBackend:

from ezpz.tracker import TrackerBackend, register_backend

class MyBackend(TrackerBackend):
    name = "my_backend"

    def __init__(self, **kwargs):
        self.entries = []

    def log(self, metrics, step=None, commit=True):
        self.entries.append(metrics)

    def log_config(self, config):
        pass

    def finish(self):
        pass

register_backend("my_backend", MyBackend)

# Now usable in History
history = History(backends="my_backend,csv", outdir="./logs")

Override the optional methods (log_table, log_image, watch) for richer functionality.

Accessing Backend-Specific Featuresβš“οΈŽ

Use the tracker property to reach backend-specific APIs:

# Attach wandb gradient tracking
wb = history.tracker.get_backend("wandb")
if wb is not None:
    wb.watch(model, log="all")

# Access the underlying wandb.Run
if history.tracker.wandb_run is not None:
    history.tracker.wandb_run.summary["best_loss"] = 0.01

# Log an image (wandb only)
history.tracker.log_image("sample", "outputs/sample.png", caption="Epoch 10")

Finalizationβš“οΈŽ

result = history.finalize(
    outdir="./outputs",
    run_name="my-experiment",
    warmup=0.05,       # drop first 5% of samples
    num_chains=128,     # chains in ridge plots
)

finalize() produces:

Output Path
xarray Dataset(s) {outdir}/train.h5, {outdir}/eval.h5 (one per group)
Matplotlib plots {outdir}/plots/mplot/{group}/*.png
Terminal plots {outdir}/plots/tplot/{group}/*.txt
Markdown report {outdir}/report.md
Metrics JSONL {outdir}/metrics.jsonl
Metrics CSV {outdir}/metrics.csv (when csv backend is active)
JSON log symlink {outdir}/{timestamp}-rank0.jsonl β†’ logs/...

All output is co-located in {outdir}:

  • The CSV backend is automatically redirected to {outdir} so metrics.csv, config.json, and training_history.csv land alongside plots and reports.
  • A symlink to the structured JSON log file is created in {outdir} so you don't have to hunt for it under logs/.

Grouped outputβš“οΈŽ

When metrics use prefixed keys (e.g. "train/loss", "eval/acc"), finalize() produces separate datasets, plots, and output directories per group. Each group has its own draw dimension β€” no NaN padding between groups with different numbers of steps.

outputs/my-experiment/
β”œβ”€β”€ train.h5                    # train metrics only (101 steps)
β”œβ”€β”€ eval.h5                     # eval metrics only (1250 steps)
β”œβ”€β”€ plots/
β”‚   β”œβ”€β”€ mplot/
β”‚   β”‚   β”œβ”€β”€ train/              # train/ matplotlib plots
β”‚   β”‚   └── eval/               # eval/ matplotlib plots
β”‚   └── tplot/
β”‚       β”œβ”€β”€ train/              # train/ terminal plots
β”‚       └── eval/               # eval/ terminal plots
β”œβ”€β”€ report.md
└── metrics.jsonl

Return value: When multiple groups exist, finalize() returns dict[str, xr.Dataset] mapping group prefix to its dataset. With a single group (or unprefixed metrics), it returns a single xr.Dataset for backward compatibility.

result = history.finalize(outdir="./outputs")

# Grouped (train/ + eval/ prefixes):
result["train"]  # xr.Dataset with train metrics
result["eval"]   # xr.Dataset with eval metrics

# Unprefixed (single group):
result.data_vars  # xr.Dataset directly

It also uploads the training history table to any active backends and calls tracker.finish() to flush and close all backend connections.

Terminal plot output

finalize() generates text-based plots directly in the terminal. Each metric gets individual plots, and distributed runs include min/max/mean/std variants:

                  accuracy                             accuracy/min
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
0.984─                   β–Ÿβ––  β––β–Œβ–„ β–Ÿβ– β–™β–Œβ”‚0.969─         -- -- -----------------β”‚
0.921─      β–— β–„β–—β–™β–„β–™β–™β–Ÿβ–ˆβ–„β–„β–ˆβ–ˆβ–ˆβ–Ÿβ–Ÿβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–›β–ˆβ–œβ–ˆβ”‚0.902─ - --------------------- -------β”‚
     β”‚ β–— β–– β–Ÿβ–ˆβ–Ÿβ–Œβ–ˆβ–œβ–ˆβ–ˆβ–ˆβ–›β–ˆβ–›β–›β– ▝▛▀▀  β–β–Œβ–Œβ–€ β–€β”‚0.770─ ------   -                     β”‚
0.857─ ▐ β–™β–ˆβ–œβ–β–˜ β–˜β–      β–Œ              β”‚0.703─---                             β”‚
0.793─ β–β–Ÿβ–ˆβ–ˆ                           β”‚0.570─--                              β”‚
     β”‚β–—β–β–›β–ˆ                            β”‚     β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜
0.729β”€β–β–Ÿ ▝                            β”‚     1.0    49.2    97.5   145.8 194.0
     β”‚β–ˆβ–œ                              β”‚accuracy/min        iter
0.665β”€β–œ                               β”‚                accuracy/std
0.602─▐                               β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜0.078─   *                            β”‚
     1.0    49.2    97.5   145.8 194.0 0.065─ *****                          β”‚
accuracy            iter               0.039─******** **         *      *    β”‚
                accuracy/mean          0.026─************************ *******β”‚
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”0.000─  ******************************β”‚
0.977─                   Β·Β·   Β·   Β·Β·Β·Β·β”‚     β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜
0.916─         Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·β”‚     1.0    49.2    97.5   145.8 194.0
     β”‚     Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β· Β·Β·Β·Β·  Β· Β· Β·Β·β”‚accuracy/std        iter
0.855─ Β· Β·Β·Β·Β·Β·Β· Β· Β·    Β·              β”‚                accuracy/max
0.795─ Β·Β·Β·Β· Β·                         β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚ Β·Β·Β·                            β”‚0.984─             + + +++++++++++++++β”‚
0.734─ Β·Β·                             β”‚0.930─ + ++++++++++++++++++++++ ++++++β”‚
0.674─··                              β”‚0.820─ ++++ ++  +                     β”‚
     β”‚Β·Β·                              β”‚0.766─++                              β”‚
0.613─·                               β”‚0.656─++                              β”‚
     β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜     β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜
     1.0    49.2    97.5   145.8 194.0      1.0    49.2    97.5   145.8 194.0
accuracy/mean       iter               accuracy/max        iter

Combined summary with all statistics overlaid (Β· mean, - min, + max, β–ž raw):

     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
0.984─ ++ accuracy/max                           β–—β–Œ        β–—β–Œ  +   β–—  β–— +β–Ÿ+β–— β”‚
     β”‚ -- accuracy/min                          β––β–β–Œβ–Ÿ     β–Ÿ β–β–Œ β–—+  ▗▐ β–—β–ˆ Β·β–ˆβ–β–ˆ β”‚
     β”‚ Β·Β· accuracy/mean            β––+  β–Ÿβ–Ÿ    + β–β–™β–˜β–Œβ–ˆ+β–—β–Œ+ β–Œβ–Œβ–β–Œβ––β–ˆβ–Ÿβ–Œβ–„β–β–β–šβ–›β–ˆβ–—β–žβ–ˆβ–β–ˆΒ·β”‚
     β”‚ β–žβ–ž accuracy       Β·+β–—β–Œ β–„+β–—β–Œβ–β–Œβ–— β–Ÿβ–ˆβ–ˆΒ·+Β·β–—Β·β–Ÿβ–β–ˆΒ·β–›β–β–Ÿβ–Ÿβ–Œβ–—β–Œβ–Œβ–™β–ˆβ–Œβ–ˆβ–ˆβ–Œβ–œβ–ˆβ–›β–ŸΒ·β–Œβ–β–ˆβ–Œβ–˜β–œβ–Œβ–›β”‚
0.915─         +   +β–—β–Œ  β–—β–œΒ·β–Ÿβ–Œβ–Ÿβ–β––β–β–Œβ–›β–Œβ–ˆβ–Ÿβ–ˆβ–ˆβ–ˆβ–Ÿβ–ˆβ–β–β–žΒ·β–ˆβ–œ-β–˜β–-β–ˆβ–™β–ˆβ–ˆβ–Œβ–ˆβ–β–β–β–Œβ–˜Β·β–β–ŒΒ·Β·β–Œβ–β–ˆβ–Œ β–β–Œβ–Œβ”‚
     β”‚         + β–—β–šβ–—β–β–Œβ–žβ––β–Œβ–Β·β–ˆβ–œβ–ˆβ–β–™β–ˆβ–šβ–Œβ–™β–ˆβ–Œβ–˜β–ˆβ–ˆβ–ˆβ–›β–Ÿβ–Ÿβ–Œ-▝  ·▝ β–ˆβ–œβ–β–β–˜β–œΒ·     β–β–Œ--β–Œ β–˜β–˜ β–β–Œβ–Œβ”‚
     β”‚   β–—   β–– +β–—β–ˆβ–β–ˆβ–β–™β–Œβ–™β–˜-β–Œβ–›Β·β–ˆ-β–ˆβ–ˆ β–˜β–˜β–€β–Œ- β–€ β–˜ β–ˆ     -  β–œ    Β·-     β–β–Œ  β–˜     Β· β”‚
     β”‚   β–ˆ  β–β–Œβ–—+β–β–œβ–β–Œβ–ˆβ–ˆβ–ŒΒ·--β–™β–˜Β·β–ˆ-▝▝         - β–ˆ     -                          β”‚
     β”‚   β–ˆ+ β–β–™β–ˆβ––β–ŒΒ·β–β–Œβ–˜β–β–˜   ▝ Β·β–ˆ              β–œ                                β”‚
0.846─   β–ˆ+ β–β–ˆβ–ˆβ–™β–Œ-Β· Β· -     -▝                                               β”‚
     β”‚  +β–ˆβ–—β–Œβ–žβ–ˆβ–ˆβ–ˆβ–Œ - Β· -     -                                                β”‚
     β”‚ ++β–ˆβ–β–β–Œβ–ˆβ–›β–›β–Œ - -       -                                                β”‚
     β”‚ ++β–ˆβ–Ÿβ–β–Œβ–ˆ--  - -                                                        β”‚
0.777─ ++β–ˆβ–›Β·-β–ˆ      -                                                        β”‚
     β”‚ β––Β·β–ˆβ–˜- β–ˆ                                                               β”‚
     β”‚β–β–ŒΒ·β–ˆΒ·  β–ˆ                                                               β”‚
     β”‚β–β–Œβ–—β–œ-  ▝                                                               β”‚
0.708β”€β–β–Œβ–Ÿβ–-                                                                  β”‚
     β”‚β–Ÿβ–ˆβ–Œβ–-                                                                  β”‚
     β”‚β–ˆβ–ˆΒ·β–-                                                                  β”‚
     β”‚β–ˆβ–ˆΒ·                                                                    β”‚
     β”‚β–β–ˆ-                                                                    β”‚
0.639β”€Β·β–ˆ-                                                                    β”‚
     β”‚Β·β–ˆ                                                                     β”‚
     β”‚-β–œ                                                                     β”‚
     β”‚-                                                                      β”‚
0.570─-                                                                      β”‚
     β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”˜
     1.0              49.2             97.5              145.8          194.0

Histograms for each statistic:

            accuracy/mean hist                      accuracy/max hist
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
80.0─                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚77.0─                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚
    β”‚                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚    β”‚                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚
66.7─                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚64.2─                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚
53.3─                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚51.3─                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚
    β”‚                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚    β”‚                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚
40.0─                       β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β”‚38.5─                       β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚
    β”‚                       β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚    β”‚                       β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚
26.7─                       β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚25.7─                       β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚
13.3─                β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚12.8─                    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚
    β”‚             β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚    β”‚             β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚
 0.0β”€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚ 0.0β”€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚
    β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜    β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜
   0.60    0.70    0.79    0.89   0.99    0.64    0.73    0.82    0.91   1.00
             accuracy/min hist                      accuracy/std hist
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
83.0─                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚67.0─   β–ˆβ–ˆβ–ˆβ–ˆ                          β”‚
    β”‚                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚    β”‚   β–ˆβ–ˆβ–ˆβ–ˆ                          β”‚
69.2─                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚55.8β”€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                          β”‚
55.3─                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚44.7β”€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                          β”‚
    β”‚                          β–ˆβ–ˆβ–ˆβ–ˆ   β”‚    β”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                          β”‚
41.5─                          β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚33.5β”€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                       β”‚
    β”‚                       β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚    β”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                       β”‚
27.7─                       β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚22.3β”€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                    β”‚
13.8─                       β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚11.2β”€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                    β”‚
    β”‚                β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚    β”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ             β”‚
 0.0β”€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚ 0.0β”€β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ”‚
    β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜    β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜
   0.55    0.66    0.77    0.88   0.99   -0.003   0.018   0.039   0.060 0.082

StopWatch: Timing Context Managerβš“οΈŽ

from ezpz.history import StopWatch

with StopWatch("forward pass", wbtag="timing/forward"):
    output = model(batch)

Logs elapsed time to the logger and optionally to W&B. Useful for profiling individual phases within your training loop.

Environment Variablesβš“οΈŽ

Variable Effect
EZPZ_NO_DISTRIBUTED_HISTORY Disable distributed aggregation
EZPZ_LOCAL_HISTORY Alias for above
EZPZ_TRACKER_BACKENDS Fallback backend list when backends arg is None
EZPZ_TRACK_MEMORY Set to 0 to suppress per-step device memory keys emitted by ezpz.get_memory_metrics. Default: 1 (enabled on CUDA/XPU, no-op on CPU/MPS)
WANDB_MODE Controls wandb mode (disabled, offline, online)
WANDB_DISABLED Set to 1 to disable wandb entirely
WB_PROJECT / WANDB_PROJECT Default wandb project name
EZPZ_TPLOT_MARKER Marker style for terminal plots (braille, fhd, hd)
EZPZ_TPLOT_TYPE Default plot type (line, hist)

See Alsoβš“οΈŽ