Face100K-Edge

training parameters: 100138 encoder parameters: 98208 embedding: 192-d LFW BGR: 70.87% Raspberry Pi 4: ~5 ms/face format: safetensors + ONNX license: Apache-2.0

A ~100K-parameter depthwise-separable CNN face encoder trained from scratch (no distillation, no pretrained teacher) as a 10-class closed-set classifier on a small private family dataset plus 12,000 public negative faces (CASIA-WebFace, CelebA, DigiFace-1M, FFHQ-128 samples relabeled as background). This release publishes the encoder only (classification head removed): 112x112 aligned BGR crop in, 192-dimensional embedding out.

Sibling releases: TinyFaceNet-1M (distilled, 1.03M params) and TinyFaceNet-300K-Smallplain (distilled, 232K params). Face100K-Edge is the smallest and the only one trained without a teacher.

No personal images, real identity names, identity mapping, enrollment embeddings, centroids or classification weights are included. The encoder needs a separately supplied, consented enrollment gallery to identify anyone.

✨ Highlights

  • 3x smaller than Smallplain and 10x smaller than the 1M sibling — and the only one trained without a teacher.
  • ~5 ms per face on a Raspberry Pi 4 at 4 threads (201.8 images/s) — 5.9x faster than Smallplain, 13.5x faster than 1M v2.
  • 93.88% private family accuracy (46/49), within one face of a model 3x its size, measured on an untouched test set.
  • 99.45% stranger rejection in the closed-set classifier variant — carried by 12K public negatives, not architecture.

Results

Same protocols as the Smallplain release: LFW all 6,000 official pairs, 10 contiguous folds, threshold chosen on the other 5,400 pairs; private set is 59 crops (49 family + 10 background), nearest-training-centroid argmax.

Model Params LFW BGR mean ± fold SD Legacy RGB LFW Private family (49) Private overall (59)
1M v2 1.03M 73.22% ± 1.84 63.62% 95.92% 89.83%
Smallplain 299K 73.60% ± 1.47 63.17% 95.92% 91.53%
Face100K-Edge 100K 70.87% ± 1.97 63.17% 93.88% 81.36%

✨ Highlight: at 100K params (10x smaller than 1M v2), family accuracy drops only ~2 points (93.88% vs 95.92%) — and this row is the only one measured on an untouched test set.

Two protocol differences in Face100K-Edge's favor to disclose, and one against:

  • The private numbers here are untouched-test: the final epoch was taken with no checkpoint selection. The 1M/Smallplain private numbers reused the holdout for best-checkpoint selection (disclosed in those model cards), so this row is conservatively measured relative to the others.
  • The low private-overall number comes from the background class: trained on 12K diverse public negatives, the encoder's background centroid does not match the 10 specific background crops in the holdout (2/10 via centroid argmax). Family accuracy (46/49) is within one face of Smallplain (47/49).
  • Face100K-Edge saw 12K public faces as negatives during training; the distilled models saw only the private dataset (but inherited a teacher trained on millions of faces).

Stranger rejection (closed-set classifier variant, not included in this release)

The same architecture trained with its classification head rejects unseen DigiFace-1M faces as background at 99.45% (2,000 random synthetic strangers, 11 false accepts). The variant trained without public negatives managed only 61.2% — the negatives, not the architecture, carry open-set robustness.

Speed (Raspberry Pi 4 Model B, 4 GB)

CPU threads Model Median ms p90 ms Images/s
1 Face100K-Edge 12.35 13.55 81.0
1 Smallplain 300K 51.82 53.36 19.3
1 1M v2 166.48 170.48 6.0
4 Face100K-Edge 4.95 5.32 201.8
4 Smallplain 300K 29.20 43.63 34.2
4 1M v2 66.78 113.65 15.0

✨ Highlight: ~5 ms per face at 4 threads (201.8 images/s) — 5.9x faster than Smallplain, 13.5x faster than 1M v2 on the same board.

Measured 2026-10-12 on Raspberry Pi 4 Model B Rev 1.4 (Cortex-A72 @ 1.8 GHz), Raspberry Pi OS Bookworm (aarch64), Python 3.11, ONNX Runtime 1.31.0, FP32 opset-17 exports verified against PyTorch (< 1e-6 max abs diff). 10 warmup + 50 measured single-image calls; synthetic input; excludes detection, alignment, I/O and gallery matching. The board was running its normal resident services (~2.6 GB RAM in use), SoC at 52 °C, no throttling. The Face100K-Edge timing above is the full classifier (100,138 params); the published encoder is marginally cheaper.

Usage

pip install torch safetensors numpy opencv-python
import cv2, torch
import torch.nn.functional as F
from safetensors.torch import load_file
from model import Face100kEncoder

model = Face100kEncoder().eval()
model.load_state_dict(load_file('model.safetensors'), strict=True)
crop = cv2.imread('aligned_crop.png')  # BGR, five-point aligned, 112x112
x = torch.from_numpy(crop).permute(2, 0, 1).unsqueeze(0).float() / 127.5 - 1
with torch.inference_mode():
    emb = F.normalize(model(x), dim=1)  # (1, 192)

model.onnx is the same encoder (input x: 1x3x112x112 float32, output emb: 1x192, not L2-normalized — normalize before cosine). Do not convert the BGR input to RGB.

Training

From scratch, single seed, CPU (~25 minutes). 1,852 private crops (same train split as the Smallplain release, split SHA-256 a0a8a755fd5c63009e8b00c6715db3f40fd358bce90c1659104e1c46d693b21c) + 12,000 public negatives. AdamW lr 3e-3, weight decay 1e-4, cosine annealing, 30 epochs, batch 64, class-weighted cross-entropy (weights capped at 15x), random flip / ±8 px shift / brightness / contrast augmentation. train_reference.py is the exact training script; it cannot be rerun without the withheld private dataset.

Limitations

Trained to separate 9 specific people from everyone else; LFW 70.87% confirms it is not a general face recognizer. One seed, one training run, 59-crop private holdout (±2 faces ≈ ±4%). No liveness or spoof resistance. The public negative datasets carry their own licenses/terms (CASIA-WebFace and DigiFace-1M are research-use); their images are not redistributed here. Not validated for security-critical identification.

Downloads last month
21
Safetensors
Model size
101k params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support