Contrastive Learning : Guide complet — Apprentissage par Contraste
Résumé — Le Contrastive Learning est une méthode d’apprentissage auto-supervisé qui apprend des représentations visuelles de qualité sans aucune étiquette. En rapprochant les représentations de deux versions augmentées de la même image (paires positives) et en éloignant celles d’images différentes (paires négatives), le modèle développe une compréhension profonde de la sémantique visuelle. SimCLR, MoCo et BYOL ont démontré que cette approche rivalise avec le supervisé sur des tâches classiques de vision.
Principe mathématique
1. InfoNCE Loss
La fonction de coût centrale du Contrastive Learning est le Normalized Information Noise-Contrastive Estimation (InfoNCE) :
$$L = -\log \frac{\exp(\text{sim}(z_i, z_j) / \tau)}{\sum_{k=1}^{2N} \mathbb{1}_{[k \neq i]} \exp(\text{sim}(z_i, z_k) / \tau)}$$
Où :
– z_i, z_j : projections de deux vues augmentées de la même image (paire positive)
– sim(u,v) : similarité cosinus = (u · v) / (||u|| · ||v||)
– τ : température, contrôle la netteté de la distribution (typiquement 0.07-0.5)
– 2N : tous les échantillons du batch (chaque image a 2 vues, donc N images = 2N vues)
– Le dénominateur inclut toutes les autres vues comme négatifs
2. Architecture SimCLR
SimCLR utilise un pipeline en trois étapes :
1. Augmentation : pour chaque image $x$, créer deux vues augmentées $\tilde{x}_i$ et $\tilde{x}_j$ (crop, couleur, flou)
2. Encodeur : f(x) extrait les features (ResNet-50) → h = f(x) ∈ R^d
3. Tête de projection : g(h) = W_2 · ReLU(W_1 · h) → z ∈ R^128
Après pré-entraînement, on jette g() et on utilise f(x) comme extracteur de features pour des tâches en aval.
3. MoCo (Momentum Contrast)
MoCo résout le problème de la taille de batch (SimCLR nécessite des milliers d’échantillons) en maintenant une queue de négatifs et un encodeur momentum :
$$k_{m} = m \cdot k_{m} + (1 – m) \cdot k$$
L’encodeur momentum k_m est une moyenne exponentiellement mobile de l’encodeur principal k. Cela garantit des représentations cohérentes pour les négatifs stockés dans la queue.
Intuition
Imaginez un enfant qui trie des photos sans connaître les noms des objets.
On lui donne deux photos du même chat — l’une recadrée sur la tête, l’autre avec des couleurs légèrement différentes. L’enfant apprend que ces deux photos viennent du même chat car elles se ressemblent malgré les modifications.
En même temps, il regarde des photos de chiens, de voitures, d’arbres — toutes différentes. Il apprend que ces images sont distinctes.
Sans jamais entendre le mot “chat”, l’enfant développe une représentation interne qui regroupe les vues similaires et sépare les vues différentes. C’est exactement ce que fait le Contrastive Learning.
Implémentation Python
1. SimCLR simple avec PyTorch
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.transforms as transforms
from torchvision.models import resnet18
class SimCLR(nn.Module):
def __init__(self, projection_dim=128):
super().__init__()
encoder = resnet18(weights=None)
hidden_dim = encoder.fc.in_features
encoder.fc = nn.Identity()
self.encoder = encoder
self.projection = nn.Sequential(
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(inplace=True),
nn.Linear(hidden_dim, projection_dim),
)
def forward(self, x):
h = self.encoder(x)
z = self.projection(h)
return F.normalize(z, dim=1)
def nt_xent_loss(z_i, z_j, temperature=0.5):
batch_size = z_i.size(0)
z = torch.cat([z_i, z_j], dim=0)
similarity = z @ z.T / temperature
mask = torch.eye(2 * batch_size, dtype=torch.bool, device=z.device)
similarity = similarity.masked_fill(mask, float("-inf"))
targets = (torch.arange(2 * batch_size, device=z.device) + batch_size) % (
2 * batch_size
)
return F.cross_entropy(similarity, targets)
transform = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.2, 1.0)),
transforms.RandomHorizontalFlip(),
transforms.ColorJitter(0.8, 0.8, 0.8, 0.2),
transforms.RandomGrayscale(0.2),
transforms.GaussianBlur(kernel_size=9, sigma=(0.1, 2.0)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
model = SimCLR(projection_dim=128)
optimizer = torch.optim.Adam(model.parameters(), lr=3e-4)
# Exemple minimal exécutable avec deux vues synthétiques du même batch
batch_size = 16
x = torch.randn(batch_size, 3, 224, 224)
x_i = x + 0.05 * torch.randn_like(x)
x_j = x + 0.05 * torch.randn_like(x)
for epoch in range(3):
z_i = model(x_i)
z_j = model(x_j)
loss = nt_xent_loss(z_i, z_j, temperature=0.5)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Epoch {epoch + 1} | Loss: {loss.item():.4f}")
2. Évaluation par Linear Probing
from torch.utils.data import DataLoader, TensorDataset
for param in model.encoder.parameters():
param.requires_grad = False
feature_dim = model.encoder(torch.randn(1, 3, 224, 224)).shape[1]
probe = nn.Linear(feature_dim, 10) # ici 10 classes
probe_optimizer = torch.optim.Adam(probe.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()
images = torch.randn(32, 3, 224, 224)
labels = torch.randint(0, 10, (32,))
loader = DataLoader(TensorDataset(images, labels), batch_size=8, shuffle=True)
model.eval()
for epoch in range(3):
epoch_loss = 0.0
for x_batch, y_batch in loader:
with torch.no_grad():
features = model.encoder(x_batch)
logits = probe(features)
loss = criterion(logits, y_batch)
probe_optimizer.zero_grad()
loss.backward()
probe_optimizer.step()
epoch_loss += loss.item()
print(f"Linear probing epoch {epoch + 1} | Loss: {epoch_loss / len(loader):.4f}")
Hyperparamètres
| Hyperparamètre | Valeur typique | Description |
|---|---|---|
| temperature (τ) | 0.07-0.5 | Contrôle la netteté de la distribution (plus petit = plus discriminant) |
| batch_size | 256-8192 | Critique pour SimCLR (plus = plus de négatifs); MoCo pallie ce problème |
| projection_dim | 128-256 | Dimension de l’espace de projection (après le MLP) |
| encoder | ResNet-50 | Backbone pour l’extraction de features |
| learning_rate | 1e-3-3e-4 | Adam, souvent avec warmup de 10 epochs |
| epochs | 200-1000 | Le Contrastive Learning nécessite beaucoup d’epochs |
Avantages
- Pas besoin d’étiquettes : Apprend directement à partir de données brutes, économisant des milliers d’heures d’annotation.
- Représentations génériques : Les features apprises se transfèrent à de multiples tâches (classification, détection, segmentation).
- Performance concurrentielle : SimCLR avec ResNet-50 atteint 76.5% top-1 sur ImageNet, proche des 77% du supervisé direct.
- Robustesse : Les représentations sont plus robustes aux corruptions et perturbations d’images que les modèles supervisés.
Limites
- Coût calcul élevé : SimCLR nécessite des milliers d’épochs et de gros batches. MoCo est plus efficace mémoire.
- Dépendance aux augmentations : Des augmentations trop faibles ne fournissent pas assez de variété; trop fortes et les paires positives deviennent trop différentes.
- Négatifs redondants : Dans un grand batch, la majorité des négatifs sont faciles (images très différentes), fournissant peu d’information.
4 cas d’usage concrets
1. Classification médicale avec peu de données labellisées
Pré-entraîné par Contrastive Learning sur 100 000 radiographies non labellisées, puis fine-tuné avec seulement 500 radiographies annotées pour détecter des pathologies. Les performances surpassent un modèle entraîné uniquement en supervisé avec les mêmes 500 exemples.
2. Recherche d’images par similarité
Un moteur de recherche e-commerce utilise les features extraites par un modèle SimCLR pour trouver des images visuellement similaires à une image requête. Les représentations contrastives capturent mieux la similarité sémantique que des features extraites d’un modèle supervisé classique.
3. Apprentissage de représentations audio
En adaptant les augmentations au domaine audio (time stretching, pitch shifting, ajout de bruit), le Contrastive Learning apprend des représentations robustes pour la reconnaissance de la parole ou la classification de sons environnementaux.
4. NLP auto-supervisé
Contrastive multi-task learning pour les représentations de phrases : deux versions d’une phrase (avec des mots supprimés différents) sont rapprochées, tandis que des phrases aléatoires sont éloignées. Cela complète les approches MLM comme BERT et améliore la robustesse des embeddings sur les tâches de similarité textuelle.
Comparaison des méthodes contrastives
Le domaine a évolué rapidement depuis SimCLR (2020). Voici les principales approches :
Méthodes avec négatifs (contraste explicite) :
– SimCLR : gros batch pour beaucoup de négatifs, simples mais efficaces
– MoCo (v1-v3) : queue de négatifs + encodeur momentum, efficace en mémoire
– SupCon : Supervised Contrastive Learning, utilise les étiquettes pour créer plus de paires positives
Méthodes sans négatifs (contraste implicite) :
– BYOL (Bootstrap Your Own Latent) : deux réseaux avec momentum, pas de négatifs nécessaires
– SimSiam : encore plus simple, pas de momentum ni de négatifs, juste deux branches avec stop-gradient
– DINO : auto-distillation avec un professeur et un élève, émergence spontanée de l’attention
Méthodes par reconstruction masquée :
– MAE (Masked Autoencoder) : masque 75% de l’image et reconstruit les pixels, compétitif avec le contrastif
– iBOT : combine distillation et reconstruction masquée
Conclusion
Le Contrastive Learning a inauguré l’ère de l’apprentissage auto-supervisé moderne. En apprenant des représentations riches sans aucune étiquette, il réduit drastiquement le besoin en annotations manuelles tout en maintenant des performances compétitives.
SimCLR et MoCo ont ouvert la voie à des méthodes encore plus avancées comme DINO (auto-distillation sans négatifs) et MAE (Masked Autoencoder), confirmant que l’avenir du learning profond est auto-supervisé.
Voir aussi
- Maîtrisez le Mahjong avec Python : Guide Complet pour Développeurs et Enthousiastes
- Maîtriser les Graphes en Grille avec Python : Guide Complet et Astuces d’Optimisation

