Features & Representation

Triplet Loss

A loss for learning embeddings that asks an anchor image to be closer to a positive of its own class than to a negative of another class by a margin, how it grew out of relative-comparison metric learning, and how the choice of triplets (random, semi-hard, batch-hard, distance-weighted) decides whether training learns, stalls or collapses.

intermediate

The triplet loss trains an embedding from triples of images: an anchor, a positive of the same class (the same person, product or object) and a negative of a different class. It asks that the anchor be closer to the positive than to the negative by at least a margin, and it is zero once that holds. Because it only constrains relative distances, it is also called a triplet ranking loss or triplet margin loss. It is one of the losses of metric learning, best known from FaceNet (Schroff, Kalenichenko & Philbin, 2015). Most of what makes it work or fail lies in which triplets it is given, a choice called triplet mining or triplet selection.

Definition

Given an embedding ff, a distance DD and a margin α>0\alpha > 0, the triplet loss of an anchor xax^a, a positive xpx^p with the same label and a negative xnx^n with a different label is the hinge

ℓ(xa,xp,xn)=[ D(f(xa),f(xp))−D(f(xa),f(xn))+α ]+,[z]+=max⁡(z,0).\ell(x^a, x^p, x^n) = \big[\, D\big(f(x^a), f(x^p)\big) - D\big(f(x^a), f(x^n)\big) + \alpha \,\big]_+ , \qquad [z]_+ = \max(z, 0).

A triplet is active when its loss is positive, that is, when the negative is not at least α\alpha farther from the anchor than the positive. Only active triplets produce gradients.

Intuition

Think of a gallery of photographs of many people. A pair loss, such as the contrastive loss, tells the network an absolute target: same person, distance near zero; different people, distance beyond a fixed threshold. A triplet states only an order: this photograph of Ana should be nearer to another photograph of Ana than to one of Ben. Nothing fixes how tight Ana’s cluster must be or where it sits, only that Ben stays a margin farther away than Ana’s own photographs, as seen from each of them. FaceNet’s authors argued for this freedom: a pair loss pulls every image of one identity toward a single point, while the triplet loss lets an identity’s images spread along a manifold, as long as other identities stay a margin away (Schroff et al., 2015). Wu et al. (2017) gave the same reason in terms of the space: with no predefined threshold, it can be distorted to fit classes of different spread.

The catch is in the number of triplets. With nn training images there are O(n3)O(n^3) of them, and after a little training almost all are satisfied: the network has learned that a disk with horizontal stripes is not a cross with vertical ones, and showing it that again teaches nothing. The informative triplets are the few that are still violated, and finding them without drowning in noise or outliers is the mining problem.

Formal Definition

The loss and its distance

For a set T\mathcal{T} of triplets, the training objective is ∑(a,p,n)∈Tℓ(xa,xp,xn)\sum_{(a, p, n) \in \mathcal{T}} \ell(x^a, x^p, x^n), often averaged. Two choices define the variant.

Squared or unsquared distance. FaceNet and Wang et al. (2014) used the squared Euclidean distance D=∥f(x)−f(y)∥22D = \lVert f(x) - f(y) \rVert_2^2. Hermans, Beyer and Leibe (2017) used the unsquared distance D=∥f(x)−f(y)∥2D = \lVert f(x) - f(y) \rVert_2, and Wu et al. (2017) proposed it as a remedy and compared both. The difference shows in the gradients. Writing fa,fp,fnf_a, f_p, f_n for the three embeddings, an active triplet with squared distances has

∂ℓ∂fn=2 (fa−fn),∂ℓ∂fp=2 (fp−fa),∂ℓ∂fa=2 (fn−fp),\frac{\partial \ell}{\partial f_n} = 2\,(f_a - f_n), \qquad \frac{\partial \ell}{\partial f_p} = 2\,(f_p - f_a), \qquad \frac{\partial \ell}{\partial f_a} = 2\,(f_n - f_p),

