Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,13 @@ What differs from the platform loop:
the next `dvc repro`, or the build errors on the stale lockfile.
Design: `docs/specs/2026-08-14-annotator-test-growth-design.md`.
- **False-positive boxes are class 99**, not `0`: they mark where the detector
fired, not smoke. `build_sequential_dataset.py` pins annotator-sourced FP
sequences ahead of its clustering, so the ones chosen upstream actually reach
the built dataset.
fired, not smoke. Annotator-sourced FP sequences are never embedded — their
identity comes from `recurring_objects.json` — and in train and val both
builders pin them ahead of clustering: the sequential build takes every
sequence, the YOLO build one image per recurring object. In test they reach
`sequential_test` only through `freeze_test_selection.py`, up to the quota —
surplus pins are deferred until new test smoke opens slots. See
`docs/specs/2026-08-14-recurring-object-fp-identity-design.md`.
- Each folder carries a `meta.json` with every lane's full track, including boxes
left out of `labels/`, so an object-level dataset can be derived later without
re-exporting.
Expand Down
2 changes: 1 addition & 1 deletion data/reporting/leakage_results.xml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="17" time="0.315" timestamp="2026-08-14T12:46:10.441113+02:00" hostname="desktop"><testcase classname="tests.test_data_leakage" name="test_wildfire_yolo_no_image_leakage[train-test]" time="0.018" /><testcase classname="tests.test_data_leakage" name="test_wildfire_yolo_no_image_leakage[val-test]" time="0.003" /><testcase classname="tests.test_data_leakage" name="test_wildfire_yolo_no_image_leakage[train-val]" time="0.017" /><testcase classname="tests.test_data_leakage" name="test_yolo_datasets_no_image_leakage[train_val_dir0-train]" time="0.020" /><testcase classname="tests.test_data_leakage" name="test_yolo_datasets_no_image_leakage[train_val_dir1-val]" time="0.005" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a0-dir_b0-train-val-wildfire]" time="0.005" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a0-dir_b0-train-val-fp]" time="0.005" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a1-dir_b1-train-test-wildfire]" time="0.005" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a1-dir_b1-train-test-fp]" time="0.005" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a2-dir_b2-val-test-wildfire]" time="0.001" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a2-dir_b2-val-test-fp]" time="0.001" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a0-dir_b0-train-val-wildfire]" time="0.062" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a0-dir_b0-train-val-fp]" time="0.038" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a1-dir_b1-train-test-wildfire]" time="0.063" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a1-dir_b1-train-test-fp]" time="0.037" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a2-dir_b2-val-test-wildfire]" time="0.012" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a2-dir_b2-val-test-fp]" time="0.007" /></testsuite></testsuites>
<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="19" time="0.538" timestamp="2026-08-14T16:28:20.043266+02:00" hostname="desktop"><testcase classname="tests.test_data_leakage" name="test_wildfire_yolo_no_image_leakage[train-test]" time="0.032" /><testcase classname="tests.test_data_leakage" name="test_wildfire_yolo_no_image_leakage[val-test]" time="0.006" /><testcase classname="tests.test_data_leakage" name="test_wildfire_yolo_no_image_leakage[train-val]" time="0.031" /><testcase classname="tests.test_data_leakage" name="test_yolo_datasets_no_image_leakage[train_val_dir0-train]" time="0.036" /><testcase classname="tests.test_data_leakage" name="test_yolo_datasets_no_image_leakage[train_val_dir1-val]" time="0.008" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a0-dir_b0-train-val-wildfire]" time="0.006" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a0-dir_b0-train-val-fp]" time="0.008" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a1-dir_b1-train-test-wildfire]" time="0.008" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a1-dir_b1-train-test-fp]" time="0.009" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a2-dir_b2-val-test-wildfire]" time="0.002" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_sequence_leakage[dir_a2-dir_b2-val-test-fp]" time="0.002" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a0-dir_b0-train-val-wildfire]" time="0.101" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a0-dir_b0-train-val-fp]" time="0.061" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a1-dir_b1-train-test-wildfire]" time="0.097" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a1-dir_b1-train-test-fp]" time="0.059" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a2-dir_b2-val-test-wildfire]" time="0.017" /><testcase classname="tests.test_data_leakage" name="test_sequential_no_image_leakage[dir_a2-dir_b2-val-test-fp]" time="0.010" /><testcase classname="tests.test_data_leakage" name="test_no_recurring_object_spans_splits" time="0.005" /><testcase classname="tests.test_data_leakage" name="test_lockfile_matches_the_test_quota" time="0.005" /></testsuite></testsuites>
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,10 @@ This does not displace anything. The import adds as many smoke sequences as the
holds (58 here), which raises `quota = n_wf` by the same number — the pinned FPs consume
exactly the slots their own positives created.

