对比学习 Contrastive Learning

Contrastive Learning

一种通过在嵌入空间中「拉近正样本对、推远负样本对」来训练表征模型的自监督/弱监督学习范式,是当代文本嵌入、图像表征、多模态对齐的核心基础算法。

详细解释

Contrastive Learning(对比学习) 一句话定义:不想花钱标几百万条数据?那就自己构造「相似对」和「不相似对」给模型看——两张同一个人的照片、两段同一个意思的句子,模型要把它们映射到向量空间里距离很近;两张不同人的照片、两段完全不相干的句子,模型要把它们推得很远。核心损失函数叫 InfoNCE Loss = -log(exp(sim(正样本对)/τ) / Σ exp(sim(负样本对)/τ)),看起来复杂本质就是「softmax 分类:在一堆负样本里正确挑出正样本的概率最大化」。

对比学习是 2018 年之后深度学习最重要的算法突破之一:2020 年谷歌的 SimCLR 和 He 恺明团队的 MoCo 把它在 CV 上做火,2021 年谷歌的 Sentence-T5 和斯坦福的 SimCSE 把它引入 NLP 文本嵌入领域,直接让文本向量的语义匹配准确率从之前 Siamese 网络的 70% 提升到 85%+。现在你用的几乎所有文本 embedding 模型(OpenAI text-embedding-3、BAAI/bge 系列、Cohere Embed v3)、图像 CLIP 模型、多模态对齐模型,底层 100% 是对比学习训练出来的。

唯元智创 的 Embedding 微调服务就是「用户上传自己的业务领域相似对数据 → 我们在通用 bge-m3 基础上做对比学习微调 1-3 轮 → 得到业务领域准确率涨 10-20% 的私有 embedding 模型」。不需要用户懂对比学习细节,只需要提供「(句子A, 句子B, 相似/不相似)」的标注对,平台自动处理负样本挖掘、温度 τ 调优、InfoNCE 损失、难负样本挖掘这些细节。

对比学习的三大核心要素(决定效果好坏的 90%)

要素作用最优实践
① 正样本对构造(Positive Pair)模型从「这两个东西是一类」学到语义不变性NLP:同句子 dropout 两次(SimCSE 无监督)、同文本的回译、同文档的两个 chunk、同义改写;CV:同图像的两次不同增强(随机裁剪+颜色抖动)、同物体不同角度
② 负样本对构造 + 难负样本挖掘(Hard Negative)模型从「这两个看着像但其实不一样」学到判别力——这是最关键的,没有难负样本对比学习基本没用最基础:batch 里其他样本都是随机负样本(in-batch negatives);进阶:从向量库里检索 Top-5 相似但其实标签不同的样本当难负样本(hard negatives);再加 10% 随机负样本防止过拟合。难负样本:随机负样本 ≈ 1:3 最佳
③ 温度系数 τ(Temperature)控制「相似度分布的尖锐程度」——τ 小模型要死命区分非常细微的差别,τ 大模型马马虎虎就行NLP 文本:0.05 至 0.1 是黄金区间(最常用 0.07);CV 图像:0.07 至 0.2;多模态图文对齐:0.05 至 0.08。τ 调对了比改网络结构提升还大

对比学习常见落地场景 + 收益表

场景基线方法加对比学习后的方法准确率提升所需标注对数
企业私有知识库 RAG 检索直接用通用 bge-large 嵌入收集 1000 条「用户查询-正确答案 chunk」正样本对 + 自动挖难负样本,对比学习微调 2 轮Recall@10 +12% 至 +18%1000-5000 对足够
语义搜索(电商/文档搜索)BM25 关键词匹配BM25 + 对比学习微调后的领域向量检索 + RRF 融合NDCG@5 +15% 至 +25%5000-20000 对
重复数据检测/去重字符串匹配(Jaccard/Levenshtein)对比学习训练的 Siamese 编码 + 向量 ANN 近似去重F1 +20% 至 +30%,速度快 100 倍3000-10000 对
图像分类/商品图检索ImageNet 预训练 ResNet 直接用业务私有商品图 对比学习自监督预训练(SimCLR/MoCo)再微调分类Top-1 Acc +5% 至 +10%1 万至 10 万无标注图像足够(无监督)
多模态图文对齐分开训练文本和图像模型CLIP 风格 图文对比学习(图文对正样本,batch内其他图/文为负)图文检索 Recall@1 +20% 至 +35%10 万至 100 万图文对

对比学习工程落地的四个大坑

坑 1:对比学习不需要负样本太少——batch size 至少 512,最好 4096+。InfoNCE Loss 本质是分类问题,类别数 = batch size - 1(每个样本和 batch 里其他样本做负样本对比)。batch size = 64 时,等于让模型在 63 个选项里挑 1 个正样本,太简单了学不到东西;batch size = 4096 时,在 4095 个选项里挑 1 个,难度才够。很多小团队 GPU 显存不够跑不了大 batch,就用 Memory Bank(MoCo 队列存过去的 embedding 当负样本,相当于把 batch 虚拟放大到 65536)或者 GradCache 技术解决,效果一样好。

坑 2:别把难负样本挖得太狠,否则模型会过拟合到「只有难样本的特征」。比如你在法律知识库 RAG 里挖的难负样本全是「同一部法律的另外两个条款」(非常像但不一样),模型会学到「只盯着条款号区分」,但遇到真正不相似的日常文本反而分不清。正确配比:每一条训练样本配 1 个难负样本 + 3 个随机负样本 + 1 个超简单负样本(完全不相关领域),负样本难度呈金字塔分布,模型学到的表征才既有判别力又有泛化性。

