Face Verification - ResNet50 + ArcFace

512-D L2-normalised face embeddings. ResNet50 (ImageNet-initialised) fine-tuned with a hand-implemented ArcFace additive-angular-margin loss plus batch-hard triplet loss on LFW. No face-recognition library is used - the only pretrained component is the ImageNet backbone.

Embeddings are unit-norm, so cosine similarity is a plain dot product.

Results

Measured on 120 LFW identities held out of training (identity-disjoint split, so no test person appears anywhere in training).

Metric Value
ROC-AUC 0.9587
Equal Error Rate 9.61%
Verification accuracy 90.60% @ cosine threshold 0.1838
Rank-1 / Rank-5 64.00% / 85.62%
TAR @ FAR = 1% 72.76%
TAR @ FAR = 0.1% 43.76%
Open-set DIR @ FPIR = 1% 28.62%

The threshold is fitted on the validation split and applied unchanged to test.

Scoring all 530,965 test pairs rather than a 10,000-pair sample gives AUC 0.9638 and EER 8.81% - within 0.005 AUC of the sampled protocol, so the sampling is representative and what changes is resolution at low FAR.

Masked faces (MLFW)

Same checkpoint, same threshold, same pairing protocol, both datasets through the same detection and alignment pipeline.

Dataset ROC-AUC EER TAR @ FAR 1%
LFW (unmasked) 0.9587 9.61% 72.76%
MLFW (masked) 0.8388 24.58% 35.36%

MLFW is restricted to the 1,779 of its 2,996 identities absent from training; the rest overlap, because MLFW derives from CALFW which derives from LFW. This model never saw a masked face during training, and the gap is a real degradation rather than a protocol artefact.

Usage

import torch, numpy as np
from huggingface_hub import hf_hub_download
from model import load_model, embed          # from the GitHub repo below

path = hf_hub_download("yashMaini/face-verification-resnet50", "best_model.pth")
model = load_model(path, "cpu")

z = embed(model, ["a.jpg", "b.jpg"], "cpu")  # (2, 512), L2-normalised
print(float(z[0] @ z[1]) > 0.1838)           # same person?

Faces must be detected and aligned first - dataset_preparation.py in the GitHub repo does this with YuNet 5-point landmarks mapped onto the canonical ArcFace template. Feeding raw uncropped photographs will degrade results.

Details

  • Input 224x224 RGB, ImageNet mean/std normalisation
  • ResNet50 -> 2048 pool -> dropout 0.4 -> Linear(512, no bias) -> BatchNorm -> L2 norm
  • ArcFace scale 30, margin 0.5; batch-hard triplet margin 0.3 on cosine distance
  • P x K sampling, 16 identities x 4 images per batch
  • Trained on 1,500 identities / ~7,000 images
  • Embeddings use horizontal-flip test-time augmentation

Checkpoint sha256: db30180907e023cc0e3e5cb7e4d1d763305523a80f9997572b50f54e27947366

Limitations

Trained on ~7,000 images - orders of magnitude smaller than the corpora behind production face recognition, so absolute accuracy sits below published LFW figures. LFW skews heavily towards light-skinned adult males in frontal poses, so these results are not evidence of fairness across demographics. Open-set rejection is measured rather than assumed, and it is weak: at 1% FPIR only 28.62% of enrolled probes are correctly identified, meaning most unenrolled faces are not confidently rejected. Masked-face performance degrades sharply.

Not suitable for surveillance, law enforcement, or any deployment where a false match carries consequences for the person misidentified.

Code, evaluation pipeline and full report: https://github.com/Yashmaini30/face-verification-resnet50

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. 🙋 1 Ask for provider support