Quora 重复问题

此文件夹包含的脚本演示了如何为**信息检索**训练 SentenceTransformers。作为一个简单的示例,我们将使用 Quora 重复问题数据集。它包含超过 50 万个句子和超过 40 万个关于两个问题是否重复的成对标注。

在此数据集上训练的模型可用于挖掘重复问题,即给定大量句子(本例中为问题),识别所有重复的句子对。请参阅释义挖掘,了解如何使用句子转换器挖掘重复问题/释义的示例。这种方法可以扩展到数十万个句子。

您还可以为此任务训练和使用 CrossEncoder 模型。有关更多详细信息,请参阅交叉编码器 > 训练示例 > Quora 重复问题

训练

选择正确的损失函数对于微调有用的模型至关重要。对于给定的任务,有两个损失函数特别适合:OnlineContrastiveLossMultipleNegativesRankingLoss

对比损失

完整的训练示例,请参阅training_OnlineContrastiveLoss.py

Quora 重复问题数据集有一个 pair-class 子集,它由问题对和标签组成:1 表示重复,0 表示不同。

正如我们的损失概览所示,这允许我们使用ContrastiveLoss。标签为 1 的相似对被拉近,使它们在向量空间中靠近,而距离小于预定间隔的不相似对则在向量空间中被推远。

一个改进版本是 OnlineContrastiveLoss。这个损失函数会寻找哪些负例对的距离小于最大正例对的距离,以及哪些正例对的距离大于最小负例对的距离。也就是说,这个损失函数会自动检测批次中的难例,并仅为这些情况计算损失。

损失可以这样使用:

from datasets import load_dataset

train_dataset = load_dataset("sentence-transformers/quora-duplicates", "pair-class", split="train")
# => Dataset({
#     features: ['sentence1', 'sentence2', 'label'],
#     num_rows: 404290
# })
print(train_dataset[0])
# => {'sentence1': 'What is the step by step guide to invest in share market in india?', 'sentence2': 'What is the step by step guide to invest in share market?', 'label': 0}
train_loss = losses.OnlineContrastiveLoss(model=model, margin=0.5)

MultipleNegativesRankingLoss (多负例排序损失)

完整的示例,请参阅training_MultipleNegativesRankingLoss.py

MultipleNegativesRankingLoss 特别适用于信息检索/语义搜索。一个很好的优点是它只需要正例对,即我们只需要重复问题的例子。有关该损失如何工作的更多信息,请参阅NLI > MultipleNegativesRankingLoss

使用这个损失函数很简单,不需要调整任何超参数

from datasets import load_dataset

train_dataset = load_dataset("sentence-transformers/quora-duplicates", "pair", split="train")
# => Dataset({
#     features: ['anchor', 'positive'],
#     num_rows: 149263
# })
print(train_dataset[0])
# => {'anchor': 'Astrology: I am a Capricorn Sun Cap moon and cap rising...what does that say about me?', 'positive': "I'm a triple Capricorn (Sun, Moon and ascendant in Capricorn) What does this say about me?"}
train_loss = losses.MultipleNegativesRankingLoss(model)

由于“is_duplicate”(是否重复)是一种对称关系,我们不仅可以将 (anchor, positive) 加入训练样本集,还可以加入 (positive, anchor)

from datasets import concatenate_datasets

train_dataset = concatenate_datasets([
    train_dataset,
    train_dataset.rename_columns({"anchor": "positive", "positive": "anchor"})
])
# Dataset({
#     features: ['anchor', 'positive'],
#     num_rows: 298526
# })

注意

增加批次大小通常会产生更好的结果,因为任务变得更难。从 100 个问题中识别出正确的重复问题比从 10 个问题中识别更困难。因此,建议将训练批次大小设置得尽可能大。我在 32GB GPU 显存上以 350 的批次大小进行了训练。

注意

MultipleNegativesRankingLoss 只有在 j != i 时,*(a_i, b_j)* 确实是一个负例、非重复的问题对时才有效。在少数情况下,这个假设是错误的。但在大多数情况下,如果我们随机抽取两个问题,它们不是重复的。如果您的数据集不能满足这个特性,MultipleNegativesRankingLoss 可能效果不佳。

多任务学习

ContrastiveLoss 对成对分类效果很好,即给定两个句子对,判断它们是否重复。它将负例对在向量空间中推得很远,从而很好地区分重复和非重复对。

另一方面,MultipleNegativesRankingLoss 主要是在大量候选者中减小正例对之间的距离。然而,非重复问题之间的距离不是很大,因此这个损失函数对成对分类的效果不是那么好。

training_multi-task-learning.py 中,我演示了如何使用两种损失函数来训练网络。核心代码是定义这两种损失,并将其传递给 fit 方法。

from datasets import load_dataset
from sentence_transformers.losses import ContrastiveLoss, MultipleNegativesRankingLoss
from sentence_transformers import SentenceTransformerTrainer, SentenceTransformer

model_name = "stsb-distilbert-base"
model = SentenceTransformer(model_name)

# https://hugging-face.cn/datasets/sentence-transformers/quora-duplicates
mnrl_dataset = load_dataset(
    "sentence-transformers/quora-duplicates", "triplet", split="train"
)  # The "pair" subset also works
mnrl_train_dataset = mnrl_dataset.select(range(100000))
mnrl_eval_dataset = mnrl_dataset.select(range(100000, 101000))

mnrl_train_loss = MultipleNegativesRankingLoss(model=model)

# https://hugging-face.cn/datasets/sentence-transformers/quora-duplicates
cl_dataset = load_dataset("sentence-transformers/quora-duplicates", "pair-class", split="train")
cl_train_dataset = cl_dataset.select(range(100000))
cl_eval_dataset = cl_dataset.select(range(100000, 101000))

cl_train_loss = ContrastiveLoss(model=model, margin=0.5)

# Create the trainer & start training
trainer = SentenceTransformerTrainer(
    model=model,
    train_dataset={
        "mnrl": mnrl_train_dataset,
        "cl": cl_train_dataset,
    },
    eval_dataset={
        "mnrl": mnrl_eval_dataset,
        "cl": cl_eval_dataset,
    },
    loss={
        "mnrl": mnrl_train_loss,
        "cl": cl_train_loss,
    },
)
trainer.train()

预训练模型

目前,在 Quora 重复问题数据集上训练的以下模型可用

您可以这样加载和使用预训练模型

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("distilbert-base-nli-stsb-quora-ranking")