Python:两个大型numpy数组之间的余弦相似度

时间:2018-08-26 23:18:50

标签: python numpy scikit-learn cosine-similarity

我有两个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);

1 个答案:

答案 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秒。