具有numpy的大型稀疏矩阵的余弦相似度

Posted

技术标签:

【中文标题】具有numpy的大型稀疏矩阵的余弦相似度【英文标题】:cosine similarity on large sparse matrix with numpy 【发布时间】:2017-04-15 11:58:29 【问题描述】:

下面的代码会导致我的系统在完成之前耗尽内存。

您能否建议一种更有效的方法来计算大型矩阵上的余弦相似度,如下所示?

我希望计算原始矩阵 (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 实例上运行,都会发生这种情况。

【问题讨论】:

sparse 有自己的random 函数,可以创建一个有很多零的矩阵。 【参考方案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 

【讨论】:

对于 Python3,将 xrange 替换为 range 我对这一行感到困惑 cosine_similarity_chunk =similarity_cosine_by_chunk(chunk_start, chunk_start+chunk_size) 为什么我们一次又一次地覆盖 cosine_similarity_chunk 变量? @ShivamAgrawal 。该行正在计算块中的余弦相似度。每次通过时,您只会对该块具有余弦相似度。【参考方案2】:

同样的问题。我有一个大的非稀疏矩阵。它正好适合内存,但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

我没有崩溃; YMMV。尝试不同的批量大小以使其更快。我以前一次只比较 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))

【讨论】:

这是一个非常适合我的解决方案。谢谢。【参考方案3】:

内存不足是因为您尝试存储 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.]])

【讨论】:

我的错误 - 我不打算让它成为一个稀疏矩阵。我打算使用我的原始矩阵(垫子)并用它来计算一个 65000 x 65000 矩阵,其中每个元素代表两行之间的余弦相似度。我已经更新了我的问题以反映这一变化。你知道我如何计算原始矩阵上所有行的余弦相似度吗? @Sal 好吧,一个 65000x65000 矩阵的大小约为 31.5GiB,所以在 64GB 的机器上,你应该没问题。但是您将无法存储两个这样的矩阵。所以如果python在构造过程中试图复制矩阵,你就会遇到麻烦。计算机似乎冻结的那些实例可能是它正在交换(或分页)的情况。也许你应该让它运行一段时间,比如一夜之间......就计算矩阵而言。但是要真正对矩阵做任何事情,你会很难。

以上是关于具有numpy的大型稀疏矩阵的余弦相似度的主要内容,如果未能解决你的问题,请参考以下文章

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

余弦相似度

余弦相似度计算

R语言使用lsa包计算余弦相似度(Cosine Similarity)实战:两个向量的余弦相似度矩阵的余弦相度

余弦相似度聚类

使用 quanteda 在 R 中的大型语料库上计算余弦相似度