An end-to-end vision data pipeline, with its evidence

ForgeSight

Photographs scraped from Wikimedia Commons are labelled by a foundation model, used to train an object detector written from scratch, and then checked by a language model that has to call Python tools for every number it reports.

    Dashed boxes are the labels Grounding DINO produced. Solid boxes are what the detector, trained only on such labels, predicts for this held-out photograph. The detector never saw this image during training.

    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.

    Every candidate ends in the manifest with a status. A rejected candidate keeps the reason, so the funnel can be recounted from 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.

      Thin outline: the box's four corners after the transform. Solid: the axis-aligned box that becomes the training label, after clipping the outline to the canvas. This panel recomputes the maths in your browser; the tested implementation is the Python one.

      
              

      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

      Total loss per epoch on the training and validation splits.
      Validation AP per epoch. The vertical line marks the checkpoint that was kept. These numbers measure agreement with the teacher's labels.

      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.

      teacher labeldetector prediction

      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

      Known limits

        Photograph credits and licenses for every image in the dataset