web-dev-qa-db-ja.com

スパース行列とnumpy配列の使用

私はPythonでWordカウントを使用してnumpy配列を作成しています:行はドキュメント、列はWord Xのカウントです。カウントがゼロの場合は、これらをさらに処理するときにスパース行列を使用することをお勧めします。分類器で。 numpy配列とスパース行列をScikit ロジスティック回帰分類器 に渡すと、大きな違いは見られませんでした。だから私は3つのことについて疑問に思っていました:

  • ウィキペディア 言う

    スパース行列は、ほとんどの要素がゼロの行列です

    それは、スパース行列形式をいつ使用するかを決定する適切な方法です-値の> 50%がゼロになるとすぐに?または、念のために使用するのは理にかなっていますか?

  • 特にnumpy配列または標準リストと比較して、スパース行列は私のようなタスクのパフォーマンスにどのくらい役立ちますか?
  • これまで、データをnumpy配列に収集してから、 Scipy でcsr_matrixに変換しました。それが正しい方法ですか?スパースマトリックスをゼロから構築する方法がわかりませんでしたが、それは不可能かもしれません。

どんな助けも大歓迎です!

14
patrick

scipyスパースマトリックスパッケージ、およびMATLABの同様のパッケージは、大きなスパース線形方程式(有限差分や有限要素の実装など)の解決など、線形代数問題から開発されたアイデアに基づいていました。したがって、行列積(numpy配列のdot積)や方程式ソルバーのようなものは十分に開発されています。

私の大まかな経験は、スパースcsr行列積が同等の密dot操作よりも速くなるために1%のスパース性を持たなければならないことです。つまり、99個のゼロごとに1つの非ゼロ値です。 (ただし、以下のテストを参照)

しかし、人々はまた、メモリを節約するためにスパース行列を使用しようとします。しかし、そのようなマトリックスは値の3つの配列を格納する必要があることに注意してください(少なくともcoo形式で)。したがって、メモリの保存を開始するには、スパース性が1/3未満でなければなりません。最初に密な配列を作成し、そこから疎な配列を作成する場合、明らかにメモリを節約するつもりはありません。

scipyパッケージは、多くのスパース形式を実装しています。 coo形式は、理解して構築するのが最も簡単です。ドキュメントに従ってビルドし、.data.row、および.col属性(3つの1d配列)を確認します。

csrcscは通常coo形式から作成され、データを少し圧縮するため、理解が少し難しくなります。しかし、ほとんどの数学機能があります。

csr形式にインデックスを付けることもできますが、一般的にこれは同等の密行列/配列の場合よりも遅くなります。値の変更(特に0から非ゼロ)、連結、増分成長などの他の操作も遅くなります。

lil(リストのリスト)も理解しやすく、インクリメンタル構築に最適です。 dokは、実際には辞書のサブクラスです。

重要な点は、スパース行列が2dに制限され、多くの点でnp.matrixクラスのように動作することです(サブクラスではありません)。

scikit-learnおよびsparseを使用した他の質問の検索は、これらのマトリックスを使用することの長所/短所を見つける最良の方法かもしれません。多くの質問に答えましたが、「学習」側よりも「疎」側の方がよくわかっています。それらは便利だと思いますが、フィット感は必ずしも最高ではないという感覚が得られます。カスタマイズはlearn側にあります。これまで、sparseパッケージはこのアプリケーション用に最適化されていません。


sparse.randomメソッドを使用して、指定されたスパース性を持つスパースマトリックスを作成するマトリックス製品テストをいくつか試しました。スパース行列乗算は、予想よりも優れたパフォーマンスを発揮しました。

In [251]: M=sparse.random(1000,1000,.5)

In [252]: timeit M1=M*M
1 loops, best of 3: 2.78 s per loop

In [253]: timeit Ma=M.toarray(); M2=Ma.dot(Ma)
1 loops, best of 3: 4.28 s per loop

これはサイズの問題です。小さい行列の場合、密なdotは高速です

In [255]: M=sparse.random(100,100,.5)

In [256]: timeit M1=M*M
100 loops, best of 3: 3.24 ms per loop

In [257]: timeit Ma=M.toarray(); M2=Ma.dot(Ma)
1000 loops, best of 3: 1.44 ms per loop

しかし、インデックス作成を比較する

In [268]: timeit M.tocsr()[500,500]
10 loops, best of 3: 86.4 ms per loop

In [269]: timeit Ma[500,500]
1000000 loops, best of 3: 318 ns per loop

In [270]: timeit Ma=M.toarray();Ma[500,500]
10 loops, best of 3: 23.6 ms per loop
16
hpaulj

スパース行列は、ほとんどの要素がゼロの行列です。これは、スパース行列形式をいつ使用するかを決定する適切な方法です-値の> 50%がゼロになるとすぐに?または、念のために使用するのは理にかなっていますか?

一般的なルールはありません。それは、後で正確な使用方法にのみ依存します。スパース行列に基づいて、およびなしでモデルの複雑さを計算する必要があり、「スイートスポット」を見つけることができます。これは、サンプル数と次元の両方に依存します。一般に、次の形式の行列乗算に要約されます。

X' W

ここで、Xはデータ行列N x d、Wは重み行列d x Kです。したがって、行ごとの平均スパース性がpがNdKであると仮定すると、スパースでは「密」乗算にNpdK時間かかります。したがって、スパース性が50%の場合、ほぼ2倍の高速動作が期待できます。より難しいのは、高度に最適化された稠密ベースではなく、疎アクセスのオーバーヘッドを推定することです。

特にnumpy配列または標準リストと比較して、スパース行列は私のようなタスクのパフォーマンスにどのくらい役立ちますか?

LRの特定のケースでは、これは高密度フォーマットよりも数倍高速ですが、違いを観察するには、高次元(> 100)の大量のデータ(> 1000)が必要です。

これまで、データをnumpy配列に収集してから、Scipyでcsr_matrixに変換しました。それが正しい方法ですか?スパースマトリックスをゼロから構築する方法がわかりませんでしたが、それは不可能かもしれません。

いいえ、それは良いアプローチではありません。たとえば、最初に辞書を作成してから変換するなどして、「ゼロから」作成することができます。そもそも、密な行列を使わずに疎行列を作成する方法はたくさんあります。

4
lejlot

@hpauljあなたのtimeitが間違っています、あなたはそれを念頭に置いてsparse.randomをnumpy配列(遅い)にマッピングする遅い結果を得ています:

M=sparse.random(1000,1000,.5)
Ma=M.toarray()

%timeit -n 25 M1=M*M
352 ms ± 1.18 ms per loop (mean ± std. dev. of 7 runs, 25 loops each)

%timeit -n 25 M2=Ma.dot(Ma)
13.5 ms ± 2.17 ms per loop (mean ± std. dev. of 7 runs, 25 loops each)

Numpyに近づけるには、

M=sparse.random(1000,1000,.03)

%timeit -n 25 M1=M*M
10.7 ms ± 119 µs per loop (mean ± std. dev. of 7 runs, 25 loops each)

%timeit -n 25 M2=Ma.dot(Ma)
11.4 ms ± 564 µs per loop (mean ± std. dev. of 7 runs, 25 loops each)


1
komuher