坑 3:对比学习之后模型的向量分布可能「坍缩」——所有样本向量都挤到一起,余弦相似度全在 0.7 到 0.9 之间。这叫 Representation Collapse(表征坍缩),是对比学习最常见的失败模式,原因通常是:(a) 温度 τ 设太大了(比如 >0.5),模型不需要分辨细节;(b) 正样本太像、负样本太不像,任务太简单;(c) 没有用 Projector Head(投影头,把 embedding 先过一个 2 层 MLP 再算损失,训练完扔掉 MLP 只用 backbone 输出)。Fix:把 τ 调小到 0.05-0.1、加上 2 层 MLP projector、加入 Alignment 和 Uniformity 两个正则项约束,90% 的坍缩问题都能解决。

坑 4:对比学习微调后千万别直接替换向量库——旧库是用旧模型 embedding 的,维度/分布完全不一样,召回直接全崩。每次微调完必须:(a) 用新模型把全知识库重新 embedding 一遍(离线批量跑,可能几小时);(b) 新建一个独立向量索引;(c) 线上 5% 流量灰度跑 A/B 实验,确认 Recall/Precision 真涨了再切全量;(d) 旧索引保留一周回滚窗口。很多团队上线时忘重做 embedding,直接替换模型,导致 RAG 系统当天召回率从 80% 掉到 5%,教训惨痛。

常见问题

没钱买标注对怎么搞对比学习?纯无监督能做吗?
当然可以,而且无监督对比学习在很多场景下效果已经能到有监督的 80% 至 90%,成本几乎为零。NLP 无监督三大经典方法:(1)SimCSE——最简单有效,同一个句子过两次带 dropout 的 encoder(两次随机 dropout 相当于不同「视图」),两次输出当正样本对,batch 内其他当负样本,不需要任何标注,拿 100 万无标注句子就能训;(2)ESimCSE 改进版——在 SimCSE 基础上加「动量对比(MoCo 风格)」和「难负样本=同 batch 里句子长度最接近的句子」,SimCSE 再涨 3%;(3)基于文档的自监督——同一篇长文档里切出的两个不重叠 chunk 当正样本(它们语义相关),不同文档的 chunk 当负样本,这个在企业长文档 RAG 里特别适合,只要你有几千篇 PDF/Word 就能训,零标注成本。CV 无监督就更成熟了:SimCLRv2、MoCo v3、DINO 都是纯无监督,用 100 万张无标注图片训出来的 backbone 迁移到下游分类任务上,和有监督预训练的差距已经在 1% 至 2% 以内。
对比学习和传统的 Triplet Loss 有什么区别?为什么现在都用 InfoNCE 不用 Triplet?
一句话核心区别:Triplet Loss 是「1 锚 + 1 正 + 1 负」一次只比 3 个样本,属于二分类(正对距离比负对小 margin 就行);InfoNCE 是「1 正 + N 负」一次在 N+1 个样本里做 softmax 分类,属于多分类。为什么 InfoNCE 全面取代 Triplet 的四个原因:(1)梯度更密集——InfoNCE 一个 batch 里每个样本都能和其他所有样本产生梯度信号,梯度利用率是 Triplet 的 N 倍(N=batch size);Triplet 里很多负样本对因为满足 margin 条件根本不产生梯度(easy triplet 梯度为 0),训练效率极差;(2)不需要人工设 margin 超参数——Triplet 需要手动设 margin=0.5 还是 1.0,调不好模型要么不收敛要么过拟合;InfoNCE 只需要温度 τ,区间稳定(0.05-0.2)基本不用调;(3)天然支持 batch 内负样本(in-batch negatives)——InfoNCE 不需要额外采样策略,batch 里其他样本天然就是负样本,数据利用率极高;Triplet 必须精心设计「负样本挖掘 + 难负样本三元组生成」的采样流程,工程复杂度高很多;(4)效果更好——在几乎所有公开 Benchmark 上,相同数据相同 epoch,InfoNCE 训练出来的模型比 Triplet Loss 高 3% 至 8%,没有任何悬念。
对比学习微调 embedding 模型时要冻住底层多少层?GPU 不够全量微调怎么办?
三种典型微调策略,按 GPU 资源从多到少排:(1)全量微调(Full Fine-tuning)—— GPU 显存 ≥ 48G(A100/4090 24G 用 LoRA也行,近似全量效果),所有层都训;适合标注对 ≥ 10 万条,数据够多不怕过拟合,效果上限最高,通常比通用模型涨 15% 至 20%;(2)只训 Top 2 至 4 层 + Projector Head——冻住底层(比如 BERT-base 12 层里只训第 11、12 层 + 2 层 MLP projector),显存是全量的 1/4,适合标注对 1 万至 10 万条,效果能到全量微调的 80% 至 90%,性价比最高,绝大多数团队选这个;(3)LoRA/QLoRA 微调——只在每一层 Attention 的 Q/V 矩阵上加低秩适配器,额外参数量只有原模型的 0.1% 至 1%,10 系列消费级显卡(16G)就能跑 7B 模型对比学习;适合标注对 1000 至 1 万条小数据集,效果大概是全量微调的 60% 至 80%,但成本极低。经验:无论哪种策略,对比学习训完之后一定要做「嵌入分布可视化」——用 t-SNE/UMAP 把训前训后的 embedding 降维到 2D 画出来,训后相同类别的点应该聚成簇、不同类别明显分开,如果还是一团乱麻说明没训好,回去调 τ、调 batch size、挖难负样本。