1-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 classifier reads 40 of its dimensions, 20 person-positive and 20 person-negative, sums the positives, subtracts the negatives, and compares the result to one threshold. The dimension indices and their signs are fixed structure; the threshold, 25.284, is the only value fitted to data.

patches = backbone(image)["x_norm_patchtokens"]      # (2304, 768)
pooled  = layernorm(patches, 768).max(dim=0)         # (768,)
score   = pooled[pos_dims].sum() - pooled[neg_dims].sum()
present = score > threshold                          # the only free parameter
from infer import PersonDetector
det = PersonDetector.load('baseline')
score, present = det.predict('image.jpg')

Variants

variant dims F1 precision recall prop-FPR
baseline 40 0.8886 0.9011 0.8763 5.93 %
tight_fpr 55 0.8527 0.8967 0.8127 2.72 %

baseline is measured on all 5000 images of COCO val2017 through a backbone forward at 768 px. tight_fpr keeps the same 20 positive dimensions and extends the negative set with 15 mined from person-associated objects photographed without people; it trades 0.036 F1 for a prop false-positive rate of 2.72 %. Its row is measured by discovery/prop_specificity.json at extra_neg_k 15, which recorded no pool, and is not comparable with the VAL5000 figure.

Dimension selection

discovery/dim_selection.json: 100,000 random 92-dimension subsets of the 768-D space, a ridge classifier per subset, the top 1 % kept, dimension occurrence counted across that cohort. Dimension 48 appears in 100 % of the top 1000 subsets; the next strongest, 525, appears in 31 %.

discovery/dim48_characterization.json: five analyses on dimension 48. F1 against K. Activation distributions for person-positive and person-negative images, Cohen's d 1.98. Per-class activation delta across all 80 COCO categories. Pairwise correlation among the ten most frequent dimensions, maximum absolute value 0.57. Spatial IoU of peak activations against ground-truth person boxes, mean 0.17.

discovery/variant_leaderboard.json: 20 classifier forms from 1 to 769 parameters. Ternary ±1 over 50 positive and 50 negative dimensions leads at F1 0.893.

Dimension 48 responds to people and to person-associated objects and is suppressed on non-human animals and on non-anthropogenic structures. Alone it reaches F1 0.83 as a 2-parameter classifier. The other 39 dimensions carry largely orthogonal axes and reach 0.89 at one free parameter.

Prop specificity

discovery/prop_specificity.json and prop_image_manifest.json measure separation between "person present" and "person-associated object present, no person". 8,479 ImageNet training images across 20 such synsets were filtered with YOLO26l at confidence 0.25 to keep only frames with no detected person. baseline fires on 5.9 % of them. Adding prop-specific negative dimensions takes that to 2.7 % at K=15, which is the knee and what classifier_tight_fpr.json carries.

Circuit

Inputs are the 40 selected channels as signed INT8, post-layernorm, post-max-pool and indexed. Output is one bit, combinational, with no multipliers and no memory. Two forms, each synthesized with thresholds as runtime inputs and with them baked in.

variant thresholds slices LUT4 CCU2C bound ns
sum runtime 312 31 312 carry 10.40
sum_folded baked 312 21 312 carry 10.40
popcount runtime 146 291 118 lut 10.80
popcount_folded baked 118 109 118 carry 10.80
additive   score = sum(pos) - sum(neg)          40 x 8-bit adder tree
           out   = score > T                    16-bit signed comparator

popcount   b_i   = f_i > t_i                    40 x 8-bit comparators
           out   = popcount(b_0..b_19)
                 - popcount(b_20..b_39) > K     two 20->5 bit counts, small compare

The popcount form replaces the signed adder tree with 40 independent one-bit decisions, taking the carry chain from 312 cells to 118. Each channel retains only which side of its threshold it fell on.

form                          F1
additive, float               0.884
popcount, K=13                0.876    -0.008

Measured on BALANCED_VAL, where the additive figure is 0.884 against the 0.889 measured on VAL5000. Both come from the same measurement.

Synthesis is nosis targeting a Lattice ECP5 LFE5U-25F. Counts are LUT4s, carry cells and slices on that device. calibrate.py selects the 40 per-dimension thresholds and the integer K and writes per_dim_thresholds.json; rtl_gen.py emits all four modules from that file. INT8 constants are the calibrated float values scaled by 8.

Layout

common/                     pooled features, ternary scoring, metrics, named pools
classifier.json, .safetensors                dimensions, signs and the threshold
classifier_tight_fpr.json, .safetensors      the low-false-fire variant
head.py                     the decision as a fused Linear with ternary weights
verify.py                   scores a config over a named pool, writes eval.json
calibrate.py                per-dimension calibration, writes per_dim_thresholds.json
rtl_gen.py                  Verilog generation from per_dim_thresholds.json
synth.py                    nosis synthesis, writes circuit.json
infer.py                    loader for both variants
discovery/                  how the 40 dimensions were chosen
rtl/                        the four decision modules, all generated
tests/                      consistency suite, no backbone or dataset required

Each measured JSON opens with a provenance block naming its generating script, the classifier config it read and that config's hash, and the pool. tests/test_artifacts.py enforces the pairing. The five files under discovery/ record a null generator; their sweeps are not committed.

Running

pip install -e .
make test                       # consistency suite
python verify.py                # baseline on VAL5000
python calibrate.py             # thresholds, then all four modules
make synth                      # synthesize with nosis

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.

Evaluation pools

Declared in common/pools.py and named in each artifact's provenance block. Figures are comparable only within a pool.

pool images
VAL5000 all 5000 of COCO val2017
CALIB1000 first 1000 val2017 ids
VAL500 first 500 val2017 ids
BALANCED_VAL val2017 subsampled to equal classes

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.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for phanerozoic/1-parameter-classifier

Finetuned
(8)
this model

Dataset used to train phanerozoic/1-parameter-classifier

Paper for phanerozoic/1-parameter-classifier