静かなる名辞

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

2019/03/22:TechAcademyがteratailの質問・回答を盗用していた件
2019/03/26:TechAcademy盗用事件 公式発表と深まる疑念


scikit-learnのStandardScalerで疎行列型のまま標準化する

ことのあらまし

 データの標準化は機械学習の前処理としてとても重要です。そして疎行列型データ構造は、スパースなデータを表現するためにはとても適しています。

 残念ながら、普通に疎行列型を標準化しようとすると、疎行列性が失せます。考えてみればわかるのですが、普通の標準化では平均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とは?

 標準化の式というと、普通は

\displaystyle z_i = \frac{x_i - \bar{x}}{\sigma _x}

 これで考えると思います。しかし今回は中心化を行わずに

\displaystyle z_i = \frac{x_i}{\sigma _x}

 で処理します。

 こうすると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配列としてスパースなデータを入力した場合は特にメリットはありません。

まとめ

 疎行列型のまま扱うとパフォーマンス上のメリットが得られることが多いので、積極的に試してみるべきだと思います。