2016-12-01 78 views
3

下面的代码会导致我的系统在完成前耗尽内存。大块稀疏矩阵上的余弦相似性numpy

您能否提出一个更有效的方法来计算大矩阵上的余弦相似度,比如下面的那个?

我想为我的原始矩阵(mat)中的每个65000行计算相对于所有其他行的余弦相似度,以便结果为65000 x 65000矩阵,其中每个元素是余弦相似度原始矩阵中有两行。

import numpy as np 
from scipy import sparse 
from sklearn.metrics.pairwise import cosine_similarity 

mat = np.random.rand(65000, 10) 

sparse_mat = sparse.csr_matrix(mat) 

similarities = cosine_similarity(sparse_mat) 

运行最后一行后,我总是用完内存,程序冻结或崩溃,并伴随MemoryError。无论我是在我的8 GB本地RAM还是在64 GB EC2实例上运行,都会发生这种情况。

+0

'sparse'都有自己的'random'功能,可以创建有大量的矩阵零。 – hpaulj

回答

1

由于您试图存储65000x65000矩阵,因此内存不足。请注意,您构建的矩阵是而非稀疏。 np.random.rand生成0到1之间的随机数。因此,csr_matrix没有足够的零来实际压缩数据。实际上,有almost surely根本没有零。

如果您在您的MemoryError回溯仔细观察,你可以看到,cosine_similarity尝试使用稀疏的点积如果可能的话:

MemoryError     Traceback (most recent call last) 
    887   Y_normalized = normalize(Y, copy=True) 
    888 
--> 889  K = safe_sparse_dot(X_normalized, Y_normalized.T, dense_output=dense_output) 
    890 
    891  return K 

所以,问题不在于cosine_similarity,它与你的矩阵。尝试初始化一个实际的稀疏矩阵(含1%稀疏,例如)是这样的:

>>> a = np.zeros((65000, 10)) 
>>> i = np.random.rand(a.size) 
>>> a.flat[i < 0.01] = 1  # Select 1% of indices and set to 1 
>>> a = sparse.csr_matrix(a) 

然后,32GB RAM的机器上(8GB的RAM是不够的我),没有内存错误以下运行:

>>> b = cosine_similarity(a) 
>>> b 
array([[ 0., 0., 0., ..., 0., 0., 0.], 
     [ 0., 0., 0., ..., 0., 0., 0.], 
     [ 0., 0., 0., ..., 0., 0., 0.], 
     ..., 
     [ 0., 0., 0., ..., 1., 0., 0.], 
     [ 0., 0., 0., ..., 0., 0., 0.], 
     [ 0., 0., 0., ..., 0., 0., 0.]]) 
+0

我的错误 - 我不打算将它作为稀疏矩阵。我打算拿我的原始矩阵(mat),并用它来计算65000 x 65000矩阵,其中每个元素表示两行之间的余弦相似度。我更新了我的问题以反映这一变化。你知道我怎样才能在原始矩阵上计算所有行上相互之间的余弦相似性? – Sal

+0

@Sal Well,65000x65000矩阵的大小约为31.5GiB,所以在64GB的机器上,你应该没问题。但是你将无法存储两个这样的矩阵。所以如果python试图在构建过程中复制矩阵,你就会陷入困境。计算机似乎冻结的那些实例可能是交换(或分页)的情况。也许你应该让它运行一段时间,说一夜之间...就计算矩阵而言。但要真正用矩阵来做任何事情,你将会很困难。 – Praveen

1

同样的问题在这里。我有一个很大的非稀疏矩阵。它适合内存很好,但cosine_similarity崩溃的原因可能是什么,可能是因为他们在某处复制矩阵太多了。所以我做了比较小批量的“左侧”行而不是整个矩阵:

import numpy as np 
from sklearn.metrics.pairwise import cosine_similarity 

def cosine_similarity_n_space(m1, m2, batch_size=100): 
    assert m1.shape[1] == m2.shape[1] 
    ret = np.ndarray((m1.shape[0], m2.shape[0])) 
    for row_i in range(0, int(m1.shape[0]/batch_size) + 1): 
     start = row_i * batch_size 
     end = min([(row_i + 1) * batch_size, m1.shape[0]]) 
     if end <= start: 
      break # cause I'm too lazy to elegantly handle edge cases 
     rows = m1[start: end] 
     sim = cosine_similarity(rows, m2) # rows is O(1) size 
     ret[start: end] = sim 
    return ret 

没有崩溃的我;因人而异。尝试不同的批量大小以使其更快。我以前一次只能比较1行,并且在我的机器上花了大约30倍的时间。

愚蠢而有效的完整性检查:

import random 
while True: 
    m = np.random.rand(random.randint(1, 100), random.randint(1, 100)) 
    n = np.random.rand(random.randint(1, 100), m.shape[1]) 
    assert np.allclose(cosine_similarity(m, n), cosine_similarity_n_space(m, n)) 
1

我会运行它在块这样

from sklearn.metrics.pairwise import cosine_similarity 

# Change chunk_size to control resource consumption and speed 
# Higher chunk_size means more memory/RAM needed but also faster 
chunk_size = 500 
matrix_len = your_matrix.shape[0] # Not sparse numpy.ndarray 

def similarity_cosine_by_chunk(start, end): 
    if end > matrix_len: 
     end = matrix_len 
    return cosine_similarity(X=your_matrix[start:end], Y=your_matrix) # scikit-learn function 

for chunk_start in xrange(0, matrix_len, chunk_size): 
    cosine_similarity_chunk = similarity_cosine_by_chunk(chunk_start, chunk_start+chunk_size) 
    # Handle cosine_similarity_chunk (Write it to file_timestamp and close the file) 
    # Do not open the same file again or you may end up with out of memory after few chunks 
+0

对于Python3,用'range'替换'xrange'。 – MERose