共计 3722 个字符,预计需要花费 10 分钟才能阅读完成。
引言
自然语言处理(NLP)领域中,预训练语言模型如 BERT(Bidirectional Encoder Representations from Transformers)已经取得了巨大成功。然而,BERT 等模型的性能高度依赖于大量标注数据,这在实际应用中往往是一个瓶颈。对比学习(Contrastive Learning)作为一种自监督学习方法,能够有效利用未标注数据,提升模型的表征(representation)能力。本文将介绍如何将对比学习与 BERT 结合,通过 PyTorch 实现一个简单的对比学习任务,并分享实践中的经验与技巧。

背景:BERT 与对比学习的结合价值
BERT 通过大规模预训练学习通用的语言表征,但其微调阶段仍需要大量标注数据。对比学习的核心思想是通过学习正样本对(相似样本)和负样本对(不相似样本)之间的差异,使模型能够更好地区分不同类别的数据。这种方法的优势在于:
- 减少对标注数据的依赖:对比学习可以利用未标注数据,通过数据增强生成正负样本对。
- 提升表征质量:通过对比学习,BERT 可以学习到更具判别性的表征,从而在下游任务中表现更好。
- 适用于多模态任务:对比学习不仅可以用于文本,还可以扩展到图像、音频等多模态数据。
技术对比:对比学习与传统监督学习
传统监督学习通过最小化预测标签与真实标签之间的差异(如交叉熵损失)来训练模型。而对比学习则通过优化正负样本对之间的相似度来学习表征。具体来说,对比学习的核心是 InfoNCE 损失函数(Noise Contrastive Estimation):
$$
\mathcal{L}{InfoNCE} = -\log \frac{\exp(sim(q, k+) / \tau)}{\sum_{i=1}^N \exp(sim(q, k_i) / \tau)}
$$
其中:
– (q) 是查询样本(query)的表征。
– (k_+) 是正样本(positive key)的表征。
– (k_i) 是负样本(negative key)的表征。
– (\tau) 是温度系数(temperature),控制相似度的分布。
– (sim(\cdot, \cdot) ) 是相似度函数,通常为余弦相似度。
与传统监督学习相比,对比学习的优势在于:
- 更鲁棒的表征:通过对比学习,模型能够学习到更具判别性的表征,而不仅仅是拟合标签。
- 更强的泛化能力:对比学习可以通过数据增强生成多样化的正负样本对,提升模型的泛化能力。
核心实现
1. 加载 BERT 模型
我们使用 HuggingFace Transformers 库加载预训练的 BERT 模型:
from transformers import BertModel, BertTokenizer
import torch
# 加载 BERT 模型和分词器
model_name = 'bert-base-uncased'
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertModel.from_pretrained(model_name)
# 示例:编码文本
text = "Hello, world!"
inputs = tokenizer(text, return_tensors='pt', padding=True, truncation=True)
outputs = model(**inputs)
embeddings = outputs.last_hidden_state # [batch_size, seq_len, hidden_size]
2. 构建正负样本对
对比学习的关键是构建正负样本对。对于文本数据,可以通过以下方式生成正样本:
- 数据增强:对同一文本进行轻微修改(如替换同义词、随机删除词等)。
- 上下文窗口:从同一文档中提取相邻的句子作为正样本。
负样本则通常来自同一批次(batch)中的其他样本。
# 示例:构建正负样本对
def generate_positive_pairs(texts, tokenizer, model):
# 对文本进行数据增强(这里简化为重复文本)augmented_texts = [text + "(augmented)" for text in texts]
# 编码原始文本和增强文本
inputs_original = tokenizer(texts, return_tensors='pt', padding=True, truncation=True)
inputs_augmented = tokenizer(augmented_texts, return_tensors='pt', padding=True, truncation=True)
with torch.no_grad():
outputs_original = model(**inputs_original)
outputs_augmented = model(**inputs_augmented)
# 获取 CLS token 的表征作为句子的全局表征
embeddings_original = outputs_original.last_hidden_state[:, 0, :] # [batch_size, hidden_size]
embeddings_augmented = outputs_augmented.last_hidden_state[:, 0, :]
return embeddings_original, embeddings_augmented
3. 实现对比损失函数
以下是 InfoNCE 损失函数的 PyTorch 实现:
import torch.nn.functional as F
def contrastive_loss(query, positive_key, temperature=0.1):
# 计算查询样本与正样本的相似度
positive_sim = F.cosine_similarity(query, positive_key, dim=-1) # [batch_size]
# 计算查询样本与批次中所有样本的相似度(包括正样本和其他负样本)all_sim = F.cosine_similarity(query.unsqueeze(1), positive_key.unsqueeze(0), dim=-1) # [batch_size, batch_size]
# 计算 InfoNCE 损失
numerator = torch.exp(positive_sim / temperature)
denominator = torch.sum(torch.exp(all_sim / temperature), dim=1)
loss = -torch.log(numerator / denominator)
return loss.mean()
避坑指南
1. 温度系数的调参经验
温度系数 (\tau) 是对比学习中的关键超参数:
- 较小的 (\tau) 会放大相似度的差异,使模型更关注困难负样本(hard negatives)。
- 较大的 (\tau) 会平滑相似度的分布,使模型更关注全局结构。
建议从 (\tau = 0.1) 开始,根据验证集性能进行调整。
2. 负样本数量与 batch size 的关系
对比学习的性能高度依赖于负样本的数量。较大的 batch size 可以提供更多的负样本,从而提升模型性能。然而,较大的 batch size 也会增加计算开销。实践中,建议:
- 使用梯度累积(gradient accumulation)来模拟更大的 batch size。
- 使用内存库(memory bank)或动量编码器(momentum encoder)来增加负样本数量。
3. 梯度爆炸的预防措施
对比学习可能会因为相似度的计算导致梯度爆炸。以下是一些预防措施:
- 梯度裁剪(gradient clipping):限制梯度的最大范数。
- 学习率调整:使用较小的学习率或学习率预热(learning rate warmup)。
- 归一化:对表征进行 L2 归一化,避免相似度计算时的数值不稳定。
性能分析:计算开销与表征质量
对比学习的计算开销主要来自:
- 正负样本对的构建:尤其是数据增强和编码过程。
- 相似度计算:对于大批次数据,相似度矩阵的计算会占用大量内存。
为了平衡计算开销和表征质量,可以考虑以下优化:
- 使用较小的 BERT 模型(如 BERT-tiny 或 BERT-small)作为编码器。
- 采用分层对比学习(hierarchical contrastive learning),仅在局部或全局层面进行对比。
- 使用混合精度训练(mixed precision training)加速计算。
开放性问题
对比学习与领域自适应(domain adaptation)的结合是一个有趣的研究方向。例如:
- 如何利用对比学习减少源域(source domain)和目标域(target domain)之间的分布差异?
- 如何设计跨域的正负样本对,以提升模型在目标域上的泛化能力?
- 对比学习是否可以帮助解决领域自适应中的负迁移(negative transfer)问题?
这些问题值得进一步探索和实践。
结语
本文介绍了 BERT 与对比学习的结合方法,从基础原理到实战实现。通过对比学习,BERT 可以更好地利用未标注数据,学习到更具判别性的表征。希望本文能够帮助 NLP 初学者快速入门对比学习,并在实际项目中应用这一技术。
