与BERT的字符串比较似乎忽略了句子中的“not”

Posted

技术标签:

【中文标题】与BERT的字符串比较似乎忽略了句子中的“not”【英文标题】:String comparison with BERT seems to ignore "not" in sentence 【发布时间】:2021-11-04 13:10:45 【问题描述】:

我使用 SentenceTransformers 和 BERT 实现了一个字符串比较方法,如下所示

from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity

model = SentenceTransformer('sentence-transformers/all-distilroberta-v1')

sentences = [
    "I'm a good person",
    "I'm not a good person"
]

sentence_embeddings = model.encode(sentences)

cosine_similarity(
    [sentence_embeddings[0]],
    sentence_embeddings[1:]
)

请注意我的句子示例非常相似但含义相反。问题是余弦相似度返回 0.9,表明这两个字符串在上下文中非常相似,而我期望它返回接近零的值,因为它们具有相反的含义。

如何调整我的代码以返回更准确的结果?

【问题讨论】:

【参考方案1】:

TL;DR:NLI 就是你所需要的

首先,余弦相似度相当高,因为句子在以下意义上是相似的:

它们是关于同一个主题(对一个人的评价) 它们的主题(“我”)和属性相同(“做一个好人”) 它们具有相似的句法结构 他们的词汇几乎相同

所以,从形式上看,它们应该被认为是相似的。此外,从实践的角度来看,它们通常应该被认为是相似的。例如,如果您在 Google 上搜索“GMO 会导致癌症”,您可能会发现带有标签“GMO are not cause 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

【讨论】:

很好的解释,例子是暗示性的。 太棒了!它真的有帮助!谢谢,大卫【参考方案2】:

结果并不令人惊讶。您已经通过了两个非常相似但含义相反的句子。句子嵌入是从在通用语料库上训练的模型中获得的,因此,如果句子相似,通常期望模型给出的嵌入彼此接近。这就是正在发生的事情,余弦相似度表明嵌入彼此接近,句子也是如此。示例中的句子可能具有相反的含义,但它们彼此相似。

如果您期望两个意思相反的相似句子彼此远离,那么您必须使用一种分类模型(例如情感分析,如果您的示例基于积极和消极的情绪)。或其他一些相关的任务。

【讨论】:

以上是关于与BERT的字符串比较似乎忽略了句子中的“not”的主要内容,如果未能解决你的问题,请参考以下文章

bert不同句子中的词向量会变化吗

来自转换器的 BERT 句子嵌入

如何使用 BERT 对相似的句子进行聚类

删除 Bert 中的 SEP 令牌以进行文本分类

如何使用微调的 BERT 模型进行句子编码?

BERT 获取句子嵌入