数据集格式
本参考涵盖:数据集如何映射到损失函数、数据不匹配时如何重塑、以及如何挖掘困难负样本。
两条规则
来自 sentence-transformers 训练概述:
- 如果损失函数需要标签,数据集必须有一个名为
label、labels、score或scores的列。任何具有这些名称之一的列就是标签。 - 所有其他列都是输入。损失函数定义它期望多少个输入列。列的名称无关紧要;顺序才重要。
示例:CoSENTLoss期望 2 个输入 + 一个浮点标签。列顺序为["premise", "hypothesis", "score"]的数据集可以工作。["score", "premise", "hypothesis"]的数据集不行——需要先重新排序。
每种损失函数的数据形状
按类型的损失参考(losses_sentence_transformer.md、losses_cross_encoder.md、losses_sparse_encoder.md)是数据形状到损失函数的权威映射。这些表没有展示的跨领域配方要点:
CosineSimilarityLoss希望score归一化到[0, 1];CoSENTLoss/AnglELoss是成对排序的,忽略绝对尺度,所以在stsb(原始 0-5)上,仅在使用余弦相似度时除以 5。BatchAllTripletLoss/BatchHardTripletLoss/BatchSemiHardTripletLoss需要batch_sampler=BatchSamplers.GROUP_BY_LABEL,以便每个标签的多个样本出现在同一批次中。MSELoss(蒸馏)的标签是教师的完整嵌入向量(浮点数列表),而不是标量分数。MarginMSELoss(蒸馏)的标签是teacher_score(q, pos) - teacher_score(q, neg),每行预计算。- MNRL 的N 元组形状
(anchor, positive, negative_1, negative_2, ..., negative_N)(1 索引)由mine_hard_negatives(..., output_format="n-tuple")产生;"labeled-list"输出格式产生 CrossEncoder listwise 形状。
重塑操作
如果你的数据不符合损失函数期望的形状:
重新排序列
# 列顺序是 ["hypothesis", "premise", "score"] 但 CoSENTLoss 期望 premise 在前。dataset=dataset.select_columns(["premise","hypothesis","score"])重命名标签列
# 你的标签叫 "relevance" 但 ST 期望 "label"。dataset=dataset.rename_column("relevance","label")删除多余列
# ST 会将每个非标签列视为输入。删除元数据。dataset=dataset.remove_columns(["source_id","created_at","language"])转换数据类型
# 标签是 str,CoSENTLoss 需要 float。dataset=dataset.map(lambdax:{"label":float(x["label"])})困难负样本挖掘
mine_hard_negatives(位于sentence_transformers.util)使用检索器生成带挖掘负样本的训练数据集。困难负样本是检索模型质量中杠杆率最高的单一手段。
基本用法
fromsentence_transformersimportSentenceTransformerfromsentence_transformers.utilimportmine_hard_negatives retriever=SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")mined=mine_hard_negatives(dataset=train_pairs,# 有 (anchor, positive) 或 (q, a) 列model=retriever,num_negatives=5,range_min=0,range_max=100,# 从中采样困难负样本的排名窗口sampling_strategy="top",# "top" = 排名第 1 的困难样本;"random" = 窗口内随机output_format="n-tuple",# "triplet" | "n-tuple" | "labeled-pair" | "labeled-list"use_faiss=True,)输出格式
"triplet"—(anchor, positive, negative)三元组。每个(query, negative)对一行。"n-tuple"—(anchor, positive, negative_1, negative_2, ..., negative_N)(1 索引)——每个查询一行。"labeled-pair"—(anchor, text, label),正例label=1,负例label=0。适合BinaryCrossEntropyLoss。"labeled-list"—(anchor, texts, labels)— 每个查询一行,带候选列表。适合 listwise 损失函数。
过滤假负样本
如果检索器返回的"负样本"实际上是相关的,它们就会变成假负样本并损害训练。过滤它们:
mined=mine_hard_negatives(dataset=train_pairs,model=retriever,cross_encoder=CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2"),# 对候选打分num_negatives=5,max_score=0.9,# 丢弃打分高于 0.9 的候选relative_margin=0.05,# 要求 neg_score < pos_score * (1 - 0.05)absolute_margin=0.2,# 要求 neg_score < pos_score - 0.2output_format="n-tuple",use_faiss=True,)使用relative_margin或absolute_margin之一,通常不要同时使用。max_score作为硬性上限独立有用。
CLI
scripts/mine_hard_negatives.py是一个 CLI 包装器——参见它获取可运行的命令。
选择合适的range_min/range_max
range_max=None是默认值;传入整数以限制从排名列表向下采样多远。
range_min=0、range_max=100— 从检索到的 top-100 中采样。好的默认值。range_min=10、range_max=100— 跳过 top-10(通常包含真阳性)。如果缺少交叉编码器,这样做更安全。range_min=0、range_max=1000— 更大的范围,更多样化的负样本,更慢。sampling_strategy="top"— 总是选择排名第 1 的困难样本。每行训练信号最大。sampling_strategy="random"— 在范围内随机选择。如果你的检索器本身有噪声,这样更稳健。
快速的 Hub 侧数据集检查
hf datasets sql "SELECT * FROM 'hf://datasets/<id>/<split>' LIMIT 5"通过 DuckDB 流式读取行,无需load_dataset(...)—— 在完整验证运行之前确认列名与你的损失函数匹配的最快方法。hf datasets info <id>显示配置 / 拆分 / 大小;hf datasets card <id> --text渲染 README。
陷阱
remove_unused_columns=True(默认):训练器会删除未传给模型 forward 的列。通常没问题,但如果你依赖使用元数据列的自定义 collator,请设置remove_unused_columns=False。- CSV 加载后浮点数存储为字符串:
load_dataset("csv", ...)默认将列保留为字符串。使用.map(lambda x: {"label": float(x["label"])})转换。 - 带
include_positives=True的挖掘困难负样本会将正例作为负样本包含在输出列表中——仅在你构建评估器或想测量正例排名时有用。训练时请保持False。