RankGaussってどんなの?

データ分析の前処理について勉強していたら、数値変数の変換方法が気になったので自分用の備忘録として残しておく。

一般的なスケーリングについて

数値データにおける前処理といえば正規化とか標準化のような、いわゆるスケーリングしか使ったことがなかったけど、 統計や機械学習などに使用される数値変換のテクニックは他にもいろいろ存在するらしい。

スケーリングは数値変数の分布における縮尺を調整することで、異なる変数でも同一のレンジで扱えるようにする方法。 これは機械学習における予測性能を向上させる上でも必要な処理であるが、スケーリングはただ変数の分布を伸縮させているだけなので形状までは修正できない。

そのため変数の分布に極端な偏りがある場合、スケーリング手法を用いても分布の偏りは解消されない。

RankGauss

RankGaussは数値変数をガウス分布に変換する方法。2017年のKaggleコンペ "Porto Seguro’s Safe Driver Prediction” で1位になったMichael Jahrerさんが提案した手法。 特に、予測モデルにニューラルネットを使用する際の特徴量変換の手法として、Min-Max Normalizationや標準化よりも優れた性能を発揮するとのこと。

Input normalization for gradient-based models such as neural nets is critical. For lightgbm/xgb it does not matter. The best what I found during the past and works straight of the box is "RankGauss". Its based on rank transformation. First step is to assign a linspace to the sorted features from 0..1, then apply the inverse of error function ErfInv to shape them like gaussians, then I substract the mean. Binary features are not touched with this trafo (eg. 1-hot ones). This works usually much better than standard mean/std scaler or min/max.

RankGaussでは、対象となる変数の値を順位付けし、その順位を-1~1の範囲にスケーリング(Min-Max Normalization)する。 (-1~1の範囲でランキングのlinspaceをとる)

次に、スケーリングした値を逆誤差関数(誤差関数:  \frac{2}{\sqrt{\pi}} \int\
_0^{x} e^{-t^{2}} dt 逆関数)に通すことで、ガウス分布になるように変換する。

pythonでRankGauss

scikit-learnのサンプルデータセット(boston dataset)を使ってRankGaussを試してみます。 まずはデータの準備。

import pandas as pd
from sklearn.datasets import load_boston

data = load_boston()

# datasetをdataframeに変換
df = pd.DataFrame(data.data, columns=data.feature_names)

AGEという変数の分布をヒストグラムで見てみるとこんな感じ。 f:id:tsumit:20200620041733p:plain

この変数に対してMin-Max Normalizationを適用すると、 f:id:tsumit:20200620041548p:plain

値が0~1の範囲内に収まっていることがわかる。 しかし、ヒストグラムの形状は元のままで偏りが見られる。

RankGaussを試してみる。 pythonでRankGaussを使うなら、scikit-learnにQuantileTransformerというクラスが用意されているので簡単に利用できる。 使用方法は以下の通り。

from sklearn.preprocessing import QuantileTransformer

qt = QuantileTransformer(random_state=0, output_distribution='normal')
# 変換する列を指定
num_cols = ['RM', 'AGE', 'RAD', 'TAX']
# num_colsに対して変換用の変位値を計算
qt.fit(df[num_cols])

# RankGaussによる変換を行い、num_colsの値を置換
df[num_cols] = qt.transform(df[num_cols])

QuantileTransformerのパラメータoutput_distributionは、normalにすれば変換後の分布がガウス分布に従い、uniformとすれば一様分布になる。

変換後のAGE変数の分布はこんな感じになる。 RankGaussだけだと値の範囲が-inf~infになるので、念のためMin-Max Normalizationでスケーリングもしておいた。 f:id:tsumit:20200620041826p:plain

ガウス分布に近づいた気がする。

まとめ

データ分析の前処理方法として、変数の分布を形状ごと変えるRankGaussがどんなものか紹介して、実際にpythonで使ってみた。

Michael JahrerさんはRankGaussを使えば一般的に他の正規化よりも性能が良くなると謳ってたけど、 最初に数値データを順位に変換してるから、身長・体重みたいな比例尺度に対してRankGaussを使うと数値間の差や比率に意味がなくなるのでは?

間隔尺度に変換されても問題ない変数なら有効のような気がするけど、必要な情報量が削がれてしまう可能性があるので、適用する場面には注意した方が良いと思う。

参考

Kaggleで勝つデータ分析の技術

Kaggleで勝つデータ分析の技術

今回のRankGaussもそうだが、その他の非線形変換手法なども説明してくれている。基礎から学ぶことができるので、これからデータ分析について勉強したい人や、Kaggle等のコンペをやってみたい人の入門書としてちょうど良い内容だと思う。