Collect
A real browser walks Wikimedia Commons category pages, opens each file page, and keeps only photographs it is allowed to use and can trace back to an author.
data/manifest/candidates.jsonl.How the crawler behaves
A sample of what was kept
Select a photograph to see its author and license.
Accepted images per source category
Label
Grounding DINO proposes boxes from a text prompt. Curation keeps the confident ones, records why each other box was dropped, and never edits the raw predictions.
From raw boxes to labels
Ignore regions are boxes the teacher proposed but could not be trusted with: a low score or an ambiguous phrase. They are not labels, but they are not treated as background either. Points inside them are left out of the classification loss.
Transform
Resizing and augmentation are one matrix. The image is warped with it once, and every box goes through the same matrix, corner by corner.
Two conventions that must not be mixed
Boxes live in continuous edge coordinates: an image W pixels wide spans 0 to W. OpenCV addresses pixel centres. A horizontal flip is x' = W - x for a box edge but u' = W - 1 - u for a pixel index. The matrix handed to OpenCV is shifted by half a pixel on each side so the two agree.
Why clip the outline, not the box
After a rotation, the bounding box of a rotated rectangle can overlap the canvas even when the rectangle itself is entirely outside. Clipping the four-corner outline first means an object that left the frame is dropped instead of leaving a phantom label behind.
Train
The detector is defined layer by layer in this repository and starts from random weights. The loop that trains it is written out by hand.
Architecture
Every convolution is 3×3 except the 1×1 projections on shortcuts and pyramid laterals. Residual blocks keep gradients moving through a network that has no pretrained weights to lean on. Group normalisation is used because batches are small. The last normalisation in each residual branch starts at zero, so each block begins as an identity.
Loss
L = L_cls + L_box + L_ctr
L_cls = sum over points, classes of
focal(p, y) / N_pos
L_box = sum over positives of
c * (1 - GIoU(b, b*)) / sum(c)
L_ctr = sum over positives of
BCE(o, c) / N_pos
A point is a positive when it lies inside a labelled box, near its centre, at the pyramid level matching the box size. c is the centerness target. A batch with no objects still trains the classifier and yields zero, graph-connected box and centerness terms.
Training curves
With and without affine augmentation
What the model actually trained on
Evaluate
The held-out split is scored with COCO metrics. The reference boxes are the teacher's, so the scores say how well the detector agrees with the teacher, not how accurate it is.
Per class
No human checked these boxes. A prediction that disagrees with the teacher may be the detector's mistake or the teacher's. Telling them apart needs a hand-labelled set, which this project does not have.
Audit
A language model answers questions about the dataset and the training run. Every count and percentage comes from a Python tool the model chooses to call. The records behind those results are then pulled from a vector index, so the answer can describe and cite them.
These are recordings of a live run. Nothing on this page calls a model.
Scored on questions with known answers
Each expected value is computed separately with plain Python, then compared with what the model wrote. No model grades another model. A case passes only if every check that applies to it passes.
Reproduce
Each stage is one command, and each artifact carries the fingerprint of the dataset it came from.
python collect_and_annotate.py crawl # Playwright, resumable python collect_and_annotate.py dedupe python collect_and_annotate.py annotate # Grounding DINO python collect_and_annotate.py build --freeze python train.py prepare && python train.py fit python train.py evaluate --split val python train.py evaluate --split test python llm_auditor.py register && python llm_auditor.py build-index python llm_auditor.py demo