Skip to content
DataCourt AI
Sign inAudit a dataset

Evaluation

Benchmark with known answers: precision, recall, review effort, what-if.

DataCourt ships its own benchmark so every detector can be scored against known answers. The results below come from one full run, committed as docs/benchmark/results.json. They are reported as produced, without selecting favourable seeds or metrics.

What this shows: the methods behave as designed on controlled data with known problems. What it does not show: performance on your real dataset. Synthetic shapes are much easier to separate than natural images, and injected errors are cleaner than real annotation mistakes.

Reproduce

cd services/api
pip install -e ".[dev]"
export DATABASE_URL=postgresql+psycopg://postgres@127.0.0.1:5432/datacourt_bench
alembic upgrade head
datacourt-benchmark --profile deep --seed 7 --out benchmark-results

The run uses the real ingestion pipeline, job queue, worker and audit pipeline. Nothing is mocked.

The benchmark: DataCourt Synthetic Shapes

datacourt/benchmark/generator.py renders a seeded five-class image dataset (cross, disc, square, star, triangle) with varied backgrounds, lighting, positions, scales and textures. It then injects known issues and records the ground truth:

Injected issueCount (seed 7)Details
Label errors40train/test images saved under the wrong class folder
Exact duplicates20byte-identical copies inside train
Near duplicates42resized, cropped, flipped and brightness-changed copies inside train
Cross-split exact12test images byte-identical to train images
Cross-split variants10validation images that are transformed train images
Quality defects28heavy blur, tiny resolution, severe underexposure
Rare-but-valid8a valid but visually unusual sub-type of one class
Class imbalance1 classone class has far fewer images
Shortcut185% of training "star" images on a blue border/background
Condition gap1"triangle" never appears in low light; other classes often do

1,121 files in total; 1,119 samples after ingestion.

Setup

  • Profile: deep (5 folds, perturbation tests, influence).
  • Embedding backend: dc-descriptor-v1, the CPU default. DINOv2 was unavailable in the build environment.
  • Baseline macro-F1 on the uploaded evaluation split: 0.775.
  • Pipeline time: 47.8 s; end-to-end wall-clock including generation and ingestion: 62.6 s. Measured on a shared cloud container (CPU only).

Results

Duplicate detection

MetricValue
Pair precision1.000 (82 / 82)
Pair recall0.976 (82 / 84)
Recall by injected relationexact 1.0 · resized 1.0 · flipped 1.0 · photometric 1.0 · cropped 0.8
Edge precision by predicted relationexact, resized, photometric, flipped and possible crop all 1.0

The two missed pairs are crops, the hardest relation. Crop verification needs enough keypoints in the overlap.

Leakage detection

MetricValue
Cross-split pair precision / recall1.000 / 1.000 (22 / 22)
Leaked evaluation samples, precision / recall1.000 / 1.000 (22 / 22)

Label-error triage

Ranking all samples by the label suspicion score:

MetricValue
Average precision0.744 (random ranking: 0.036)
Precision@40 / Recall@400.70 / 0.70
Precision@80 / Recall@800.45 / 0.90
POSSIBLE_RELABEL verdicts20, of which 14 were real errors (precision 0.70). The suggested target class was correct for 13.

How the 40 label errors were judged:

VerdictLabel errors
POSSIBLE_RELABEL14
STRONG_REVIEW15
REVIEW6
LIKELY_RARE5

Every label error received a case. However, 5 of 40 (12.5%) were classed as likely rare, which lowers their review priority. This is the cost of protecting rare examples (next section).

Rare or wrong

MetricValue
Rare-but-valid samples8
False removal recommendation rate0.0 (no rare-valid sample received POSSIBLE_REMOVE or POSSIBLE_RELABEL)
Verdicts for rare-valid samples6 without a case, 1 REVIEW, 1 LIKELY_RARE

Review effort (time saved)

How many reviews it takes to find a given share of the injected label errors, reviewing in DataCourt's order versus a random order (expected value):

