在检索器训练中,减轻多重负例排序损失中的假阴性问题
多重负例排序损失和批内负例
在使用对比学习训练sentence-transformers(用于文本嵌入的双编码器模型)时,一个常见的目标是多重负例排序(MNR)损失。在此设置中,我们有一个锚点句子和一个正例句子,它们构成一个真正的配对(例如,查询和相关段落,或两个释义)。模型被训练成锚点的嵌入与它的正例接近,同时远离其他被视为负例的句子嵌入。重要的是,MultipleNegativesRankingLoss大量使用了批内负例:给定一批(锚点,正例)对(或锚点-正例-负例三元组),批次中任何其他不匹配的句子都被视为负例。换句话说,对于给定的锚点,批次中其他配对的所有正例都充当负例(假设它们不相关)。这种批内负例采样大大提高了效率,因为我们无需显式挖掘即可获得许多免费的负例。
为什么使用批内负例? 批次越大,我们可以推导出的负例对越多,通常模型学习得越好。直观地说,批次中每增加一个示例就提供了一个锚点不应该匹配的“干扰项”,迫使模型锐化其嵌入区分度。MultipleNegativesRankingLoss本质上最大化真实(锚点,正例)对的相似度,同时最小化锚点与批次中其他(锚点',正例')对的相似度。这可以看作是批次中的一种InfoNCE对比损失。
然而,这种方法假设任何两个不匹配的句子都是真正的负例。这种朴素的假设可能会失效——有时批次中的“锚点”和某个其他“正例”实际上可能在语义上相似(例如,关于同一主题的两个不同问题,或释义的句子)。在这种情况下,将它们视为负例是错误的;它们是假阴性,因为损失不应该将它们推开。不幸的是,最初设计的MultipleNegativesRankingLoss仍然会将它们视为负例,这可能会损害训练。我们将讨论如何解决这个问题。
难负例与批内负例
并非所有负例都相同。实际上,我们区分难负例和默认的批内负例(如果未特别挖掘,有时称为随机负例)
- 难负例: 这些负例之所以被选中,是因为它们特别具有挑战性——例如,与查询在词汇或语义上相似但不相关的段落,或与锚点仅有细微差别的句子。难负例通常通过挖掘获得:使用现有检索模型或交叉编码器来查找给定查询的排名靠前但不正确的结果。由于它们与正例相似,难负例可以教会模型进行细粒度区分。许多最先进的嵌入模型管道都包含一个挖掘步骤,以收集这些困难的负例(例如,在大语料库上使用双编码器或交叉编码器教师模型)。缺点是,如果挖掘方法过于“好”,一些检索到的“负例”实际上可能是未标记的真阳性——即假阴性。例如,在NV-Retriever论文中,作者引用了先前的研究(Qu et al., 2020),表明在与MS MARCO查询最相似的段落中(通常用作难负例),约有70%实际上是相关的,应该被标记为正例。这突出了一个风险:如果标签不完整,使用极其相似(排名靠前)的项目作为负例会引入大量噪声。
- 批内负例: 如前所述,它们是训练批次中的其他示例,根据构造,它们被认为是与锚点无关的。它们是“免费”的负例,不需要挖掘步骤。批内负例的挑战在于,大多数都是容易的负例(真正不相关的句子),特别是如果您的批次由随机配对组成。容易的负例不如难负例教会模型那么多。然而,优点是数量:批次大小为B时,每个锚点提供B-1个负例。如果B很大,负例的纯粹多样性可能偶然包含一些中等难度的负例(并且至少迫使模型区分许多不同的句子)。如果B很小,负例很少,您可能会错过从困难对比中学习的机会。总而言之,大批次使批内负例更有效,正如多项工作所指出的那样。事实上,最近的E5嵌入模型论文明确指出,将批次大小从1K扩展到32K在多个评估集上产生了持续的增益。他们还指出,如果大批次不可行,可以通过在较小批次中添加一些难负例来弥补,以实现类似的好处。同样,BAAI的BGE M3-Embedding模型强调优化批处理策略,以实现非常大的批次和高吞吐量,从而提高了学习嵌入的区分度。
总而言之,难负例提供了有针对性的困难比较,但需要仔细挖掘(如果挖掘过于激进,则存在假阴性的风险),而批内负例易于获取且可扩展,但需要大批量才能达到最大效果。许多现代训练设置实际上结合了两者:例如,为每个锚点使用几个挖掘的难负例,并且仍然使用批次中的其余部分作为额外的负例。这样,即使中等大小的批次也可以包含一些强负例。如果提供了难负例,您可能不需要那么大的批次即可达到良好的性能——事实上,使用难负例可以使您在训练有效模型的同时使用较小的批次。但如果您只依赖随机批内负例,增加批次大小对于获得强大性能至关重要。
假阴性问题
虽然增加批次大小通常会改善对比学习,但它加剧了前面提到的问题:假阴性。在从多样化数据集中提取的非常大的批次中,批次中的某些其他示例实际上与给定锚点在语义上相关的可能性越来越大(即使它不是指定的正例)。例如,如果您的批次有1024个句子对,那么每个锚点就有1023个潜在的“负例”——其中至少一个实际上是真实匹配(可能是重复问题或释义)的几率随着批次的增长而增加。将那些偶然相关的对分开对模型有害。MS MARCO或Natural Questions等数据集有许多缺失的标签——查询通常有多个相关段落,但只有一个被标记为正例。所有其他相关段落,默认情况下,在训练中被视为负例,这会向对比目标注入噪声。NV-Retriever的作者强调了这一点,指出标准的难负例挖掘正是因为这个原因会引入假阴性:许多排名靠前的“负例”在仔细检查后被证明是相关的。
批内负例也有同样的问题:通过始终将批次中任何不匹配的对视为负例,我们可能会惩罚模型将高相似度分配给实际上可能是相关对的情况。我们需要一种机制来在训练期间检测并避免假阴性,特别是当批次(或挖掘的负例池)变大时。
在难负例挖掘和批内负例中移除假阴性
在解决嵌入模型训练中的假阴性问题时,区分它们可能出现的两个阶段很重要——在难负例挖掘期间(在数据准备阶段)和在批内负例中(在模型训练期间)。关键区别在于,难负例挖掘中的假阴性通常在数据采样过程中在训练之前进行过滤,而批内负例中的假阴性则在使用引导模型进行训练期间动态过滤。这种区别突出了根据假阴性在训练管道中出现的时间和位置而采用的不同缓解策略。
解决嵌入模型训练中假阴性问题有两种主要方法。第一种方法是利用基于边际的过滤标准对锚点-正例对进行正例感知难负例挖掘,这在NV-Retriever论文中得到了广泛探讨。第二种方法专门通过使用引导模型从批内负例中移除假阴性。
减轻假阴性的第一种方法是在难负例挖掘过程中,通过在负例选择过程中引入基于边际的阈值来应用正例感知挖掘。这个想法最近在NV-Retriever论文中进行了探讨,该论文提出使用正例的相关性得分作为锚点来决定哪些负例需要过滤掉。本质上,如果一个负例的得分接近正例的得分,则将其从训练中移除。NV-Retriever的工作探讨了两种方法:他们尝试了绝对得分阈值(例如,删除任何相关性得分高于0.7的负例)和正例相对阈值(删除得分至少为正例得分95%的负例)。他们发现正例感知相对阈值表现最佳,因为它适应了每个查询的特定正例得分。如果一个负例几乎与正例相当(在几个百分点之内),则应将其移除,这是假阴性的强烈指标。这种“边际”方法提供了更精细的控制:而不是忽略所有中等高得分的负例,您可以调整“太接近”的程度。例如,在NV-Retriever的消融研究中,5%的边际(仅保留相似度小于正例相似度95%的负例)被证明是最佳设置。
以下是使用 sentence-transformers
库中的 mine_hard_negatives
函数的示例。
from sentence_transformers.util import mine_hard_negatives
from sentence_transformers import SentenceTransformer
from datasets import load_dataset
# Load a Sentence Transformer model
model = SentenceTransformer("all-MiniLM-L6-v2")
# Load a dataset to mine hard negatives from
dataset = load_dataset("sentence-transformers/natural-questions", split="train")
dataset = mine_hard_negatives(
dataset=dataset,
model=model,
range_min=10,
range_max=50,
max_score=0.8,
relative_margin=0.05,
num_negatives=5,
sampling_strategy="random",
batch_size=128,
use_faiss=True,
)
第二种方法,即通过使用引导(教师)模型专门从批内负例中移除假阴性,已在Sentence Transformers库中实现为GISTEmbedLoss(Guided In-sample Selection of Training Negatives for Text Embedding Fine-tuning)。GISTEmbedLoss通过结合引导模型来辅助选择批内负例,从而扩展了标准MultipleNegativesRankingLoss。在训练期间,对于每个锚点和批次中每个潜在的负例,引导模型会计算一个相似度分数。如果引导模型确定某个负例比其对应的正例更相似于锚点,则该对将被屏蔽并从损失计算中排除。通过过滤掉这些可能的假阴性,GISTEmbedLoss提供了更清晰、更可靠的训练信号,从而提高了模型的稳定性和嵌入的鲁棒性。正如人们所预期的那样,更高质量的训练数据会带来更好的模型。事实上,GISTEmbedLoss已被证明可以提高性能,这在原始论文和我们自己的实验中都有体现。
以下是使用 sentence-transformers
库中的 CachedGISTEmbedLoss
训练嵌入模型的示例,该函数支持基于边际的过滤以在训练期间排除假阴性。
from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses
from datasets import Dataset
model = SentenceTransformer("microsoft/mpnet-base")
guide = SentenceTransformer("all-MiniLM-L6-v2")
train_dataset = Dataset.from_dict({
"anchor": ["It's nice weather outside today.", "He drove to work."],
"positive": ["It's so sunny.", "He took the car to the office."],
})
loss = losses.GISTEmbedLoss(
model,
guide,
mini_batch_size=64,
margin_strategy="absolute", # or "relative" (e.g., margin=0.05 for max. 95% of positive similarity)
margin=0.1
)
trainer = SentenceTransformerTrainer(
model=model,
train_dataset=train_dataset,
loss=loss,
)
trainer.train()
这两种方法——通过基于边际的过滤在难负例挖掘中移除假阴性,以及使用引导模型在批内负例中移除假阴性——提供了互补的策略,以有效减少假阴性,从而产生更清晰的训练信号和更好的嵌入模型性能。
CachedMultipleNegativesRankingLoss:支持大批量训练
在深入探讨引导损失和边际之前,值得一提的是使实际中大规模批处理成为可能的一项创新:CachedMultipleNegativesRankingLoss。这是MultipleNegativesRankingLoss的一种变体,在Sentence Transformers中引入,它使用两步计算(嵌入缓存,然后损失计算),以便在不耗尽GPU内存的情况下有效使用极其大的虚拟批次大小。其思想(受梯度检查点/缓存和跨设备负例池化等技术启发)是首先计算大量示例的嵌入(聚合多个小批次),然后通过重用存储的嵌入而不是一次将整个批次保留在内存中,计算该较大集合上的对比损失。这允许,例如,模拟数千个示例的批次,即使您的GPU一次只能容纳128个。 CachedMultipleNegativesRankingLoss使研究人员能够将批次大小进一步扩展(例如到数千),从而通过利用更多批内负例来提高模型性能。事实上,Sentence Transformers文档引用的关于《在内存受限设置下扩展深度对比学习批次大小》的原始论文表明,此类负例缓存可以在不增加内存成本的情况下显著提高检索性能。
在此基础上,CachedGISTEmbedLoss 将 CachedMultipleNegativesRankingLoss 的内存效率与 GISTEmbedLoss 的假阴性过滤功能相结合。它将两者的优点结合起来:模拟大规模批次的能力以及使用引导模型识别和过滤误导性负例的鲁棒性。缓存机制确保了高吞吐量训练和稳定的内存使用,而引导组件主动删除了可能导致训练不稳定的假阴性。结果是更具可扩展性、更稳定和更有效的对比学习目标。CachedGISTEmbedLoss 使实践者能够充分利用大规模批次训练(负例丰富)的全部功能,而不会牺牲信号质量。
以下是使用 CachedGISTEmbedLoss
的示例,这是 sentence-transformers
库提供的一种损失函数,通过缓存嵌入并单独计算损失来支持大批量训练。
from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses
from datasets import Dataset
model = SentenceTransformer("microsoft/mpnet-base")
guide = SentenceTransformer("all-MiniLM-L6-v2")
train_dataset = Dataset.from_dict({
"anchor": ["It's nice weather outside today.", "He drove to work."],
"positive": ["It's so sunny.", "He took the car to the office."],
})
loss = losses.CachedGISTEmbedLoss(
model,
guide,
mini_batch_size=64,
margin_strategy="absolute", # or "relative" (e.g., margin=0.05 for max. 95% of positive similarity)
margin=0.1
)
trainer = SentenceTransformerTrainer(
model=model,
train_dataset=train_dataset,
loss=loss,
)
trainer.train()
边际增强GISTEmbedLoss(绝对边际与相对边际)
CachedGISTEmbedLoss 采用了 NV-Retriever 论文中提出的正例感知策略,通过引入可配置的边际策略来过滤负例,使用户能够精细控制过滤潜在假阴性的激进程度。您可以选择两种模式:
- 绝对边际: 使用固定值边际 m。引导模型计算锚点-正例对的相似度得分(记为 S_pos)以及每个锚点-负例对的相似度得分(S_neg)。任何满足
S_neg ≥ S_pos – m
的负例都将被丢弃。本质上,如果一个负例的得分与正例得分的差距在 m 之内,我们认为它太接近而不能算作真正的负例。例如,如果 m = 0.1 且正例相似度为 0.8,则任何与锚点得分 ≥ 0.7 的负例都将被过滤掉。 - 相对边际: 基于百分比的标准。我们不使用绝对差值,而是使用正例得分的一部分。如果
S_neg ≥ S_pos * (1 - r)
(其中 r 为某个比率),则过滤掉负例。例如,当 r = 0.05(即 5%)时,如果正例得分为 0.8,我们丢弃得分 ≥ 0.8 * 0.95 = 0.76 的负例。这会根据正例的难度进行缩放:如果 S_pos 较低,则负例的阈值在绝对值上也会较低。相对策略意味着“不允许负例的得分达到正例得分的 X% 以内。”
这些基于边际的过滤器有助于捕获临界情况。小的边际(绝对或相对)意味着只移除几乎与正例一样好的负例;大的边际甚至会过滤掉中等相似的负例。因此,可以根据您对将某项标记为“假阴性”的严格或宽松程度来调整边际。在实践中,相对边际被发现效果非常好,因为它会适应每个示例的上下文。NV-Retriever的实验证实了这一点:他们表现最好的方法,称为TopK-PercPos,有效地使用了5%的相对边际(他们保留了相似度小于正例相关性得分95%的负例)。在他们的消融研究中,这种动态阈值优于静态截止值。CachedGISTEmbedLoss允许您轻松尝试这两种策略(margin_strategy="absolute"
或"relative"
),并为您的任务设置适当的margin
值。
通过调整边际,可以控制负例过滤中的查准率-查全率权衡。零边际本质上复制了原始 GISTEmbedLoss 的行为(只删除与正例相似度至少相同的负例)。略微的边际(例如 5-10%)将捕捉到负例几乎相似但未超过正例得分的情况。进一步增加边际将使过滤更具侵略性(可能会过滤掉一些碰巧具有中等相似度的合法负例)。
何时使用CachedGISTEmbedLoss
当您拥有丰富的数据且数据中存在一些潜在的噪声/重叠,并希望最大程度地发挥嵌入性能时,CachedGISTEmbedLoss 会大放异彩
- 如果您能承受更长的训练时间并希望获得最佳模型,那么使用大批量(带缓存)通常会带来小批量无法比拟的改进。例如,对于语义搜索,您可能使用有效批量大小为 16k 或更大的数据对进行训练,以最大化召回率。
- 如果您的数据集有噪声或有许多相似的对,引导模型将防止从假阴性中学习。这通常转化为更好的泛化能力。
- 另一方面,如果您的训练数据非常干净(没有假阴性的可能性)并且时间有限,为了简单起见,您可以选择 CachedMultipleNegativesRankingLoss(不带引导),如果批量大小不是问题,甚至可以选择标准 MNRL。GIST 的优点是以额外的计算(运行引导模型)和稍慢的训练速度为代价获得的。
实验
依赖批内负例的对比损失可能会遭受假阴性的影响——即实际上语义相关但被视为负例的例子。为了解决这个问题,我们在自定义的 CachedGISTEmbedLoss
中引入了一种灵活的、基于边际的过滤机制。通过丢弃任何与锚点相似度过高(或高于)正例相似度的负例——无论是固定量(绝对边际)还是比例(相对边际)——我们确保模型专注于真正的难负例。
下面我们总结两组实验:
- 英语数据实验:在 AllNLI(sentence-transformers/all-nli)数据集上训练 mpnet-base 模型。
- 韩语数据实验:在韩语查询-段落对数据集上微调 intfloat/multilingual-e5-small。
实验1:AllNLI,mpnet-base
我们使用Hugging Face的 AllNLI(sentence-transformers/all-nli) 数据集对微软的 mpnet-base 模型进行了微调,使用前100,000个锚点-正例-负例作为训练数据,并使用标准开发/测试集进行评估。作为引导模型,我们利用 all-mpnet-base-v2 在我们的 CachedGISTEmbedLoss
中计算引导相似度。
对于每个训练运行,我们设置了
损失边际策略
绝对值 (例如
margin=0.1
):丢弃满足以下条件的负例:sim(负例,锚点) ≥ sim(正例,锚点) – 0.1
相对值 (例如
margin=0.05
):丢弃满足以下条件的负例:sim(负例,锚点) ≥ sim(正例,锚点) × 0.95
超参数
- 设置1:批次大小512,学习率=2 × 10⁻⁵,1个epoch,预热0.1,FP16
- 设置2:批次大小2048,学习率=4 × 10⁻⁵,1个epoch,预热0.1,FP16


