我有两个numpy数组:
数组1 :500,000行x 100列
数组2 :160,000行x 100列
我想找到 数组1中的每一行 和 数组2 之间最大的余弦相似度。换句话说,我计算数组1中第一行与数组2中所有行之间的余弦相似度,找到最大余弦相似度,然后计算数组1中第二行与数组1中所有行之间的余弦相似度。数组2,求出最大余弦相似度;并针对阵列1的其余部分执行此操作。
我目前正在使用this.b
的{{1}}函数并执行以下操作,但这非常慢。我想知道是否有一种更快的方法不涉及多处理/多线程来完成我想做的事情。而且,我拥有的数组也不稀疏。
let MM = {
a: function(){
this.b();
},
b: function(){
console.log('b');
}
};
function makeClosure(M) {
let { a, b } = M;
function a2(){ b2() };
function b2(){ console.log('b2'); };
return function( arg ){
if ( arg === 1 ) a.call({ b });
if ( arg === 2 ) a2();
}
}
let c = makeClosure( MM );
c(1);
c(2);
答案 0 :(得分:2)
在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秒钟。因此,您的数据大约需要1000秒。