Target recallDataCourt orderRandom orderReduction
50%26 reviews≈ 56095.4%
80%55 reviews≈ 89593.9%

Review Budget coverage

There are 152 ground-truth issues: label errors, quality defects and the redundant copy of every duplicate pair. 62 of them count as high-impact: all 40 label errors plus the 22 evaluation images leaked from training.

BudgetObjectivePrecisionIssue coverageHigh-impact coverageEst. minutes
25balanced0.480.0790.12921.5
25label errors0.520.0860.09720.1
25leakage0.560.0920.22627.9
50balanced0.480.1580.25844.6
100balanced0.410.2700.53291.6
100leakage0.410.2700.53294.4

Budget queues spread effort across families and classes by design (diminishing returns). As a result, their precision against label errors only is lower than the pure suspicion ranking above.

Quality detection

MetricValue
Recall of injected defects0.964 (27 / 28): blur 0.83, low resolution 1.0, underexposure 1.0
Precision0.45 (27 / 60)

The 33 "false positives" are mostly potential underexposure (28). The generator deliberately renders dark low-light images as valid data, and the quality rules correctly describe them as dark. Precision counts them as errors anyway. This is also why quality findings are only "potential" issues and never trigger removal on their own.

Shortcut detection

The injected "star on blue border" shortcut was detected: bias-corrected Cramér's V = 0.32 (moderate), with no other shortcut findings.

Coverage

  • The injected "triangle × low light" condition gap was detected (medium priority).
  • The class imbalance was detected.
  • 16 gaps were reported in total. The others are boundary zones and weak modes, and are not scored against ground truth.

Model metrics before and after guided cleanup (What-if Lab)

Three experiments on the same audit, compared on the preserved holdout (296–300 evaluation samples untouched by any action):

ExperimentChangesMacro-F1 before → afterΔTraining-bootstrap stdWithin noisePaired eval 95% CI
Guided review of the top 80 (a simulated reviewer fixes only the real errors), plus duplicate copies removed and leakage moved out of evaluation36 relabelled, 73 removed, 22 dropped from eval0.781 → 0.812+0.0310.030yes[+0.002, +0.061]
Unreviewed jury suggestions applied blindly, with rare examples protected20 relabelled, 27 removed, 22 dropped from eval0.780 → 0.777−0.0040.024yes[−0.035, +0.030]
Random removal control (36 random training samples)36 removed0.771 → 0.765−0.0060.017yes[−0.019, +0.006]

Reading these honestly:

  • Human-verified fixes gave the largest gain. The paired evaluation CI excludes zero, but the change is still within training-data noise (|Δ| ≤ 2 std). On this benchmark it is suggestive, not conclusive.
  • Applying the jury's suggestions without human review did not help. This supports the design rule that suggestions go to reviewers and are not applied automatically.
  • Random removal did not help, as expected.

False-positive summary

AreaFalse-positive measure
Duplicates0 false pairs out of 82 predicted
Leakage0 false pairs out of 22 predicted
Relabel suggestions6 of 20 POSSIBLE_RELABEL were not label errors
Removal of rare-valid data0 of 8
Quality33 of 60 medium/high findings outside the injected set (see above)

Limitations

  • Synthetic data. Procedural shapes are easier than natural images, and injected label errors are uniform random swaps. Real errors cluster on ambiguous classes.
  • One seed, one run. The numbers are for seed 7. The Rare-or-Wrong result is based on only 8 rare samples. Run other seeds with --seed.
  • Descriptor backend. Results use dc-descriptor-v1. With DINOv2, witness reliability and label triage are expected to change, but that has not been measured here.
  • Baseline, not your model. What-if deltas are for the linear baseline on frozen embeddings.
  • Review simulation. The guided experiment assumes a reviewer who is always right about the samples they see.
  • Unscored outputs. Influence, the Blame Map and Dataset DNA drift have no ground truth in this benchmark.