我有两个numpy数组:
阵列1:500,000行x 100列
阵列2:160,000行x 100列
我想在数组1和数组2的每一行之间找到最大的余弦相似度。换句话说,我计算数组1的第一行与数组2的所有行之间的余弦相似度,并找到最大的余弦相似度,然后,我计算数组1中第二行与数组2中所有行之间的余弦相似度,并找到最大余弦相似度;并针对阵列1的其余部分执行此操作。
我目前使用sklearn
的cosine_similarity()
函数并执行以下操作,但是它非常慢。我想知道是否有一种更快的方式不涉及多处理/多线程来完成我想做的事情。另外,我拥有的数组也不稀疏。
from sklearn.metrics.pairwise import cosine_similarity as cosine
results = []
for i in range(Array1.shape[0]):
results.append(numpy.max(cosine(Array1[None,i,:], Array2)))
最佳答案
在Python中进行迭代可能会很慢。最好总是“向量化”并尽可能多地对数组使用numpy操作,这会将工作传递给numpy的低级实现,这是快速的。cosine_similarity
已被矢量化。因此,理想的解决方案将只涉及cosine_similarity(A, B)
,其中A和B是您的第一个和第二个数组。不幸的是,这个矩阵是500,000 x 160,000,太大了,无法在内存中处理(它会引发错误)。
然后,下一个最佳解决方案是将A(按行)拆分为大块(而不是单个行),以便结果仍适合内存并对其进行迭代。我发现,在您的数据中,每个块中使用100行适合内存。还有很多,这是行不通的。然后,我们只需使用.max
并为每次迭代获取100个最大值,最后我们可以将其收集在一起。
但是,这种方式强烈建议我们节省更多时间。两个向量的余弦相似度的公式为u.v / | u || v |,它是两者之间夹角的余弦。因为我们正在迭代,所以每次都会重新计算B行的长度并将结果扔掉。解决此问题的一种好方法是使用这样的事实:如果缩放矢量(角度相同),则余弦相似度不会改变。因此,我们只能计算一次所有的行长,然后将它们除以使行成为单位矢量。然后我们简单地以u.v计算余弦相似度,这可以通过矩阵乘法对数组进行。我对此进行了快速测试,速度大约快了3倍。
放在一起:
import numpy as np
# Example data
A = np.random.random([500000, 100])
B = np.random.random([160000, 100])
# There may be a proper numpy method for this function, but it won't be much faster.
def normalise(A):
lengths = (A**2).sum(axis=1, keepdims=True)**.5
return A/lengths
A = normalise(A)
B = normalise(B)
results = []
rows_in_slice = 100
slice_start = 0
slice_end = slice_start + rows_in_slice
while slice_end <= A.shape[0]:
results.append(A[slice_start:slice_end].dot(B.T).max(axis=1))
slice_start += rows_in_slice
slice_end = slice_start + rows_in_slice
result = np.concatenate(results)
这需要大约每1000行A运行2秒。因此,您的数据大约需要1,000秒。
关于python - Python:两个大型numpy数组之间的余弦相似度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52030945/