ことのあらまし
データの標準化は機械学習の前処理としてとても重要です。そして疎行列型データ構造は、スパースなデータを表現するためにはとても適しています。
残念ながら、普通に疎行列型を標準化しようとすると、疎行列性が失せます。考えてみればわかるのですが、普通の標準化では平均0にしてしまいます。たとえば非負の疎行列だとすると、大半を占める0のデータはたぶん負値になることでしょう。そしてスパース性は維持できません。
scikit-learnの素敵な対策
ま、この辺は当然考えられていて、なんと超親切なメッセージが出ます。
from scipy.sparse import csr_matrix from sklearn.preprocessing import StandardScaler a = csr_matrix([[0, 1, 0, 3, 0], [1, 0, 0, 1, 2], [0, 0, 4, 0, 0]]) print(repr(a)) ss = StandardScaler() result = ss.fit_transform(a) # ここでエラー発生 print(repr(result))
ValueError: Cannot center sparse matrices: pass `with_mean=False` instead. See docstring for motivation and alternatives.
少し前のscikit-learnだと本当にこの挙動だったかどうかは少し記憶が怪しいんですが、今0.22で試したらこうなっていました。
with_mean=Falseとは?
標準化の式というと、普通は
これで考えると思います。しかし今回は中心化を行わずに
で処理します。
こうすると0は0のままで、かつ分散は1に調整されます。
この場合は疎行列として表現できてよさげですね。scikit-learnはこの場合、疎行列型を維持したまま計算してくれます。とても助かります。
値としてはこんな結果になります。
from scipy.sparse import csr_matrix from sklearn.preprocessing import StandardScaler a = csr_matrix([[0, 1, 0, 3, 0], [1, 0, 0, 1, 2], [0, 0, 4, 0, 0]]) print(repr(a.A)) """ => array([[0, 1, 0, 3, 0], [1, 0, 0, 1, 2], [0, 0, 4, 0, 0]], dtype=int64) """ ss = StandardScaler(with_mean=False) result = ss.fit_transform(a) print(repr(result.A)) """ => array([[0. , 2.12132034, 0. , 2.40535118, 0. ], [2.12132034, 0. , 0. , 0.80178373, 2.12132034], [0. , 0. , 2.12132034, 0. , 0. ]]) """
なお、ただのnumpy配列としてスパースなデータを入力した場合は特にメリットはありません。
まとめ
疎行列型のまま扱うとパフォーマンス上のメリットが得られることが多いので、積極的に試してみるべきだと思います。