so the push on a negative is proportional to its distance from the anchor and vanishes for the hardest negatives, those right on top of the anchor. With unsquared distances, ∂ℓ/∂fn=(fa−fn)/∥fa−fn∥\partial \ell / \partial f_n = (f_a - f_n) / \lVert f_a - f_n \rVert has unit length at every distance. Wu et al. pointed to the vanishing push as the reason hard-negative mining with the squared loss collapses: hard positives are pulled in strongly while hard negatives are pushed out weakly, so all points gather together. Hermans et al. likewise noticed in initial experiments, without a side-by-side comparison, that the squared distance made training more prone to collapse, and that it makes the margin harder to interpret because it is no longer a distance.

Normalized or free embeddings. FaceNet constrained the embedding to the unit hypersphere, ∥f(x)∥2=1\lVert f(x) \rVert_2 = 1. For unit vectors ∥u−v∥2=2−2cos⁡(u,v)\lVert u - v \rVert^2 = 2 - 2\cos(u, v), as derived in similarity and distance measures, so the squared distance lies in [0,4][0, 4] and the squared-distance constraint becomes a constraint on cosines:

∥fa−fn∥2−∥fa−fp∥2≥α  ⟺  cos⁡(fa,fp)−cos⁡(fa,fn)≥α2.\lVert f_a - f_n \rVert^2 - \lVert f_a - f_p \rVert^2 \ge \alpha \iff \cos(f_a, f_p) - \cos(f_a, f_n) \ge \tfrac{\alpha}{2} .

FaceNet’s margin α=0.2\alpha = 0.2 is therefore a cosine gap of 0.1. Without normalization the margin has no fixed scale: by our reasoning, multiplying a network’s output by s>1s > 1 multiplies every distance gap by ss (by s2s^2 for squared distances), so any triplet already ordered correctly can be pushed past any margin by growing the embedding. Hermans et al. trained without a normalization layer anyway; they argued that it can hide problems such as slowly collapsing or exploding embeddings, and their training logs show embeddings that stayed bounded with a margin of 0.2.

Soft margin. Hermans et al. also replaced the hinge with the softplus ln⁡(1+ez)\ln(1 + e^{z}), which never reaches zero and so keeps pulling same-class images together. Their logs show the soft-margin version kept pushing embeddings apart throughout training.

Easy, semi-hard and hard negatives

With dap=D(fa,fp)d_{ap} = D(f_a, f_p) and dan=D(fa,fn)d_{an} = D(f_a, f_n), a negative falls in one of three bands relative to a given anchor and positive:

Band Condition Loss
easy dan≥dap+αd_{an} \ge d_{ap} + \alpha zero
semi-hard dap<dan<dap+αd_{ap} < d_{an} < d_{ap} + \alpha positive, below α\alpha
hard dan≤dapd_{an} \le d_{ap} at least α\alpha

FaceNet’s Eq. 1 is the easy condition (with a strict inequality; equation numbers here are the CVPR text’s, which prints Eq. 1 on the images rather than on f(x)f(x), while arXiv v2 and v3 number the semi-hard condition Eq. 4), and its authors noted that triplets satisfying it contribute nothing but still cost a forward pass. The ideal hard examples, in their Section 3.2, are the hardest positive arg⁡max⁡pdap\arg\max_{p} d_{ap} and the hardest negative arg⁡min⁡ndan\arg\min_{n} d_{an}. Their semi-hard negatives satisfy Eq. 3, dap<dand_{ap} < d_{an}, with the text adding that such negatives lie inside the margin. The three bands are the convention of later implementations, not a table from the paper, and implementations read “semi-hard” differently: Wu et al. (2017) formalized FaceNet’s rule as the closest negative with dan>dapd_{an} > d_{ap} for each anchor-positive pair, which may lie outside the margin, while pytorch-metric-learning’s semi-hard miner keeps every triplet inside the band.

