Cross-Encoder 损失函数完全指南:sentence-transformers 排序模型微调实战

发布时间:2026/9/20 11:48:25
Cross-Encoder 损失函数完全指南:sentence-transformers 排序模型微调实战 Cross-Encoder 损失函数完全指南sentence-transformers 排序模型微调实战【免费下载链接】sentence-transformersState-of-the-Art Embeddings, Retrieval, and Reranking项目地址: https://gitcode.com/gh_mirrors/se/sentence-transformerslosses是 sentence-transformers 中专门用于微调 Cross-Encoder 重排序模型的损失函数集合覆盖分类、对比学习、蒸馏与列表式排序四大类任务。选择正确的损失函数直接决定模型在具体下游任务上的表现。本文以sentence_transformers.cross_encoder.losses模块为主线逐一拆解 14 个损失函数的数学动机、参数语义、输入数据格式与完整训练示例并结合仓库源码验证其底层实现帮助你快速为手头的数据格式选型并落地训练。losses模块为 Cross-Encoder 模型提供了 14 种开箱即用的损失函数统一注册于 losses/init.py类别损失函数分类/点式CrossEntropyLoss、BinaryCrossEntropyLoss对比in-batch negativesMultipleNegativesRankingLoss、CachedMultipleNegativesRankingLoss蒸馏回归MSELoss、MarginMSELoss列表式排序listwiseLambdaLoss、ListMLELoss、PListMLELoss、ListNetLoss、RankNetLoss、ADRMSELoss没有一个损失函数是放之四海而皆准的选型主要取决于两点你手头训练数据的格式以及目标任务。动手前建议先阅读 Loss Overview 中的数据格式—损失函数匹配表。一、选型核心先认清你的训练数据格式所有 Cross-Encoder 损失函数的差异本质上源于它们消费的训练数据格式不同。下表是 docs/cross_encoder/loss_overview.md 中给出的完整对应关系输入格式标签模型输出数适用损失函数(input_A, input_B)pairsclassnum_classesCrossEntropyLoss(anchor, positive)pairs无1MultipleNegativesRankingLoss、CachedMultipleNegativesRankingLoss(anchor, positive/negative)pairs1 if positive, 0 if negative1BinaryCrossEntropyLoss(input_A, input_B)pairs[0, 1]浮点相似度1BinaryCrossEntropyLoss(anchor, positive, negative)triplets无1MultipleNegativesRankingLoss、CachedMultipleNegativesRankingLoss(anchor, positive, negative_1, …, negative_n)无1MultipleNegativesRankingLoss、CachedMultipleNegativesRankingLoss(query, [doc1, doc2, …, docN])[score1, …, scoreN]1LambdaLoss、PListMLELoss、ListNetLoss、RankNetLoss、ListMLELoss、ADRMSELoss蒸馏场景用小模型模仿强模型单独成表输入格式标签适用损失函数(input_A, input_B)pairssimilarity scoreMSELoss(query, document_one, document_two)tripletsgold_sim(q,d1) - gold_sim(q,d2)MarginMSELoss(query, positive, negative_1, …, negative_n)差值列表或分数列表MarginMSELoss数据格式之间往往可以互相转换。例如带class标签的(input_A, input_B)pairs可以通过采样同类/异类样本转换为(anchor, positive, negative)triplets。更通用的是借助sentence_transformers.util.hard_negatives.mine_hard_negatives将(anchor, positive)一键转换为triplet(anchor, positive, negative)三元组n-tuple(anchor, positive, negative_1, …, negative_n)多元组labeled-pair(anchor, document, label)label 为 0负/ 1正labeled-list(anchor, [doc1, …, docN], [label1, …, labelN])labels 为 0/1。设置output_scoresTrue时还会输出相似度分数而非二值标签从而适配蒸馏类损失。详细参数见 hard_negatives.py。二、分类式损失多分类与二分类2.1 CrossEntropyLoss多分类对判断用于训练模型对给定输入对预测正确类别类别数必须等于模型输出标签数num_labels 1。源码位于 losses/cross_entropy.py。from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses from datasets import Dataset model CrossEncoder(microsoft/mpnet-base, num_labels2) train_dataset Dataset.from_dict({ sentence1: [How can I be a good geologist?, What is the capital of France?], sentence2: [What should I do to be a great geologist?, What is the capital of Germany?], label: [1, 0], # 1: duplicate, 0: not duplicate }) loss losses.CrossEntropyLoss(model) trainer CrossEncoderTrainer(modelmodel, train_datasettrain_dataset, lossloss) trainer.train()关键参数activation_fn默认nn.Identity在计算损失前作用于 logits 的激活函数**kwargs透传给底层torch.nn.CrossEntropyLoss。从forward实现看它要求数据集恰好有两个非标签列len(inputs) ! 2时抛ValueError将两列 zip 成 pairs 后经model.preprocess编码并取scores再计算交叉熵。适合文本蕴含NLI等多分类任务参考 训练示例。2.2 BinaryCrossEntropyLoss正负对 / 相似度回归用于让模型对正样本对给出高 logit、负样本对给出低 logit模型需初始化为num_labels 1默认值。历史上 MS MARCO 重排序榜单上的多个强模型都基于它训练源码位于 losses/binary_cross_entropy.py。model CrossEncoder(microsoft/mpnet-base) train_dataset Dataset.from_dict({ query: [What are pandas?, What are pandas?], response: [Pandas are a kind of bear., Pandas are a kind of fish.], label: [1, 0], }) loss losses.BinaryCrossEntropyLoss(model) trainer CrossEncoderTrainer(modelmodel, train_datasettrain_dataset, lossloss) trainer.train()支持两种标签语义二值标签(anchor, positive/negative)对1 正 0 负连续相似度(input_A, input_B)对 [0, 1]浮点分数。参数说明pos_weight默认None正样本加权用于处理类别不平衡传torch.tensor(4)表示正类权重为 4activation_fn默认nn.Identity**kwargs透传给torch.nn.BCEWithLogitsLoss。实现上forward将 logitsview(-1)后与labels.float()计算 BCE。构造时会校验model.num_labels 1。推荐配合mine_hard_negatives(output_formatlabeled-pair)将问答对转换为带硬负样本的标注对参考 STS 示例、Quora 示例 与 MS MARCO 示例。三、对比式损失in-batch negatives3.1 MultipleNegativesRankingLoss给定(anchor, positive)对或(anchor, positive, negative)三元组该损失让模型从 batch 内所有正负候选中给对应 positive 最高分。batch 越大in-batch negatives 越多模型通常越强。该损失也称为 InfoNCE loss、SimCSE loss、in-batch negatives loss源码位于 losses/multiple_negatives_ranking.py。model CrossEncoder(microsoft/mpnet-base) train_dataset Dataset.from_dict({ query: [What are pandas?, What is the capital of France?], answer: [Pandas are a kind of bear., The capital of France is Paris.], }) loss losses.MultipleNegativesRankingLoss(model) trainer CrossEncoderTrainer(modelmodel, train_datasettrain_dataset, lossloss) trainer.train()参数num_negatives默认4每个 anchor 采样的 in-batch negatives 数量None时使用全部scale默认10.0logits 放大系数即温度缩放activation_fn默认nn.Sigmoid作用于 logits 的激活函数。实现上get_in_batch_negatives先用掩码排除每个 anchor 自身的 positive 与硬负样本再通过torch.multinomial随机采样num_negatives个负例calculate_loss将 scores reshape 为(batch_size, 1 随机负例数 硬负例数)后以第 0 列为正类计算交叉熵。官方提示当前默认值未来可能调整鼓励自行实验。3.2 CachedMultipleNegativesRankingLossGradCache 加速版与MultipleNegativesRankingLoss数学上等价但通过梯度缓存GradCache在几乎不增加显存的前提下支持超大 batch从而获得更好性能代价是训练稍慢论文称约牺牲 20% 计算时间。源码位于 losses/cached_multiple_negatives_ranking.py继承自MultipleNegativesRankingLoss并复用其采样逻辑。loss losses.CachedMultipleNegativesRankingLoss(model, mini_batch_size32)其三步流程在 docstring 中有明确说明无梯度/计算图的快速预测得到全部 logits计算 loss 并反向传播到 logits 层缓存 logits 的梯度带计算图做第二次预测把缓存梯度接入反向链。参数在基类之上新增mini_batch_size默认32单次前向的 mini-batch 大小直接决定显存占用show_progress_bar默认False。使用建议采用大的per_device_train_batch_size 小的mini_batch_size以获得最佳性能。两种对比损失都建议配合BatchSamplers.NO_DUPLICATES防止 in-batch negatives 与 anchor/positive 重复参考 MS MARCO 示例 与 rerankers 示例。四、蒸馏式损失向强教师模型对齐4.1 MSELoss分数级回归蒸馏直接回归「模型预测的 query-document 分数」与「目标分数」之间的 MSE常用于从教师 Cross-Encoder 或标注分数蒸馏知识源码位于 losses/mse.py。student_model CrossEncoder(microsoft/mpnet-base) teacher_model CrossEncoder(cross-encoder/ms-marco-MiniLM-L12-v2) train_dataset Dataset.from_dict({ query: [What are pandas?, What is the capital of France?], answer: [Pandas are a kind of bear., The capital of France is Paris.], }) def compute_labels(batch): return {label: teacher_model.predict(list(zip(batch[query], batch[answer])))} train_dataset train_dataset.map(compute_labels, batchedTrue) loss losses.MSELoss(student_model) trainer CrossEncoderTrainer(modelstudent_model, train_datasettrain_dataset, lossloss) trainer.train()注意要留意标签与模型输出的量纲匹配。若教师模型用 Sigmoid 将 logits 限制在[0, 1]则损失中也应传入 Sigmoid 作为activation_fn。参数为activation_fn与透传torch.nn.MSELoss的**kwargs参考 蒸馏训练示例。4.2 MarginMSELoss带边际的蒸馏回归目标为sim(Query, Pos) - sim(Query, Neg)与金标准边际gold_sim(Q, Pos) - gold_sim(Q, Neg)之间的 MSE。相比MultipleNegativesRankingLoss它不要求两篇文档严格一正一负——两者都可以相关或不相关因此约束更灵活源码位于 losses/margin_mse.py。student_model CrossEncoder(microsoft/mpnet-base) teacher_model CrossEncoder(cross-encoder/ms-marco-MiniLM-L12-v2) train_dataset Dataset.from_dict({ query: [What are pandas?, What is the capital of France?], positive: [Pandas are a kind of bear., The capital of France is Paris.], negative: [Pandas are a kind of fish., The capital of France is Berlin.], }) def compute_labels(batch): positive_scores teacher_model.predict(list(zip(batch[query], batch[positive]))) negative_scores teacher_model.predict(list(zip(batch[query], batch[negative]))) return {label: positive_scores - negative_scores} train_dataset train_dataset.map(compute_labels, batchedTrue) loss losses.MarginMSELoss(student_model) trainer CrossEncoderTrainer(modelstudent_model, train_datasettrain_dataset, lossloss) trainer.train()forward支持四种标签形态源码中以labels.shape分支处理单个边际分数、正负分数列表自动相减、多个负例的边际列表、正例加多个负例的分数列表。内部先分别计算 positive 与每个 negative 的 logits再求差得到margin_logits与目标做 MSE。参数为activation_fn与透传torch.nn.MSELoss的**kwargs参考 margin-MSE 蒸馏示例。五、列表式排序损失query 下多文档直接排序列表式listwise损失以「一个 query 及其全部候选文档」为基本训练单元直接面向重排序/学习排序指标优化。这类损失允许每个 query 的文档数不同batch 内按最长列表 padding。以下损失函数的输入格式均为(query, [doc1, doc2, ..., docN]) [score1, score2, ..., scoreN]模型统一要求num_labels 1。推荐配合mine_hard_negatives(output_formatlabeled-list)构造数据。5.1 LambdaLoss排序指标优化框架实现 LambdaLoss 框架论文。经验上它是同输入格式下表现最好的损失。model CrossEncoder(microsoft/mpnet-base) train_dataset Dataset.from_dict({ query: [What are pandas?, What is the capital of France?], docs: [ [Pandas are a kind of bear., Pandas are kind of like fish.], [The capital of France is Paris., Paris is the capital of France., Paris is quite large.], ], labels: [[1, 0], [1, 1, 0]], }) loss losses.LambdaLoss(model) trainer CrossEncoderTrainer(modelmodel, train_datasettrain_dataset, lossloss) trainer.train()五种加权方案weighting_scheme参数默认NDCGLoss2PPScheme方案类语义备注NoWeightingScheme权重恒为 1.0退化为普通 pairwiseNDCGLoss1SchemeNDCG Loss1原论文中不如以下两种NDCGLoss2SchemeNDCG Loss2更紧的上界性能紧随 Loss2LambdaRankSchemeLambdaRank 加权优化 NDCG 的粗略上界—NDCGLoss2PPSchemeNDCG Loss2Loss2 与 LambdaRank 的混合mu * ndcg_weights lambda_weightsmu默认 10.0原论文中效果最强为默认值其他参数k默认NoneNDCGK 考虑的文档数None表示全部sigma默认1.0sigmoid 内分数差的权重eps默认1e-10数值稳定性常数reduction_logbinarylog2默认或naturallogactivation_fn、mini_batch_size。实现上forward对 logits 与 labels 按分数降序排序计算 gain 与 discount再按加权方案生成 pairwise 权重最终对 sigmoid 概率取负对数平均。参考 MS MARCO 训练示例。5.2 RankNetLoss无加权的 pairwise 基座RankNet 通过对文档两两比较来学习排序函数数学上是LambdaLoss使用NoWeightingScheme的特例源码中直接继承LambdaLoss并固定weighting_schemeNoWeightingScheme()见 losses/rank_net.py。参数与LambdaLoss一致不含weighting_scheme参考 训练示例。5.3 ListMLELoss排列最大似然基于排列的最大似然估计最大化 ground truth 标签诱导出的排列概率Plackett-Luce 模型源码位于 losses/list_mle.py。它直接继承PListMLELoss且不传入lambda_weight。loss losses.ListMLELoss(model) # 默认 respect_input_orderTrue关键参数respect_input_order默认True为 True 时假定输入文档已按相关性降序排列为 False 时按标签值排序后再计算。5.4 PListMLELoss位置感知的 ListMLE即 Position-Aware ListMLEp-ListMLE在 ListMLE 基础上对不同排序位置施加不同权重通常优于ListMLELoss源码位于 losses/plist_mle.py。# 方式一默认位置权重 2^(num_docs - rank) - 1 lambda_weight losses.PListMLELambdaWeight() loss losses.PListMLELoss(model, lambda_weightlambda_weight) # 方式二自定义位置折扣函数 import torch def custom_discount(ranks): # e.g. ranks: [1, 2, 3, 4, 5] return 1.0 / torch.log1p(ranks) lambda_weight losses.PListMLELambdaWeight(rank_discount_fncustom_discount) loss losses.PListMLELoss(model, lambda_weightlambda_weight)PListMLELambdaWeight的rank_discount_fn为None时使用默认折扣2^(num_docs - rank) - 1实现中权重会归一化为每行和为 1。位置权重乘到 Plackett-Luce 对数似然的每一项上参考 MS MARCO 示例。5.5 ListNetLoss分布交叉熵排序最小化预测排序分布与 ground truth 排序分布之间的交叉熵top-one 概率源码位于 losses/list_net.py。实现中用labels_matrix.softmax(dim1)作为目标分布与 logits 矩阵计算torch.nn.CrossEntropyLoss。参数仅activation_fn与mini_batch_size参考 训练示例。5.6 ADRMSELoss可微近似排序的回归ADR-MSEApprox Discounted Rank Mean Squared Error列表式排序损失直接最小化真实排序位置与可微近似预测排序位置之间的误差并使用受 nDCG 启发的 log 折扣加权源自 Rank-DistiLLM 论文源码位于 losses/adr_mse.py。近似排序公式approx_rank(i) 1 sum_{j ! i} sigmoid(alpha * (s_j - s_i))。对标签并列的文档取平均排名使模型对并列文档给出相同分数时近似排名可达避免任意打破平局带来的非零最小损失。ADRMSELoss支持连续分数标签不限于 0/1。model CrossEncoder(microsoft/mpnet-base) train_dataset Dataset.from_dict({ query: [What are pandas?, What is the capital of France?], docs: [ [Pandas are a kind of bear., Pandas are kind of like fish.], [The capital of France is Paris., Paris is the capital of France., Paris is quite large.], ], scores: [[0.95, 0.1], [0.98, 0.92, 0.2]], }) loss losses.ADRMSELoss(model) trainer CrossEncoderTrainer(modelmodel, train_datasettrain_dataset, lossloss) trainer.train()参数alpha默认1.0ApproxRank sigmoid 的温度。越大近似越紧越不平滑但已充分分离的 pair 梯度越弱activation_fn、mini_batch_size语义同前。实现上approximate_ranks通过scores.unsqueeze(1) - scores.unsqueeze(2)计算 pairwise 差用 mask 剔除 paddingforward中loss discount * squared_error再按 mask 取平均。原论文直接对比对象是RankNetLossLLM 蒸馏设定下两者 nDCG10 差距约 0.002参考 训练示例。六、共同参数mini_batch_size 与 activation_fn列表式损失普遍提供两个控制训练行为的关键参数源码 docstring 中均有统一说明mini_batch_size默认None——控制每次前向处理的样本数显著影响显存与速度三种取值行为None等于整个 batch 大小 0batch 按该大小拆分多个 mini-batch 依次前向 0整个 batch 一次前向。activation_fn默认nn.Identity——在计算损失前作用于 logits 的激活函数在蒸馏场景中用于对齐教师分数的量纲如教师输出经 Sigmoid 则学生损失也传 Sigmoid。七、实战从数据准备到完整训练7.1 用 mine_hard_negatives 构造训练数据绝大多数排序场景的起点是(anchor, positive)问答对。通过 hard_negatives.py 中的mine_hard_negatives可一步产出适合各损失的数据output_formatlabeled-list→ 配LambdaLoss等列表式损失学习排序常用output_formatlabeled-pair→ 配BinaryCrossEntropyLoss同样表现强劲output_formattriplet/n-tuple→ 配MultipleNegativesRankingLoss/CachedMultipleNegativesRankingLossoutput_scoresTrue→ 输出连续分数以适配MSELoss/MarginMSELoss蒸馏。7.2 端到端训练骨架所有损失均与CrossEncoderTrainer配合使用统一训练流程为from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses from datasets import Dataset model CrossEncoder(microsoft/mpnet-base) train_dataset Dataset.from_dict({...}) # 列名需匹配所选损失的输入格式 loss losses.选型(model) trainer CrossEncoderTrainer(modelmodel, train_datasettrain_dataset, lossloss) trainer.train()每个损失的forward方法签名均为forward(inputs, labels, promptNone, taskNone)inputs是 batch 内文本的嵌套列表外层每个元素对应数据集一列labels来自数据集的label/labels/score/scores列。仓库中 examples/cross_encoder/training/ms_marco 下提供了training_ms_marco_bce.py、training_ms_marco_lambda.py、training_ms_marco_cmnrl.py、training_ms_marco_listmle.py、training_ms_marco_listnet.py、training_ms_marco_adrmse.py、training_ms_marco_ranknet.py、training_ms_marco_plistmle.py等可直接运行的脚本覆盖本文全部排序类损失。7.3 选型速查数据是二值标注对 / 0-1 相似度优先BinaryCrossEntropyLoss传统且难以被超越只有正样本对无标签MultipleNegativesRankingLoss显存受限则换CachedMultipleNegativesRankingLoss有 query 下多文档与相关性分数目标是重排序指标LambdaLoss默认 NDCGLoss2 加权优先其次PListMLELoss多分类如 NLICrossEntropyLoss知识蒸馏分数级用MSELoss边际级用MarginMSELoss。八、自定义损失函数当内置 14 种损失无法满足需求时可以自定义损失并接入CrossEncoderTrainer。根据 loss_overview.md 的要求自定义损失需满足必须是torch.nn.Module子类构造器第一个参数必须是model实现forward(inputs, labels)inputs为 batch 内文本的嵌套列表需自行组合成可 tokenize 的 pairs 并喂给模型labels为可选的列表的张量返回单个损失值或返回「组件名 → 损失值」的字典字典会被求和得到最终损失各组件还会单独记录日志便于监控。若希望获得自动模型卡生成支持可额外实现get_config_dict()返回损失参数的字典citation属性引用字符串会写入所有使用该损失的训练模型卡片。仓库中各损失类均已实现这两个接口例如LambdaLoss.get_config_dict返回weighting_scheme、k、sigma、eps、reduction_log等citation返回论文 BibTeX可作为自定义实现的最佳参考模板。【免费下载链接】sentence-transformersState-of-the-Art Embeddings, Retrieval, and Reranking项目地址: https://gitcode.com/gh_mirrors/se/sentence-transformers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考