Use recurring objects instead of DINOv2 embeddings for annotator-sourced FP sequences - #37
Merged
Merged
Conversation
The frozen-lockfile test branch and the pinning simplification compose: test negatives copy the lockfile verbatim (no selection, no embeddings); train/val keep per-source pinning with the shared helpers from pyro_dataset.fp.selection. The pinning-helper tests stay in tests/test_fp_pinning.py; tests/test_build_sequential_pinning.py keeps only the frozen_test_selection coverage from main.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #35.
The annotator cohort's identity — "same recurring artefact on the same camera" — no longer gets reconstructed from geometry and DINOv2 distance: it is read from
recurring_objects.json, which records it authoritatively. Embed only what we cannot identify. Design, with the decisions recorded:docs/specs/2026-08-14-recurring-object-fp-identity-design.md.What changes
embed_fp_for_selection.pyskipssource: pyro-annotatorentries. Their embedding path was degenerate anyway — score-less five-field labels tiebest_scoring_frameon every frame, so the crop was whatever directory traversal yielded first (FP selection ranks annotator sequences on a confidence column their labels do not have #29). Per-splitembeddings_meta.jsonnow holds only sequences whose identity must be inferred.build_fp_yolo_dataset.pypins one background image per recurring object ahead of the clustering (new--recurring-objectsarg; the ledger joins the stage deps). The mapping inverts each ledger entry'singested_folders, restricted to registered pinned folders; a pinned folder missing from the ledger, or claimed twice, is a hard error. Frame pick is deterministic: lexicographically first folder of the object, then highest score / first filename.build_sequential_dataset.pykeeps pinning every sequence, and drops the now-dead filter that excluded annotator rows from the embedding items.partition_pinned,remaining_quota, newfolder_to_recurring_object) move topyro_dataset.fp.selection.Verification against a pre-change baseline snapshot
two_stage+50_pinned/two_stage+8_pinned), all 58 pinned objects present, test split unchanged; train/val clustered remainder re-rolls as expected since k changed.test_data_leakage: 17 passed. Full suite: 156 passed.Merge with #36 (append-only test growth)
Main merged the test-growth implementation mid-review; this branch merges it back in (
71f3da6) and the two designs compose:build_sequential_dataset.pyresolves to Grow the sequential test set from annotator imports, append-only #36's structure — the test split copies the frozen lockfile verbatim, no selection — with this PR's train/val simplifications inside the else-branch (helpers imported frompyro_dataset.fp.selection, deadpinned_foldersfilter gone).freeze_test_selection.pypins annotator test FPs straight from the registry (no embeddings needed), and its clustering fill simply cannot see un-embedded surplus pins — they defer until new test smoke opens slots, instead of competing by embedding distance. Recorded in the spec; the test-growth spec's §5 wording ("surplus eligible for two-stage selection") is weakened accordingly.frozen_test_selectiontests are kept intests/test_build_sequential_pinning.py; the pinning-helper tests live intests/test_fp_pinning.pywhere this PR moved them.Post-merge verification (all local, real stages):
dvc reproon the merged tree: frozen test negatives materialise exactly (builtsequential_testFP set == lockfile, 151/151); leakage gate passes with Grow the sequential test set from annotator imports, append-only #36's new recurring-object-in-test assertions (19 tests);dvc statusclean; 189 unit tests pass.diff -qracross 3,208 train + 330 val + 302 test sequence folders, images and labels — zero differences).Notes