如何检测两个句子是否相似?

26

我希望计算任意两个句子之间的相似程度。例如:

  1. 一位数学家找到了这个问题的解决方案。
  2. 这个问题是由一位年轻的数学家解决的。

我可以使用标记器、词干提取器和解析器,但我不知道如何检测这些句子是否相似。


你是否考虑在Linguistics.SE上询问这类问题?我发现自然语言处理的问题在那里得到了更好的解答。 - tchrist
@tchrist 但这是一个编程/算法相关的问题! - Ravinder Payal
3个回答

33
这两个句子不仅相似,它们几乎是释义,即表达相同含义的两种替代方式。这也是一个非常简单的释义案例,在这种情况下,两个话语使用相同的词语,唯一的例外是一个主动语态,而另一个是被动语态。(这两个句子并不完全相同,因为在第二个句子中,数学家是“年轻的”。这个额外的信息使得两个句子之间的语义关系不对称。在这些情况下,你会说第二个话语"蕴含"了第一个话语,或者换句话说,第一个话语可以从第二个话语推断出来)。
从这个例子中无法确定你是否真正关心释义检测、文本蕴涵或一般的句子相似性问题,这甚至是一个更广泛和模糊的问题。例如,“人们吃食物”更类似于“人们吃面包”还是“男人吃食物”?

句子重述检测和文本相似度是自然语言处理中复杂且开放的研究问题,有着庞大而活跃的研究人员社区在致力于这些问题。不清楚您对此话题的兴趣程度,但请注意,即使许多杰出的研究人员已经花费了整个职业生涯来尝试解决这个问题,我们仍然远未找到能够普遍有效的可靠解决方案。

除非您只对适用于特定情况且无法捕捉句法变化(如此例)的表面解决方案感兴趣,否则我建议您更深入地研究文本相似度问题。一个很好的起点是书籍《统计自然语言处理基础》(Foundations of Statistical Natural Language Processing),它提供了大多数统计自然语言处理主题的非常良好的组织形式。一旦您澄清了自己的要求(例如,在什么条件下您的方法应该工作?您需要达到什么水平的精确度/召回率?哪些现象您可以安全忽略,哪些您需要考虑?),您就可以通过深入研究最近的研究工作来开始寻找具体方法。在这方面,一个很好的起点是计算语言学协会(ACL)的在线档案,它是该领域大多数研究结果的出版者。

为了让您有些实用的内容,句子相似性的一个非常粗略的基线是两个二进制向量之间的余弦相似度,这些向量代表以词袋形式表示的句子。词袋是文本的一种非常简化的表示方法,通常用于信息检索,其中您完全忽略语法,并且将一个句子表示为一个矢量,其大小是词汇表的大小(即语言中的单词数量),其组件“i”的值为“1”,如果词汇表中第“i”位置的单词出现在句子中,则为“0”,否则为零。


3
余弦相似度将显示这两个句子相同:我喝牛奶,但不喝酒我不喝牛奶,但喝酒 - Ravinder Payal
1
@RavinderPayal,这就是要在自然语言理解下解决的问题。 - Amit Kumar
@amit_kumar 是的,这个具体问题可以通过将动词与名词进行映射和标记化来解决。 - Ravinder Payal
不要误解,自然语言处理和词性标注只涉及句法。而自然语言理解则关注语义和语用学。自然语言理解引擎是最先进的技术之一,但我们仍然没有一个可以适用于所有领域的通用解决方案。 - Amit Kumar

6

更现代的方法(2021年)是使用机器学习自然语言处理模型。有预训练的模型专门用于此任务,其中许多都是从BERT派生而来,因此您不必训练自己的模型(如果您想的话也可以)。这里是一个使用优秀的Huggingface Transformers库和PyTorch的代码示例。它基于此示例

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch

model_name = "bert-base-cased-finetuned-mrpc"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)

sequence_0 = "A mathematician found a solution to the problem."
sequence_1 = "The problem was solved by a young mathematician."

tokens = tokenizer.encode_plus(sequence_0, sequence_1, return_tensors="pt")
classification_logits = model(**tokens)[0]
results = torch.softmax(classification_logits, dim=1).tolist()[0]

classes = ["not paraphrase", "is paraphrase"]
for i in range(len(classes)):
    print(f"{classes[i]}: {round(results[i] * 100)}%")

1
对于两个相同的句子,它仍然会给出不同的结果。例如,sequence_0 =“口服一片” sequence_1 =“口服一片”,它给出了94%的相似度。为什么? - Droid-Bird
1
此外,正如Ravinder指出的那样,这种方法也存在相同的缺点,即对于结构意图的处理效果不佳,使得“我喜欢馅饼胜过蛋糕”和“我喜欢蛋糕胜过馅饼”虽然含义相反,但在被解释为相互转述时会被错误地归类。 - Maxhirez

1
在某些情况下,可以将句子自动转换为代表其含义的话语表示结构。如果两个句子产生相同的话语表示结构,则它们很可能具有相似的含义。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接