Since [2026-08-14-recurring-object-fp-identity-design.md](2026-08-14-recurring-object-fp-identity-design.md),
pinning extends to the YOLO build (one image per recurring object) and the
cohort is no longer embedded at all.

### 7. Changes to existing pyro-dataset code

Everything else is new code. Exactly two existing files change, and both changes are
Expand Down
21 changes: 14 additions & 7 deletions docs/specs/2026-08-14-recurring-object-fp-identity-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,11 @@ score, ties broken by filename. Annotator labels are score-less, so in practice
the first labeled frame by name — but the rule stays correct if a scored sequence ever
carries a `source`.

Test split: untouched. Annotator sequences never enter test
(`assignments_from_splits` refuses it), so round-robin is unaffected.
Test split: untouched by this design. When it was written, annotator sequences
could not enter test (`assignments_from_splits` refused it); the test-growth
design has since repealed that refusal, so annotator test FPs will eventually
appear in the round-robin pool — accepted, as the test-growth design declares
detector-test behaviour out of its scope.

### `scripts/build_sequential_dataset.py`

Expand Down Expand Up @@ -196,8 +199,12 @@ Integration verification, in order:
beyond fewer sequences passing through KMeans.
- Interaction with
[2026-08-14-annotator-test-growth-design.md](2026-08-14-annotator-test-growth-design.md)
(designed separately): it opens `split: test` to annotator imports, repealing the
"test is never assigned" premise this document leans on, and its §5 makes surplus
annotator test FPs "eligible for two-stage selection" — which presumes embeddings
this design stops producing. That reconciliation belongs to the test-growth
implementation, not to this one.
(designed separately, merged since): it opens `split: test` to annotator imports,
repealing the "test is never assigned" premise this document originally leaned on.
Its §5 makes surplus annotator test FPs "eligible for two-stage selection", which
presumes embeddings this design stops producing. How that lands in practice:
`freeze_test_selection.py` pins annotator test FPs from the registry — no
embeddings needed — and its clustering fill simply cannot see the surplus, so a
deferred sequence waits for future slots instead of competing by embedding
distance. Weaker than §5's letter, consistent with both designs' spirit: exact
identity is never traded back for estimated identity.
106 changes: 55 additions & 51 deletions dvc.lock
Original file line number Diff line number Diff line change
Expand Up @@ -592,18 +592,18 @@ stages:
build_fp_yolo_dataset:
cmd: uv run python scripts/build_fp_yolo_dataset.py --registry data/raw/fp/registry.json
--data-dir data/raw/fp/data --wf-dataset data/processed/wildfire_yolo --embeddings-dir
data/interim/fp_sequence_embeddings --output data/processed/fp_yolo --random-seed
0
data/interim/fp_sequence_embeddings --recurring-objects data/raw/pyro-annotator/recurring_objects.json
--output data/processed/fp_yolo --random-seed 0
deps:
- path: data/interim/fp_sequence_embeddings/train
hash: md5
md5: 51edec8cc1a3f1c8b93e7af595f80717.dir
size: 20216589
md5: d7c1ce9531de9fc9d89bf6f4305ba12d.dir
size: 20043098
nfiles: 2
- path: data/interim/fp_sequence_embeddings/val
hash: md5
md5: aac0d5327b7b6ea3e05a66dccf154749.dir
size: 2491101
md5: 35a4487a1ba6a41ca2b529c6a699e6c3.dir
size: 2463314
nfiles: 2
- path: data/processed/wildfire_yolo
hash: md5
Expand All @@ -619,19 +619,23 @@ stages:
hash: md5
md5: 7983fceca0836f734bdf7e6bf032063e
size: 1318880
- path: data/raw/pyro-annotator/recurring_objects.json
hash: md5
md5: ecc9988f92c56db6f9516052b8aae959
size: 50832
- path: scripts/build_fp_yolo_dataset.py
hash: md5
md5: ca087d24c82e3a93ff766c015e852a37
size: 10573
md5: 7017e8e7f6d9d2d9e1ca981b63f25e7c
size: 13137
- path: src/pyro_dataset/fp/selection.py
hash: md5
md5: e9d2065653a9aafdc035c700fa2d678f
size: 7999
md5: e8b6b6673a571baeba4f94e18faeb98a
size: 10121
outs:
- path: data/processed/fp_yolo
hash: md5
md5: cf82b8283d478d11ae8458335327f778.dir
size: 332638599
md5: 218aa610b9887a65ec9b27009d6a6f6f.dir
size: 332994040
nfiles: 6002
merge_yolo_dataset:
cmd: uv run python scripts/merge_yolo_dataset.py --wf-dataset data/processed/wildfire_yolo
Expand All @@ -640,8 +644,8 @@ stages:
deps:
- path: data/processed/fp_yolo
hash: md5
md5: cf82b8283d478d11ae8458335327f778.dir
size: 332638599
md5: 218aa610b9887a65ec9b27009d6a6f6f.dir
size: 332994040
nfiles: 6002
- path: data/processed/wildfire_yolo
hash: md5
Expand All @@ -660,17 +664,17 @@ stages:
nfiles: 5281
- path: data/processed/yolo_train_val
hash: md5
md5: 038270d9336432fa9001cf92069731b2.dir
size: 2027739474
md5: a43f8eae91dd781aa91984d92bbe65d2.dir
size: 2028094915
nfiles: 33635
visualize_yolo_train_val:
cmd: uv run python scripts/visualize_dataset.py --dir-dataset data/processed/yolo_train_val
--dir-save data/reporting/viz/yolo_train_val
deps:
- path: data/processed/yolo_train_val
hash: md5
md5: 038270d9336432fa9001cf92069731b2.dir
size: 2027739474
md5: a43f8eae91dd781aa91984d92bbe65d2.dir
size: 2028094915
nfiles: 33635
- path: scripts/visualize_dataset.py
hash: md5
Expand All @@ -679,8 +683,8 @@ stages:
outs:
- path: data/reporting/viz/yolo_train_val
hash: md5
md5: 7b3748c47c77eb11844bf0628927c3b4.dir
size: 3658088575
md5: 922d0a257ec915d91bb299f11df8ae27.dir
size: 3658189215
nfiles: 16817
visualize_yolo_test:
cmd: uv run python scripts/visualize_dataset.py --dir-dataset data/processed/yolo_test
Expand Down Expand Up @@ -710,13 +714,13 @@ stages:
deps:
- path: data/interim/fp_sequence_embeddings/train
hash: md5
md5: 51edec8cc1a3f1c8b93e7af595f80717.dir
size: 20216589
md5: d7c1ce9531de9fc9d89bf6f4305ba12d.dir
size: 20043098
nfiles: 2
- path: data/interim/fp_sequence_embeddings/val
hash: md5
md5: aac0d5327b7b6ea3e05a66dccf154749.dir
size: 2491101
md5: 35a4487a1ba6a41ca2b529c6a699e6c3.dir
size: 2463314
nfiles: 2
- path: data/raw/fp/data
hash: md5
Expand All @@ -742,16 +746,16 @@ stages:
size: 348611
- path: scripts/build_sequential_dataset.py
hash: md5
md5: 7cb5dd1ef282a34d5cd0436a020f664f
size: 12785
md5: a279800d6a6e0a1c507b77256174d805
size: 11749
- path: src/pyro_dataset/fp/lockfile.py
hash: md5
md5: cfd051c856b57783e3201555d1b4b8c6
size: 2623
- path: src/pyro_dataset/fp/selection.py
hash: md5
md5: e9d2065653a9aafdc035c700fa2d678f
size: 7999
md5: e8b6b6673a571baeba4f94e18faeb98a
size: 10121
outs:
- path: data/processed/sequential_test
hash: md5
Expand All @@ -760,23 +764,23 @@ stages:
nfiles: 15320
- path: data/processed/sequential_train_val
hash: md5
md5: 9faac139ef157197191b84bc24bd1598.dir
size: 8555476683
nfiles: 144452
md5: ef44e3f9519456b587f424e27b5ad8a2.dir
size: 8556786817
nfiles: 144472
build_toy_dataset:
cmd: uv run python scripts/build_toy_dataset.py --yolo-src data/processed/yolo_train_val
--seq-src data/processed/sequential_train_val --yolo-output data/processed/yolo_toy
--seq-output data/processed/sequential_toy --ratio 0.05 --random-seed 0
deps:
- path: data/processed/sequential_train_val
hash: md5
md5: 9faac139ef157197191b84bc24bd1598.dir
size: 8555476683
nfiles: 144452
md5: ef44e3f9519456b587f424e27b5ad8a2.dir
size: 8556786817
nfiles: 144472
- path: data/processed/yolo_train_val
hash: md5
md5: 038270d9336432fa9001cf92069731b2.dir
size: 2027739474
md5: a43f8eae91dd781aa91984d92bbe65d2.dir
size: 2028094915
nfiles: 33635
- path: scripts/build_toy_dataset.py
hash: md5
Expand All @@ -790,8 +794,8 @@ stages:
nfiles: 7728
- path: data/processed/yolo_toy
hash: md5
md5: 9bab5ff7b4ca24749be1120daa85f88a.dir
size: 101768902
md5: 257cdce256f23272ee72010f90fca5cd.dir
size: 101552974
nfiles: 1683
test_data_leakage:
cmd: uv run pytest tests/test_data_leakage.py -v --junit-xml=data/reporting/leakage_results.xml
Expand All @@ -803,9 +807,9 @@ stages:
nfiles: 15320
- path: data/processed/sequential_train_val
hash: md5
md5: 9faac139ef157197191b84bc24bd1598.dir
size: 8555476683
nfiles: 144452
md5: ef44e3f9519456b587f424e27b5ad8a2.dir
size: 8556786817
nfiles: 144472
- path: data/processed/wildfire_yolo
hash: md5
md5: 7e25bea7a3afa9a9bf704b8e42c56a97.dir
Expand All @@ -818,8 +822,8 @@ stages:
nfiles: 5281
- path: data/processed/yolo_train_val
hash: md5
md5: 038270d9336432fa9001cf92069731b2.dir
size: 2027739474
md5: a43f8eae91dd781aa91984d92bbe65d2.dir
size: 2028094915
nfiles: 33635
- path: data/raw/fp/registry.json
hash: md5
Expand Down Expand Up @@ -857,12 +861,12 @@ stages:
size: 1318880
- path: scripts/embed_fp_for_selection.py
hash: md5
md5: 27c76f4126615f2be3fb4b798e8c55ef
size: 8024
md5: ba8c3fb00f8f15b129aa481889d816b8
size: 8525
- path: src/pyro_dataset/fp/selection.py
hash: md5
md5: e9d2065653a9aafdc035c700fa2d678f
size: 7999
md5: e8b6b6673a571baeba4f94e18faeb98a
size: 10121
outs:
- path: data/interim/fp_sequence_embeddings/test
hash: md5
Expand All @@ -871,13 +875,13 @@ stages:
nfiles: 2
- path: data/interim/fp_sequence_embeddings/train
hash: md5
md5: 51edec8cc1a3f1c8b93e7af595f80717.dir
size: 20216589
md5: d7c1ce9531de9fc9d89bf6f4305ba12d.dir
size: 20043098
nfiles: 2
- path: data/interim/fp_sequence_embeddings/val
hash: md5
md5: aac0d5327b7b6ea3e05a66dccf154749.dir
size: 2491101
md5: 35a4487a1ba6a41ca2b529c6a699e6c3.dir
size: 2463314
nfiles: 2
materialise_annotator_sequences:
cmd: uv run python scripts/materialise_annotator_sequences.py --export-dir data/raw/pyro-annotator/export
Expand Down
2 changes: 2 additions & 0 deletions dvc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ stages:
--data-dir data/raw/fp/data
--wf-dataset data/processed/wildfire_yolo
--embeddings-dir data/interim/fp_sequence_embeddings
--recurring-objects data/raw/pyro-annotator/recurring_objects.json
--output data/processed/fp_yolo
--random-seed 0
deps:
- scripts/build_fp_yolo_dataset.py
- src/pyro_dataset/fp/selection.py
- data/raw/fp/registry.json
- data/raw/fp/data
- data/raw/pyro-annotator/recurring_objects.json
- data/processed/wildfire_yolo
- data/interim/fp_sequence_embeddings/val
- data/interim/fp_sequence_embeddings/train
Expand Down
Loading
Loading