Origins

Relative comparisons. Schultz and Joachims (2003) learned a distance from statements of the form “A is closer to B than A is to C”, motivated by data such as search-engine clicks, where relative judgments are easier to obtain than absolute ones. They learned a weighted Euclidean distance (a diagonal weighting, optionally after a fixed linear or kernel map) with an SVM-style program: each comparison (i,j,k)(i, j, k) required the squared distance from xix_i to xkx_k to exceed that to xjx_j by 1, up to a slack variable, while the weights stayed close to the plain Euclidean metric. This is the triplet hinge with unit margin on squared distances, on fixed features, and the problem was a convex quadratic program. Their experiments used web pages, not images.

Large margin nearest neighbors. LMNN (Weinberger, Blitzer & Saul, 2005; Weinberger & Saul, 2009) wrote its push term as a hinge on triples: an input, one of its target neighbors and a differently labeled impostor, with a unit margin on squared Mahalanobis distances; its objective is in the metric learning article. Its solver already exploited the fact that most triples incur no margin violation, checking only a working set of active triples between full re-checks (Weinberger & Saul, 2009, Appendix A). FaceNet describes its loss as based on LMNN. Hermans et al. (2017) state the difference: LMNN fixed each point’s target neighbors before training, allowing a class to form several separate clusters as nearest-neighbor classification permits, whereas FaceNet’s triplets use any positive, which over a long training eventually pulls each class into one cluster without collapsing it to a point.

Deep networks. Wang et al. (2014) trained a convolutional network on triplets of images to rank images by fine-grained similarity, with the triplet hinge on squared Euclidean distances (their “gap parameter” gg is the margin) and three copies of the network sharing weights. Positives and negatives came from relevance scores within a search query’s results, so a negative could be in the same category but less similar (“in-class”) or from another query (“out-of-class”). On their roughly 14,000 human-rated test triplets, the multiscale network ranked 85.7% of the triplets correctly, against 82.8% for an ImageNet classification network with the architecture of its main path and 80.3% for the “golden feature”, a learned weighted combination of 27 features, that had generated its training relevance (their Tables 1 and 2). FaceNet (Schroff et al., 2015) then trained a face embedding of 128 dimensions with the loss on normalized embeddings and α=0.2\alpha = 0.2.

Mining Strategies

Offline and online. FaceNet noted that computing the hardest examples over the whole training set is infeasible and would also select mislabeled and poorly imaged faces. It considered two alternatives: generating triplets every few steps with the latest checkpoint on a subset of the data (offline), or choosing them within each mini-batch (online). It used online selection with batches of about 1,800 images, about 40 per identity plus random negatives, used every anchor-positive pair in the batch rather than the hardest positive (more stable, and slightly faster to converge early on), and chose semi-hard negatives, because the hardest negatives could lead to bad local minima early in training, including a collapsed model.

Batch hard and batch all. Hermans et al. (2017) built each batch from PP classes with KK images each and mined inside it. Batch hard takes, for every anchor, its hardest positive and its hardest negative within the batch:

LBH=∑i=1P∑a=1K[ α+max⁡pD(f(xai),f(xpi))−min⁡j≠i,  nD(f(xai),f(xnj))]+,\mathcal{L}_{\text{BH}} = \sum_{i=1}^{P} \sum_{a=1}^{K} \Big[\, \alpha + \max_{p} D\big(f(x^i_a), f(x^i_p)\big) - \min_{j \ne i,\; n} D\big(f(x^i_a), f(x^j_n)\big) \Big]_+ ,

