Skip to content

Preserve the supervised-YOLO baseline training record (#51) - #77

Merged
jonfroehlich merged 6 commits into
mainfrom
chore/yolo-baseline-record
Jul 28, 2026
Merged

Preserve the supervised-YOLO baseline training record (#51)#77
jonfroehlich merged 6 commits into
mainfrom
chore/yolo-baseline-record

Conversation

@jonfroehlich

@jonfroehlich jonfroehlich commented Jul 28, 2026

Copy link
Copy Markdown
Member

What

Preserves the supervised-YOLO baseline experiment (issue #51) as a durable,
reproducible record in the repo, and surfaces its in-flight status in the central
results doc.

Adds scripts/model_comparison/yolo_baseline/:

  • README.md — config grid, the training-instability finding, and a provenance block
    (toolchain, dataset + prep params, hyperparameters, Slurm job IDs, dates).
  • runs/<config>/{results.csv,args.yaml} — per-epoch curves + resolved config for all six
    configs (y11x_tiles has args.yaml only — it was dropped before epoch 1).
  • plot_training_curves.py + figures/*.png — four diagnostic figures, regenerable from
    the committed CSVs alone (CPU-only, no network, no checkpoints).

Plus a "supervised baseline in progress" note in docs/model_comparison.md so the
baseline's absence from the results tables reads as not yet run, not withheld.

Why

Per the "record scientific progress in GitHub" standing rule (CLAUDE.md): the runs'
outputs live on /gscratch/scrubbed, which auto-purges after ~21 idle days. This captures
the perishable record before it's lost, following the repo convention (curated small-text
results tracked; weights/logs stay out).

The finding — and a correction to an earlier version of this PR

Fresh per-epoch data pulled from the live runs falsified three claims this branch
previously carried
. They are corrected in the record, and the correction changes what
should happen next:

previously claimed what the data shows
"YOLO26 and the tiles configs trained cleanly" All five remaining configs hit the same collapse. The tiles runs were two epochs into it when that was written.
"Small-batch BatchNorm/EMA instability" Refuted by the grid. y11l_pano and y26_pano share batch 4 / imgsz 1280 and both collapsed, then both recovered. Batch spans 2/4/6 and imgsz 1024/1280 with no separation.
"The collapsed runs hold a non-reportable epoch-1 pretrained artifact, not worth keeping" By epoch 1 the model has trained on all 150k images — the score is real. Those best.pt files are genuine best-val checkpoints and currently the only supervised baseline we have.

What actually happens: validation mAP peaks at epoch 1, collapses at epoch 3, and
recovers as the LR decays — in every config. Epoch 3 is exactly where the warmup ramp
peaks (lr/pg0 0.010 → 0.029, warmup_epochs=3). Training loss falls straight through
the collapse while val cls_loss explodes 1.2 → 8.2, with no NaN or AMP error. The ckpt
preemptions are scattered across epochs 2–13 and do not line up with it. optimizer=auto
resolves to MuSGD(lr=0.01, momentum=0.9) in every job.

So the clean test is a lower peak LR or longer warmup, not a larger batch — which is
what #70 currently proposes. Worth re-scoping before that rerun burns another week.

The failure signature is a recall collapse at sustained high precision: the model stops
firing rather than starting to guess. A checkpoint caught inside the dip would understate
recall catastrophically.

Overlap with #76 — resolved

#76 (merged) carries the canonical training code at scripts/model_comparison/run_yolo_train.slurm
and the repo-root hyak_yolo_runbook.sh. This branch's duplicate snapshots under
yolo_baseline/ are deleted here, so merging leaves exactly one copy of each. The
provenance those snapshots preserved is in git history.

Scope / notes for review

  • No benchmark-city eval numbers yet — these results.csv values are the internal YOLO
    val-split proxy (training health), not the reported baseline. Benchmark eval is pending, and
    is now unblocked: every run's best.pt is already on disk.
  • Weights are not committed (*.pt/*.pth now gitignored). The README has a TODO for the
    durable URL.
  • Any benchmark number from today's checkpoints is a lower bound — they are undertrained
    relative to a stable schedule, and the README says so.

Related: #51 (parent), #70, #71, #72.


🤖 Generated with Claude Code

jonfroehlich and others added 2 commits July 28, 2026 14:08
The supervised-YOLO baseline runs (issue #51) trained on Hyak, whose outputs
live on /gscratch/scrubbed and auto-purge after ~21 idle days. Capture the
perishable, reproducible record in git before it's lost, following the repo's
existing convention (curated small-text results tracked; bulk/binary/regenerable
artifacts stay out — see .gitignore).

Adds scripts/model_comparison/yolo_baseline/:
- run_yolo_train.slurm + hyak_yolo_runbook.sh: the training code (previously
  only on the cluster), so the runs are reproducible.
- runs/<config>/{results.csv,args.yaml}: per-epoch curves + resolved config for
  all six configs (y11x_tiles has args only — it was dropped before epoch 1).
- README.md: config grid, metrics table, the training-instability finding, and a
  provenance block (toolchain, dataset + prep params, hyperparameters, Slurm job
  IDs, dates).

Finding: the two YOLO11 pano configs (y11x_pano, y11l_pano) collapsed — val mAP
fell to ~0 for 4+ epochs while training loss stayed healthy and no NaN/AMP error
occurred, a validation-side collapse consistent with BatchNorm/EMA instability at
small physical batch (2-4) and high input resolution (1280). YOLO26 and the tiles
runs were unaffected. Weights are intentionally not committed; healthy best.pt
files go to durable external storage (URL to be recorded in the README).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per the "record scientific progress in GitHub" standing rule (CLAUDE.md): make the
in-flight supervised baseline discoverable from docs/model_comparison.md rather than
only in the sub-directory README. Adds a short "supervised baseline in progress" note
to the coverage-matrix section — the collapse (negative result) and the dropped/pending
configs (gaps) are stated explicitly so the baseline's absence from the results tables
reads as "not yet run", not "withheld".

Also flags in the README that the launcher/runbook copies are as-run snapshots to be
reconciled with the canonical Phase-0 harness (#51) when it merges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jonfroehlich added a commit that referenced this pull request Jul 28, 2026
The architecture-vs-data ablation for issue #51: does a generic detector
trained on the RampNet dataset also beat the zero-shot field, or is
RampNet's keypoint architecture doing the work?

- YoloDetector + yolo_results_to_boxes in detectors.py, plus the `yolo`
  branch in build_detector. Identity is the weights file stem, so the
  detection cache key is machine-independent; signature() hashes the
  weights bytes, so a retrain busts the cache. It emits scored boxes, so
  YOLO gets AP, PR curves and --sweep like the other open detectors.
- compare.py: --yolo-model / --yolo-conf (0.05 floor) / --yolo-iou /
  --yolo-imgsz. requirements-vlm.txt: ultralytics.
- prepare_yolo_dataset.py: tiles+pano dataset builder with pluggable
  box-size strategies and deterministic background thinning.
- run_yolo_{data,prep,train}.slurm and hyak_yolo_runbook.sh (repo root,
  beside hyak_qwen_runbook.sh).
- +10 tests.

The training record these scripts produced is PR #77, kept separate so
this PR is reviewable as code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jonfroehlich

Copy link
Copy Markdown
Member Author

Heads-up from #76 (the code half of #51), which was opened concurrently — the two
PRs now touch disjoint files, but there's one conflict git won't flag because the
paths differ.

This PR adds scripts/model_comparison/yolo_baseline/run_yolo_train.slurm and
.../yolo_baseline/hyak_yolo_runbook.sh. #76 adds the canonical
scripts/model_comparison/run_yolo_train.slurm and the repo-root
hyak_yolo_runbook.sh. Merging both leaves two copies of each, and:

  • the two run_yolo_train.slurm files are byte-identical;
  • the two runbooks are not — the copy here is three lines older. It predates
    the ARG_MAX fix to the dataset count display (ls "$DATA/$s"/*.jpg | wc -l
    overflows on the ~150k-file train split, so ls errors to suppressed stderr and
    it prints a false train: 0 on a healthy download).

The difference is display-only and cannot affect the recorded runs, so the record
stands either way. But shipping the pre-fix text as a second live copy is a trap
for the next person who runs the runbook.

Suggested resolution for whichever merges second: keep the canonical copies, drop
the two under yolo_baseline/, and point this README at them — git history
already preserves both texts, and the README can state which version the recorded
runs used.

jonfroehlich and others added 2 commits July 28, 2026 14:42
- Config table: record the as-run batches from args.yaml (y11x_tiles 3->12
  across its two attempts, y26_tiles 6) -- batch is the load-bearing variable
  in the collapse hypothesis, so the table must be exact
- Provenance: box-size corrected to pitch/1.8m per run_yolo_prep.slurm's as-run
  defaults, with a TODO to confirm from the klone prep log before scratch
  purges; add y11x_tiles's inferred job ID 37745359
- Drop the run_yolo_train.slurm / hyak_yolo_runbook.sh snapshots: the canonical
  copies land via #76 (launcher byte-identical; runbook drift was display-only,
  documented in #76). Removes the two-live-copies trap at merge time.
- .gitignore: enforce the stated weights-out policy structurally (*.pt, *.pth)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fresh per-epoch data from the live Hyak runs falsifies two claims this record
previously made, and the correction changes what should happen next.

What was wrong:

- "YOLO26 and the tiles configs trained cleanly" — they did not. All five
  remaining configs show the same collapse; the tiles runs were simply only
  two epochs into it when the record was written.
- "Small-batch BatchNorm/EMA instability" — refuted by the grid itself.
  y11l_pano and y26_pano share batch 4 and imgsz 1280 and both collapsed,
  then both recovered. Batch spans 2/4/6 and imgsz 1024/1280 with no
  separation between healthy and sick.
- "The collapsed runs hold only a non-reportable epoch-1 pretrained artifact,
  not worth keeping" — wrong on both counts. By epoch 1 the model has trained
  on all 150k images; the score is real. Those best.pt files are genuine
  best-val checkpoints and currently the only supervised baseline we have.

What the data actually shows: validation mAP peaks at epoch 1, collapses at
epoch 3, and recovers as the LR decays, in every config. Epoch 3 is exactly
where the warmup ramp peaks (lr/pg0 0.010 -> 0.029, warmup_epochs=3). Training
loss falls straight through the collapse while val cls_loss explodes 1.2 -> 8.2,
with no NaN or AMP error — an optimization instability, not a crash or a data
fault. The ckpt preemptions are scattered across epochs 2-13 and do not line up
with it. optimizer=auto resolves to MuSGD(lr=0.01, momentum=0.9) in every job.

So the clean test is a lower peak LR or longer warmup, not a larger batch —
which is what #70 currently proposes.

Adds plot_training_curves.py and four figures. The script reads only the
committed results.csv files, so every figure regenerates from a clean checkout
with no GPU, network, or checkpoints.

Recorded per the "record scientific progress in GitHub" rule: the runs live on
/gscratch/scrubbed, which auto-purges.

Refs #51, #70, #71, #72.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jonfroehlich added a commit that referenced this pull request Jul 28, 2026
- Runbook: default ACCOUNT to ckpt-makelab (run_yolo_train.slurm pins the ckpt
  partition, which the old gpu-l40s default cannot submit to) and pin per-config
  BATCH in the train stage -- unpinned, jobs ran batch=-1 (auto), defeating the
  identical-LR-schedule rationale the .slurm header states
- Runbook: align BOX_SIZE default with the as-run prep (pitch, ramp 1.8m, per
  run_yolo_prep.slurm) and point the multi-hour data/prep stages at their
  compute-node sbatch wrappers (login nodes reap heavy processes)
- prepare_yolo_dataset.py: stamp the full prep config into data.yaml as comments
  -- box size et al. are train-only knobs no downstream args.yaml records, and
  exactly that gap made the #77 provenance question unanswerable from disk;
  regression test added (188 pass)
- prepare_yolo_dataset.py: render each overlay view once, not twice; --overlay-n
  help now says PER SPLIT
- detectors.py: hoist the hashlib import to module top

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jonfroehlich

Copy link
Copy Markdown
Member Author

Review fixes pushed in 562277a:

  • Config table: batches corrected to the as-run args.yaml values (y11x_tiles 3→12 across its two attempts, y26_tiles 6) — batch is the load-bearing variable in the collapse hypothesis, so the table must be exact.
  • Provenance: box-size corrected to pitch + ramp 1.8 m per run_yolo_prep.slurm's as-run defaults; the earlier fixed:0.03 matched the runbook default, not the prep launcher. Needs a one-command confirmation on klone before scratch purges: head logs/yolo_prep_*.out prints box=... ramp=....
  • Added y11x_tiles's job ID 37745359, inferred from the one gap in the otherwise-contiguous six-job block (its batch-12 resubmit ID went unrecorded; sacct can recover both).
  • Dropped the run_yolo_train.slurm / hyak_yolo_runbook.sh snapshots — the canonical copies land via Add the supervised YOLO baseline to the comparison harness (#51) #76 (launcher byte-identical; runbook drift was display-only, documented there). This removes the two-live-copies trap at merge time.
  • .gitignore now enforces the stated weights-out policy structurally (*.pt, *.pth).

Data cross-check during review: every number in the status table matched the committed CSVs at snapshot time (epoch counts, ep1 values, current mAP50, the P≈0.94/R≈0.03 flicker, the 4 straight literal-0 epochs, the train-loss trends). One observation about collapse timing is on #70.

🤖 Generated with Claude Code

jonfroehlich added a commit that referenced this pull request Jul 28, 2026
Merging the canonical YOLO harness code. Its `scripts/model_comparison/run_yolo_train.slurm` and repo-root `hyak_yolo_runbook.sh` are the copies of record; the duplicate snapshots under `yolo_baseline/` are removed by #77, which lands next.
jonfroehlich and others added 2 commits July 28, 2026 14:48
- Box-size: pitch + ramp 1.8m CONFIRMED from the session run notes (prep job
  37677130, 2026-07-25; overlay QA rejected fixed:0.03, gps fell back on
  169/200 smoke panos; output counts verified on scratch) -- removes the
  TODO(confirm) added in 562277a
- y11x_tiles job IDs now exact: 37745359 original + 37809205 batch-12
  resubmit; job<->config map was log-verified during the 2026-07-27
  preemption check, not inferred

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jonfroehlich

Copy link
Copy Markdown
Member Author

Follow-up: the box-size confirmation flagged above is resolved without needing the klone log — the session run notes record it definitively (prep job 37677130, 2026-07-25: pitch + ramp 1.8 m, chosen after overlay QA; fixed:0.03 rejected as too small, gps fell back on 169/200 smoke panos). The y11x_tiles job IDs are also now exact rather than inferred: 37745359 original, 37809205 batch-12 resubmit. Both upgraded in 34bafcd.

🤖 Generated with Claude Code

@jonfroehlich
jonfroehlich merged commit 44235ef into main Jul 28, 2026
2 checks passed
@jonfroehlich
jonfroehlich deleted the chore/yolo-baseline-record branch July 28, 2026 22:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant