如何计算两个句子的余弦相似度? – Python

从Python:tf-idf-cosine:查找文档相似度 ,可以使用tf-idf余弦计算文档相似度。 如果不导入外部库,是否有任何方法来计算2个string之间的余弦相似度?

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 

一个简单的纯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 

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

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

你也可以通过更复杂的方式来从一段文本中提取单词,干扰或使其变得更加轻微等等。

简短的回答是“不行,这是不可能的,即使是远程的,也是有原则的。” 这是自然语言处理研究中一个尚未解决的问题,也是我的博士论文的题目。 我将非常简要地总结一下我们所处的位置,并指出一些出版物:

词的意思

这里最重要的假设是有可能获得代表句子中每个单词的向量。 这个vector通常被select来捕捉单词可以出现的上下文。例如,如果我们只考虑三个上下文“吃”,“红”和“蓬松”,单词“猫”可能表示为[98,1 ,87],因为如果你要阅读一篇非常长的文本(根据今天的标准,几十亿字是不常见的),“猫”一词在“蓬松”和“吃” ,但在“红色”的情况下并不常见。 同样,“狗”可以表示为[87,2,34],“伞”可以是[1,13,0]。 将这些向量形象化为3D空间中的点,“猫”显然比“伞”更接近“狗”,因此“猫”也意味着更类似于“狗”而不是“伞”。

自从90年代初以来,这项工作已经被调查(例如Greffenstette的这项工作),并取得了一些令人惊讶的好成绩。 例如,以下是我最近通过让我的电脑阅读维基百科所创build的词库中的一些随机条目:

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

这些相似的单词列表完全没有人为干预,几个小时后你就可以提供文本。

短语的问题

你可能会问,为什么我们不会用更长的词组来做同样的事情,比如“姜狐狸爱吃水果”。 这是因为我们没有足够的文字。 为了使我们能够可靠地确定X的相似之处,我们需要看到许多X在上下文中被使用的例子。 当X是一个像“声音”一样的单词时,这并不难。 然而,随着X变长,发现X的自然发生的几率呈指数级变慢。 相比之下,尽pipe事实上这是一个非常有效的英语句子,但Google已经有大约1B页含有“狐狸”一词,而不是一个包含“姜狐狸爱吃水果”的页面,我们都明白这意味着什么。

组成

为了解决数据稀疏性问题,我们需要进行合成,也就是将容易从实际文本中获得的词语向量化,并把它们放在一起,以便捕捉它们的意义。 坏消息是到目前为止,没有人能做到这一点。

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

进一步阅读

我不会讨论到目前为止提出的更复杂的构图方法。 我build议你阅读Katrin Erk的“词义和词义的vector空间模型:一项调查” 。 这是一个很好的高级调查,让你开始。 不幸的是,在发布商的网站上并不是免费的,直接发邮件给作者来获得一份副本。 在那篇文章中,你会find更多具体方法的参考。 Mitchel和Lapata(2008)以及Baroni和Zamparelli(2010)都比较容易理解。


@vpekar评论:这个答案的底线是强调这样一个事实,虽然天真的方法确实存在 (例如,加法,乘法,表面相似性等),但这些是根本上有缺陷的 ,一般不应该期望从他们。

感谢@vpekar为您的实施。 它帮了很多。 我刚刚发现它在计算余弦相似性时忽略了tf-idf权重。 Counter(单词)返回一个字典,其中包含单词列表以及出现的单词。

cos(q,d)= sim(q,d)=(q·d)/(| q || d |)=(sum(qi,di)/(sqrt(sum(qi2))) sum(vi2)))其中i = 1至v)

  • qi是查询中项目i的tf-idf权重。
  • di是tf-idf
  • 文档中术语i的权重。 | Q | 和| d | 是q和d的长度。
  • 这是q和d的余弦相似性。 。 。 。 。 。 或者相当于q和d之间angular度的余弦。

请随时在这里查看我的代码。 但首先你将不得不下载anaconda软件包。 它会自动设置你在Windows中的Pythonpath。 在Eclipse中添加这个python解释器。