which gives PKPK terms; the authors call them moderate triplets, since they are the hardest only within a small random subset of the data. Batch all sums over all PK(PK−K)(K−1)PK(PK - K)(K - 1) valid triplets in the batch. On their validation split of the MARS person re-identification training set, with a small network trained from scratch on half-resolution images, the plain triplet loss with random triplets reached 41.7% mAP at margin 0.2; offline hard mining reached 61.4% at that margin but got trapped in bad optima at margins 0.1 and 0.5; batch hard reached 65.3%; batch all 60.5%, rising to 64.3% when the loss was averaged only over its non-zero terms; and batch hard with the soft margin scored best, 65.8% (their Table 1). The authors attributed the weakness of plain batch all to the many zero terms “washing out” the few useful ones in the average, and limited their conclusion to person re-identification.

Distance-weighted sampling. Wu et al. (2017) argued that hard negatives give gradient directions dominated by noise when they sit very close to the anchor, random negatives give no loss, and semi-hard mining finds a narrow band that eventually runs out of examples, so training stops progressing. They sampled negatives with probability inversely proportional to the density of distances on the hypersphere, clipped for the closest ones (the formula is in the metric learning article). Trained from scratch on Stanford Online Products with batches of 80, and reporting each method at its best epoch of 100, the squared triplet loss reached Recall@1 49.7 with semi-hard negatives and 53.4 with distance-weighted ones; the unsquared loss 47.4 and 54.5 (their Tables 1 and 2).

Anchor swap. Balntas et al. (2016), learning local patch descriptors, computed the third distance in each triplet, between positive and negative, and used the smaller of dand_{an} and dpnd_{pn} as the negative distance, a cheap in-triplet form of hard-negative mining.

Properties

  • Relative, per anchor. Each constraint compares two distances from one anchor, so the loss sets no global threshold. By our reasoning, if every triplet of a training set satisfies the margin, each image’s farthest same-class neighbor is closer than its nearest other-class neighbor, which gives perfect nearest-neighbor retrieval on that set, but one class can still be spread wider than the gap between two others, so no single distance threshold need separate all same-class pairs from all different-class pairs. Verification, which thresholds a distance, relies on the classes being tight as well as ordered.
  • Collapse is a fixed point. By our reasoning from the gradients above, if every image maps to the same point, every triplet has loss exactly α\alpha and, with squared distances, every gradient is zero. With unit-length embeddings the network cannot output f(x)=0f(x) = 0, the collapse FaceNet describes, but it can map everything to one point on the sphere.
  • A flat loss under mining does not mean no progress. Hermans et al. observed that with hard mining the loss can stay high while the embedding improves, because each solved case is replaced by a harder one; they recommended monitoring the fraction of active triplets in a batch, the norms of the embeddings and the pairwise distances (their supplementary Section D). They also described a typical run as first pulling all embeddings toward their center, then making them pass each other into their clusters, a difficult “packed” phase in a very small network’s log; runs collapse when the margin is too large and the initial spread too small for this crossing, and in their experience got stuck only with too weak networks or with offline hard mining.

Examples

Mining strategies on synthetic image classes

The PyTorch script below renders 32×3232 \times 32 grayscale images of 36 classes, each a (shape, stripe angle) pair, with each object shifted by up to five pixels and given a random size, stripe phase, lighting ramp and noise; each image is standardized to zero mean and unit variance. It is the generator of the deep example in metric learning. A small convolutional network with a 32-dimensional, ℓ2\ell_2-normalized output is trained for 400 steps on 18 classes with batches of P=8P = 8 classes by K=4K = 4 images, using PyTorch’s TripletMarginLoss (unsquared Euclidean distance) with margin 0.2, averaged over the non-zero terms as in Hermans et al.’s variant. Three strategies choose the triplets from each batch:

  • random: every anchor-positive pair with one negative drawn uniformly from the batch;
  • semi-hard: every anchor-positive pair with the closest negative farther than the positive (FaceNet’s Eq. 3 as Wu et al. formalized it); pairs with no such negative are skipped, a choice FaceNet does not specify;
  • batch-hard: each anchor with its hardest positive and hardest negative (Hermans et al.).

