Ale*_*lex 6 python numpy cosine-similarity scikit-learn
我有两个 numpy 数组:
数组 1:500,000 行 x 100 列
数组 2:160,000 行 x 100 列
我想在 Array 1和Array 2 中的每一行之间找到最大的余弦相似度。换句话说,我计算数组 1 中第一行与数组 2 中所有行之间的余弦相似度,并找到最大余弦相似度,然后计算数组 1 中第二行与数组 2 中所有行之间的余弦相似度数组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)))
Run Code Online (Sandbox Code Playgroud)
在 Python 中迭代可能会很慢。总是最好“向量化”并尽可能多地在数组上使用 numpy 操作,这将工作传递给 numpy 的低级实现,这很快。
cosine_similarity已经矢量化了。因此,理想的解决方案只涉及cosine_similarity(A, B)A 和 B 是您的第一个和第二个数组的位置。不幸的是,这个矩阵是 500,000 x 160,000,这在内存中太大了(它会引发错误)。
下一个最佳解决方案是将 A(按行)拆分为大块(而不是单个行),以便结果仍然适合内存,并对其进行迭代。我发现您的数据在每个块中使用 100 行适合内存;更多,但它不起作用。然后我们简单地使用.max并获得每次迭代的 100 个最大值,我们可以在最后收集它们。
不过,这种方式强烈建议我们节省额外的时间。两个向量的余弦相似度公式为uv / |u||v| ,它是两者之间夹角的余弦值。因为我们在迭代,所以每次都不断地重新计算 B 行的长度并将结果扔掉。解决此问题的一个好方法是利用以下事实:如果缩放向量(角度相同),余弦相似度不会改变。所以我们可以只计算一次所有的行长度,然后除以它们来制作行单位向量。然后我们简单地将余弦相似度计算为uv,这可以通过矩阵乘法对数组完成。我对此进行了快速测试,速度提高了大约 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)
Run Code Online (Sandbox Code Playgroud)
每运行 1,000 行 A 大约需要 2 秒。因此,您的数据应该是大约 1,000 秒。
| 归档时间: |
|
| 查看次数: |
10710 次 |
| 最近记录: |