kaggleのIEEEコンペで8位になった話

KaggleのIEEE-CIS Fraud Detectionコンペで 8位/6381チーム中 になったので、記念に記事を書きます。
![gold.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509790/85f52695-46ab-f085-d6a6-e13c9587ad6f.png)

[IEEE-CIS Fraud Detection](https://www.kaggle.com/c/ieee-fraud-detection)

#コンペに参加するまでの流れ
去年の頭あたりから機械学習をちょびちょびと勉強し始める
→Kaggle([TalkingDataコンペ](https://www.kaggle.com/c/talkingdata-adtracking-fraud-detection/))に参加したり、機械学習の数理に手を出したりする
→世界史ゲー(Europa Universalis 4)に溺れて完全に行方不明になる
→機械学習とデータ分析を習慣づけるためにKaggleに復帰
→[Instant Gratificationコンペ](https://www.kaggle.com/c/instant-gratification)に参加するも惨敗
→リベンジすべく、[CouseraのKaggle講座](https://www.coursera.org/learn/competitive-data-science)を受講
→その成果を活かすべくIEEEコンペに参戦

#コンペの概要
クレジットカード・デビットカードなどの不正取引の検知。
正例の割合が少ない(Train dataの3.4%程) 不均衡データで、評価指標はAUCでした。
大部分の列の意味がぼかされている匿名データコンペですが、列名の頭にDがある(D13など)場合は時間関係、Cがある場合はカウント関係など、その列の属性はある程度分かるようになっています。

Courseraの講座を受講したことでKaggleの全体像をある程度把握できるようになり、飛躍的に視野が広がったことが勝因だったのかなと思います。

#取り組み

##全体的な心掛け
①仮説に基づいて特徴量を作成する
②生データをつぶさに観察してデータの特徴を掴むようにする

この2点に気を付けて特徴量生成を行ってスコアを上げていく方針を取りました。モデリング？知らない子ですね…

##チームマージ前
###Discussionの読み込み
8/10頃に初めて手を付けたものの、やることが良くわからないのでまずはDiscussionを読み込むところから始めました。
主催者が列名の情報や不正取引の定義など重要事項をいろいろ話してくれたので、主催者からの情報と参加者からの情報を分けて、Evernoteに雑にまとめました。
###カーネル読み込み+1サブ目
以下のカーネルを参考にしてデータの特徴を掴みながら、それらを適当に組み合わせて1サブ目を作りました。
https://www.kaggle.com/artgor/eda-and-models
https://www.kaggle.com/nroman/lgb-single-model-lb-0-9419
https://www.kaggle.com/timon88/lgbm-baseline-small-fe-no-blend

ここで1サブすることで後々スコアを伸ばそうというモチベーションが湧きます。
しかし、このサブはベースラインとしては使いませんでした。

###ベースライン作成
以下のカーネルが出てきたので、そちらをベースラインとする方向に切り替えました。
https://www.kaggle.com/kyakovlev/ieee-gb-2-make-amount-useful-again?scriptVersionId=18941048
同時期に、5-fold(shuffle=False)が一番LBとの相関が高かったという報告を見かけたので、今後はそのCVで固定する方針に決めました。
以降はそれに特徴量を加えていく作業がメインになります。

###特徴量生成(Public: 0.9492)
8/17から1週間休暇だったので、特徴量生成に時間を費やしました。
この時点でシングルモデルのスコアが上位3%くらいだったかなと思います。
主な特徴量は以下の通りです。

####前回クリックからの秒数・次回クリックまでの秒数(+0.0015)
TalkingDataコンペの焼き直しです。過去のコンペから学ぶのは重要ですね。
同じメールドメインや同じブラウザなどの取引で、1個前と1個後との秒数の間隔を取りました。また、秒数の間隔の平均も取っています。
似たような取引が例えば1分以内にあったら怪しいのではないかという仮説に基づいて作りました。
####前回クリック・次回クリックとのC13(カウント系特徴量)の差分(+0.0007)
取引額やカード情報が共通する取引で、1個前と1個後とのC13の差分を取りました。これが0であるとき、不正取引の割合が高かったです。
この特徴量は以下の調査に基づいて作りました。

①取引額やカード情報が共通する取引をC13の値でグルーピング。
②C13の値でターゲットの平均を取ると、例えばC13が18のところに不正取引が集中しているといった観測結果が得られた。
③不正取引の場合はC13が変化しないと考えることができた。

##チームマージ
ここまででシングルモデルのスコアが上位3%程度になったのと、休暇が終わって1人だと心もとないと考えてDiscussion経由でチームマージしました。最終的には5人チームになりました。チームとしては特に方針を決めず、各々が自由にやりたいことに取り組む形で進めていきました。
チームメイトは皆とても優秀で、いろいろ学ばせていただけてとても楽しかったです。ありがとうございました。

###全員の特徴量の集約(LB0.9541)
チームマージして全員分の特徴量が集まったので、1つのモデルに集約してサブミットします。スコアは0.9541でした。
特徴量としては、例えば以下のようなものがありました。

- ユーザーグループ(ex. card1~card6+addr1+addr2)毎のnumerical featureの平均・分散・平均との比・平均との差
- D1~D15の値をランクに変換
- categorical featureに対するLDA・NMF (TalkingDataでも使われていた手法)
- 各取引をAutoEncoderで圧縮→復元したときの誤差 (LB+0.001ぐらい。異常検知の分野では知られている手法のようです)

###Magic発見
チームのスコアが中々伸びない中で特徴量生成に取り組んでいたところ、Magicを見つけてしまいました。
そのMagicは、同一ユーザー特定のIDでした。
具体的には、以下のIDを作りました(チーム内ではuid4と呼んでいました)。
card1 + addr1 + P_emaildomain + ProductCD + (days - D1) (daysは取引日)

D1は、そのカードの初取引からの経過日数と考えられるため([参考](https://www.kaggle.com/c/ieee-fraud-detection/discussion/108707#latest-628791
))、days - D1で初取引日が計算できます。初取引日はかなりhigh-cardinalityなのでユーザーの厳密な特定に使えるのでは？と考えました。
実際このuid4はかなり強力で、uid4でグルーピングして予測値の平均を取るという後処理を行っただけで、LBが0.9541から0.9557まで上がりました。

###Magicからの特徴量生成(LB0.9607)
上記の後処理をチームに共有したところ、uid4を使った特徴量生成こそがMagic featuresであるという流れになりました。自分が把握している範囲では、以下の特徴量が使われています。

- uid4毎のnumerical featuresの平均・分散・平均との比・平均との差
- uid4が同じである、1個前・1個後の取引との秒数の間隔

この結果、シングルモデルの最高スコアが0.9607まで上がりました(ただし、このモデルは複数人の特徴量を集約したもののため、最後のアンサンブルでは使われていません)。

###Catboost・NN
アンサンブルに使うために、チームメイトがCatboostとNNを作ってくれました。
CatboostはLB0.9590、NNはLB0.9564でした。
このNNのスコアには度肝を抜かれました。実際、1位のチームでもNNのスコアは0.9430だったようです。ダントツでトップです。
以下のdiscussionにNNの解法が投稿されているので、是非見ていってください。
https://www.kaggle.com/c/ieee-fraud-detection/discussion/111476#latest-643764
uid4をembedding layerにぶち込んで最後のdense layerと合体させることが鍵となっているらしいです(よく分からない)。

###アンサンブル(LB0.9649)
LB0.9597・0.9601のLightGBM2つと上記Catboost・NNをシンプルなrank blendを用いてアンサンブルします。
結果はPublic 0.9649(13位)/ Private 0.9422(8位)でした。
他のチームと比べると、CatboostとNNが優秀だったことで、アンサンブルのスコアの伸び幅が大きかったように感じます。

### その他効かなかった手法
uid4の他に別のユーザーIDを作ってそこから特徴量生成を行ったのですが、全く効きませんでした。
具体的には、
card1 + ProductCD + id31(ブラウザの種類) + DeviceInfo + P_emaildomain + (days - D1)
を用いてaddr1がnullである部分にも対応しようとしたり、uid4にC1～C14をビニングした特徴量を加えて新たなユーザーIDとしたりなどしていました。

他にはチームメイトが2nd subとしてshake-upに強いモデルを作るべく、特徴量へのDAEを行ったり、Kolmogorov-Smirnov検定を用いて特徴量の絞り込みを行ったりしていたのですが、Public 0.9585/ Private 0.9280と、かえってスコアの下がり幅が大きくなってしまいました。

##振り返り

###良かったこと
- 仮説思考で特徴量を作ることができた
- データを観察して特徴を掴む能力が向上した

特徴量生成に全振りしていたため、データの観察能力は特に向上したように思います。

###反省点
- モデリングをチームメイトに丸投げしたため、モデリングスキルが身についていない
- コードの管理ができていない
- 思いつきで発言し過ぎ

コードの管理をしなかったため、ローカルのフォルダがEDAノートブックで溢れかえることになりました。反省。
また、次回は自分でモデリングもやるように心がけたいと思います。

###感想

匿名データコンペの常としてパズル要素がかなり強かったため、初心者でもアイデア一つでかなり上位まで食い込めるコンペだったと思います。初心者が自信をつけるにはよかったのかも。
ただ、主催者が知っているはずのユーザーIDを特定する作業が何かの役に立つとも思えず、そういう意味では次は匿名でないコンペで勝ちたいと思ったものでした。