Drop barrel, add held-out stop-sign distance, add the GPU runbook
Aryan Sethi+1: Claude Opus 5 (1M context)
5d449ff
Barrel is out of the pipeline: schema carries three classes, the Argoverse 2
extractor no longer maps CONSTRUCTION_BARREL, and depth routing follows. AV2
barrels are still read to locate the road surface for the per-log camera-height
fit -- they rest on the road and there are many of them -- but they never become
labels.
Distance is now verified on data the model never saw: 20 further Argoverse 2
logs, 2,431 frames, 6,332 objects, scored with the existing checkpoint rather
than retraining. The pinhole estimator comes out far better there than on
nuScenes -- 0.21 m median error under 10 m, 0.79 m overall at 5% off, 98% within
25% -- because AV2 calibration is derived per log and its camera has higher
angular resolution, while the nuScenes figure was dominated by one scene with a
road gradient.
Stop-sign distance is measured for the first time: 21 detections, 9.22 m median,
18% off. Thin, and reported as such. The count is a data limit rather than a
threshold one -- AV2 stop signs sit …
26 days ago
Make the gradient-norm probe safe to leave on
Aryan Sethi+1: Claude Opus 5 (1M context)
be3d1cc
No architectural change was involved in getting the gradient norm -- the model,
loss, optimiser, data pipeline and resolved training config are untouched, and
the logging attaches through the public add_callback API. But the probe patches
torch.nn.utils.clip_grad_norm_, and reviewing that for what goes wrong in
practice turned up three things worth fixing.
Ultralytics fires on_train_end only on success, so a crash left the patch
installed and the second model's probe then wrapped the wrapper, making
restoration hand back a wrapper instead of the real function. install() now
marks the function it creates and refuses to wrap a marked one, and train.py
calls close() from a finally block.
float() on a CUDA tensor synchronises the device, and clip_grad_norm_ does not
otherwise sync since error_if_nonfinite defaults to False. Converting every step
added a stall to every step of the run. Norms are kept as detached tensors and
converted once per epoch instead.
Gradient norm is now its own f…
Log train loss, val loss and gradient norm to Weights & Biases
Aryan Sethi+1: Claude Opus 5 (1M context)
e4f085d
Ultralytics' own W&B integration is gated behind SETTINGS["wandb"], which
defaults to False and is persisted to the user's global config, so enabling it
reconfigures their machine rather than just the run. It also does not log
gradient norm. Since a module was needed for that anyway, this registers its own
callbacks: one file states exactly which series are logged and nothing outside
the process is changed.
Gradient norm needs a hook rather than a callback. optimizer_step calls
clip_grad_norm_, whose return value is the total pre-clip norm, discards it, and
calls zero_grad() in the same method. No callback fires between the two -- the
nearest, on_train_batch_end, runs once the gradients are already gone, so it
cannot be recomputed. Wrapping clip_grad_norm_ for the duration of training is
the only hook available, and also the most accurate: the value is captured after
scaler.unscale_(), so it is a true unscaled norm and the same number the
optimiser acted on. Summarised per epoch, sinc…
Keep the training recipe identical to the reference notebook
Aryan Sethi+1: Claude Opus 5 (1M context)
4a96ce6
Verified rather than assumed. Resolving both the reference CLI invocation and
this script's YOLO.train() call through ultralytics.cfg.get_cfg on the pinned
8.3.40 gives 105 config keys, of which four differ -- device, project, name and
exist_ok -- all plumbing. All 55 recipe keys match: optimiser, schedule, loss
gains and every augmentation. Validation differs only in device, and since
Model.train() reloads best.pt on completion, model.val() scores the same
checkpoint the reference validates.
One difference was real. The notebook passed --batch -1, and Ultralytics
rescales weight decay by batch * accumulate / nbs, which only compensates
exactly when the batch divides 64. AutoBatch derives its batch from free GPU
memory and returns an arbitrary integer, so -1 would have trained with effective
weight decay anywhere from 25% below to 56% above the reference, varying with
whichever GPU Colab handed out. Default is now 16, matching both Ultralytics and
the reference, and train.py warns on …
Phase 1: unified data pipeline, QA and training
Aryan Sethi+1: Claude Opus 5 (1M context)
9274a8d
Extract cones, barriers, barrels and stop signs from nuScenes, Argoverse 2,
COCO and BDD100K into one manifest carrying camera intrinsics and ground-truth
distance, then train YOLO11n and YOLO11s on the derived YOLO export.
The manifest is the artifact, not the .txt files: .txt cannot hold intrinsics
or gt distance, and phase 2's geometric distance estimator needs both. 2D boxes
are projected from the 3D cuboids rather than taken from a pre-made 2D
annotation file, so boxes and distances come from one convention.
merge.py asserts the cross-dataset geometry rather than leaving it to be
eyeballed -- distance positive, distance and box-bottom row negatively
correlated within a camera, median cone distance plausible. A coordinate
convention mismatch otherwise produces a manifest that trains fine and makes
the depth numbers nonsense a day later.
Extraction and export run locally; only training moves to Colab, so the dataset
is published to the Hugging Face Hub as tar shards with the mani…