计算给定2个句子字符串的余弦相似度

2024-05-14 14:30:04 发布

您现在位置:Python中文网/ 问答频道 /正文

Python: tf-idf-cosine: to find document similarity,可以使用tf-idf余弦计算文档相似度。在不导入外部库的情况下,是否有任何方法可以计算两个字符串之间的余弦相似度?

s1 = "This is a foo bar sentence ."
s2 = "This sentence is similar to a foo bar sentence ."
s3 = "What is this string ? Totally not related to the other two lines ."

cosine_sim(s1, s2) # Should give high cosine similarity
cosine_sim(s1, s3) # Shouldn't give high cosine similarity value
cosine_sim(s2, s3) # Shouldn't give high cosine similarity value

Tags: tos3fooistfsimthissentence
3条回答

感谢@vpekar的实现。这帮了大忙。我刚发现它在计算余弦相似性时忽略了tf-idf权重。 计数器(word)返回一个字典,其中包含单词列表及其出现。

cos(q,d)=sim(q,d)=(q·d)/(| q | d |)=(和(q i,di)/(sqrt(和(qi2)))*(sqrt(和(vi2))),其中i=1到v)

  • qi是查询中项i的tf idf权重。
  • di是tf idf
  • 文件中术语i的权重。|q和d是q的长度 和d
  • 这是q和d的余弦相似性。或者, 等价地,q和d之间角的余弦

请随意查看我的代码here。但首先你得下载水蟒的软件包。它将在Windows中自动为您设置python路径。在Eclipse中添加这个python解释器。

一个简单的纯Python实现是:

import re, math
from collections import Counter

WORD = re.compile(r'\w+')

def get_cosine(vec1, vec2):
     intersection = set(vec1.keys()) & set(vec2.keys())
     numerator = sum([vec1[x] * vec2[x] for x in intersection])

     sum1 = sum([vec1[x]**2 for x in vec1.keys()])
     sum2 = sum([vec2[x]**2 for x in vec2.keys()])
     denominator = math.sqrt(sum1) * math.sqrt(sum2)

     if not denominator:
        return 0.0
     else:
        return float(numerator) / denominator

def text_to_vector(text):
     words = WORD.findall(text)
     return Counter(words)

text1 = 'This is a foo bar sentence .'
text2 = 'This sentence is similar to a foo bar sentence .'

vector1 = text_to_vector(text1)
vector2 = text_to_vector(text2)

cosine = get_cosine(vector1, vector2)

print 'Cosine:', cosine

印刷品:

Cosine: 0.861640436855

这里使用的余弦公式是here描述的。

这不包括tf-idf对单词的权重,但是为了使用tf-idf,您需要有一个相当大的语料库来估计tfidf权重。

你也可以进一步发展它,用更复杂的方法从一段文字中提取单词,将其词干或引黄,等等

简而言之,答案是“不,不可能以一种原则性的方式做到这一点,哪怕是一点效果也不好”。这是自然语言处理研究中尚未解决的问题,也是我博士研究的课题。我将非常简要地总结一下我们的现状,并向您介绍一些出版物:

词义

这里最重要的假设是可以得到一个向量,表示quesion语句中的每个单词。这个向量通常被用来捕捉单词出现的上下文。例如,如果我们只考虑“吃”、“红”和“蓬松”这三个上下文,“猫”这个词可以用[98,1,87]来表示,因为如果你要阅读一段很长的文本(按照今天的标准,有几十亿个词并不少见),那么“猫”这个词经常出现在“蓬松”和“吃”的上下文中,但在“红色”的背景下并不常见。同样,“狗”可以表示为[87,2,34],“伞”可以表示为[1,13,0]。把这些向量想象成三维空间中的点,“猫”显然比“伞”更接近“狗”,因此“猫”也意味着更类似于“狗”而不是“伞”。

这项工作从90年代初开始就进行了研究(例如greffensette的this工作),并取得了一些令人惊讶的好结果。例如,下面是我最近通过让我的电脑阅读维基百科而建立的同义词表中的一些随机条目:

theory -> analysis, concept, approach, idea, method
voice -> vocal, tone, sound, melody, singing
james -> william, john, thomas, robert, george, charles

这些类似单词的列表完全是在没有人工干预的情况下获得的——你输入文本,几小时后回来。

短语的问题

你可能会问,为什么我们不做同样的事情,对较长的短语,如“姜狐狸爱水果”。因为我们没有足够的文字。为了让我们能够可靠地建立X的相似性,我们需要看到许多X在上下文中使用的例子。当X是一个像“voice”这样的单词时,这并不太难。然而,随着X变长,发现X的自然出现的几率会成倍地变慢。相比之下,Google有大约1B个页面包含“fox”这个词,而没有一个页面包含“ginger foxes love fruit”,尽管它是一个完全有效的英语句子,我们都理解它的意思。

组成

为了解决数据稀疏的问题,我们需要进行组合,即用向量表示单词,这些向量很容易从真实文本中获得,并以捕捉其含义的方式组合在一起。坏消息是到目前为止还没有人能做到这一点。

最简单和最明显的方法是将各个词向量相加或相乘。这会导致不良的副作用,即“猫追狗”和“狗追猫”对你的系统意味着相同。另外,如果你是乘法,你必须格外小心,否则每一个句子都会以[0,0,0,…,0]来表示,这会使你的观点落空。

进一步阅读

我不会讨论迄今为止提出的更复杂的构图方法。我建议你读卡特林·埃尔克的"Vector space models of word meaning and phrase meaning: a survey"。这是一个很好的高层调查,让你开始。不幸的是,不能在出版商的网站上免费获得,直接给作者发电子邮件获取一份副本。在那篇论文中,你会找到更多具体方法的参考。更容易理解的是Mitchel and Lapata (2008)Baroni and Zamparelli (2010)


@vpekar评论后编辑: 这个答案的底线是强调这样一个事实,即虽然简单的方法确实存在(例如加法、乘法、表面相似性等),但这些方法都是有根本缺陷的,一般来说,人们不应该期望它们有很好的性能。

相关问题 更多 >

    热门问题