不同长度向量的余弦相似度?

Posted

技术标签:

【中文标题】不同长度向量的余弦相似度?【英文标题】:Cosine Similarity of Vectors of different lengths? 【发布时间】:2011-03-08 11:10:15 【问题描述】:

I'm trying to use TF-IDF 将文档分类。我已经计算了一些文档的 tf_idf,但是现在当我尝试计算其中两个文档之间的余弦相似度时,我得到一个回溯说:

#len(u)==201, len(v)==246

cosine_distance(u, v)
ValueError: objects are not aligned

#this works though:
cosine_distance(u[:200], v[:200])
>> 0.52230249969265641

对向量进行切片以便 len(u)==len(v) 是正确的方法吗?我认为余弦相似度适用于不同长度的向量。

我正在使用this function:

def cosine_distance(u, v):
    """
    Returns the cosine of the angle between vectors v and u. This is equal to
    u.v / |u||v|.
    """
    return numpy.dot(u, v) / (math.sqrt(numpy.dot(u, u)) * math.sqrt(numpy.dot(v, v))) 

另外——向量中 tf_idf 值的顺序重要吗?是否应该对它们进行排序——或者它对这个计算不重要?

【问题讨论】:

【参考方案1】:

您需要将向量中相应单词的条目相乘,因此单词应该有一个全局顺序。这意味着理论上你的向量应该是相同的长度。

在实践中,如果一个文档先于另一个文档出现,则第二个文档中的单词可能会在看到第一个文档之后添加到全局顺序中,因此即使向量具有相同的顺序,第一个文档也可能是更短,因为它没有不在该向量中的单词的条目。

文档 1:敏捷的棕狐跳过了懒狗。

Global order:     The quick brown fox jumped over the lazy dog
Vector for Doc 1:  1    1     1    1     1     1    1   1   1

文档 2: 跑步者很快。

Global order:     The quick brown fox jumped over the lazy dog runner was
Vector for Doc 1:  1    1     1    1     1     1    1   1   1
Vector for Doc 2:  1    1     0    0     0     0    0   0   0    1     1

在这种情况下,理论上您需要在 Document 1 向量的末尾填充零。实际上,在计算点积时,只需将元素相乘到向量 1 的末尾(因为省略向量 2 的额外元素并将它们乘以零完全一样,但访问额外元素会更慢)。

然后您可以分别计算每个向量的大小,因此这些向量不需要具有相同的长度。

【讨论】:

在计算余弦相似度时,填充的零确实会产生影响。我用于余弦相似度的函数是: cosine-similarity = dot-product(u, v) / sqrt(dot-product(u, u))*sqrt(dot-product(v,v)) cosine-similarity ([1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,], [1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1]) == 0.3333333333333333 余弦相似度([1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 0, 0, 0, 0, 0, 0, 0])== 0.4714045207910316看起来问题在于余弦相似度的除数执行自身的点积。所以填充有效果。你知道吗? @erikcw:当你计算Doc1*Doc1 的点积时,你需要得到Doc1 的全长,但不要再进一步了。对于Doc2*Doc2,您需要查看Doc2 的完整长度,但不再赘述。对于点积的那部分,不要截断较长的向量,但没有必要填充较短的向量。但是,在计算 Doc1*Doc2 时,您可以安全地截断较长的向量。因此,您的代码应该设计为让dot-product 函数做出有关填充/截断的决定,而不是让cosine-similarity 函数做出这些决定。 @larsmans:第二张图中向量的长度不匹配是故意的。 @KenBloom:好的,对不起!【参考方案2】:

你在计算词向量的余弦相似度吗?词向量应该是相同的长度。如果文档中不存在单词,则该术语的值应为 0。

我不完全确定您将余弦相似度应用于哪些向量,但是在进行余弦相似度时,您的向量应该始终具有相同的长度,并且顺序非常重要。

例子:

Term | Doc1 | Doc2
Foo     .3     .7
Bar  |  0   |  8
Baz  |  1   |  1

这里有两个向量 (.3,0,1) 和 (.7,8,1),可以计算它们之间的余弦相似度。如果您比较 (.3,1) 和 (.7,8),您会将 Baz 的 Doc1 分数与 Bar 的 Doc2 分数进行比较,这是没有意义的。

【讨论】:

我传递给 cosine_distance 函数的向量是 tf_idf 值的 Python 列表。 v [5] == [0.0060830126968545294,0.00048241996565891193,0.0020712248617478965,0.0110036199241575,0.0110036199241575]你说的顺序问题 - 什么是向量的内容进行排序(smallest->最大,在文件的话秩序?)的正确方法 您必须为单词分配一些全局顺序。如果 bar 在 doc2 中出现在 foo 之前,那么 bar 的 tf_idf 值应该仍然是第一项。【参考方案3】:

在将向量输入 cosine_distance 函数之前尝试构建向量:

import math
from collections import Counter
from nltk import cluster

def buildVector(iterable1, iterable2):
    counter1 = Counter(iterable1)
    counter2= Counter(iterable2)
    all_items = set(counter1.keys()).union( set(counter2.keys()) )
    vector1 = [counter1[k] for k in all_items]
    vector2 = [counter2[k] for k in all_items]
    return vector1, vector2


l1 = "Julie loves me more than Linda loves me".split()
l2 = "Jane likes me more than Julie loves me or".split()


v1,v2= buildVector(l1, l2)
print(cluster.util.cosine_distance(v1,v2))

【讨论】:

以上是关于不同长度向量的余弦相似度?的主要内容,如果未能解决你的问题,请参考以下文章

余弦相似度计算

余弦相似度

20-余弦相似度及其R实现

Spark笔记(1) :余弦相似度计算

相似度算法之余弦相似度

智能推荐算法基础-余弦相似度计算