- 在批量大小为512的情况下,绝对边距0.2产生了最高的开发集准确率(约0.889),比标准MultipleNegativesRankingLoss高出约0.017点。无边距或标准MultipleNegativesRankingLoss导致性能相对较低。
- 在批量大小为2048的情况下,相对边距0.15实现了最佳的早期收敛和最高准确率(约0.857),比MultipleNegativesRankingLoss高出约0.014点。无边距或标准MultipleNegativesRankingLoss导致性能相对较低。
实验2:韩语查询-段落检索
然后,我们将 CachedGISTEmbedLoss 应用于韩语检索任务,使用韩语查询-段落对数据集进行训练。我们微调了 intfloat/multilingual-e5-small 模型,并使用相同的模型作为引导模型,在训练期间计算相似度分数。评估在 MTEB-ko-retrieval 数据集 (Git-Hub) 上进行。
- 超参数
- 设置:批量大小20,000,学习率=2.5 × 10⁻⁴,2个epoch,预热0.05,FP16
基线
- 基础模型(未训练):NDCG@10 = 0.671
- MNR(多重负例排序损失):0.626(相对于基础模型-0.045)
- GISTEmbedLoss(无边距):0.678(相对于基础模型+0.007)
相对边距(GISTEmbedLoss)
- 边距0.85 → 0.683(相对于基础模型+0.012)
- 边距0.90 → 0.684(相对于基础模型+0.013)
- 边距0.95 → 0.682(相对于基础模型+0.011)
绝对边距(GISTEmbedLoss)
- 边距0.85 → 0.678(相对于基础模型+0.007)
- 边距0.90 → 0.686(相对于基础模型+0.015)
- 边距0.95 → 0.686(相对于基础模型+0.015)

