前処理の追加・変更時に機械学習のモデルの精度が大きく下がった際に疑うべき3つのこと

前処理の追加・変更時に機械学習のモデルの精度が大きく下がった際に疑うべき3つのこと


# 初心者 # 機械学習

基本的なことではありますが、特に機械学習の雰囲気に慣れない頃は色々コードを書いていると案外どころかドツボにはまってしまったので自戒の念を込めつつ、誰かの参考になれば幸いと思い記事にしておきます。

起きたこと

既存のライブラリやソースを使って、機械学習のモデルを構築して評価を行っていました。 AUCやMER等の評価指標でそれなりの値が出ていましたが、目標とする数値には一歩及ばず前処理を追加することとなりました。

色々と調査して良さそうな前処理を見つけたもの、どうやらライブラリ化はされておらずスクラッチに近い形での実装が必要でした。

とはいえ理解をしてしまえばそこまで難しい処理ではないので、意気揚々と前処理を実装し、再学習・評価を行いました。

すると、AUCはほぼ0.5、MERは元の数10~数100倍と、明らかに何かがおかしい状態でした。 原因調査を進めてもなかなか気がつくことが出来ず、時間だけが過ぎていくのでした…

 

データのインデックスが入れ替わってしまっていた

クロスバリデーション等の処理を自前で実装した際に起こりやすいと思います。

scikit-learnはあくまで、numpyのarrayを受け取ります。 そのため、pandasのDataFrame等を引数に渡した場合は、DataFrameのindexの情報は全く考慮されずに処理が行わるため、特徴量と予測値のindexがずれることがあります

joinやmergeの順番によっても並び順が変わることは往々にして起こるので慣れないうちは1ずつつ丁寧に確認しましょう。

これに気がついたときはDataFrameの殻をかぶっていても、あくまで実際のデータはnumpyなんだということを肝に命じました…。

以下のような空のindexだけのDataFrameにjoinすることで並び順を整えることが出来ます。

index_df = pd.DataFrame(index=x.index)
# 前処理 
x = MyClass.transform(x)

return index_df.join(x)

上記のような処理もしくはsort_indexを自作の処理の前後にすべて加えてしまうのがいいかもしれません。

データのカラムの並び順が変わってしまっていた

こちらも自作の前処理を行った際に起こりやすいかと思います。sciki-learnのFeatureUnionが使いづらい処理がある際に、自作した際に悲劇は起きました。

こちらも上記と同様に、あくまでPandasの殻をかぶっていてもscikit-learnではnumpyとして扱われるので、基本的に特徴量が数値化さえしまえばとりあえず動いてしまいます。 データの中身なんて関係ありません。学習時のデータが0,1のみでテスト時のデータが-9999~9999みたいなものでも動きます、動いちゃいます…。

そもそも並び順変わらないように処理を書けよ、という話ではあるのですが人間だもの、ミスはどうしたっておきます。

なので、fitやpredictメソッドを実行する前にデータの並び順を保証するような処理を挟んであげるのがいいと思います。pklの内容が増えたりで若干面倒ですが安全と安心には変えられません。

from sklearn.linear_model import LogisticRegression
train_x, train_y, test_x = ....

model = LogisticRegression()
model.fit(train_x, train_y)

fit_columns = list(train_x.columns)

# pickle化するならこんな感じでpickle化する
# import pickle
# pickle.dump({'model': model, 'fit_columns': fit_columns})

# カラムの並び順を整えてからpredict関数に渡す
model.predict(test_x[fit_columns])

並び順だけでなく、おかしなカラム名が来たときにも例外が出るようになるので、とりあえず動くではなくきちんと動く状態に近づくかと思います。

リークが起きる前処理を作ってしまった

Kaggleで使われてから脚光を浴びるようになった、Target Encodingですが処理の際に工夫をしてあげないとバリバリにリークが起きているような状態となります。 (リークはモデル適用時には知りうることの出来ない情報が特徴量に混ざり込むことです。学習データでは高精度、テストデータでガクッと精度が下がるということが起こります。)

こちらは学習データの評価とテストデータの評価の差を見るなどで発見していくこととなります。

やや面倒ですが、追加した特徴量と目的変数の相関やその特徴量のみでモデリングをした際の精度を見るなど地道な検証をすることとなります。 前処理を作るって大変ですよね…。

まとめ

自分で前処理を作成することは勉強のためには非常に重要なことだと思います。なので、私もどんどん書いていきたいとは思っています。 しかし、きちんと動いているかの確認は思わぬ落とし穴が多く、手間がかかるとともにそんなに簡単でもありません。

そのため、実際にコンペや業務でモデリングを行う際は、先人の知恵や努力を活かすためにも既存のライブラリやツールを最大限活用して行くことが望ましいと思います。