Skip to content
AI360Xpert
Beta

Triplet Loss Anchor Training

Triplet loss trains on triples of anchor, match and stranger, demanding the match beat the stranger by a margin every single time.

The semi-hard triplet with distances 0.30 and 0.40 pays loss 0.10 while the easy triplet pays 0, so only margin violations teach.
The semi-hard triplet with distances 0.30 and 0.40 pays loss 0.10 while the easy triplet pays 0, so only margin violations teach.

Why Does This Exist?

Contrastive loss judges pairs in isolation against one global margin, which struggles when some identities naturally vary more than others: a strict margin that suits passport photos crushes travel selfies of the same person. A relative rule works better: whatever the scale, the match must beat the stranger. Triplet loss is that relative rule, and it made FaceNet practical. Each training unit carries its own context, an anchor with both a friend and a stranger, so the gradient always knows what closer currently means.

This page covers the margin inequality, the easy to hard split, and mining. Pair losses live in contrastive loss, the embedding-space setup lives in metric learning, and the face pipeline that consumes the embeddings lives in face detection and recognition.

Think of It Like This

A judge at a lookalike contest

A judge holds the anchor photo, stands one contestant claiming to be the twin on the left and a stranger on the right, and demands the twin stand clearly closer. The judge never measures absolute distance to the stage: only the gap between the two contestants matters, and ties go to retraining. Triplets judge embeddings the same way.

The analogy stops at the margin: the judge's clearly is a number α\alpha added to the inequality and enforced by gradient descent, usually 0.20.2 on squared distances.

How It Actually Works

The margin inequality

For anchor aa, positive pp (same identity) and negative nn (a stranger), with dd the squared Euclidean distance on normalized embeddings and α\alpha the margin, the loss is L(a,p,n)=max⁡(0,d(a,p)−d(a,n)+α)L(a, p, n) = \max(0, d(a, p) - d(a, n) + \alpha). The inequality demands d(a,n)≥d(a,p)+αd(a, n) \geq d(a, p) + \alpha: the stranger must trail the match by at least the margin, not merely lose. The gradient pulls aa toward pp and pushes aa from nn only while the inequality is violated; satisfied triplets contribute exactly zero.

Easy, semi-hard, hard

Easy triplets already trail by more than the margin and cost zero. Semi-hard triplets sit inside the margin but still order correctly (d(a,p)<d(a,n)<d(a,p)+αd(a, p) < d(a, n) < d(a, p) + \alpha) and teach steadily. Hard triplets currently order wrong (d(a,n)<d(a,p)d(a, n) < d(a, p)) and teach fastest but noisily, since the hardest cases in a dirty dataset are mislabelled pairs. Online mining assembles each batch from the semi-hard and hard cases, because batches of easy triplets burn compute for zero gradient.

Worked triplet numbers

Use squared distances with margin α=0.2\alpha = 0.2. A semi-hard case has d(a,p)=0.30d(a, p) = 0.30 and d(a,n)=0.40d(a, n) = 0.40: 0.30−0.40+0.2=0.100.30 - 0.40 + 0.2 = 0.10, so L=max⁡(0,0.10)=0.10L = \max(0, 0.10) = 0.10, a live gradient. An easy case with d(a,p)=0.05d(a, p) = 0.05 and d(a,n)=0.40d(a, n) = 0.40 gives 0.05−0.40+0.2=−0.150.05 - 0.40 + 0.2 = -0.15, so L=0L = 0 and nothing is learned. A hard case with d(a,p)=0.50d(a, p) = 0.50 and d(a,n)=0.30d(a, n) = 0.30 gives 0.50−0.30+0.2=0.400.50 - 0.30 + 0.2 = 0.40, the strongest pull of the three. The diagram plots exactly these three: only the two violations move the anchor.

Code

The point of this snippet is the hinge: the same three number pairs from the worked example replayed as code, so the 0.10, 0.0, and 0.40 can be checked.

def triplet_loss(d_ap: float, d_an: float, margin: float = 0.2) -> float:    return max(0.0, d_ap - d_an + margin)

print(round(triplet_loss(0.30, 0.40), 2))# -> 0.1print(round(triplet_loss(0.05, 0.40), 2))# -> 0.0print(round(triplet_loss(0.50, 0.30), 2))# -> 0.4

The semi-hard pair pays 0.100.10, the easy pair pays 0.00.0, and the hard pair pays 0.400.40. Mining keeps the first and third kinds and discards the second before the backward pass.

Watch Out For

Hard-only mining that chases label noise

The hardest triplets in a noisy dataset are mislabelled pairs, and training on them exclusively pulls the space toward the noise. The symptom is oscillating loss with degrading validation retrieval. Fix it with semi-hard mining: train on negatives inside the margin but still farther than the positive, and clean the noisiest labels first.

Forgetting to normalize before measuring

The margin assumes distances share one scale, which holds only for unit-length embeddings. The symptom is a loss stuck near the margin value with retrieval that never improves, because raw dot products drift across batches. Fix it by L2-normalizing every embedding before computing dd, so α=0.2\alpha = 0.2 means the same gap everywhere.

The Quick Version

  • Triplet loss enforces d(a,n)≥d(a,p)+αd(a, n) \geq d(a, p) + \alpha per triplet, a relative order rather than an absolute distance.
  • Distances 0.300.30 and 0.400.40 with margin 0.20.2 pay loss 0.100.10; distances 0.050.05 and 0.400.40 pay 00.
  • Easy triplets cost zero while semi-hard and hard triplets supply the gradient.
  • Online mining inside each batch is mandatory because random triplets are trivial.
  • Hard-only mining overfits label noise, so prefer semi-hard sampling on dirty data.