2016-12-01 26 views
3

以下のコードは、完了する前にシステムのメモリが不足する原因となります。大きなスパース行列でのコサイン類似度numpy with

次のような大きな行列でコサインの類似性を計算するより効率的な方法を提案できますか?

元の行列(mat)の65000行のそれぞれについて計算したコサイン類似度を、他のすべてに対して相対的に計算し、結果が65000 x 65000の行列になるようにしたいと思います。各要素はコサインの類似性です元の行列の2つの行。

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でクラッシュしたりします。これは、私が8GBのローカルRAM上か64GBのEC2インスタンス上で動作するかに関係なく発生します。

+0

'のような塊でそれを実行しますゼロ。 – 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) 

を次に、32ギガバイトのRAMを搭載したマシン上で、ノーメモリエラーで、次のラン(8ギガバイト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

私の間違い - 私はそれがまばらな行列であることを意図していませんでした。私はオリジナルの行列(マット)を使って65000 x 65000行列を計算することを意図していました。ここで、各要素は2つの行の間のコサインの類似性を表しています。私はこの変更を反映するために私の質問を更新しました。元の行列上のすべての行について、どのようにしてコサインの類似点を計算することができるか分かりますか? – Sal

+0

@Sal 65000x65000のマトリックスは約31.5GiBのサイズを持っているので、64GBのマシンでは大丈夫です。しかし、2つの行列を格納することはできません。したがって、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 

私にとってはクラッシュはありません。 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)) 
1

私はsparse`がたくさんで行列を作成することができ、独自の `random`機能を有している。この

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では 'xrange'を' range'で置き換えます。 – MERose

関連する問題