静かなる名辞

pythonとプログラミングのこと


sklearnの変数選択は疎行列型(csr_matrix)でやると速いっぽいよ

はじめに

 疎行列はメモリ消費こそ少ないものの、scikit-learnで使うと内部でnumpy配列に変換されたりしてあまり恩恵を受けられないことが多いです。

 でも、変数選択に使うときはどうやら効くっぽいです。

 関連記事
scikit learnのモデルに疎行列(csr_matrix)を渡したときの速度 - 静かなる名辞

実験

 淡々とやりましょう。20newsgroups+SelectKBestという組み合わせです。

import time
import warnings

import numpy as np
from sklearn.datasets import fetch_20newsgroups_vectorized
from sklearn.feature_selection import SelectKBest

# サンプリングすると全行0の列がでて警告出るので
warnings.filterwarnings("ignore")

def main():
    # 読み込み。デフォルトでcsr_matrix
    news20 = fetch_20newsgroups_vectorized(
        subset="all", remove=("headers", "footers", "quotes"))    

    skb = SelectKBest(k=5000)

    # ランダムサンプリング
    idx = np.random.choice(news20.data.shape[0], 2000, replace=False)
    y = news20.target[idx]
    data = news20.data[idx]

    # 疎行列型
    print(type(data))
    t1 = time.time()
    res = skb.fit_transform(data, y)
    t2 = time.time()
    print("{:.2f}".format(t2 - t1))
    print(res.shape)
    p1 = skb.pvalues_

    # numpy配列
    data = data.toarray()
    print(type(data))
    t1 = time.time()
    res = skb.fit_transform(data, y)
    t2 = time.time()
    print("{:.2f}".format(t2 - t1))
    print(res.shape)
    p2 = skb.pvalues_

    # nanを含むとこう比較しないとだめなはず
    print(np.allclose(p1, p2, equal_nan=True))

if __name__ == "__main__":
    main()

 結果

<class 'scipy.sparse.csr.csr_matrix'>
0.35
(2000, 5000)
<class 'numpy.ndarray'>
2.94
(2000, 5000)
True

 10倍の速度差がありますね。

 サンプル数を増やしてもcsr_matrixの方は同じような時間でやれている雰囲気があり、実際は更に効率的です。ちなみに、全サンプルだとcsr_matrixの方はいけますが、numpy配列の方は何十秒も粘った後にMemoryErrorで落ちました。

結論

 スパースな配列を疎行列型にしていたら、そのまま変数選択に渡すべき。ndarrayでやると、とても問題があります。