2016-12-01 77 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