Scipy余弦相似度与sklearn余弦相似度

Posted

技术标签:

【中文标题】Scipy余弦相似度与sklearn余弦相似度【英文标题】:Scipy cosine similarity vs sklearn cosine similarity 【发布时间】:2020-08-12 20:46:38 【问题描述】:

我注意到scipysklearn 都具有余弦相似度/余弦距离函数。我想测试每个向量对的速度:

setup1 = "import numpy as np; arrs1 = [np.random.rand(400) for _ in range(60)];arrs2 = [np.random.rand(400) for _ in range(60)]"
setup2 = "import numpy as np; arrs1 = [np.random.rand(400) for _ in range(60)];arrs2 = [np.random.rand(400) for _ in range(60)]"

import1 = "from sklearn.metrics.pairwise import cosine_similarity"
stmt1 = "[float(cosine_similarity(arr1.reshape(1,-1), arr2.reshape(1,-1))) for arr1, arr2 in zip(arrs1, arrs2)]"

import2 = "from scipy.spatial.distance import cosine"
stmt2 = "[float(1 - cosine(arr1, arr2)) for arr1, arr2 in zip(arrs1, arrs2)]"

import timeit
print("sklearn: ", timeit.timeit(stmt1, setup=import1 + ";" + setup1, number=1000))
print("scipy:   ", timeit.timeit(stmt2, setup=import2 + ";" + setup2, number=1000))
sklearn:  11.072769448000145
scipy:    1.9755544730005568

sklearn 的运行速度几乎比scipy 慢 10 倍(即使您删除了 sklearn 示例的数组 reshape 并生成了已经处于正确形状的数据)。为什么一个明显比另一个慢?

【问题讨论】:

我不熟悉sklearnscipy的内部工作;但是,除了您在一个实验中而不是在另一个实验中重塑数组这一事实之外,我认为这不是一个公平的比较,因为cosine_similarity 计算两个输入数组中所有样本的成对余弦距离(尽管您正在对一个样本的数组调用它),但scipy 中的cosine 函数仅适用于一维数组,因此可能有更有效的实现。 @today 即使您摆脱了数组整形(使用np.random.rand(1, 400) 而不是np.random.rand(400) 创建数组以防止整形),sklearn 仍然较慢。我怀疑 sklearn 是为二维数组设计的这一事实可能与它有关,但性能差异仍然很大。 【参考方案1】:

如 cmets 部分所述,我认为比较不公平,主要是因为 sklearn.metrics.pairwise.cosine_similarity 旨在比较给定输入二维数组中样本的成对距离/相似性。另一方面,scipy.spatial.distance.cosine 旨在计算两个一维数组的余弦距离。

也许更公平的比较是使用scipy.spatial.distance.cdistsklearn.metrics.pairwise.cosine_similarity,两者都计算给定数组中样本的成对距离。然而,令我惊讶的是,这表明 sklearn 实现比 scipy 实现快得多(我目前对此没有解释!)。这是实验:

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from scipy.spatial.distance import cdist

x = np.random.rand(1000,1000)
y = np.random.rand(1000,1000)

def sklearn_cosine():
    return cosine_similarity(x, y)

def scipy_cosine():
    return 1. - cdist(x, y, 'cosine')

# Make sure their result is the same.
assert np.allclose(sklearn_cosine(), scipy_cosine())

这是计时结果:

%timeit sklearn_cosine()
10 loops, best of 3: 74 ms per loop

%timeit scipy_cosine()
1 loop, best of 3: 752 ms per loop

【讨论】:

以上是关于Scipy余弦相似度与sklearn余弦相似度的主要内容,如果未能解决你的问题,请参考以下文章

推荐引擎:余弦相似度与测量每个向量分量之间的差异百分比

如何在 Python 中快速计算大量向量的余弦相似度?

不同库的余弦相似度输出不同

sklearn.manifold 中的余弦相似度 TSNE

所有记录的Python sklearn余弦相似度循环

使用 sklearn 如何计算文档和查询之间的 tf-idf 余弦相似度?