Over the last 50 steps it records the fraction of all valid triplets in the batch that are active, the fraction of the picked triplets that are active, and the mean pairwise distance in the batch; it then reports Recall@1 on 540 images of the 18 unseen classes, where each image queries the other 539 (Recall@K). Choosing a neighbor at random would succeed with probability 29/539≈0.05429/539 \approx 0.054. It uses PyTorch 2.14.1 on one CPU thread, runs three seeds per strategy, and takes about 28 seconds; two runs printed identical output.

import torch
import torch.nn as nn
import torch.nn.functional as F

torch.set_num_threads(1)
H = 32
yy, xx = torch.meshgrid(torch.arange(H) - 15.5, torch.arange(H) - 15.5, indexing="ij")

def make_images(labels, gen):
    """32x32 striped shapes, class = shape * 6 + stripe angle (36 classes), with
    +-5 px shifts, random size, stripe phase, lighting ramp and noise."""
    n = len(labels)
    U = lambda lo, hi, *s: lo + (hi - lo) * torch.rand(n, *s, generator=gen)
    r = U(7, 11)[:, None, None]
    c = U(-5, 5, 2)[:, :, None, None]
    y, x = yy - c[:, 0], xx - c[:, 1]
    shapes = torch.stack([
        x**2 + y**2 < r**2,                                               # disk
        (x.abs() < 0.8 * r) & (y.abs() < 0.8 * r),                        # square
        (y < 0.7 * r) & (y > -r + 2 * x.abs()),                           # triangle
        ((x.abs() < 0.35 * r) | (y.abs() < 0.35 * r)) & (x.abs() < r) & (y.abs() < r),  # cross
        (x**2 + y**2 < r**2) & (x**2 + y**2 > (0.6 * r) ** 2),            # ring
        (x.abs() < r) & (y.abs() < 0.45 * r)], 1)                         # bar
    mask = shapes[torch.arange(n), labels // 6].float()
    t = torch.deg2rad(30.0 * (labels % 6))[:, None, None]
    stripes = 0.5 + 0.5 * torch.sin(2 * torch.pi * (x * t.cos() + y * t.sin()) / 5
                                    + U(0, 2 * torch.pi)[:, None, None])
    g = U(-0.025, 0.025, 2)[:, :, None, None]
    img = (U(0.2, 0.6)[:, None, None] + g[:, 0] * xx + g[:, 1] * yy       # background + ramp
           + U(0.3, 0.6)[:, None, None] * mask * stripes                  # object
           + 0.1 * torch.randn(n, H, H, generator=gen))                   # sensor noise
    img = (img - img.mean((1, 2), keepdim=True)) / img.std((1, 2), keepdim=True)
    return img[:, None]

def embedder(d=32):
    return nn.Sequential(
        nn.Conv2d(1, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
        nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
        nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(),
        nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(64, d))

def mine(D, y, mining, gen):
    """Return (anchor, positive, negative) indices of the triplets a strategy picks."""
    same = y[:, None] == y[None]
    pos, neg = same & ~torch.eye(len(y), dtype=torch.bool), ~same
    if mining == "batch-hard":              # Hermans et al. (2017): hardest p and n per anchor
        a = torch.arange(len(y))
        p = D.masked_fill(~pos, -1).argmax(1)
        n = D.masked_fill(~neg, float("inf")).argmin(1)
        return a, p, n
    a, p = pos.nonzero(as_tuple=True)       # otherwise: every (anchor, positive) pair
    if mining == "random":                  # one negative drawn uniformly
        n = torch.multinomial(neg[a].float(), 1, generator=gen).squeeze(1)
    else:                                   # semi-hard (FaceNet, Eq. 3): closest n with D_an > D_ap
        Dn = D[a].masked_fill(~neg[a] | (D[a] <= D[a, p][:, None]), float("inf"))
        keep = Dn.amin(1) < float("inf")    # pairs with no semi-hard negative are skipped
        a, p, n = a[keep], p[keep], Dn[keep].argmin(1)
    return a, p, n

def active_fraction(D, y, margin):
    """Share of ALL valid triplets in the batch with D_ap - D_an + margin > 0."""
    same = y[:, None] == y[None]
    valid = (same & ~torch.eye(len(y), dtype=torch.bool))[:, :, None] & ~same[:, None, :]
    return ((D[:, :, None] - D[:, None, :] + margin > 0) & valid).sum() / valid.sum()

def run(seed, mining, steps=400, P=8, K=4, margin=0.2):
    torch.manual_seed(seed)
    gen = torch.Generator().manual_seed(seed)
    cls = torch.arange(36)
    seen = cls[(cls // 6 + cls % 6) % 2 == 0]             # 18 training classes
    unseen = cls[(cls // 6 + cls % 6) % 2 == 1]           # 18 new (shape, angle) pairs
    ytr, yte = seen.repeat_interleave(60), unseen.repeat_interleave(30)
    xtr, xte = make_images(ytr, gen), make_images(yte, gen)
    net = embedder()
    opt = torch.optim.Adam(net.parameters(), lr=3e-3)
    loss_fn = nn.TripletMarginLoss(margin=margin, reduction="none")   # unsquared L2 distance
    log = []
    for step in range(steps):
        pick = seen[torch.randperm(18, generator=gen)[:P]]  # P classes x K images
        idx = torch.cat([torch.nonzero(ytr == k).flatten()[torch.randperm(60, generator=gen)[:K]]
                         for k in pick])
        z = F.normalize(net(xtr[idx]), dim=1)               # unit-length embeddings
        D = torch.cdist(z, z).detach()                      # distances used only for mining
        a, p, n = mine(D, ytr[idx], mining, gen)
        terms = loss_fn(z[a], z[p], z[n])
        loss = terms.sum() / (terms > 0).sum().clamp(min=1) # average over non-zero terms
        opt.zero_grad()
        loss.backward()
        opt.step()
        if step >= steps - 50:                              # last 50 steps
            log.append([active_fraction(D, ytr[idx], margin).item(),
                        (terms > 0).float().mean().item(), D.mean().item()])
    with torch.no_grad():
        zt = F.normalize(net(xte), dim=1)
        S = zt @ zt.T
        S.fill_diagonal_(-float("inf"))
        r1 = (yte[S.argmax(1)] == yte).float().mean().item()
    return torch.tensor(log).mean(0).tolist() + [r1]

print("PyTorch", torch.__version__)
print("mining      seed  active(all)  active(picked)  mean dist   R@1 unseen")
for mining in ["random", "semi-hard", "batch-hard"]:
    for seed in range(3):
        act_all, act_picked, dist, r1 = run(seed, mining)
        print(f"{mining:10s}  {seed:4d}  {act_all:11.3f}  {act_picked:14.3f}  {dist:9.3f}  {r1:11.3f}")
PyTorch 2.14.1
mining      seed  active(all)  active(picked)  mean dist   R@1 unseen
random         0        0.097           0.099      0.929        0.613
random         1        0.101           0.104      0.927        0.630
random         2        0.098           0.099      0.942        0.722
semi-hard      0        0.052           0.391      1.110        0.780
semi-hard      1        0.046           0.369      1.086        0.837
semi-hard      2        0.044           0.337      1.084        0.909
batch-hard     0        1.000           1.000      0.001        0.430
batch-hard     1        1.000           1.000      0.001        0.404
batch-hard     2        1.000           1.000      0.001        0.456

Semi-hard mining gave the best Recall@1 on the unseen classes in every seed, and random triplets the next best. The two columns of active fractions show why. With random negatives, the triplets picked were no more often active than the batch as a whole, about one in ten, so nine-tenths of each batch’s terms carried no gradient. Semi-hard mining picked triplets of which a third or more were active, and it left fewer of all the batch’s triplets violated (about one in twenty): it spent its gradient where it was needed and ended with better-ordered training batches.

Batch hard collapsed in all three seeds. A mean distance of 0.001 between unit vectors means every image sits at nearly the same point of the sphere: every triplet is active, each loss term is within about 0.001 of the margin, and the small residual differences keep Recall@1 above chance but well below the other two strategies. In our reading the runs never got past the crossing described under Properties, the phase Hermans et al. call “packed”; that the small network (about 25,000 parameters) and short schedule are the cause is our guess, which the run does not test. Nor does it show that batch hard is worse in general: Hermans et al. found it the best of their variants with a 5-million-parameter network. What it does show is their diagnostic: a loss stuck near the margin is ambiguous under hard mining, but an active fraction of 1.000 and vanishing pairwise distances identify the collapse.

Libraries

PyTorch’s torch.nn.TripletMarginLoss (version 2.14.1) computes the hinge for triplets that the caller has already formed; it does no mining. Its defaults are a margin of 1, the unsquared ℓp\ell_p distance with p=2p = 2, a small ε=10−6\varepsilon = 10^{-6} added to the difference vector, and reduction="mean", which averages over all triplets including the zero ones; it raises an error for a margin that is not positive. With swap=True it uses min⁡(dan,dpn)\min(d_{an}, d_{pn}) as the negative distance, the anchor swap of Balntas et al. (behaviour checked against the source; the documentation names the paper under a different title from the one printed). It does not normalize its inputs, so a margin of 1 on unit vectors is half the sphere’s diameter. TripletMarginWithDistanceLoss accepts any distance function, for example a squared or cosine distance.

The pytorch-metric-learning library (version 2.9.0) makes different choices: its TripletMarginLoss defaults to a margin of 0.05, normalizes embeddings, forms all triplets in the batch unless a miner supplies them, and averages over the non-zero terms; miners such as TripletMarginMiner (all margin-violating triplets, or only their hard or semi-hard ones, or the easy ones, as in the table above) and BatchHardMiner supply the triplets. The same name therefore covers losses whose defaults differ in scale, normalization and averaging.

Common Misconceptions

  • “The hardest negatives teach the most.” In a large pool they are often mislabeled or degenerate images, their gradients are noisy, and selecting them from the start can collapse training (Schroff et al., 2015; Wu et al., 2017); the small network of the example collapsed under batch-hard mining.
  • “A falling loss means a better embedding.” Averaged over all triplets, the loss falls mostly because easy triplets come to dominate, and under hard mining it can stay flat while the embedding improves (see Properties). Evaluate on held-out classes.
  • “The triplet loss beats the contrastive loss.” Wu et al. found that much of the triplet loss’s advantage came from its sampling: with semi-hard sampling a contrastive loss reached 49.4 Recall@1 on Stanford Online Products against 30.1 with random pairs, close to the triplet loss’s 49.7 (their Table 1). The fair comparison of many losses under one protocol is in the metric learning article.

Where It Is Used

  • Face recognition. FaceNet trained its embedding with the triplet loss and semi-hard mining. The margin-based softmax losses SphereFace, CosFace and ArcFace are an alternative that needs no mining.
  • Person re-identification. Hermans et al. (2017) trained a pretrained ResNet-50 with the soft-margin batch-hard loss and reported the highest mAP in their comparison on Market-1501 and MARS, for example 69.1% single-query mAP on Market-1501, with test-time augmentation, against 65.5% for the best other entries, although an identification-trained model had a slightly higher rank-1 accuracy there (85.1% against 84.9%; their Table 2).
  • Image similarity and retrieval. Wang et al. (2014) ranked images by fine-grained similarity for search by example, and Stanford Online Products, on which Wu et al. compared mining strategies, is a product-retrieval benchmark.
  • Local descriptors. Balntas et al. (2016) trained patch descriptors for feature matching with triplets and in-triplet mining.
  • Image–text retrieval. Visual–semantic embeddings use a bidirectional triplet ranking loss between images and captions (image–text retrieval).
  • Self-supervised learning. Contrastive learning losses such as InfoNCE compare each positive with many negatives at once through a softmax, which weights the hard ones without explicit mining.

Related

  • Embeddings

    Fixed-length vectors that a learned or hand-designed map assigns to images so that similar images get nearby vectors, where they come from (classifier features, metric and contrastive training, joint image–text models), how they are normalized and whitened, the geometry of the spaces they live in, and what they are used for.

  • Similarity and Distance Measures

    The functions that score how alike two feature vectors, histograms or binary codes are (Euclidean and other ℓp distances, cosine similarity and the dot product, Mahalanobis, Hamming, histogram measures such as χ², intersection and Hellinger, and the earth mover's distance), which of them are true metrics, and where each is used in descriptor matching, histogram comparison and embedding retrieval.

  • Contrastive Learning

    Training an encoder so that embeddings of matching pairs, such as two augmented views of one image, are close and embeddings of non-matching pairs are far apart, through margin losses, the InfoNCE and NT-Xent softmax losses, and recipes such as SimCLR and MoCo.

  • Loss Function

    The function that scores how far a network's output is from the desired output on one example; training minimizes its average over the training images, and the choice of loss encodes what counts as a good prediction for each vision task.

  • Recall@K

    The fraction of queries whose ranked results contain a correct item within the first K, the standard score of image–text retrieval, metric learning, place recognition and re-identification, with its two definitions, median rank, rsum, and the alternatives that look beyond the first hit.

  • Feature Descriptors

    Vectors that summarize the image appearance around a keypoint so that the same scene point seen in different images gets similar vectors, from raw patches and gradient histograms to binary strings and learned embeddings.

References

  1. Schultz, M. & Joachims, T. (2003). Learning a Distance Metric from Relative Comparisons. Advances in Neural Information Processing Systems 16 (NIPS).
  2. Weinberger, K. Q., Blitzer, J. & Saul, L. K. (2005). Distance Metric Learning for Large Margin Nearest Neighbor Classification. Advances in Neural Information Processing Systems 18 (NIPS).
  3. Weinberger, K. Q. & Saul, L. K. (2009). Distance Metric Learning for Large Margin Nearest Neighbor Classification. Journal of Machine Learning Research, 10(9), 207–244.
  4. Wang, J., Song, Y., Leung, T., Rosenberg, C., Wang, J., Philbin, J., Chen, B. & Wu, Y. (2014). Learning Fine-grained Image Similarity with Deep Ranking. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 1386–1393.
  5. Schroff, F., Kalenichenko, D. & Philbin, J. (2015). FaceNet: A Unified Embedding for Face Recognition and Clustering. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 815–823.
  6. Balntas, V., Riba, E., Ponsa, D. & Mikolajczyk, K. (2016). Learning local feature descriptors with triplets and shallow convolutional neural networks. British Machine Vision Conference (BMVC), 119.1–119.11.
  7. Hermans, A., Beyer, L. & Leibe, B. (2017). In Defense of the Triplet Loss for Person Re-Identification. arXiv:1703.07737 (v4, including the supplementary material).
  8. Wu, C.-Y., Manmatha, R., Smola, A. J. & Krähenbühl, P. (2017). Sampling Matters in Deep Embedding Learning. IEEE International Conference on Computer Vision (ICCV), 2859–2867.
  9. PyTorch developers. torch.nn.TripletMarginLoss. PyTorch 2.14 documentation.
  10. Musgrave, K. pytorch-metric-learning, version 2.9.0: losses/triplet_margin_loss.py, miners/triplet_margin_miner.py and distances/base_distance.py. GitHub repository KevinMusgrave/pytorch-metric-learning (August 2025).