Zero-Parameter Classifier
Image-level person classification on EUPE-ViT-B features. A 768 pixel image gives 2304 patch tokens at the final layer; layernorm across the 768 channels and max-pool across patches gives one 768-D vector. The decision compares two sums of that vector. The boundary is zero.
patches = backbone(image)["x_norm_patchtokens"] # (2304, 768)
pooled = layernorm(patches, 768).max(dim=0) # (768,)
present = pooled[pos_dims].sum() > pooled[neg_dims].sum()
At two dimensions:
person present ⟺ feat[48] > feat[637]
from infer import PersonDetector
det = PersonDetector.load('d6')
present = det.predict('image.jpg')
Rules
Dimensions are selected on COCO train2017, 118,287 images, and scored on val2017, 5000 images. The splits are disjoint.
| rule | dims | F1 | precision | recall | slices | LUT4 | CCU2C | ns |
|---|---|---|---|---|---|---|---|---|
d2 |
2 | 0.8681 | 0.8685 | 0.8678 | 4 | 7 | 0 | 0.40 |
d4 |
4 | 0.8817 | 0.9069 | 0.8578 | 10 | 8 | 10 | 0.90 |
d6 |
6 | 0.8977 | 0.9461 | 0.8541 | 20 | 9 | 20 | 1.40 |
d8 |
8 | 0.9039 | 0.9475 | 0.8641 | 30 | 9 | 30 | 1.90 |
d12 |
12 | 0.9068 | 0.9500 | 0.8674 | 60 | 10 | 60 | 2.90 |
d16 |
16 | 0.9126 | 0.9568 | 0.8723 | 84 | 10 | 84 | 3.90 |
d20 |
20 | 0.9217 | 0.9587 | 0.8875 | 108 | 11 | 108 | 4.90 |
d40 |
40 | 0.9307 | 0.9698 | 0.8945 | 266 | 12 | 266 | 9.90 |
Train and validation F1 differ by 0.0006 at 40 dimensions and by at most 0.0078
across the set. d6 is the default.
Dimensions
d2 48 > 637
d4 48 + 71 > 637 + 90
d6 48 + 71 + 292 > 637 + 90 + 82
Selection is greedy over the 192 dimensions with the largest class-mean
separation, alternating sides and adding whichever remaining dimension most
improves F1 at a zero boundary. Rules nest; tests/test_rules.py checks the
nesting.
Dimension 48 responds to people and to person-associated objects and is suppressed on non-human animals and on non-anthropogenic structures.
Offset
A decision of the form sum(pos) - sum(neg) > t requires t because the two
sums carry a relative offset. Sets selected under a zero boundary carry none. A
40-dimension set selected at t = 25.28 scores F1 0.7410 on these images when
t is set to zero.
Dimension indices and signs are fixed structure. Each rule has no free parameters and 2 to 40 fixed ones.
Circuit
rtl_gen.py emits one Verilog module per rule. synth.py synthesizes them with
nosis for a Lattice ECP5 LFE5U-25F.
Counts are LUT4s, carry cells and slices on that device. Inputs are the selected
channels as signed INT8, post-layernorm and post-max-pool. Output is one bit,
combinational, with no multipliers, no memory and no constants.
d2 contains no adder and is LUT-bound at 0.40 ns. Wider rules are carry-bound,
with area and delay linear in dimension count.
tests/test_rtl.py simulates each module against a Python reference under
Icarus Verilog, on uniform inputs and on inputs at the decision boundary.
Layout
common/ pooled features, the comparison rule, metrics, named pools
cache.py pooled feature cache for a COCO split
choose.py dimension selection on train2017, writes rules.json
verify.py scoring on val2017, writes eval.json
rtl_gen.py Verilog generation from rules.json
synth.py nosis synthesis, writes synth.json
infer.py loader for every rule
rtl/ one module per rule, all generated
tests/ consistency suite, no backbone or dataset required
Each measured JSON opens with a provenance block naming its generating script
and the pool it read. tests/test_artifacts.py enforces the pairing and that
selection and scoring name different splits.
Running
pip install -e .
python cache.py --split train2017
python cache.py --split val2017
python choose.py
python verify.py
make synth
make test
COCO_ROOT is the dataset root. BACKBONE is the backbone repo id or a local
path. BACKBONE_SRC supplies argus.py from a local directory; otherwise it is
fetched from the backbone repo. Caching the two splits is a backbone forward
over 123,287 images; every later step reads the cache.
bfloat16 kernels select reduction orders by batch size, so cached values depend
on --batch. A cache must be built at one batch size throughout.
Source backbone
EUPE-ViT-B from Meta FAIR (arXiv:2603.22387, Zhu et al., March 2026), distilled from PEcore-G + PElang-G + DINOv3-H+ via a 1.9B proxy teacher. License: FAIR Research License, non-commercial. This classifier is an artifact derived from that backbone's feature geometry.
Model tree for phanerozoic/zero-parameter-classifier
Base model
facebook/EUPE-ViT-B