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.
Model tree for phanerozoic/1-parameter-classifier
Base model
facebook/EUPE-ViT-B