Calibrate the int8 detectors on library proxies, in chunks, and measure them

The first int8 files found no faces at all, and for two reasons the
tool now guards against. The calibration set was landscape photographs
with no faces in them, so the score head's ranges had never seen the
face regime; the set is now proxies from the library itself. And ONNX
Runtime's strided and moving-average calibration modes both degrade
these graphs measurably (a quarter of the faces at eight images, none
at ninety-six), while driving the calibrator in chunks by hand gives
ranges identical to a single pass — so the tool does that, four images
at a time, and feeds quantize_static through its range cache.

Measured against f32 over 400 proxies (docs/inference.md §10.1): the
10g form finds every face above 32 px the f32 form finds; 500m and
2.5g find 96%, and what they lose sits at a median confidence of 0.52
against the 0.50 threshold. Shipped with the number on record.

The Android unpack list gains the three int8 files; without that the
tablet never saw them. D13's runtime half records the reopening.
This commit is contained in:
2026-09-19 16:02:44 +02:00
parent 4ed29b9d81
commit 76bc5652d7
9 changed files with 105 additions and 53 deletions
+27
View File
@@ -397,6 +397,33 @@ In order, with the gate each is:
M1 and M2 are the ones the rest is conditional on, and M2 is the one that needs a person.
### 10.1 M2 result · 2026-09-19
Each int8 detector against its own f32 form, over 400 proxies evenly spaced through the reference
library, on ONNX Runtime's CPU provider (the int8 graph is the same file the Hexagon loads;
`ui/dr-ui/examples/face_detectors`). Calibrated on 64 proxies from the same library, disjoint
from the 400.
| Detector | f32 faces | int8 faces | both | int8 only | f32 only | found ≥ 32 px | found, all sizes |
|---|---|---|---|---|---|---|---|
| scrfd_500m | 1342 | 1319 | 1287 | 32 | 55 | 95.6% | 95.9% |
| scrfd_2.5g | 1525 | 1482 | 1478 | 4 | 47 | 96.1% | 96.9% |
| scrfd_10g | 1769 | 1760 | 1749 | 11 | 20 | 100% | 98.9% |
The 10g form clears the 97% gate; 500m and 2.5g sit one point under it. What they lose is
specific: the faces in the "f32 only" column have a **median confidence of 0.52** against a
threshold of 0.50 — detections the f32 graph itself barely made, that int8 rounding drops to the
other side of the line — and the extra faces int8 finds are the same kind (median 0.51–0.52).
Not a size-band failure: the losses are spread across bands in proportion. Shipped as they are,
with the number on record; a threshold of 0.48 for the int8 forms would recover most of the
margin, and is the first thing to try if a library's count on the tablet reads low.
Two things the calibration taught, both in `tools/quantise-models.py`: the calibration set has
to contain faces (a first attempt on landscape photographs produced a graph that found nothing —
the score head's ranges had never seen the face regime), and ONNX Runtime's own strided and
moving-average calibration modes both measurably degrade the result on these graphs, while
driving the calibrator in chunks by hand reproduces the plain min/max ranges exactly.
---
## 11. Order