对比学习(Contrastive Learning)是近年来深度学习领域备受关注的训练策略。它的核心假设很直观:在表示空间中,相似的样本应该靠得更近,不相似的样本应该离得更远。与传统的监督学习依赖人工标注不同,对比学习可以从数据本身构造监督信号,这使得它在自监督学习、跨模态对齐等任务中展现出强大的能力。本文将围绕两个经典模型——SimCLR和CLIP——展开讨论,帮助读者理解对比学习的基本原理和落地方式。

一、对比学习的基础:InfoNCE损失与正负样本构造
对比学习的目标是学习一个编码器 f,它能把输入样本 x 映射到一个归一化的向量 z = f(x)。在训练过程中,我们构造若干样本对,其中一部分是“正样本对”(例如同一张图片的两个不同增强版本),另一部分是“负样本对”(例如不同图片的增强版本)。模型需要最大化正样本对的相似度,同时最小化负样本对的相似度。这个优化目标通常用 InfoNCE 损失(又称对比损失)来表达。
假设一个批次内有 N 个样本,经过两种不同的数据增强后得到 2N 个视图。对于每个视图 i,与它同源的那个增强视图构成一个正样本对,其余 2N-2 个视图都视为负样本。InfoNCE 损失的计算方式为:
import torch
import torch.nn.functional as F
def info_nce_loss(features, temperature=0.1):
# features: [2N, dim],已经做了L2归一化
device = features.device
batch_size = features.shape[0] // 2
# 相似度矩阵:元素(i, j)表示第i个样本与第j个样本的余弦相似度
sim_matrix = torch.matmul(features, features.T) / temperature
# 正样本索引:i与i+batch_size互为增强对
positive_mask = torch.zeros_like(sim_matrix, dtype=torch.bool)
for i in range(batch_size):
positive_mask[i, i + batch_size] = True
positive_mask[i + batch_size, i] = True
# 去掉自身与自身的相似度(对角线)
sim_matrix = sim_matrix.masked_fill(torch.eye(2*batch_size, dtype=torch.bool, device=device), -1e9)
# 计算每个样本的log-softmax
logits = F.log_softmax(sim_matrix, dim=1)
# 只取正样本位置上的log概率,并取负号
loss = - (logits * positive_mask).sum(dim=1) / positive_mask.sum(dim=1)
return loss.mean()
上面这段代码展示了 InfoNCE 损失的一种实现思路。先构造相似度矩阵,然后屏蔽对角线防止自我匹配,再计算 log_softmax,最后只取正样本位置上的对数概率求平均。实际工程中会使用更高效的向量化操作,但核心思想相同。温度参数 τ 控制分布的尖锐程度,通常设为 0.1~0.5 之间。
为什么这种损失能学到有用的表示?因为模型在优化过程中被迫捕捉样本之间的语义共性。例如对于图像,同一张照片的不同裁剪、颜色扰动应当映射到接近的向量,而其他照片则推开。这迫使编码器忽略低层次的像素变化,提取高层语义特征。这也是 SimCLR 能够不靠标签学习到媲美监督模型表示的原因。
二、SimCLR:视觉对比学习的经典框架
SimCLR(Simple Framework for Contrastive Learning of Visual Representations)由 Google 团队提出,它证明了对比学习在视觉自监督任务上的有效性。SimCLR 的框架非常简洁:对每个输入图像 x,随机应用两种不同的数据增强,得到两个视图 x_i 和 x_j;这两个视图分别送入同一个编码器 f(通常是 ResNet),得到表示 h_i、h_j;然后经过一个小的投影头 g(例如两层 MLP)映射到 z_i、z_j;最后用 InfoNCE 损失约束这两个投影向量的相似度。
SimCLR 的几个关键设计值得注意。首先是“强数据增强”的组合:随机裁剪并缩放、颜色抖动、随机高斯模糊等。增强的强度直接影响表示质量,过弱则模型容易依赖低层统计特征,过强则可能破坏语义信息。其次是投影头 g:它是一个只在训练阶段使用的非线性映射,将编码器输出映射到较低维空间(如 128 维),再进行对比损失计算。训练完成后投影头被丢弃,只用编码器 f 作为特征提取器。最后是大 batch 训练:SimCLR 需要足够多的负样本才能有效,论文中使用了最多 4096 的 batch size,同时配合 LARS 优化器来稳定训练。
下面是一个简化的 SimCLR 训练循环示例,使用 PyTorch 描述:
# 假设已经定义好 encoder (ResNet) 和 projection_head (MLP)
encoder = ResNet50()
projection_head = nn.Sequential(nn.Linear(2048, 512), nn.ReLU(), nn.Linear(512, 128))
optimizer = torch.optim.SGD(list(encoder.parameters()) + list(projection_head.parameters()), lr=0.1, momentum=0.9)
for images, _ in dataloader: # images: [N, C, H, W],不关心标签
# 1. 对同一批图像应用两次数据增强,得到两个视图
view1 = aug(images)
view2 = aug(images)
# 2. 编码 + 投影
z1 = projection_head(encoder(view1)) # [N, 128]
z2 = projection_head(encoder(view2)) # [N, 128]
z1 = F.normalize(z1, dim=1)
z2 = F.normalize(z2, dim=1)
# 3. 拼接成 2N 个特征,计算对比损失
features = torch.cat([z1, z2], dim=0) # [2N, 128]
loss = info_nce_loss(features, temperature=0.1)
optimizer.zero_grad()
loss.backward()
optimizer.step()
需要注意的是,上述代码只是教学示意,真实训练中数据增强通常使用 torchvision.transforms 的组合,且 batch size 要足够大。SimCLR 的贡献不仅在于提出了一种有效的方法,更在于通过消融实验揭示了对比学习成功的关键因素:数据增强组合和投影头的非线性层。这些发现启发了后续一大批工作。
三、CLIP:跨模态对比学习与零样本分类
CLIP(Contrastive Language-Image Pre-training)是 OpenAI 提出的模型,它将对比学习从单模态拓展到跨模态对齐。传统的视觉模型依赖固定类别的标签,而 CLIP 通过一个图像编码器和一个文本编码器分别提取图像和文本的特征,然后用对比损失让匹配的图文对在嵌入空间中靠近,不匹配的远离。训练数据是互联网上收集的 4 亿个图文对,文本为图像的自然语言描述。
CLIP 的训练过程可以用一个伪代码表示:
# image_encoder: Vision Transformer 或 ResNet # text_encoder: Transformer 编码器 # 假设一个 batch 包含 N 个图文对 (image_i, text_i) image_features = image_encoder(images) # [N, D] text_features = text_encoder(texts) # [N, D] image_features = F.normalize(image_features, dim=1) text_features = F.normalize(text_features, dim=1) # 相似度矩阵:N x N,对角线为正样本 logits = torch.matmul(image_features, text_features.T) * temperature # 双向对比损失:图像->文本 + 文本->图像 loss_i = F.cross_entropy(logits, torch.arange(N)) loss_t = F.cross_entropy(logits.T, torch.arange(N)) loss = (loss_i + loss_t) / 2
CLIP 的关键创新在于它把文本和图像映射到同一个嵌入空间,并且使用对比损失来训练。与 SimCLR 不同,CLIP 没有显式的“负样本构造”步骤,因为 batch 内除了对角线之外的样本对都自动成为负样本。这种简单而优雅的设计使得 CLIP 能够从海量弱监督数据中学习到强大的视觉-语言联合表示。
训练完成后,CLIP 最突出的能力是零样本分类。给定一张图像,我们可以用任意类别的自然语言描述(如“一张猫的照片”)作为文本输入,计算图像特征与所有文本特征的相似度,选择相似度最高的类别。这种方式完全不需要针对具体下游任务微调,就能达到与监督模型相当甚至更好的效果。此外,CLIP 还可以用于图像检索、文本到图像生成引导等任务,成为多模态学习的基石之一。
四、SimCLR 与 CLIP 的对比与应用场景
SimCLR 和 CLIP 虽然都基于对比学习,但目标不同。SimCLR 专注于单模态视觉自监督,目的是学习一个好的图像特征提取器,可以迁移到分类、检测等下游任务。它不需要文本,训练数据就是无标签图像。而 CLIP 的目标是跨模态对齐,让图像和文本的语义相匹配,从而支持零样本识别和跨模态检索等任务。二者可以结合:例如先使用 SimCLR 预训练图像编码器,再将其作为 CLIP 的图像分支进行微调,可能进一步提升性能。
从实现角度看,SimCLR 的损失只考虑图像内部的对比,而 CLIP 计算图文两个模态之间的对比。SimCLR 对 batch size 和增强策略非常敏感,训练成本较高;CLIP 则需要大规模的图文对数据,对文本质量有一定要求。在实际应用中,如果只处理图像分类任务且缺乏标签,优先考虑 SimCLR 或其后继模型(如 MoCo、BYOL);如果需要处理图文混合任务或希望利用自然语言作为监督信号,CLIP 是更好的选择。
对比学习作为一个通用范式,已经催生出众多变体,包括视频对比学习、音频对比学习,以及图表示学习中的对比方法。理解 SimCLR 和 CLIP 的核心思想,可以帮助我们设计自己的对比学习框架,解决数据标注不足或跨模态对齐的难题。随着自监督和多模态学习的持续发展,对比学习将继续扮演重要角色。
本文从 InfoNCE 损失出发,介绍了 SimCLR 和 CLIP 的原理、实现要点和应用场景。希望读者通过代码示例和讲解,能够快速上手对比学习,并在自己的项目中实践。