Face Recognition and Metric Learning: Siamese Networks, Triplet Loss, ArcFace
A fixed classification head predicts among the identities it was trained to recognize. Some face-recognition systems must also enroll new identities without fitting a new output class each time. An embedding model supports this by turning an image into a vector that can be stored and compared. Classification remains useful for learning that representation, as ArcFace illustrates below. The distinction is between the training objective and the recognition procedure at deployment.
Verification and identification
Verification compares a probe image with a claimed identity’s reference template. Identification searches a gallery. Closed-set identification assumes the person is enrolled; open-set identification must also reject unenrolled probes. For an unenrolled probe, testing more templates creates more opportunities for a false match. The often-used calculation \(1-(1-p)^K\) assumes \(K\) independent comparisons, all with false-match probability \(p\). Comparisons sharing the same probe are generally dependent, so a verification error rate does not determine the gallery error by itself.
How to choose an objective for a given problem is the subject of Loss Function Design: Choosing an Objective That Matches the Problem.
One-shot learning through embeddings
Learn an encoder that maps images of the same identity to nearby vectors and separates different identities under a chosen comparison rule. Enrollment with a fixed encoder then adds one or more reference vectors to a gallery. “One-shot” here means enrollment from one example of a new identity, not learning a general-purpose encoder from one photo. Multiple reference images can capture variation; a template may store several vectors or aggregate them. New enrollment need not retrain the encoder, though the index and operational evaluation must stay current.
L2 normalization is common: divide a nonzero embedding by its norm to obtain a unit vector. A zero or near-zero encoder output needs handling rather than being assumed to lie on the unit sphere. For unit vectors, \(\lVert u-v\rVert^2=2-2u^Tv\), so cosine similarity and squared Euclidean distance induce opposite but equivalent rankings. Their thresholds have different values and directions: cosine \(\geq0.75\) corresponds to squared distance \(\leq0.5\). For \(u=(1,0)\), \(v=(0.8,0.6)\), the similarity is 0.8 and squared distance is 0.4. Use the same encoder version and preprocessing for probes and stored references.
Siamese networks
A Siamese network applies shared encoder weights to two inputs, then compares their representations. The pair loss backpropagates through both uses of that encoder. Weight sharing gives a common coordinate system, but a useful identity representation still has to be learned. Independently trained encoders are not automatically compatible; separately trained models can also be explicitly aligned or trained for compatible embeddings.
Two ways to train it
The binary classification formulation. Use the absolute elementwise difference of two embeddings to predict a same-identity label of 1 versus a different-identity label of 0:
\[\hat y=\sigma\left(\sum_k w_k\left|f(x_1)_k-f(x_2)_k\right|+b\right)\]
The absolute difference makes the score symmetric in the two inputs. During training, feed the pre-sigmoid score to BCEWithLogitsLoss. Gallery embeddings can be cached here and in triplet-trained systems, leaving one encoder pass per probe. But the learned pair scorer is not automatically a distance metric or a monotone function of cosine similarity: unconstrained weights can define a different ranking. At deployment, use the trained scorer or validate a replacement comparison rule. Pair sampling also affects whether its sigmoid output is calibrated for deployment.
Triplet loss. Take an anchor \(A\), a positive \(P\) of the same identity, and a negative \(N\) of a different one. Require the anchor to be closer to the positive than to the negative by at least a margin \(\alpha\):
\[\mathcal{L}=\max\left(\lVert f(A)-f(P)\rVert^2-\lVert f(A)-f(N)\rVert^2+\alpha,\;0\right)\]
With squared distances, zero margin permits a constant embedding to achieve zero loss. A positive margin makes that collapsed solution incur loss \(\alpha\), but does not guarantee escape: if all embeddings coincide, the squared-distance gradients are still zero. This also holds for a constant unit vector, so normalization alone does not prevent collapse. A triplet strictly beyond the required margin has zero gradient. At exact equality the hinge has a kink; PyTorch’s ReLU uses a zero derivative there.
import torch
import torch.nn.functional as F
def triplet_loss(anchor, positive, negative, margin=0.2):
"""Equal nonempty (N,D) tensors; squared-distance formulation."""
if (anchor.ndim != 2 or anchor.shape != positive.shape
or anchor.shape != negative.shape or anchor.shape[0] == 0):
raise ValueError("expected matching nonempty (N,D) embeddings")
if margin < 0:
raise ValueError("margin must be nonnegative")
d_pos = (anchor - positive).pow(2).sum(dim=1)
d_neg = (anchor - negative).pow(2).sum(dim=1)
return F.relu(d_pos - d_neg + margin).mean()
a = torch.tensor([[1., 0.], [1., 0.]])
p = torch.tensor([[0.8, 0.6], [0.8, 0.6]])
n = torch.tensor([[0., 1.], [0.96, 0.28]])
print(round(triplet_loss(a[:1], p[:1], n[:1]).item(), 4))
print(round(triplet_loss(a[1:], p[1:], n[1:]).item(), 4))
print(round(triplet_loss(a, p, n).item(), 4))
# 0.0
# 0.52
# 0.26
All vectors in this example have unit length. The positive squared distance is 0.4 in both rows. Negative distances are 2.0 and 0.08, giving losses 0 and 0.52, then a batch mean of 0.26. The margin is in squared-distance units. This function differs from the default unsquared-distance formula in PyTorch’s TripletMarginLoss; exchanging the two changes the objective.
Hard negative mining
As an encoder learns to separate identities, randomly selected negatives can leave many triplets beyond the margin. Measure the active-triplet fraction rather than assuming all random triplets are uninformative. Early in training, or on difficult data, random triplets may still contribute gradients. Mining selects useful comparisons from the available examples.
Batch-hard mining chooses the farthest positive and nearest negative for each anchor within the batch. A batch with at least two identities and at least two images per identity ensures both kinds of comparison are available. The code excludes self-comparisons, averages only anchors with valid positives and negatives, and raises an error if none are valid. Singleton identities therefore contribute no anchor loss, though their embeddings can still serve as negatives for other anchors.
def batch_hard_triplet_loss(emb, labels, margin=0.2):
"""Normalized (N,D) embeddings and (N,) identity labels on the same device."""
if emb.ndim != 2 or labels.shape != (len(emb),) or margin < 0:
raise ValueError("expected embeddings (N,D), labels (N,), nonnegative margin")
dist = torch.cdist(emb, emb, p=2).square()
same = labels[:, None] == labels[None, :]
eye = torch.eye(len(labels), dtype=torch.bool, device=emb.device)
pos_mask = same & ~eye
neg_mask = ~same
valid = pos_mask.any(dim=1) & neg_mask.any(dim=1)
if not valid.any():
raise ValueError("no anchor has both a positive and a negative")
hardest_pos = dist.masked_fill(~pos_mask, -torch.inf).max(dim=1).values
hardest_neg = dist.masked_fill(~neg_mask, torch.inf).min(dim=1).values
return F.relu(hardest_pos[valid] - hardest_neg[valid] + margin).mean()
emb = torch.tensor([[1.,0.], [0.,1.], [-1.,0.], [0.,-1.]])
labels = torch.tensor([0, 0, 1, 1])
print(round(batch_hard_triplet_loss(emb, labels).item(), 4))
# 0.2
Batch-hard is not semi-hard mining. For squared distances, a semi-hard negative satisfies \(d_{AP}^2<d_{AN}^2<d_{AP}^2+\alpha\): it is farther away than the positive but still violates the margin. A batch-hard negative can be closer than the positive. Extremely hard selections may emphasize mislabeled examples or outliers, so inspect labels and compare selection policies; restricting the search to a batch does not itself guarantee stability.
Margin-based softmax: ArcFace
ArcFace learns embeddings using a classification head over training identities. Normalize both an embedding and each class-weight vector so their dot product is \(\cos\theta_j\). Scale the logits by \(s\), and replace the true-class cosine with \(\cos(\theta_y+m)\) during training. This adds an angular margin to the cross-entropy objective:
\[\mathcal{L}=-\log\frac{e^{s\cos(\theta_y+m)}}{e^{s\cos(\theta_y+m)}+\sum_{j\ne y}e^{s\cos\theta_j}}\]
Here \(y\) is the training identity, \(m\) is measured in radians, and \(s\) controls logit scale. For \(\theta_y=30^\circ\) and \(m=0.5\), the target angle becomes about \(58.65^\circ\), reducing its cosine from about 0.866 to 0.520. The model must improve the target alignment to compensate. Practical implementations handle the angular boundary so the target transformation remains appropriate when \(\theta_y+m\) goes beyond \(\pi\). Values such as \(s=64,m=0.5\) are reported settings, not universal constants. At deployment the training-identity head can be discarded and normalized embeddings compared for previously unseen identities.
The full pipeline
A common pipeline detects a face, finds landmarks, aligns and crops it, applies the encoder’s preprocessing, extracts an embedding, and compares it with reference templates. Many checkpoints use a similarity transform based on landmarks; the appropriate alignment is part of the model’s input convention. Some models tolerate or train for different crops and poses, so alignment is not universally mandatory. Match the training and deployment pipeline, and count detection or quality-control failures in system evaluation rather than silently discarding them.
A threshold converts a score into a match decision. Higher cosine thresholds usually reduce false matches at the cost of more missed genuine matches; a distance threshold moves in the opposite direction. Choose the operating point on validation data for the intended error costs, then freeze it for testing. Authentication also requires evaluating presentation attacks separately: a low impostor-match rate does not establish resistance to photographs or replayed video.
import numpy as np
# Synthetic similarity scores, not measurements from a face model.
genuine = np.array([0.9, 0.8, 0.7])
impostor = np.array([0.85, 0.6, 0.2])
for threshold in (0.75, 0.9):
false_match = (impostor >= threshold).mean()
false_nonmatch = (genuine < threshold).mean()
print(threshold, round(false_match, 4), round(false_nonmatch, 4))
# 0.75 0.3333 0.3333
# 0.9 0.0 0.6667
At 0.75, one impostor pair is accepted and one genuine pair is rejected. At 0.9, none of these three impostor pairs is accepted, but two genuine pairs are rejected. Zero errors in three comparisons is not evidence of a very low population error rate. Large galleries require suitable evaluation data and uncertainty estimates, including the dependence created by repeated subjects.
A practical fine-tuning and augmentation recipe is in Image Classification in Practice: Transfer Learning and Augmentation.
Evaluation and responsible use
For verification, report the false-match rate on impostor pairs and false-nonmatch rate on genuine pairs across thresholds, with counts and uncertainty. System-level false acceptance and rejection can additionally include capture failures and other decision stages. For open-set identification, evaluate false-positive identification per unenrolled probe and missed correct identifications for enrolled probes, specifying gallery size and rank cutoff. If the goal is generalization to new identities, keep training, validation, and test identities separate; genuine evaluation pairs still contain different images of the same evaluation identity.
Examine error rates across documented demographic groups and capture conditions when the evaluation data support those analyses, and report group sample sizes and how attributes were obtained. NIST’s demographic-effects study found differences that depend on the algorithm, dataset, and error type; high aggregate accuracy can conceal them. Gender Shades evaluated gender classification, not identity matching, so its results should not be cited as direct measurements of face-verification errors.
Before collecting or deploying biometric templates, determine the legal basis and data-handling requirements for the intended jurisdiction and use. Consent is not a universal substitute for that assessment. Define access, retention, deletion, and responses to errors before deployment. Set acceptance criteria for overall and group-specific performance according to the application’s consequences, then investigate failures against those criteria rather than treating every observed difference as automatically acceptable or automatically disqualifying.
Representation-learning objectives are covered in Self-Supervised Learning: SimCLR, BYOL, MAE, and CLIP; some use unannotated images, while CLIP uses paired text–image supervision.
Exercises
1. Adding an identity. A face system enrolls 500 people today and 30 more next week. For a conventional learned linear softmax head and a fixed-encoder gallery, describe what changes when 30 identities are added. Give the parameter or template count change; explain why training time cannot be inferred from those counts alone.
You should get: 30 additional classifier weight vectors and biases, or 30 new gallery vectors when storing one template per identity.
Solution
With embeddings of width \(d\), a conventional linear head grows from 500 to 530 output classes. It adds \(30(d+1)\) trainable scalars when each class has a bias. In PyTorch the weight shape is \((500,d)\), becoming \((530,d)\). New class weights need fitting in this setup, but the backbone can be frozen; retraining the whole network is not inherently required.
A fixed encoder with one template per identity adds 30 vectors, or \(30d\) stored scalar values, after encoding the enrollment images. Gallery insertion and any index update also take work. Multiple images or aggregated templates change the enrollment cost. Exact runtime depends on the model, hardware, and training procedure; “milliseconds” cannot be derived from the prompt.
Embedding systems can be trained with identity classification, then use a gallery at deployment. The benefit is separating representation training from adding reference identities.
2. When random triplets are inactive. Generate the 1,000 synthetic triplets below, where positives are deliberately close to anchors. Report the zero-loss fraction at margin 0.2, then repeat with positives sampled independently like the negatives. Explain why these constructions do not establish how random triplets behave for a trained face encoder.
You should get: all triplets inactive in the close-positive construction, but many active with independent positives.
Solution
import numpy as np
rng = np.random.default_rng(0)
def unit(n, d=128):
v = rng.normal(size=(n, d)); return v / np.linalg.norm(v, axis=1, keepdims=True)
A = unit(1000)
P = unit(1000)*0.1 + A*0.9
P /= np.linalg.norm(P, axis=1, keepdims=True) # positives: close to anchor
N = unit(1000) # negatives: random
d_pos = ((A-P)**2).sum(1); d_neg = ((A-N)**2).sum(1)
loss = np.maximum(d_pos - d_neg + 0.2, 0)
print("zero-loss fraction", round(float((loss == 0).mean()), 4))
# zero-loss fraction 1.0
print("mean loss", round(float(loss.mean()), 6))
# mean loss 0.0
P_independent = unit(1000)
d_pos_independent = ((A-P_independent)**2).sum(1)
loss_independent = np.maximum(d_pos_independent - d_neg + 0.2, 0)
print("independent positives active:", (loss_independent > 0).mean() > 0.5)
# independent positives active: True
The first construction forces positives close to anchors, and every sampled triplet satisfies the margin in this run. Independent positives instead have distance statistics similar to negatives, so many triplets violate the margin. Neither construction samples real same-identity images; the result depends on how the embeddings were generated.
For an actual encoder, measure positive and negative distance distributions and the active-triplet fraction. These observations help decide whether mining is useful and which policy to compare.
3. Gallery size under an independence model. Assume an unenrolled probe, independent impostor comparisons with a common 1% false-match rate, and one template per identity. Compute the probability of at least one false match when identifying against galleries of 100, 1,000, and 10,000 enrolled people. Find the per-comparison probability giving a 1% gallery-level error at 10,000; state how the conclusion changes without independence.
You should get: a near-certain false match at the largest gallery, and a required per-comparison false-match rate several orders of magnitude smaller.
Solution
far = 0.01
for n in (100, 1000, 10000):
print(n, round(1 - (1-far)**n, 6))
# 100 0.633968
# 1000 0.999957
# 10000 1.0
import math
boundary = -math.expm1(math.log1p(-0.01) / 10000)
print(f"{boundary:.8e}")
# 1.00503308e-06
Under these assumptions, the 1,000-template probability is about 0.999957. The calculated boundary is approximately \(1.005\times10^{-6}\); a strictly smaller probability is needed for a gallery error strictly below 1%. The printed 1.0 at 10,000 is rounding, not exact certainty.
Without independence, the formula need not hold. The union bound still gives \(P(\text{any false match})\leq\sum_i p_i\), so a common rate strictly below \(10^{-6}\) is sufficient for a bound below 1% at 10,000 comparisons, but is not necessary. Perfectly correlated comparisons with common error probability 1% would have a 1% gallery error regardless of their number. Evaluate the actual probe–gallery procedure rather than treating the independence model as a deployment prediction.
References
- Bromley et al. (1993). Signature Verification Using a Siamese Time Delay Neural Network. NIPS.
- Koch, Zemel, and Salakhutdinov (2015). Siamese Neural Networks for One-Shot Image Recognition.
- Schroff, Kalenichenko, and Philbin (2015). FaceNet: A Unified Embedding for Face Recognition and Clustering. CVPR.
- Hermans, Beyer, and Leibe (2017). In Defense of the Triplet Loss for Person Re-Identification. arXiv preprint.
- Buolamwini and Gebru (2018). Gender Shades: Intersectional Accuracy Disparities in Commercial Gender Classification. FAT* (2018).
- Deng, Guo, Xue, and Zafeiriou (2019). ArcFace: Additive Angular Margin Loss for Deep Face Recognition. CVPR.
- Grother, Ngan, and Hanaoka (2019). Face Recognition Vendor Test Part 3: Demographic Effects. NIST Interagency Report 8280.
- UK Information Commissioner’s Office. Biometric recognition guidance (UK context; check the applicable jurisdiction).
Discover more from Insightful Data Lab
Subscribe to get the latest posts sent to your email.
