TL;DR:NLI 就是你所需要的
首先,余弦相似度相当高,因为句子在以下意义上是相似的:
- 它们是关于同一主题的(对一个人的评价)
- 它们是关于相同的主题(“我”)和相同的属性(“成为一个好人”)
- 它们具有相似的句法结构
- 他们有几乎相同的词汇
所以,从形式上看,它们应该被认为是相似的。此外,从实践的角度来看,它们通常应该被认为是相似的。例如,如果您在 Google 上搜索“GMO 正在导致癌症”,您可能会发现带有“GMO are not getting cancer”标签的文本是相关的。
其次,如果你想测量句子之间的逻辑连接,嵌入的余弦相似度只是不够表达。这是因为嵌入包含大量语义文体、词汇和句法信息,但它们是固定大小的(在您的情况下为 768 维),因此它们不能包含有关两个句子含义的完整信息。因此,您需要另一个具有以下属性的模型:
- 它同时编码两个文本,因此它比较文本本身,而不仅仅是它们的固定大小嵌入
- 它经过显式训练来评估句子之间的逻辑连接
评估文本之间的逻辑联系的任务称为自然语言推理(NLI),其最常见的表述是识别文本蕴涵(RTE):这是预测第一句话是否蕴涵第二个句子的问题。
在 Huggingface 存储库中有很多为此任务训练的模型,其中roberta-large-mnli是一个很好的模型。您可以使用它来评估两个文本的等价性。如果每个文本都包含另一个文本,则它们是等价的,因此您可以将等价程度估计为两个方向上的蕴涵分数的乘积。
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
tokenizer = AutoTokenizer.from_pretrained("roberta-large-mnli")
model = AutoModelForSequenceClassification.from_pretrained("roberta-large-mnli")
def test_entailment(text1, text2):
batch = tokenizer(text1, text2, return_tensors='pt').to(model.device)
with torch.no_grad():
proba = torch.softmax(model(**batch).logits, -1)
return proba.cpu().numpy()[0, model.config.label2id['ENTAILMENT']]
def test_equivalence(text1, text2):
return test_entailment(text1, text2) * test_entailment(text2, text1)
print(test_equivalence("I'm a good person", "I'm not a good person")) # 2.0751484e-07
print(test_equivalence("I'm a good person", "You are a good person")) # 0.49342492
print(test_equivalence("I'm a good person", "I'm not a bad person")) # 0.94236994