Log batch-level metrics to W&B, not just per-epoch summaries
CI / Format (ruff format) (push) Successful in 26s
CI / Lint (ruff check) (push) Successful in 27s
CI / Sync project version with tag (push) Has been skipped
CI / Lint (ruff check) (pull_request) Successful in 25s
CI / Type check (ty) (push) Successful in 28s
CI / Format (ruff format) (pull_request) Successful in 31s
CI / Sync project version with tag (pull_request) Has been skipped
CI / Type check (ty) (pull_request) Successful in 31s
CI / Tests (push) Successful in 1m33s
CI / Tests (pull_request) Successful in 1m29s

giant train --wandb now also logs loss/grad_norm/lr every N optimizer
steps (--wandb-log-every, default 50) so W&B shows within-epoch trends,
not just one point per epoch. Both share global_step as a single
monotonic step axis (wandb.Run.log requires step to never decrease
across calls), which also fixes global_step previously only advancing
in wgan mode.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 16:20:05 +02:00
parent a05837f918
commit 29459ab1f7
4 changed files with 39 additions and 2 deletions
+9
View File
@@ -415,6 +415,14 @@ def train(
Optional[str],
typer.Option("--wandb-run-name", help="W&B run name (default: out_dir name)"),
] = None,
wandb_log_every: Annotated[
Optional[int],
typer.Option(
"--wandb-log-every",
help="Log batch-level loss/grad_norm/lr to W&B every N optimizer "
"steps (default: 50); per-epoch metrics always log in full",
),
] = None,
) -> None:
"""Train the GIANT surrogate model."""
batch_size_auto = False
@@ -455,6 +463,7 @@ def train(
"wandb": wandb,
"wandb_project": wandb_project,
"wandb_run_name": wandb_run_name,
"wandb_log_every": wandb_log_every,
}.items()
if v is not None
}
+6
View File
@@ -42,6 +42,12 @@ DEFAULT_CONFIG: dict = {
"wandb": False,
"wandb_project": "giant",
"wandb_run_name": "",
# Batch-granularity metrics (loss/grad_norm/lr) are logged every N
# optimizer steps, not every batch — a single epoch can be tens of
# thousands of steps (see steps_per_epoch above), and logging every
# one of them would flood the run with points the UI has to downsample
# anyway. Per-epoch metrics (the metrics.csv row) always log in full.
"wandb_log_every": 50,
},
"model": {
"hidden_dim": 256,
+1
View File
@@ -262,4 +262,5 @@ def run_train_job(
use_wandb=t.get("wandb", False),
wandb_project=t.get("wandb_project", "giant"),
wandb_run_name=t.get("wandb_run_name", ""),
wandb_log_every=t.get("wandb_log_every", 50),
)
+23 -2
View File
@@ -323,6 +323,7 @@ def train(
use_wandb: bool = False,
wandb_project: str = "giant",
wandb_run_name: str = "",
wandb_log_every: int = 50,
) -> None:
out_dir = Path(out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
@@ -539,7 +540,6 @@ def train(
lambda_nsec,
lambda_s2,
)
global_step += 1
if stats["did_g_step"]:
lr_sched.step()
if ema_decay > 0:
@@ -609,6 +609,23 @@ def train(
f"loss={ema_loss:.4f} gnorm={ema_grad_norm:.3f}", refresh=False
)
global_step += 1
if (
wandb_run is not None
and wandb_log_every > 0
and global_step % wandb_log_every == 0
):
wandb_run.log(
{
"batch/epoch": epoch,
"batch/loss": batch_loss,
"batch/loss_ema": ema_loss,
"batch/grad_norm": batch_grad_norm,
"batch/lr": optimizer.param_groups[0]["lr"],
},
step=global_step,
)
if shutdown.requested:
break
bar.close()
@@ -747,7 +764,11 @@ def train(
metrics_writer.writerow(metrics_row)
metrics_file.flush()
if wandb_run is not None:
wandb_run.log(metrics_row, step=epoch)
# Shares the same monotonic step axis as the per-batch
# `batch/*` logs above (global_step) rather than `epoch`,
# since a wandb run's `step` argument across `log()` calls
# must never decrease.
wandb_run.log(metrics_row, step=global_step)
ckpt: dict = {
"model": stage1_model.state_dict(),