はじめに
この記事は kaggle advent calendar 2020 、10日目の記事です。
前日は rinascimento741 さんの 【Kaggle挫折しそうな方向け】AtCoderのススメ
の記事でした。
明日は tume731 さんの wandb+Pytorch-lightning+hydraで書くNNプロジェクト です。
自己紹介
Kaggle Master です。
今年の Kaggle の成績は下記の通りです。
- OpenVaccine: COVID-19 : 847/1636
「うわ、こいつの順位低すぎ。。」
と思った方、逆に考えてみてください。
こんな私が書く最もお世話になった解法、気になりませんか...?
解法
結論から言ってしまうと
「target の予測値(oof) を利用して特徴を生成する」
です。
普段から Kaggle に取り組んでいる方には見慣れた内容かもしれませんが、
私は今年こちらの解法を Kaggle ではないコンペで活用したので、本記事のテーマとしたいと思います。
どれぐらい使われているの?
私が参加していたコンペでは、下記が該当します。
-
Kaggle / 2019 Data Science Bowl
-
Kaggle / Home Credit Default Risk
-
atmaCup / #4
-
2nd place solution(私)
どういう内容?
前述のリンク先の説明が、すでにわかりやすくまとまっているのですが、さらに例を上げると下記のようになります。
お題
とあるソーシャルゲームで、特定の日にユーザーがバトルイベントをプレイするか否かを予測する1
与えられたデータ
- train
- target が目的変数
userId | registDate | leagueRank | power | target |
---|---|---|---|---|
1 | 2017-08-07 | S | 103,131 | 1 |
2 | 2019-03-01 | B | 35,328 | 0 |
- test
| userId | registDate | leagueRank | power | target |
|:-:|:-:|:-:|:-:|:-:|:-:|
| 3 | 2018-04-02 | A | 61,722 |?|
- login
- ユーザーのログイン履歴
| userId | date | gachaEvent | holiday |
|:-:|:-:|:-:|:-:|:-:|
| 1 | 2020-01-01 | 1 | 1 |
| 1 | 2020-01-03 | 1 | 1 |
| 1 | 2020-01-05 | 0 | 1 |
| 1 | 2020-01-10 | 1 | 0 |
| 2 | 2020-01-02 | 1 | 1 |
| 2 | 2020-01-03 | 1 | 1 |
| 2 | 2020-01-07 | 0 | 0 |
| 3 | 2020-01-04 | 1 | 1 |
| 3 | 2020-01-05 | 1 | 1 |
| 3 | 2020-01-07 | 0 | 0 |
| 3 | 2020-01-08 | 0 | 0 |
特徴量生成
1, login に対して train を merge
して、target 列を付与する。
このとき、 userId=3 は target 不明なので、 null
になります。
| userId | date | gachaEvent | holiday | target |
|:-:|:-:|:-:|:-:|:-:|:-:|
| 1 | 2020-01-01 | 1 | 1 | 1 |
| 1 | 2020-01-03 | 1 | 1 | 1 |
| 1 | 2020-01-05 | 0 | 1 | 1 |
| 1 | 2020-01-10 | 1 | 0 | 1 |
| 2 | 2020-01-02 | 1 | 1 | 0 |
| 2 | 2020-01-03 | 1 | 1 | 0 |
| 2 | 2020-01-07 | 0 | 0 | 0 |
| 3 | 2020-01-04 | 1 | 1 | |
| 3 | 2020-01-05 | 1 | 1 | |
| 3 | 2020-01-07 | 0 | 0 | |
| 3 | 2020-01-08 | 0 | 0 | |
2, login の target を予測するモデルを作る
target が存在する userId=1 or 2 のデータを学習データとして
Cross Validation を行い、モデルを作ります。
userId=1 or 2 のデータに対しては oof ( Out Of Fold ) の値を付与します。
userId=3 のデータに対しては、各 Fold で作成したモデルで predict した値の平均値を付与します。
userId | date | gachaEvent | holiday | target | predictions |
---|---|---|---|---|---|
1 | 2020-01-01 | 1 | 1 | 1 | 0.22 |
1 | 2020-01-03 | 1 | 1 | 1 | 0.18 |
1 | 2020-01-05 | 0 | 1 | 1 | 0.14 |
1 | 2020-01-10 | 1 | 0 | 1 | 0.11 |
2 | 2020-01-02 | 1 | 1 | 0 | 0.05 |
2 | 2020-01-03 | 1 | 1 | 0 | 0.06 |
2 | 2020-01-07 | 0 | 0 | 0 | 0.08 |
3 | 2020-01-04 | 1 | 1 | 0.11 | |
3 | 2020-01-05 | 1 | 1 | 0.12 | |
3 | 2020-01-07 | 0 | 0 | 0.07 | |
3 | 2020-01-08 | 0 | 0 | 0.05 |
3, 予測値の集約特徴を作り、元の train, test に戻す
例えば、最小値、平均値、最大値をとって特徴量とすることができます。
- train
userId | registDate | leagueRank | power | target | predictions_min | predictions_mean | predictions_max |
---|---|---|---|---|---|---|---|
1 | 2017-08-07 | S | 103,131 | 1 | 0.11 | 0.1625 | 0.22 |
2 | 2019-03-01 | B | 35,328 | 0 | 0.05 | 0.0633 | 0.08 |
- test
userId | registDate | leagueRank | power | target | predictions_min | predictions_mean | predictions_max |
---|---|---|---|---|---|---|---|
3 | 2018-04-02 | A | 61,722 | ? | 0.05 | 0.0875 | 0.12 |
なんで上手くいくの?
前述の例であれば、他の方法(例えば、単に login から集約特徴を生成)よりも、効率よく情報を取り出せるため、というのが所感です。
TargetEncoding にも近い感覚ですが、なんだかんだリークしたり上手くいかない場合もあるので、oof を利用するのは理にかなっているのではないでしょうか。
適用できる場面が少ないのでは?
ここまでは target の oof を見てきましたが、応用して、重要度の高い説明変数の oof を利用するというやり方もあります。(特に、重要度が高いが null が多い説明変数に効果的です)
-
Kaggle / PLAsTiCC Astronomical Classification
-
5 で「hostgal_specz」という変数の oof を利用した旨が書かれています
-
Kaggle / Home Credit Default Risk
-
「ext_sources」という変数を予測するモデルを作成した旨が書かれています
-
bitgrit / SwipeToSuccess
-
3rd place solution(私)
まとめ
Kaggle で度々使われている**「target の予測値(oof) を利用して特徴を生成する」**方法とその応用についてご紹介しました。Kaggle ではないコンペですが、私が今年お世話になったやり方です。
最近は時系列テーブルコンペでも Transformer の勢いを感じますが、今回のような方法も機会があれば試してみてはいかがでしょうか。
-
もちろんダミーデータです ↩