On this page
Every method here learns embeddings (output vectors) and fails when it can solve its training task without learning the objects, their parts and the scene.
| Failure and its sign | Fix |
|---|---|
| Shared pixelslearns edges, not objects | gap and jitter between patches, drop color, hide 75% |
| Complete collapselowest loss, chance accuracy | stop-gradient, predictor or negatives |
| Dimensional collapsenear-zero singular values | projection head, then probe the encoder output |
| Too few negativesaccuracy falls with batch | queue or no negatives |
| BatchNorm leaktraining task solved almost perfectly, k-NN (a nearest-neighbor vote) falls | shuffle or sync BatchNorm |
The stop-gradient and predictor are taught in Non-Contrastive Self-Supervised Learning, and the 75% mask in Masked Image Modeling. Image-Text Contrastive Learning (CLIP) uses the other captions in its batch as negatives, so a small batch gives it the “Too few negatives” failure too.
A receptive field is the part of the input an output sees. When the inputs seen by the two compared outputs overlap or touch, the network can match the shared pixels and learn nothing about the objects in the image.
Doersch et al. (2015) asked a network which of eight positions a second 96 px patch sat at around the first. Shared edges, continuing lines and a lens’s color fringe (chromatic aberration) gave it away, so they added a gap, a random shift (jitter) and dropped color.
Figure 3. Only a wide gap plus jitter leaves content as the only clue.
CPC (Contrastive Predictive Coding, van den Oord et al. 2018) learns from speech: from the audio so far it predicts summaries of the next K short stretches. The nearest stretches overlap the current one, so predicting them can copy shared samples. The accuracy of a linear classifier at naming the phoneme (speech sound, 41 classes, LibriSpeech) from the learned vectors shows which K learns more.
Complete collapse maps every input to one embedding; the loss reaches its lowest value while accuracy falls to chance, as SimSiam does when its stop-gradient (a block on gradients to the target side) is removed. In dimensional collapse (Jing et al. 2022), the embeddings spread along only a few directions, so a linear probe scores poorly.
Negatives do not prevent it: the dimensional-collapse paper (Fig. 2) finds that SimCLR’s 128-number embeddings leave several directions unused. It has two causes. Edits that change images along a direction more than real images differ along it teach the encoder to ignore that direction (sec. 4). Stacked layers also pull their weights toward few directions (implicit regularization), even under weak edits (sec. 5).
Figure 6. The top 2 directions plot looks fine; in spread per direction, bars below the dashed line are unused.
Three settings decide whether contrastive training works. Batch size sets how many negatives (images pushed away) SimCLR sees. The temperature τ sets how sharply the loss focuses on the closest negatives. Augmentation sets what the two views share.
| Setting | Rule | Taught in |
|---|---|---|
| Temperature | tune; best 0.1 for SimCLR, 0.2 for MoCo v2 | Contrastive Learning, InfoNCE |
| Augmentation | strong; crops alone cost both methods accuracy (bars of Figure 8) | Contrastive Learning, augmentations; Section 2, collapse |
| Data other than photos | edits that keep what the sample is | Regularization, Data Augmentation for Wearables |
BatchNorm normalizes each feature with the mean and variance of the samples on one GPU. In MoCo, the query is one view’s embedding and its positive key is the other view’s. If the two sit in the same GPU batch, they share those statistics, and the network can match them by that shared signal instead of by content (MoCo App. A.9). MoCo shuffles keys across GPUs; SimCLR averages statistics over all devices (SyncBatchNorm on GPUs).
A probe and fine-tuning (training the encoder too) can rank methods differently. MoCo v2 tested two changes on MoCo: an MLP projection head and blur augmentation (Table 1, ResNet-50, 200 epochs). The head helps the frozen-encoder probe more; blur helps more once the encoder is fine-tuned for object detection (drawing boxes around objects, PASCAL VOC).
Five cheap checks catch these failures early. Alignment (how close pairs sit) and uniformity (how evenly embeddings spread) come from Wang and Isola’s paper.
| When | MoCo plots k-NN through training (Fig. A.1); no paper sets an interval |
|---|---|
| Data | a held-out batch; k-NN adds a bank of labeled training embeddings |
| Cost | forward passes only |
The first snippet checks one batch of d-number embeddings; eff_rank counts the directions in use (Roy and Vetterli 2007).
import torch
import torch.nn as nn
import torch.nn.functional as F
N, d = 256, 128
encoder = nn.Linear(32, d) # stand-in
x = torch.randn(N, 32)
with torch.no_grad():
Z = F.normalize(encoder(x), dim=1) # [N, d], unit rows
spread = Z.std(dim=0).mean() # near 1/sqrt(d)
C = torch.cov(Z.T, correction=0) # divide by N
lam = torch.linalg.svdvals(C)
shares = lam / lam.sum()
eff_rank = torch.special.entr(shares).sum().exp() # 1 to d
The second follows that paper’s code; both numbers fall as training works.
import torch
import torch.nn.functional as F
ALPHA, UNIF_T = 2, 2 # the paper's defaults
N, d = 256, 128
z1 = F.normalize(torch.randn(N, d), dim=1)
z2 = F.normalize(z1 + 0.1 * torch.randn(N, d), dim=1)
align = (z1 - z2).norm(dim=1).pow(ALPHA).mean()
sq = torch.pdist(z1).pow(2) # every pair, squared
uniform = torch.exp(-UNIF_T * sq).mean().log() # at most 0
ALPHA weights pairs that sit far apart more; at 2 it is the squared distance.UNIF_T scores only the closest pairs; a smaller one scores all pairs evenly. The paper’s best runs used t = 2 (STL-10, NYU-Depth-V2), 3 (ImageNet-100) and 5 (BookCorpus).The third is k-NN accuracy, a majority vote of the 20 nearest training embeddings.
import torch
import torch.nn.functional as F
K_NEIGHBORS, n_train, n_val, d = 20, 1000, 200, 128
bank = F.normalize(torch.randn(n_train, d), dim=1)
train_y = torch.randint(0, 10, (n_train,))
query = F.normalize(torch.randn(n_val, d), dim=1)
val_y = torch.randint(0, 10, (n_val,))
sim = query @ bank.T # [n_val, n_train]
top = sim.topk(K_NEIGHBORS, dim=1)
pred = train_y[top.indices].mode(dim=1).values # majority vote
knn_acc = (pred == val_y).float().mean()
Knowledge Distillation, a trained encoder teaching a smaller student, is a related tutorial.