7

密度の高い numpy 1D ベクトルで乗算する必要がある scipy 疎行列 (現在は CSR 形式) が多数あります。ベクトルは次のように呼ばれGます。

print G.shape, G.dtype
(2097152,) complex64

各疎行列には形状(16384,2097152)があり、非常に疎です。密度は約 4.0e-6 です。と呼ばれるこれらの疎行列の 100 個のリストがありspmatsます。

G次のように、各行列を簡単に乗算できます。

res = [spmat.dot(G) for spmat in spmats]

これにより、期待どおりの形状の密なベクトルのリストが得られ(16384,)ます。

私のアプリケーションはかなりパフォーマンスが重要なので、最初にすべての疎行列を 1 つの大きな疎行列に連結してから、次のdot()ような呼び出しを 1 回だけ使用する代替方法を試しました。

import scipy.sparse as sp
SPMAT = sp.vstack(spmats, format='csr')
RES = SPMAT.dot(G)

RESこれにより、 shape を持つ1 つの長いベクトルが生成され、予想どおり、上記(1638400,)のすべての結果ベクトルが連結されたバージョンになります。res結果が同じであることを確認しました。

たぶん私は完全に間違っているかもしれませんが、numpy 呼び出し、メモリ割り当て、python オブジェクトの作成、python ループなどがはるかに少ないため、2 番目のケースは最初のケースよりも高速であると予想していました。時間は気にしません。疎行列を連結するために必要な時間は、結果を計算する時間だけです。%timeitただし、によると:

%timeit res = [spmat.dot(G) for spmat in spmats]
10 loops, best of 3: 91.5 ms per loop
%timeit RES = SPMAT.dot(G)
1 loops, best of 3: 389 ms per loop

どちらの操作でもメモリが不足していないことを確認しましたが、怪しいことは何も起こっていないようです。私はクレイジーですか、それともこれは本当に奇妙ですか? これは、すべてのスパース行列とベクトルの積をブロックで、一度に数行ずつ実行して高速化する必要があるということですか? 私が理解している限り、密なベクトルを使用した疎行列の乗算時間は、上記の 2 つのケースで変更されていない非ゼロ要素の数で線形である必要があります。何がそのような違いを生んでいるのでしょうか?

EPD7.3を使用して、4GB RAMのシングルコアLinuxマシンで実行しています

編集:

これは、私にとって問題を再現する小さな例です。

import scipy.sparse as sp
import numpy as n

G = n.random.rand(128**3) + 1.0j*n.random.rand(128**3)

spmats = [sp.rand (128**2, 128**3, density = 4e-6, format = 'csr', dtype=float64) for i in range(100)]
SPMAT = sp.vstack(spmats, format='csr')

%timeit res = [spmat.dot(G) for spmat in spmats]
%timeit RES = SPMAT.dot(G)

私は得る:

1 loops, best of 3: 704 ms per loop
1 loops, best of 3: 1.34 s per loop

この場合のパフォーマンスの違いは、何らかの構造を持つ独自のスパース マトリックスほど大きくはありませんが (おそらくキャッシュのため)、マトリックスを連結するとさらに悪化します。

scipy 10.1 と 12.0 の両方で試しました。

4

1 に答える 1