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

7

我注意到 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 示例中的数组重塑并生成已处于正确形状的数据)。为什么一个明显比另一个慢?


1
我不熟悉sklearnscipy的内部工作原理。然而,除了你在一个实验中重塑数组而在另一个实验中没有这个事实之外,我认为这并不是一个公平的比较。因为cosine_similarity计算两个输入数组中所有样本的余弦距离(虽然你在一个样本的数组上调用它),但scipy中的cosine函数仅适用于一维数组,因此可能具有更高效的实现。 - today
即使你不进行数组重塑(使用np.random.rand(1, 400)而不是np.random.rand(400)来创建数组以防止重塑),sklearn仍然较慢。我怀疑sklearn设计用于2D数组可能与此有关,但是性能差异还是相当大的。 - Jay Mody
1个回答

15

如评论中所提到的,我认为这个比较并不公平,主要是因为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

3
我目前正在使用余弦相似度进行一些工作。Scipy似乎在几个Python循环中运行该作业,而Sklearn似乎在整个矩阵上使用向量化函数。如果您正在处理一个非常小的作业,使用Scipy实际上会更快,但如果X和Y都很大,您将需要使用Sklearn。 - jameslol

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