一、自监督学习概述
自监督学习(Self-Supervised Learning, SSL) 是一种无需人工标注数据就能学习有效表征的机器学习范式。其核心思想是从数据自身构造监督信号 ——通过设计精巧的"前置/代理任务"(Pretext Task),让模型在无标注数据上进行训练,从而学到通用的特征表示。这些预训练得到的表征可以迁移到下游任务(分类、检测、分割等),通常只需少量标注数据微调即可取得优异性能。
自监督学习近年来在计算机视觉和自然语言处理领域取得了革命性进展。从Word2Vec、BERT在NLP领域的成功,到SimCLR、MoCo、MAE等视觉自监督方法在ImageNet上超越有监督基线,SSL已经成为深度学习研究中最活跃的方向之一。
自监督学习的核心优势
摆脱对标注数据的依赖: 可以利用互联网规模的未标注数据(数十亿图像、文本)
学习通用表征: 通过代理任务学到的特征具有良好的泛化能力,可迁移到多种下游任务
降低人工成本: 无需昂贵的专家标注,尤其适用于医学影像等标注成本极高的领域
超越监督学习: 在众多基准上,自监督预训练+少量微调已超越全监督训练效果
自监督学习的主要范式
根据代理任务的设计方式,自监督学习可分为以下几类:
范式
核心思想
代表方法
典型任务
对比学习(Contrastive)
拉近正样本对、推开负样本对
SimCLR, MoCo, CLIP
实例判别(Instance Discrimination)
生成式(Generative)
遮掩部分输入并预测被遮掩内容
MAE, BERT, GPT
掩码重建(Masked Modeling)
非对比学习(Non-contrastive)
仅需正样本对,无需负样本
BYOL, SimSiam, Barlow Twins
互信息最大化 / 冗余减少
跨模态(Cross-modal)
利用不同模态之间的对应关系
CLIP, ImageBind, Data2Vec
图文匹配、音画对应
◆ 自监督学习核心范式对比 ◆
┌──────────────────────────────────────────────────┐
│ 对比学习: anchor → 正样本(相似视图) │
│ ↘ 负样本(不同样本) │
├──────────────────────────────────────────────────┤
│ 生成式: 输入[ MASK ] → 预测被遮掩内容 │
│ MAE: 可见patches → 重构缺失patches │
├──────────────────────────────────────────────────┤
│ 非对比: 视图1 → 编码器 → 预测器 → 视图2 │
│ BYOL: 无负样本, 靠不对称网络防坍塌 │
└──────────────────────────────────────────────────┘
二、对比学习框架与SimCLR
对比学习的基本原理
对比学习的核心思想是"以教代学" 。对于每个锚点样本(Anchor),我们通过数据增强构造其正样本(Positive)——即同一个样本的不同视图,并从数据集中随机选取一批负样本(Negative)。训练目标是让锚点的表征与其正样本的表征尽可能接近(相似度高),同时与所有负样本的表征尽可能远离(相似度低)。
这种实例判别 (Instance Discrimination)的代理任务巧妙地绕过了对语义标签的依赖:每个样本自身就是一个类别,正负样本的区分完全基于数据是否来自同一原始样本。模型被迫学习到既能捕捉不变性(数据增强不变)、又能区分不同样本的细粒度特征。
关键组件:数据增强(Data Augmentation)
数据增强是对比学习的灵魂。SimCLR作者通过实验发现,随机裁剪+颜色失真 的组合是最有效的增强策略。随机裁剪迫使模型关注物体的结构而非背景;颜色失真防止模型简单地依赖颜色直方图进行区分。常用的增强包括:随机裁剪调整大小、随机颜色抖动、随机灰度化、随机高斯模糊、随机水平翻转。
SimCLR框架详解
SimCLR(Simple Framework for Contrastive Learning of Visual Representations)由Google Brain于2020年提出,是一个简洁而强大的对比学习框架。其核心流程如下:
从训练集中随机采样一个Batch,对每个样本x应用两组不同的随机数据增强,得到x_i和x_j——构成一个正样本对
使用编码器(Encoder,通常为ResNet)提取特征表示h_i和h_j
将特征通过投影头 (Projection Head,一个小型MLP)映射到对比空间z_i和z_j
在一个Batch内计算所有正负样本对的对比损失(NT-Xent Loss)
以下为SimCLR核心训练的PyTorch实现:
import torch
import torch.nn as nn
import torchvision.transforms as T
import torch.nn.functional as F
# ---------- 数据增强组合 ----------
class SimCLRTransform:
def __init__ (self, size=224):
self.transform = T.Compose([
T.RandomResizedCrop(size, scale=(0.08, 1.0)),
T.RandomApply([T.ColorJitter(0.8, 0.8, 0.8, 0.2)], p=0.8),
T.RandomGrayscale(p=0.2),
T.GaussianBlur(kernel_size=23, sigma=(0.1, 2.0)),
T.RandomHorizontalFlip(),
T.ToTensor(),
T.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
def __call__ (self, x):
return self.transform(x), self.transform(x)
# ---------- Projection Head ----------
class ProjectionHead(nn.Module):
def __init__ (self, in_dim=2048, hidden_dim=2048, out_dim=128):
super().__init__()
self.net = nn.Sequential(
nn.Linear(in_dim, hidden_dim),
nn.BatchNorm1d(hidden_dim),
nn.ReLU(inplace=True),
nn.Linear(hidden_dim, out_dim)
)
def forward (self, x):
return self.net(x)
# ---------- NT-Xent Loss ----------
class NTXentLoss(nn.Module):
def __init__ (self, temperature=0.5):
super().__init__()
self.temperature = nn.Parameter(torch.tensor(temperature))
self.criterion = nn.CrossEntropyLoss()
def forward (self, z_i, z_j):
batch_size = z_i.shape[0]
N = 2 * batch_size
# 拼接所有特征: [2B, D]
z = torch.cat([z_i, z_j], dim=0)
z = F.normalize(z, dim=1) # L2归一化
# 计算相似度矩阵: [2B, 2B]
sim = torch.mm(z, z.T) / self.temperature
# 正样本对: (i, j) 和 (j, i)
mask = torch.zeros_like(sim, dtype=torch.bool)
for i in range(batch_size):
mask[i, i + batch_size] = True
mask[i + batch_size, i] = True
# 去除自身
mask.fill_diagonal_(False )
# InfoNCE损失
exp_sim = torch.exp(sim)
loss = -torch.log(
exp_sim[mask] / exp_sim.sum(dim=1).unsqueeze(1).expand_as(sim)[mask]
)
return loss.mean()
# ---------- SimCLR训练循环 ----------
def simclr_train_step(model, proj_head, batch, optimizer):
x, _ = batch # 忽略标签, 只需要图像
x_i, x_j = transforms(x) # 两组数据增强
h_i = model(x_i) # [B, D] 特征表示
h_j = model(x_j)
z_i = proj_head(h_i) # [B, 128] 投影表示
z_j = proj_head(h_j)
loss = ntxent_loss(z_i, z_j)
optimizer.zero_grad()
loss.backward()
optimizer.step()
return loss.item()
温度系数(Temperature)的作用
NT-Xent损失中的温度系数 τ 控制着对困难负样本的关注程度。较小的温度系数(如0.1)会放大相似度分数的差异,使模型更关注与锚点难以区分的负样本——这有助于学习更精细的判别特征,但也可能导致训练不稳定。较大的温度系数(如1.0)则会均匀化所有负样本的贡献,训练更稳定但可能学不到足够的判别信息。SimCLR建议的默认值为 τ=0.5。
大批量的必要性
SimCLR的一个重要限制是对大批量(Large Batch Size)的高度依赖 。负样本的数量直接决定了对比学习的效果——负样本越多,模型学到的表征越丰富。SimCLR通常需要使用4096或更大的Batch Size(对应8192个负样本),这对GPU显存提出了极高要求。这也催生了MoCo等不需要大批量的替代方案。
三、MoCo:动量对比学习
核心思想:动态字典
MoCo(Momentum Contrast)由何恺明团队于2019年提出,旨在解决对比学习对大批量的依赖问题。其核心创新是将对比学习视为字典查找 问题:维护一个持续更新的队列(Queue) 作为负样本字典,使用动量编码器(Momentum Encoder) 来保证字典中特征表示的一致性。
MoCo的队列机制使其可以解耦负样本数量与Batch Size的关系——队列中可以存储数千甚至数万个负样本特征,而Batch Size可以很小(如256)。
MoCo的两大创新
队列字典(Queue Dictionary): 维护一个FIFO队列存储过往Batch的编码特征作为负样本。队列的大小远大于Batch Size,提供丰富多样的负样本
动量编码器(Momentum Encoder): Key编码器的参数通过动量方式(指数移动平均)从Query编码器更新,保证队列中特征表示的一致性,避免因编码器快速变化导致的特征"断层"
MoCo v1/v2/v3 演进
版本 创新点 关键改进
MoCo v1
队列 + 动量编码器
首次提出动态字典机制,队列大小与Batch Size解耦
MoCo v2
MLP投影头 + 更强增强
借鉴SimCLR的投影头和增强策略,大幅提升性能
MoCo v3
ViT支持 + 改进训练稳定性
支持Vision Transformer,发现并解决了ViT自监督训练的不稳定性问题
MoCo v2的PyTorch实现关键片段:
# MoCo v2 核心实现 (关键部分)
class MoCo(nn.Module):
def __init__ (self, base_encoder, dim=128, K=65536, m=0.999, T=0.07):
"""
K: 队列大小 (负样本数量)
m: 动量系数
T: 温度系数
"""
super().__init__()
self.K = K
self.m = m
self.T = T
# 查询编码器 (Query Encoder)
self.encoder_q = base_encoder(num_classes=dim)
# 键编码器 (Key Encoder) - 动量编码器
self.encoder_k = base_encoder(num_classes=dim)
# 投影头 (MLP)
self.encoder_q.fc = nn.Sequential(
nn.Linear(2048, 2048), nn.ReLU(), nn.Linear(2048, dim)
)
self.encoder_k.fc = nn.Sequential(
nn.Linear(2048, 2048), nn.ReLU(), nn.Linear(2048, dim)
)
# 初始化Key编码器参数: 与Query编码器相同
for param_q, param_k in zip(
self.encoder_q.parameters(), self.encoder_k.parameters()
):
param_k.data.copy_(param_q.data)
param_k.requires_grad = False # 不通过梯度更新
# 创建负样本队列
self.register_buffer("queue" , torch.randn(dim, K).normal_(0, 0.01))
self.register_buffer("queue_ptr" , torch.zeros(1, dtype=torch.long))
# 动量更新: 指数移动平均
def _momentum_update_key_encoder (self):
for param_q, param_k in zip(
self.encoder_q.parameters(), self.encoder_k.parameters()
):
param_k.data = param_k.data * self.m + param_q.data * (1. - self.m)
# 入队: 将当前Batch的Key特征加入队列, 移除最旧的
def _dequeue_and_enqueue (self, keys):
batch_size = keys.shape[0]
ptr = int(self.queue_ptr)
if ptr + batch_size <= self.K:
self.queue[:, ptr:ptr + batch_size] = keys.T
else :
# 若队列尾部空间不足, 分两次写入
remaining = self.K - ptr
self.queue[:, ptr:] = keys[:remaining].T
self.queue[:, :batch_size - remaining] = keys[remaining:].T
self.queue_ptr[0] = (ptr + batch_size) % self.K
def forward (self, im_q, im_k):
batch_size = im_q.shape[0]
# 1. 查询编码器计算查询特征
q = self.encoder_q(im_q) # [B, dim]
q = F.normalize(q, dim=1)
# 2. 动量编码器计算键特征 (无梯度)
with torch.no_grad():
self._momentum_update_key_encoder()
k = self.encoder_k(im_k) # [B, dim]
k = F.normalize(k, dim=1)
# 3. 对比: 正样本对 logits + 负样本对 logits
l_pos = torch.einsum('nc,nc->n' , q, k).unsqueeze(-1) # [B, 1]
l_neg = torch.einsum('nc,ck->nk' , q,
self.queue.clone().detach()) # [B, K]
logits = torch.cat([l_pos, l_neg], dim=1) # [B, 1+K]
logits /= self.T
# 标签: 第0个位置是正样本
labels = torch.zeros(batch_size, dtype=torch.long, device=q.device)
loss = F.cross_entropy(logits, labels)
# 4. 将当前Batch的Key特征入队
self._dequeue_and_enqueue(k)
return loss
MoCo与SimCLR的对比
维度 SimCLR MoCo
负样本来源 当前Batch内的其他样本 队列中存储的过往Batch样本
负样本数量 受Batch Size限制 队列大小K可任意设定(典型值65536)
Batch Size需求 需要大批量(4096+) 小批量即可(256)
编码器一致性 所有样本使用同一编码器 Key编码器通过动量更新保持一致
实现复杂度 简单 略复杂(需维护队列和动量)
ImageNet线性评估 Top-1: ~76.5% Top-1: ~77.4% (v2)
ImageNet线性评估协议
自监督学习的标准评估协议:在ImageNet上完成无监督预训练后,冻结编码器权 ,仅训练一个线性分类头(Linear Classifier),在验证集上评估Top-1准确率。这个指标反映了预训练表征的质量 ——若表征本身足够好,即使只加一层线性分类器也能取得优秀效果。MoCo v2在ImageNet线性评估上达到77.4%,EMA-GSS则进一步提升至81.5%以上。
四、BYOL与SimSiam:无负样本对比学习
BYOL:不需要负样本
BYOL(Bootstrap Your Own Latent)由DeepMind于2020年提出,打破了对比学习需要负样本的传统认知。BYOL仅使用正样本对进行训练,通过不对称网络结构 ——在线网络(Online Network)和目标网络(Target Network)——以及预测头 (Predictor)来防止模型坍塌(Collapse)。
BYOL的训练流程如下:对输入图像应用两组不同的数据增强,得到两个视图;在线网络对视图1进行编码+投影+预测,目标网络对视图2进行编码+投影(无预测头);通过最小化预测结果与目标投影之间的均方误差来训练。目标网络的参数通过在线网络的动量更新(指数移动平均)获得。
BYOL防止坍塌的机制
不对称网络: 在线网络有预测头(Predictor),目标网络没有。这种不对称打破了对称性,防止退化解
动量目标网络: 目标网络参数是在线网络的慢速移动平均,提供稳定一致的回归目标
Batchnorm的重要性: BYOL中的Batch Normalization起到了隐式的正则化作用,有助于防止坍塌
SimSiam:极简的无负样本方法
SimSiam(Simple Siamese)由何恺明团队于2021年提出,进一步简化了无负样本对比学习。SimSiam去除了动量编码器,两个分支共享同一编码器权,仅靠Stop-gradient 操作和预测头 来防止坍塌。其优雅的设计表明:对称网络的"坍塌"问题可以通过一个简单的Stop-gradient来解决。
SimSiam的PyTorch实现非常简洁:
# SimSiam: 极简孪生网络 (核心部分)
class SimSiam(nn.Module):
def __init__ (self, backbone, dim=2048, pred_dim=512):
super().__init__()
# 编码器: backbone + 投影头
self.encoder = nn.Sequential(
backbone,
nn.AdaptiveAvgPool2d(1),
nn.Flatten(),
nn.Linear(2048, dim),
nn.BatchNorm1d(dim),
nn.ReLU(inplace=True),
nn.Linear(dim, dim),
nn.BatchNorm1d(dim),
nn.ReLU(inplace=True),
nn.Linear(dim, dim),
nn.BatchNorm1d(dim, affine=False )
)
# 预测头 (仅在线分支有)
self.predictor = nn.Sequential(
nn.Linear(dim, pred_dim),
nn.BatchNorm1d(pred_dim),
nn.ReLU(inplace=True),
nn.Linear(pred_dim, dim)
)
def forward (self, x1, x2):
# 编码两个视图
z1 = self.encoder(x1) # [B, dim] 投影特征
z2 = self.encoder(x2)
# 预测
p1 = self.predictor(z1) # 在线预测
p2 = self.predictor(z2)
# 对称损失: Stop-gradient防止坍塌
loss = -(F.normalize(p1, dim=1) *
F.normalize(z2.detach(), dim=1)).sum(dim=1).mean() / 2
loss += -(F.normalize(p2, dim=1) *
F.normalize(z1.detach(), dim=1)).sum(dim=1).mean() / 2
return loss.mean()
BYOL与SimSiam的对比
维度 BYOL SimSiam
网络结构 在线网络 + 动量目标网络 共享权孪生网络
动量更新 需要(目标网络动量更新) 不需要(共享权)
Stop-gradient 隐式(目标网络不更新) 显式(detach操作)
防止坍塌机制 动量 + 预测头 + BN Stop-gradient + 预测头
Batch Size要求 较小(256-1024) 较小(256-512)
ImageNet Top-1 74.3% (300epoch) 71.3% (200epoch)
训练epoch数 通常需要300-1000 100-800
关于表征坍塌(Representation Collapse)
无负样本方法面临的核心风险是表征坍塌 ——模型将所有输入映射到同一常数输出。这是因为在没有负样本的对比压力下,模型可能找到"捷径":输出零向量或均匀分布即可使损失最小化。BYOL和SimSiam通过不对称网络结构、预测头和Stop-gradient等机制巧妙地绕过了这个问题。理论上,SimSiam通过EM算法视角 的数学分析证明了其防止坍塌的合理性。
五、掩码自编码器(MAE)
从BERT到MAE:掩码建模范式
掩码自编码器(Masked Autoencoder, MAE)由何恺明团队于2021年提出,将BERT的掩码预训练范式成功迁移到计算机视觉领域。MAE的核心思想极为简单:随机遮挡输入图像的大部分Patches,然后重建缺失像素 。这个简单的任务迫使模型学习到图像的全局语义结构和上下文关系。
MAE的设计包含几个关键洞察:
极高的掩码率(75%): 与BERT的15%掩码率不同,MAE发现对于图像来说,75%的掩码率 效果最佳。这是因为图像具有高度的空间冗余——即使只看到25%的Patch,模型也能推断出被遮挡的内容。高掩码率迫使模型学习高级语义理解而非局部像素插值。
非对称编码器-解码器设计: 编码器只处理可见的Patches(25%的计算量),轻量级解码器在完整序列上重建像素。这种设计极大提高了训练效率。
解码器对计算量不敏感: 即使使用非常轻量的解码器(1-2层Transformer),重建质量仍然很高,表明编码器学到了高质量的表征。
MAE的PyTorch实现核心结构:
# MAE (Masked Autoencoder) 核心实现
class MAE(nn.Module):
def __init__ (self, encoder, decoder_dim=512,
mask_ratio=0.75, patch_size=16):
super().__init__()
self.encoder = encoder # ViT编码器
self.mask_ratio = mask_ratio # 掩码率 75%
self.patch_size = patch_size
self.enc_to_dec = nn.Linear(encoder.embed_dim, decoder_dim)
# 轻量级解码器
self.decoder = nn.TransformerEncoder(
nn.TransformerEncoderLayer(
d_model=decoder_dim, nhead=8, dim_feedforward=2048,
activation='gelu', batch_first=True
),
num_layers=4 # 轻量解码器
)
self.decoder_pos_embed = nn.Parameter(
torch.randn(1, 196 + 1, decoder_dim) * 0.02
)
self.mask_token = nn.Parameter(torch.randn(1, 1, decoder_dim))
self.pixel_pred = nn.Linear(decoder_dim, patch_size * patch_size * 3)
def random_masking (self, x, mask_ratio):
"""
x: [B, N, D] - 所有Patch tokens
返回: 可见tokens + 掩码索引
"""
B, N, D = x.shape
len_keep = int(N * (1 - mask_ratio))
# 随机打乱顺序
noise = torch.rand(B, N, device=x.device)
ids_shuffle = torch.argsort(noise, dim=1)
ids_restore = torch.argsort(ids_shuffle, dim=1)
# 保留前 len_keep 个token
ids_keep = ids_shuffle[:, :len_keep]
x_masked = torch.gather(
x, dim=1,
index=ids_keep.unsqueeze(-1).repeat(1, 1, D)
)
return x_masked, ids_restore, ids_keep
def forward (self, imgs):
# 1. 将图像分割为Patches并编码
patches = self.encoder.patch_embed(imgs) # [B, N, D]
patches = patches + self.encoder.pos_embed[:, 1:, :]
# 2. 随机掩码 - 保留25%的可见Patches
x_masked, ids_restore, ids_keep = self.random_masking(
patches, self.mask_ratio
)
# 3. 编码器 (仅处理可见Patches, 高效!)
cls_token = self.encoder.cls_token + self.encoder.pos_embed[:, :1, :]
cls_token = cls_token.expand(imgs.shape[0], -1, -1)
x = torch.cat([cls_token, x_masked], dim=1)
x = self.encoder.blocks(x)
x = self.encoder.norm(x)
# 4. 编码器到解码器的映射
x = self.enc_to_dec(x)
# 5. 添加掩码Token并恢复顺序
mask_tokens = self.mask_token.repeat(
x.shape[0], ids_restore.shape[1] - x.shape[1] + 1, 1
)
x_full = torch.cat([x[:, 1:, :], mask_tokens], dim=1)
x_full = torch.gather(
x_full, dim=1,
index=ids_restore.unsqueeze(-1).repeat(1, 1, x_full.shape[-1])
)
# 6. 添加解码器位置编码并通过解码器
x_full = x_full + self.decoder_pos_embed[:, 1:, :]
x_dec = self.decoder(x_full)
# 7. 预测像素值 (仅计算被掩码部分)
pred = self.pixel_pred(x_dec) # [B, N, p*p*3]
# 8. 计算MSE损失 (仅对被掩码的Patches)
target = self.patchify(imgs) # [B, N, p*p*3]
# 创建掩码标记: 哪些位置被掩码了
mask = torch.ones(imgs.shape[0], patches.shape[1], device=imgs.device)
mask[:, :int(patches.shape[1] * (1 - self.mask_ratio))] = 0
mask = torch.gather(mask, dim=1,
index=ids_restore) # 恢复顺序
loss = (pred - target) ** 2
loss = loss.mean(dim=-1) # 每个Patch的平均损失
loss = (loss * mask).sum() / mask.sum() # 仅掩码Patches
return loss
MAE为何计算高效?
编码器只处理可见Patches: 掩码率75%意味着编码器只需处理25%的序列长度,计算量减少到约原来的1/4
无需位置编码嵌入到掩码Token: 掩码Token是在编码之后才加入的,编码器不需要处理大量掩码Token
轻量解码器: 解码器只在预训练阶段使用,微调时可丢弃,不增加下游任务的计算开销
整体训练速度: MAE训练速度比对比学习方法快3倍以上(同样epoch数下)
◆ MAE架构示意 ◆
┌──────────┐ ┌─────────────┐ ┌──────────────┐
│ 图像 224x224│──→│随机掩码(75%)│──→│编码器(ViT) │
│ (196patches)│ │ 保留49 patches │ │ 仅处理可见部分 │
└──────────┘ └─────────────┘ └──────┬───────┘
│
┌──────────┐ ┌─────────────┐ ┌──────┴───────┐
│ 重建图像 │←──│解码器重构 │←──│ 加掩码Token │
│ MSE损失 │ │ 预测像素值 │ │ 恢复完整长度 │
└──────────┘ └─────────────┘ └──────────────┘
六、自监督学习的应用
NLP预训练
自监督学习在NLP领域的应用最为成熟。BERT的掩码语言建模(Masked Language Model)本质就是一种自监督任务:随机遮掩输入文本中的15%的Token,让模型根据上下文预测被遮掩的词。GPT系列的自回归语言建模(Autoregressive Language Modeling)则是预测下一个Token。这些自监督预训练模型在几乎所有NLP任务上刷新了纪录。
视觉预训练
在计算机视觉领域,自监督预训练正在逐步取代有监督预训练(ImageNet分类预训练)。DINO、iBot、MAE等方法在ViT上的表现已经显著超过有监督预训练,在下游任务(检测、分割、分类)上展现出更强的泛化能力。
多模态学习:CLIP
CLIP(Contrastive Language-Image Pre-training)由OpenAI于2021年提出,是跨模态自监督学习的里程碑。CLIP使用4亿图文对进行对比学习:将图像和其对应的文本描述作为正样本对,Batch内的其他图文对作为负样本对。训练后的CLIP模型可以进行零样本图像分类、图文检索等任务,展现出惊人的泛化能力。
# CLIP对比学习的核心思想 (伪代码)
# 输入: batch_size个 图像-文本对
image_features = image_encoder(images) # [B, D]
text_features = text_encoder(texts) # [B, D]
image_features = normalize(image_features, dim=1)
text_features = normalize(text_features, dim=1)
# 计算相似度矩阵 [B, B]
logits = image_features @ text_features.T * exp(temperature)
# 对称交叉熵损失:
# 图像→文本方向: 每行的标签是 [0, 1, 2, ..., B-1]
loss_i2t = cross_entropy(logits, labels)
# 文本→图像方向: 每列的标签是 [0, 1, 2, ..., B-1]
loss_t2i = cross_entropy(logits.T, labels)
loss = (loss_i2t + loss_t2i) / 2
医学影像应用
医学影像是自监督学习最具价值的应用场景之一。医学影像数据具有以下特点:
标注成本极高: 需要资深放射科医生逐张标注,单张CT的标注可能耗时数小时
数据量丰富: 医院每天产生海量未标注的影像数据(X光、CT、MRI、超声)
多模态特性: 同一患者常有多模态检查结果,天然形成跨模态对应关系
医学影像自监督典型案例
MoCo在胸部X光片上的应用: 使用MoCo在ChestX-ray14数据集(11万张未标注X光片)上预训练,再用少量标注数据微调,在肺炎检测任务上达到甚至超过全监督训练的ResNet
MAE在CT图像上的应用: 在三维CT图像上使用掩码自编码器,通过重建被遮掩的体素来学习器官结构和异常检测的特征
对比学习在病理切片(WSI)上的应用: 利用超大病理切片图像的局部-patch对比关系,学习肿瘤区域的细粒度特征
无监督异常检测
自监督学习在异常检测领域也有独特优势。其核心思路是:利用大量正常样本 进行自监督训练(如掩码重建、对比学习),使模型学习到"正常"的数据分布特征。在测试阶段,模型对异常样本的重建误差或表征差异会显著增大,从而实现异常检测。这种方法在工业缺陷检测、医学图像异常识别等场景中表现出色。
# 自监督异常检测: 基于重建的方法
class AnomalyDetector(nn.Module):
def __init__ (self):
super().__init__()
# 仅用正常样本训练的自编码器
self.autoencoder = MAE(encoder=vit_base())
def anomaly_score (self, x):
# 重建图像
x_recon = self.autoencoder(x)
# 逐像素重建误差作为异常分数
error_map = (x - x_recon).abs().mean(dim=1) # [B, H, W]
# 异常区域具有更高的重建误差
return error_map
# 异常评分 → 定位异常区域
error_map = detector.anomaly_score(x_test)
anomaly_mask = error_map > threshold # 二值分割
七、各方法比较与选择指南
方法
是否需要负样本
Batch Size要求
训练效率
ImageNet Top-1
最佳适用场景
SimCLR
是
大(4096+)
中等
~76.5%
有大量GPU资源时
MoCo v2
是
小(256)
中等
~77.4%
资源受限、医学影像
BYOL
否
较小(512)
较慢(需长训练)
~74.3%
理论研究、高精度需求
SimSiam
否
小(256)
快
~71.3%
简洁实现、快速实验
MAE
否
中等(1024)
极快(1/4计算量)
~87.8% (ViT-H)
大规模预训练、ViT
CLIP
是(跨模态)
大(32768)
慢
~76.2% (零样本)
多模态、零样本迁移
方法选择的一般建议
如果您资源充足 (大批量GPU)且希望实现简单——选择SimCLR
如果您资源有限 但需要强对比学习——选择MoCo v2/v3
如果您使用ViT架构 ——选择MAE 或DINO
如果您处理多模态数据 ——选择CLIP 或ImageBind
如果您进行异常检测 ——选择MAE重建 或DeepSVDD
如果您处理医学影像 ——推荐MoCo (小批量友好)或MAE (捕捉全局结构)
八、核心要点总结
自监督学习的本质: 从数据自身构造监督信号,通过精巧的代理任务(对比、掩码重建、跨模态匹配)学习通用的特征表示,无需人工标注
对比学习的核心公式: 拉近正样本对(同一图像的不同增强视图)+ 推远负样本对(不同图像),使用InfoNCE / NT-Xent损失函数
SimCLR的关键支柱: 强数据增强组合、MLP投影头、大批量(4096+)、NT-Xent损失、温度系数控制困难样本关注度
MoCo的突破: 队列字典解耦负样本数量与Batch Size,动量编码器保证特征一致性,使小批量也能获得丰富负样本
无负样本方法(BYOL/SimSiam): 通过不对称网络、预测头、Stop-gradient等机制防止表征坍塌,证明负样本并非对比学习必需
MAE的掩码重建: 75%高掩码率 + 非对称编码器-解码器设计,编码器仅处理25%可见Patch(1/4计算量),简单有效
跨范式核心统一的观点: 对比学习、掩码重建、非对比方法——所有自监督学习范式的本质都是在不同的数据视图 之间建立一致性的表征
未来趋势: 统一多模态自监督(CLIP/ImageBind)、大模型自监督预训练(LLM/VLM)、面向特定领域(医学影像、遥感、机器人)的自监督方法
九、进一步思考与实践建议
自监督学习是当前深度学习研究中最富有成果和最活跃的方向之一。随着研究的深入,以下几个方面值得进一步思考:
研究方向展望
规模化(Scaling Law): 自监督学习的性能如何随数据量、模型大小、计算量扩展?MAE和CLIP的实验表明,自监督方法在规模扩大时仍有持续的性能提升,这为大规模预训练提供了理论基础
统一框架: 能否构建一个统一的自监督框架,同时支持对比学习、掩码重建和跨模态对齐?Data2Vec和i-JEPA等方法是这一方向的有益尝试
领域自适应: 自监督预训练在自然图像上效果显著,但在医学影像、卫星图像、显微图像等专业领域是否同样有效?领域自适应自监督是重要的研究方向
与弱监督和半监督的融合: 自监督学习如何与少量标注数据结合(半监督自监督),或者与噪声标签结合(弱监督自监督)
理论基础: 对比学习中InfoNCE损失的互信息估计界、非对比方法防止坍塌的理论解释等理论问题仍有待深入研究
实践中的注意事项
数据增强的选择至关重要: 不同的数据集和任务需要不同的增强策略。在医学影像中,可能需要避免过于强烈的颜色失真
Batch Size的权衡: 虽然MoCo可以解耦Batch Size和负样本数量,但实际训练中Batch Size仍然影响训练稳定性和收敛速度
训练长度(Epoch数)的影响: 自监督学习通常需要比有监督学习更长的训练时间。BYOL需要300-1000 epoch才能达到最佳性能
微调策略: 自监督预训练后的微调策略(全微调、线性探测、部分微调)对下游任务性能影响显著
最后,推荐以下经典论文供进一步学习(按时间顺序排列):SimCLR (Chen et al., 2020), MoCo v2 (Chen et al., 2020), BYOL (Grill et al., 2020), SimSiam (Chen & He, 2021), MAE (He et al., 2022), CLIP (Radford et al., 2021), DINO (Caron et al., 2021)。这些论文不仅提供了丰富的技术细节,也展现了自监督学习研究的思想演进脉络。
本学习笔记为本人学习资料,仅供参考
基于公开学术论文、官方文档及个人学习总结整理
参考论文:SimCLR, MoCo, BYOL, SimSiam, MAE, CLIP, DINO 等文献
生成于 2026年5月5日