スケール変換をtrainデータ、testデータ別々に変換してはいけない

まぁ、当たり前といえば当たり前なのですが、、、
オライリーの「Pythonではじめる機械学習」では、標準化などの前処理をする際はテストセットを訓練セットと同じように変換することが重要と書かれています。


つまり訓練セットの分散、平均、最大値、最小値などの情報を使ってテストセットも変換する、ということですね。
自分が今まで書いたコードを見てみると、、、やっちゃってました。
 
今回は、Kaggleの House Prices: Advanced Regression Techniques | Kaggle を題材に、どの程度テストセットへの精度が変わるのか見てみたいと思います。
 

スケール変換とは?

  • 標準化
    • 平均0、分散1になるようにデータセットを変換すること。
    • Scikit-learnではpreprocessing.StandardScalerクラスが該当する。
  • 正規化
    • 決まった範囲に収まるようにデータセットを変換すること。
    • Scikit-learnではpreprocessing.MinMaxScalerクラスが該当する。
      (デフォルトは0.0~1.0)
    • 最大値、最小値の各データポイントに対する影響が大きく、外れ値に弱い。

その他、Scikit-learnには様々な前処理が用意されている。詳しくは以下を参照ください。
scikit-learn.org

今回はScikit-learnのStandardScalerで前処理を行います。

実際にやってみた

標準化した後にモデルのフィッティングを行い、テストデータに適用します。
(実際には欠損値補完や特徴量の追加を合間にしています。)

コードはこちら

import pandas as pd
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('./train.csv')
/*欠損値補完などの前処理*/

scaler = StandardScaler()
scaler.fit(df)
X = pd.DataFrame(scaler.transform(df), columns=df.columns)
/*モデルのフィッティング*/

フィッティング後、テストデータへ適用する際には以下のようにする。

df_test = pd.read_csv('./test.csv')
/*欠損値補完などの前処理*/
df_test = pd.DataFrame(scaler.transform(df_test), columns=df_test.columns)

この際、再度fitせずにトレーニング時にfitしたscalerを使用する。
(私はここで再度fitしてしまっていた。)

適切な標準化とテストデータにfitさせて標準化したデータを見比べてみよう。

f:id:brskun:20200507005647p:plain
テストデータにを基に標準化
限りなく平均0,標準偏差1であることがわかる。

f:id:brskun:20200507005613p:plain
適切な標準化
こちらは、おおよそ平均0,標準偏差1であることがわかる。
やはり、データのばらつきに違いが生じているようだ。

精度の変化

Ridge回帰、SVR、XGBoostで精度がどのように変化するかを確認する。

  • テストデータにfitさせてしまった場合
    • Ridge回帰 RmSLE:0.15353
    • SVR    RmSLE:0.14805
    • XGBoost  RmSLE:0.14433
  • 適切な標準化の場合
    • Ridge回帰 RmSLE:0.15325
    • SVR    RmSLE:0.14798
    • XGBoost  RmSLE:0.13801

全てのモデルにおいて、RmSLEが小さくなっており、精度が向上した。
特に、XGBoostでの精度の上がり方が目に付く。
言い換えると、適切に標準化を行わないと精度が下がってしまうことを意味する。

おわりに

今回は、精度向上のためにせっかくやった標準化も、やり方を間違えれば精度を落としてしまうことを比較してみました。
今度機会があれば、モデリング部分についても書いてみようかな。