最佳设置(绝对0.90/0.95)相对于MultipleNegativesRankingLoss基线提高了0.015 NDCG@10——几乎是无边距增益的两倍。这些结果证实,受NV-Retriever假阴性移除启发而来的基于边距的过滤非常有效。
实验结论
在英语和韩语检索任务中,GISTEmbedLoss 始终优于 MultipleNegativesRankingLoss,这表明其在不同语言和领域中都具有强大的鲁棒性。通过仅调整一个标量参数——边际——实践者即可显著提高对比训练性能。
有趣的是,在韩语实验中,单独使用 MultipleNegativesRankingLoss 进行训练导致性能低于基础模型,这表明未能从批内负例中移除假阴性会显著降低检索质量。这强调了在对比学习管道中有效缓解假阴性的重要性。
值得注意的是,边际的应用进一步扩大了 GISTEmbedLoss 的优势,最佳边际值因语言和引导模型而异。在英语实验中,使用 mpnet-base 模型时,0.2 的绝对边际产生了最高性能。相比之下,对于韩语查询-段落实验,最佳结果是在 0.05 到 0.1 之间的边际获得的。
这种最佳边际设置的差异与训练过程中使用的引导模型的温度值一致。温度与相似度尖锐度成反比,影响相似度分数的分布范围。较低的温度会导致更集中的相似度分数,这有利于较小的边际,而较高的温度则产生更平坦的分布,从而受益于较大的边际。英语引导模型 all-mpnet-base-v2 以 0.05 的温度进行训练,支持使用较大的边际。同时,在针对韩语测试的多语言引导模型中——intfloat/multilingual-e5-small、BAAI/BGE-m3 和 snowflake-arctic-embed-l-v2.0——snowflake-arctic-embed-l-v2.0 模型取得了最佳结果。这款 arctic-embed 模型以 0.02 的温度进行训练,其产生的集中相似度分布有利于在过滤过程中使用较小的边际设置。
有关此温度-边际交互的更详细讨论,请参阅相关 GitHub issue。
结论
在检索器模型的对比学习中,消除假阴性不仅对于提高性能至关重要,而且对于确保学习到的嵌入能够反映真实的语义关系也至关重要。假阴性——那些与锚点在语义上相关但被视为负例的样本——会误导模型将本应接近的概念分离开来,最终损害嵌入质量和泛化能力。
本文探讨了两种互补的缓解假阴性的策略:
- 在难负例挖掘过程中过滤假阴性,使用基于边际的阈值,正如NV-Retriever论文中所提出的。通过采用正例感知过滤机制,模型会丢弃那些与正例相似度过高的负例,从而防止误导性监督。绝对边际和相对边际策略都为此过滤过程提供了精细的控制。
- 使用引导模型从批内负例中移除假阴性,如GISTEmbedLoss中所实现的那样。引导模型估计相似度分数,并排除任何看起来比指定正例更相似于锚点的批内负例。这增强了损失信号的可靠性,并防止惩罚语义正确的对齐。
根据实验结果,在其他条件相同的情况下,使用 GISTEmbedLoss(带有合适的引导和边际)训练的模型通常优于使用 MultipleNegativesRankingLoss 训练的模型。这是因为它通过大批量从更丰富的负例集中学习,同时通过使用引导边际避免来自假阴性的误导性监督。结果不仅提高了基准性能,而且提高了训练稳定性。这些发现强调了主动识别和过滤假阴性的重要性——特别是在标签不完整的检索场景中——以此作为增强对比学习的手段。通过以可扩展的方式提供更清晰的监督,CachedGISTEmbedLoss 能够开发出更语义准确的嵌入模型,使其成为在各种领域训练高性能句子转换器的一个有价值的选择。
📎 参考文献
Sentence Transformers 训练概述 https://sbert.net.cn/docs/sentence_transformer/training_overview.html
Sentence Transformers Losses – MultipleNegativesRankingLoss, GISTEmbedLoss 解释和示例 https://sbert.net.cn/docs/package_reference/losses.html
GISTEmbed:文本嵌入微调的样本内训练负例引导选择 https://arxiv.org/abs/2402.16829
Arctic-Embed:可扩展、高效且准确的文本嵌入模型 https://arxiv.org/abs/2405.05374
NV-Retriever:通过有效的难负例挖掘改进文本嵌入模型 https://arxiv.org/abs/2407.15831
通过弱监督对比预训练的文本嵌入(E5) https://arxiv.org/abs/2212.03533
BGE M3-Embedding:通过自知识蒸馏实现多语言、多功能、多粒度文本嵌入 https://arxiv.org/abs/2402.03216
GradCache:在内存受限设置下扩展深度对比学习批量大小 https://arxiv.org/pdf/2101.06983
MTEB-ko-retrieval https://github.com/nlpai-lab/KURE
Git Pull Request:向 CachedGISTEmbedLoss 添加边际 https://github.com/UKPLab/sentence-transformers/pull/3299
实验结果:韩语查询-段落检索模型 https://huggingface.co/dragonkue/multilingual-e5-small-ko