kaggle体験記:NFLコンペで70位/2038
こんにちは。@kurupicalです。
昨年の10月から12月にかけて開催されていたNFLコンペに@osciiartさん、@monnuさんと出場し、70位/2038をとることができました!チーム組んでいただきありがとうございました。m( )m
私が開催している以下もくもく会で取組を紹介したので、スライドをアップしました。
connpass.com
個人的には、今までやったコンペの中でも1,2を争う面白さでした!
2枚目の金目指して引き続き頑張ります・・・!
Kaggle体験記:IEEE CIS Fraud Detectionで19位/6381
はじめに
こんにちは。くるぴー(@kurupical)です。
このたび、IEEE CIS Fraud Detectionコンペに、@pondelion1783さん、@HighGradeToppoさん、@TaTakoihirokazuさんと参加し、19位/6381の成績を残すことができました。
チームのみなさまはとても優秀で、コンペに参加した2ヶ月の間とても刺激的な時間を過ごすことができ、いい経験になりました。
チーム目標であった「金メダル」も達成できてとても嬉しいです。本当にありがとうございました!
このブログでは、これからKaggleなどのデータ分析コンペ参加しようとしている方向けに、どのようにコンペに取り組んだのかという経緯を残しておきたいと思います。
何かのお役に立てれば幸いです。
もしよろしければ、1年前に書いたkaggle体験記もあわせてご覧ください。
kurupical.hatenablog.com
1. 参加のきっかけ
きっかけは、8/3(土)に開催されたatmaCupの懇親会でした。*1
「関西のexpert以下のチームで金を取る」というお酒の勢いだけで4人チームを集め、結成しました。
「みんなテーブルコンペが得意」という理由だけで、そのとき唯一開催されていたIEEE CIS Fraud Detectionコンペに参加することを決めました。
この時点では、全員、ゼロサブどころか、コンペの概要を読んだ程度でした。
また、このオフラインコンペで@takuokoさんに公開いただいた上位ソリューション(PetFinder 2ndの知見の集合体)に感銘を受け、
何かテーブルコンペで自分でも試してみたいという気持ちがありました。
2. コンペ取り組み
2-1. コンペ説明
2-1-1. 課題設定
- ユーザーの取引データから、「不正取引」を検出したい
2-1-2. データセット
- train, testそれぞれ約50万行、約450列
- うち「不正利用」とされるデータは3%。残り97%は不正利用ではない。
- すべての項目名は匿名化されている。が、特徴の属性(カテゴリなのか日付なのかカウントなのか等)は項目名である程度わかるようになっている。
例:項目名の頭にD→日付、C→カウント 等
2-2. 序盤
2-2-1. まずは1サブミット
機械学習させるコードを持っていない人にとって、何もない状態から1サブミットするのはかなりハードルが高く、ここでめんどくさくなって挫折してしまうケースが多いです。
逆に1サブミットさえしてしまえば、そこからスコアを伸ばすモチベーションが湧いてきて、2サブ目、3サブ目以降と続けやすくなると感じています。
この挫折を回避するために、自分は以下ルーチンを実施して「とりあえず1サブミット」を目指しています。(あくまで個人的な意見です)
- 以下手順で良さそうなNotebookを探す
- Notebookをコピーする
- 1行でもいいので自分のオリジナルを加える(コピペではなく自分でやった感を出す)
- サブミットする
2-2-2. EDA
まずは全特徴量の分布を、train, testでvisualizationしました。
上のvisualizationは、ProductCDという項目をtrain/testをそれぞれ時系列に3分割してヒストグラムを出したものです。
例えばこのグラフだと、ProductCD=Hが、trainの頭・testの末に多いことがわかるので、それを深堀していきました。
この例だと、Hは12月に多く、HはHobbyのHだと仮説を立てました。分析の結果12月は明らかに分布が違うこと、不正利用が少ないことがわかったので、12月フラグという特徴量を作りました。
2-2-3. 総当りで特徴量作成(Public:0.9423, 3000位/6381)
序盤は特徴が匿名化されているため、思考停止して特徴を作成しました。
「すべての特徴(400項目ほど)から2つを取って差・比を取る」総当り。12万ほどの特徴が作成されるので、
1000個ずつモデルに投入→top10項目を取る*120回 = 特に効いていると思われる1200特徴量を突っ込んでどうなるか、試しました。
全然効きませんでした。
2-3. 中盤
2-3-1. 仮説を立てた
総当り特徴の作成はリソースを無限に食うし、こういうやり方ではAutoML系サービスに明らかに分があります。
「50万行の中には、同一のユーザーがいるのではないか」「同一のユーザーを特定すれば、スコアがかなり上昇するのではないか」という仮説を立て、EDAを実施しました。
2-3-2. ユーザー特定のIDを見つけた(Public:0.9435, 2750位/6381)
同一ユーザー特定のヒントを得るため、同じユーザーと思われる行をまずは手作業で抜き出そうと考えました。
方法としては、カード会社等の属性などが入っている項目card1〜card6を結合して仮ユーザーIDとして、同じ仮ユーザーIDを持つものをまとめてcsv出力し、
Excelで縦横に流しながらざっと眺めました。
すると、明らかに特徴が似ている行たちがいました。
この「特徴が似ている行たち」だけを切り取って眺めていると、「D1」がカード作成からの経過日数?、
「D5」が前回取引からの経過日数らしきものであることがわかりました。
目視で「多分同一人物だろうな」という人を黄色塗しました。
赤枠1+赤枠2=赤枠3。赤枠1は基準日から15日、赤枠3は19日経過した時点のデータ。赤枠2は、赤枠1の行から何日経過したか、ではないか?
※項目「DAY」はTransactionDTという経過秒を60×60×24で割って経過日にしたもの
これをチームメンバーに共有したところ、TransactionDay(取引した日)とD1(カード作成からの経過日数)の差を取れば、「カード作成日」という特徴が作れ、
ユーザーがかなり厳密に特定できるのでは?と新たに示唆をいただきました。
さらに、カード作成日を加味し、カード情報+カード作成日というユーザーIDを使って、data.groupby("userID").mean()、data.groupby("userID").std()のような、
ユーザーIDごとの特徴量平均・標準偏差のような特徴を作って実験したところ、大幅にLBが向上した(Public0.9564, 85位/6381)という報告がありました。*2
同じことを自分の手元で試したのですがスコア伸びませんでした(Public0.9435止まり)。
2-3-3. ハイパーパラメータ調整(Public0.9477, 2195位/6381)
ハイパーパラメータを調整するだけで、Publicが0.9435→0.9477に向上しました。
もともと、HomeCreditコンペで使っていたmax_leaves=60, depth=10というパラメータで実験していましたが、一番強いNotebookに記載されているハイパーパラメータを一部参考にしました。
私のかってな推察なのですが、card1、card2の特徴量はユニーク数がかなり大きく(10000くらい?)、決定木の葉っぱの数であるnum_leavesが少ないと、枝分かれが十分にできないためにLBが低かったのではないかと思っています。
Notebookのハイパーパラメータはこういったところも加味してちゃんと設定されているのだなあと感動しました。*3
2-3-4. さらなる特徴作成(Public:0.9584, 64位/6381)
いろいろな特徴量を作りました。
- userIDをベースに全特徴量の平均、標準偏差
- userIDをベースに月ごと・週ごと・日ごと等で取引回数や取引金額の平均、標準偏差
- あらたなuserIDを試す
- 特徴量をいくつかのグループに分け、それぞれのグループの「最大/最小値」、「最大/最小をとった項目名」「欠損パターン」。
例えば、以下V95,V101,V143,V167,V177,V279,V293あたりは挙動が似てるので一つのグループとしました。パターンはエクセル目視で確認しました。今思うと、特徴ごとのクラスタリングとか使ってスマートにやればよかったのですが…
2-4. 終盤
2-4-1. Negative Samplingによる実験の高速化
「不正利用」のデータ全部と「正常利用」のデータ2割で学習させても精度はそこまで変わらないので、実験が高速にできるよ!というDiscussionを読み、実践しました。
実験時間がかなり少なく済む、特徴量をたくさん入れてもMemoryErrorにならないなどかなりメリットが大きかったです。
2-4-2. 特徴選択
Negative Samplingをしたうえでfeature importanceが高い項目を採用しました。
2-4-3. 予測できていないデータの分析
自分のモデルが予測できていないデータを分析しました。結果、ProductCDがWの不正利用が検知されていないケースが多いことが判明しました。
この結果から、ProductCD=W、Cだけのモデルを作り、アンサンブルに加えました。(LB +0.001)
ProductCD=W, D1=0が不正利用検知できていないケースが多かったためこれもアンサンブルしたかったのですが、実験が間に合いませんでした...。
2-4-4. CatBoost
カテゴリがかなり多いのでCatBoostも効くのでは?という考えで、試しました。LBは低かったですが、アンサンブルでよく効きました。
パラメータ調整はdepthのみ行い、6,8,10,11あたりを試しました。結局depth=6が一番強かったです。
あと、変な特徴を入れるとCV0.960→LB0.951と出たり、挙動がよくわかりませんでした…
結局、CV0.955, LB0.955くらいまで伸ばしてアンサンブルしました。
2-4-5. seed averaging
モデルのseedを変えて学習させ、平均を取りました。
2-4-6. postprocess
厳しめに絞ったユーザーID(card1-6 + (D1-Day) + (D10-Day))を使って、以下を実施しました。
- trainデータですべて不正利用、取引回数2回以上のID → 予想を1に書き換え
- trainデータですべて不正利用ではない、取引回数5回以上のID → 予想を0に置き換え
Public+0.0007, Private+0.0003でした。
2-4-7. アンサンブル
StratifiedKFold(5), Timeseries(6)で全員のモデルを予測させ、最終foldのoofを使ってNelder-Mead法で重み付けしました。(全Foldのoofを使ったほうがPublicは低かったもののPrivateは0.001くらい高かったです。)
qiita.com
2-4-8. 最終サブの選定
2サブのうち1サブは安全にという気持ちで、Train/Testで分布が異なる特徴量を削除したモデルにしました。
2-5. 結果
全員のモデルをアンサンブルしてPublic0.9634(29位), Private 19位になりました。
チームメンバー全員、Publicで0.96を超えるSingle Modelはありませんでした。一番よくて0.9588くらいだった気がします。
そういう意味ではチームで勝つことができたのかなと思います!!
3. 振り返り
3-1. 良かったこと
- 丁寧にEDAができ、EDAから得られた知見を特徴に落とし込めた。
統計量やサマリーを見るのも大事ですが、同じくらい個々のデータを見るのも大事。ただ、いたずらに全部見るのではなく、いろいろな条件で絞り込む(IDとか、予測うまく行かなかったデータとか)とスマートにEDAできると思いました。Excel Masterになりましょう! - 新しいライブラリ(CatBoost)に触れることができた。
新しいコンペに参加するごとに、公開されているNotebookからたくさん学んでいます。Masterになったら僕も少しずつ貢献していきたいです。 - 行き詰まったら特徴作成コード全捨て
強い人がやっているのを聞いて自分もやってみましたがかなりよかったです。 - 酒の勢いで言った「チーム組みましょう」を、ノリで終わらせず、その場でちゃんとチームマージした。笑
「チーム組みましょう。また明日以降対応しますね」じゃなくて、「とりあえずチーム組みましょう、その代わり後で気が変わってやる気なくなっても文句なしで!」という方針にしました笑 - バージョン管理でgithubを使った。
管理は超適当でしたが、無いよりマシでした。
3-2. 反省点
- 実験の効率化をするのが後回しになってしまった。
コンペが進んでいくと技術的負債がすごい勢いで積み上がっていきますが、短期間だからと負債を抱えたまま走り切るより、途中途中でリファクタリングしたほうがよいと感じました。 - 検証が十分でない実験結果をむやみに発言しない。
チーム全体が誤った実験結果に引っ張られてしまい、時間が無駄になる。(終盤、脳が働かなくなって、発言を推敲する能力が著しく低下しました。。) - ログをあまり残さなかった。
この実験なんだったっけ…どういうソースコードで、どういう特徴量を使ったんだっけ?とならないようにしたいです。(と思っていたが、結局最後まで着手せず、最低限のログしか残しませんでした…。) - 途中までテストコードを書かなかった。
ガッツリ書く必要はありませんが、最低限確認したいことをassert文で書いたらよかった。バグで1週間進捗が止まったりして、結構しんどかったです。 - 睡眠時間を削った結果、集中力が低下した。
学習経過をぼーっと眺める、学習中のモデルを応援する(学習経緯を眺めながらauc上がれ!上がれ!と声をかけ始める等)ようになったら、集中力低下のサインです。kaggleやるときはkaggleやる、やらないときはやらない、メリハリをつけるのが大事だなと痛感しました。
3-3. かかった費用
16000円でした。他に趣味もないしこれくらいいいかな。
3-4. 所感
- ユーザーIDを特定できた人とできなかった人で勝敗がわかれたコンペでした。個人情報の縛りがありユーザーID公開できなかったそうですが、この特定作業は若干不毛な作業でした。
(そういう意味ではHomeCreditは良コンペだなと…) - 今回使ったモデルが実務で活きるのか不明。ユーザーIDを特徴に入れてしまっているので。
Public/PrivateでPrivateスコアがかなり下がってしまっていることからも、Train/Publicにoverfitしていると思います。
モデル作成が社会貢献につながってほしいので、次はそういうコンペを選びたい。
4. まとめ
IEEE CIS Fraud Detectionコンペの活動記録をまとめました。
少しでもみなさまのお役に立てたなら幸いです。
長文となりましたが、最後まで読んでいただきありがとうございました。
*1:第2回も11/23に開催されるようです。初心者の方でも、開催者が手取り足取り教えてくださるので、初心者から上級者のみなさまにおすすめのイベントです! atma.connpass.com
*2:このキーを発見した方の名前にちなんで、弊チームではtakoiIDと読んでいました笑
*3:ハイパーパラメータの意味は以下ブログを参考にしました。nykergoto.hatenablog.jp
ハイパーパラメータ調整方法は以下ブログを参考にしました。(IEEEコンペ5位の@MLBear2さんのブログです。終盤MLBear2さんのチームと競ってたので、「敵に塩を送っちゃって大丈夫か〜〜〜?」と思いながら読んでいたのですが、結局大敗しました…)
naotaka1128.hatenadiary.jp
<メモ>Human Protein Atlas Image Classification上位解法を読んだのでまとめる
1. まえがき
2018年11月〜2019年1月にHuman Protein Atlas Image Classificationに挑戦しました。
www.kaggle.com
結果は122位/2172と、銀メダルに届きませんでした。。悔しい。
自分がやった取り組みは余力があれば書くとして、今回は上位の皆様の解法を読んでまとめていきます。
2. コンペ
灰色文字は私の考察、それ以外は記事の記載内容と書き分けています。
訳しきれなかったところは、注釈で原文を入れています。
2-1. 概要
コンペの概要については、以下を参考にしてください。
tawara.hatenablog.com
2-2. 上位Solution
2-2-1. 1st place solution
全体像は以下の通り。 ※ディスカッションより転載
- CVはMultilabel Stratification*2で切っている。
- Augmentation
- Rotate 90
- flip
- randomly crop 512x512 patches from 768x768 images(or crop 1024x1024 patches from 1536x1536 images)
- 学習
- 学習率のスケジュール:
lr = 30e-5 if epoch > 25: lr = 15e-5 if epoch > 30: lr = 7.5e-5 if epoch > 35: lr = 3e-5 if epoch > 40: lr = 1e-5
損失関数
- FocalLoss+Lovasz.
Lovasz lossはSemantic segmentationのものでは?と思ったのですが、Lovasz lossの考え方はPrecision/Recallの考え方に一部適用できるということだそうです(Lovasz lossよくわかっていないです…)
- FocalLoss+Lovasz.
NN
- DenseNet121
DenseNetについては以下が分かりやすかったです。
qiita.com
分類レイヤーは以下の通り。
- DenseNet121
(1): AdaptiveConcatPool2d( (ap): AdaptiveAvgPool2d(output_size=(1, 1)) (mp): AdaptiveMaxPool2d(output_size=(1, 1)) ) (2): Flatten() (3): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (4): Dropout(p=0.5) (5): Linear(in_features=2048, out_features=1024, bias=True) (6): ReLU() (7): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (8): Dropout(p=0.5) (9): Linear(in_features=1024, out_features=28, bias=True)
参考:DenseNetの元論文の構造*3
原論文はAvgPoolingだけなのに対し、この解法はAdaptiveMaxPoolingも加えています。全体の情報に加えて、特徴的な情報も食わせることで、より「よしなに」やってくれる感じなんでしょうか。※AdaptiveXXXPool2d(output_size(1, 1))はGlobalXXXPooling2dと同義。
-
- lossが一番低いepochのモデルを分類器とし、random cropで予想を4回作成→予測の最大値をpredictionとする?*4
- lossが一番低いepochのモデルを分類器とし、random cropで予想を4回作成→予測の最大値をpredictionとする?*4
Metric Learning ※筆者の解釈を多く含みます。間違っていたらご指摘ください。。
」 ※(2/14追記:metric learningについては、以下がわかりやすいと情報いただきました。)
techblog.zozo.com- Metric Learningとは、平たく言えば、testデータに対してtrainデータとの類似度を計算し、その類似度が一定以上であればtrainデータのラベルに書き換える処理?のようです
いくつか、デモがされていました。(ディスカッションより転載)
一番左が「予想したいデータ」、一番左以外は、スコア(≒類似度)が高い画像から順に並べたものです。
スコアのしきい値によって1000枚ほどの画像を書き換えたところ、LBのスコアが0.03以上向上したそうです。この「Metric Learning」ですが、スコアを向上する以外だけでなく、以下のように応用ができると述べられています。
- アノテーションをするときや、アノテーションの質の確認、アノテーションの方法の教育の際に、一番スコアの高い画像と比較することでアノテーションの参考する。
- 画像を距離に基づきクラスタリングし変にアノテーションされたデータを見つけることによる、教師データのラベルの質の向上。
- 予測の見える化による、解釈可能性の向上
具体的にどのように画像間の距離を計算したのかが気になるところですが、「開催中の別コンペでも使っているのでまだ教えられない」ということでした。
- Metric Learningとは、平たく言えば、testデータに対してtrainデータとの類似度を計算し、その類似度が一定以上であればtrainデータのラベルに書き換える処理?のようです
最後に
if we compete only for win,we may loss,if we compete for learning and providing useful solution to the host,nothing to loss.
2-2.2. 3rd place solution
- CV
- trainの10%を、アンサンブル検証用に除外(holdout)
- 残り90%で5fold cv
- 異なるfold間で類似の画像がないようにする。(ahashなどで画像をハッシュ化して調べた)
- normalization.(officialデータとexternalデータで、画像の平均と分散が大きく異なったため)
- Augmentation
- 損失関数
- FocalLoss(gamma=2)
- Optimizer
- Adam(lr=5e-4)
- learning rate scheduling無し
- 大きいモデルで大きい画像を扱うときは、32枚ごとに重みをupdateするようにした?※原文の意味が読み取れず…バッチサイズ32だとメモリ不足になるのでこの対応をしている?*6
- 一番メジャーなクラスのF1スコアが減少したところで訓練を止めた(Early stopping)
- 推論
- 各訓練の最終10個のチェックポイントの重みの平均を使ったNN
- 8 TTAs
- weighted average ensemble
- 分類のしきい値
- validation setの予測割合とあうようにしきい値を選択
- モデル(以下3つをアンサンブル)
- 512*512
- ResNet34(5fold ensemble)
- 1024*1024
- inceptionv3
- se_resnext50
- 512*512
- NN構造
- 1024*1024だとglobal average poolingがworkしなかったので以下変更(よくわからなかったので原文)
- remove global average pooling.
- compute MxM logits using 1x1 convolution.
- compute weight maps using 1x1 convolution followed by softmax.
- using weight maps, compute weighted averaged logits.
global average poolingの代わりに、セルの値×各セルの重みをsoftmaxで計算した感じなんでしょうか…。
- 1024*1024だとglobal average poolingがworkしなかったので以下変更(よくわからなかったので原文)
2-2-3. 4th place solution
- GAPNet*7を使った
- GAPNetはmultiscaleの能力がある点で優位。→multiscaleのニュアンスは、画像全体(マクロ)と細かい点(ミクロ)の両方から判断できる、ということかなと解釈しています。
- GAPNetの発想をResNet18+SE-Blocksに適用した。
- Dual Loss ResNet
- 30分格闘してさらにいろいろ教えてもらったのですが消化できなかったので、後で書きます。現時点では、何が分かってなきゃいけないのか分かってないです。。
- 30分格闘してさらにいろいろ教えてもらったのですが消化できなかったので、後で書きます。現時点では、何が分かってなきゃいけないのか分かってないです。。
2-2-4. 5th place solution
- Augmentation
- Rotation
- Flip
- Shear
- Random Cropの代わりにFiveCrop*8が有効だった
- NN
- ImageNetでpretrainした重みを使用
- 最終的に採用したのは
- Inception-v3
- Inception-v4
- Xception
- モデルの変更点
- 最後のpooling層をglobal poolingに変更した→Inception-v4, Xceptionはもともとglobal average poolingになっているが、どういうニュアンスなんでしょうか?1st place solutionみたいに、global (average + max) pooling?
- pooling層とoutput層の間に、dense(128)を加えた
- 2-stage制にした
- まず512サイズで訓練
- その後650サイズ、800サイズで訓練
- 10-fold CV
- 損失関数
- MultiLabelSoftMarginLoss
- オプティマイザ
- SGD(lr=0.05)
- StepLR(gamma=0.1, step=6)
- 25epochs(650か800サイズは15epoch程度でearly stopping)
- lossが一番低いものを選択(F1scoreではない)
- sampling weightを設定。(レアなクラスほどよくサンプリングされるように)
- workしなかったもの
2-2-5. 7th place solution
- RGB入力
- 最後のpooling層を、concat([attention weighted average pooling*9, global max pooling, global std pooling])に変更
- augmentation
- contrast(各チャネル独立に)
- rotate
- scale
- shear
- shift
- 損失関数:BCE loss
- NN
- ResNet18(1024*1024)
- ResNet34(10241024, 768768)
- inceptionv3(768*768)
- trick
- テストデータ間で重複している思われるものは、重複しているデータ間で予測の平均を取るようにしたところ、スコアが0.04-0.05が向上した
2-2-6. 8th place solution
- NN
- SE-ResNext50(256, 512, 768)
- InceptionV4(512)
- BN-Inception(512)
- Xception(512)
- CyclicLR(最初のサイクルは長くして、逓減させる)
- Focal Loss, LSEP loss*10
- Augmentation:
- Brightness
- D4 and wrap transforms
- 32TTAの平均
3. 感想
- bestfitting氏、single modelで優勝スコアだわ、、実務への適用まで考えたソリューションを提案しているわでカッコ良すぎでした。
- 多くのsolutionは、最後のpooling層はいくつかのGlobalXXXPoolingをconcatしていて(論文では単純にGlobalAveragePoolingが多い)、これが主流なのかな〜と思いました。(確かに、MaxPoolingも与えてあげるとよさそう)
- externalデータはラベルの質がよくなかったようです。また、OfficialデータとExternalデータにいくつか重複があったり、分布が違ったり…というところは上位陣はしっかりおさえていました。ネットワークの調整だけでなくデータの質もしっかり見ることが大事ですね。
- 最後のほう、力尽きました。(ごめんなさい)
- solutionのまとめ記事書くだけでかなり勉強になりました。こうでもしないと流し読みして何も頭に入らないですね。
*1:https://www.kaggle.com/allunia/protein-atlas-exploration-and-baseline
*2:https://www.kaggle.com/c/human-protein-atlas-image-classification/discussion/67819
*3:https://arxiv.org/abs/1608.06993
*4:原文:I predicted the test set by using best focal loss epoch with 4 seeds to random crop 512x512 patches from 768x768 images, and got the max probs from the predictions.
*5:https://arxiv.org/pdf/1805.09501.pdf
*6:For the large model with 1024x1024 images, I used gradient accumulation so that weights are updated every 32 examples.
*7:https://openreview.net/pdf?id=ryl5khRcKm
*8:torchvision.transforms.FiveCrop(size) Crop the given PIL Image into four corners and the central crop
*9:3rdと同じ発想のよう。github.com
<実践>はじめてのKaggle体験記
機械学習を勉強し始めて1年が経ちました。
そろぞろ何か一つ目に見える実績を上げたいと思いたち、
5月から8月までHomeCreditというKaggleのコンペをやっていました。
Home Credit Default Risk | Kaggle
最終的に、7198人中62位で終わることができました!
Kaggleをやってみようかなと思っている人向けに、自分の体験記を残しておこうと思います。
わからない用語は、Kaggle Words For Beginnerを参考にしてください。
もくじ
- スペックと前準備
- コンペ内容の確認
- コンペ参加
- 感想
0. スペックと前準備
スペック
- 文系卒(数学は好き)
- SIerでCOBOLとjavaを5年→業務で機械学習1ヶ月(研修みたいなもの)
- 個人でちゃんと勉強した本(ちゃんと=写経した≠理解した)
- ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
- この本を読んでから、機械学習に興味を持ちました。(当時はDeepLearning最強、それ以外は雑魚だと思ってました・・・)
- [第2版]Python 機械学習プログラミング 達人データサイエンティストによる理論と実践 (impress top gear)
- scikit-learnの「分類」「回帰」「クラスタリング」あたりのライブラリの使い方と簡単な理論を勉強できました。
- ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
前準備
コンペを始める前に(一部は始めてから)、Kaggleのノウハウを探りました。
いろいろな記事を読み*1、以下が大事なのかなと思いました。
- 一番時間を費やすべきなのは特徴量*2作成。(特徴作成8:チューニング2くらい)
- Discussion/Kernelはvoteが最低限沢山付いているものだけでも読む。
- ドメイン知識(データ自身に対する専門知識)を入れる。
- 最後まであきらめない。
1. コンペ内容と基本的な内容の確認
Kaggleの公式ページ
詳しいコンペの内容は割愛しますが、データから「ユーザーが契約したローンを、遅延なく返せるかどうか」を当てるコンペでした。
すごくざっくり説明すると下の図のような感じです。赤枠を当てるコンペです。
Titanicのように1ファイルではなく、複数ファイル(テーブル)でデータが与えられています。
また、実際には、たくさんの特徴量があります。
1ユーザーに対して複数行の情報があるため、機械学習にかける前にデータの集計が必要でした。
EDA
Kaggleには各コンペごとにフォーラムがあり、コンペ参加者がコードを公開する場(Kernel)、議論する場(Discussion)があります。
その中でもまず見るべきなのがEDA(Exploratory Data Analysis)です。
EDAのKernelでは、投稿者が行ったデータ分析の内容を投稿してくれています。
いろいろな投稿者がEDAを投稿しており、人によってデータの分析の角度や可視化のやり方が違います。
これらの記事を見ることで、HomeCreditに留まらずデータ分析のハウツーや可視化の引き出しを増やすことができました(デキる人からすれば常識的なスキルかもしれませんが)
今回は幸運なことに、人気のEDAの日本語訳が投稿されており、この記事*3にはお世話になりました。
ただ、公開されているEDA以外にも、自分自身でEDAをやる必要があったなと反省しています。
2. コンペ参加
コンペの活動履歴を綴っていきます。
()内は、その時点のだいたいの最終順位です。
とにかく1サブミット - 3特徴量だけを使う (6600位 / 7200)
Kaggleに限らず新しいことを始めるにあたって一番しんどいのは、最初の一歩を踏むことだと思います。
完璧主義を目指して挫折するよりも、まずはどんな手法でもいいから、「1サブミット」をすることを目標にしました。
Kernelをざっと見ると、明らかに大事そうな特徴「EXT_SOURCE_1,2,3」があることに気づきました。
この3特徴量を抽出して、RandomForest(自分が最近よく使ってた手法)で分類するモデルを作りました。
1ファイル全部使う+LightGBMにチャレンジ (6100位 / 7200)
次は、
- 図の赤枠部分を使って予測すること
- LightGBMを使うこと にチャレンジしました。
LightGBMは使ったことがなく理論もわからなかったのですが、Kernelを見ながらプログラムを書いていきました。
パラメータもKernelのものを使いました。
LightGBMの場合、欠損値はそのまま処理してくれますし、カテゴリ変数も指定してあげればonehotencodeしなくてもそのまま処理してくれるので、とても便利です。
特徴量作成
特徴量作成は、思いついたものは片っ端から作って、最後に絞り込む方針でやりました。
特徴量作成① 全ファイルを機械的に集計 (1500位 / 7200)
次は、使っている全ファイルを機械的に集計することにしました。
LightGBM(に限らずほとんどの機械学習アルゴリズム)を使うとき、特徴量の数は固定でないといけません。
ユーザー1は購買履歴が3行、ユーザー2は購買履歴が10行・・・のように、各ユーザーごとにデータの特徴量が違うと都合が悪いので、図のような集計を全部の数値項目に対して行いました。
この発想は独自のものではなく、Kernelの丸パクリ*4です。
特徴量作成② 項目同士の突き合わせー数値編 ( - )
次は、項目同士の突き合わせです。
木のアルゴリズムはif文の集まりであるため、項目同士の関係を捉えにくいようです。
なので、元の特徴量をもとに新しく特徴量を生成しました。
例えば図にあるように、年収と子供の数の比を見れば、その家族が支払える金額の指標になりえます。
項目の意味を考えて、様々な特徴量を作成しました。
特徴量を作成するには、その業界特有の知識(=ドメイン知識)が必要とされます。
私はドメイン知識がなかったので、特徴量の重要度(LightGBMが出力してくれる)を参照し、重要な特徴量同士を四則演算して特徴量を作成しました。
これも、基本的な発想はKernelの丸パクリ*5で、そこから自分のアイデアを肉付けしていく形にしました。
特徴量作成③ 項目同士の突き合わせーカテゴリ編 ( - )
項目同士の突き合わせですが、カテゴリ変数は割り算ができません。
ので、以下2つのアプローチで行いました。
- Target Encoding
- ドメイン知識による数値変換
TargetEncodingとは、カテゴリ変数をTargetの平均で埋めることです。(今回であれば支払遅延有無:1を「あり」とする)
また、ドメイン(もしくは常識的な)知識によりカテゴリ変数を数値に変換しました。
これらの処理を行うことで、数値特徴量とカテゴリ特徴量の四則演算が可能になります。
特徴量作成④ 時系列アプローチ ( - )
また、時系列的な考え方も取り入れました。
普通に考えると、直近3ヶ月の履歴はそれより前の履歴よりその人の特性を表すと思ったからです。
例として、図のような特徴量を取り入れました。
また、過去30日金額÷過去90日金額など、直近の金額が過去と比べてどうであるか、のような特徴量も作りました。
このアプローチはこのdiscussion*6から自分で考えました。
特徴量選択 (600位 / 7200)
ここまでで作成した特徴量は、およそ1800個となりました。
「LightGBMは冗長な特徴量があっても大丈夫」という意見もありましたが、特徴量が多いとメモリが足りないという都合と、やっぱり特徴量絞ったほうがいい結果が出る実感があったので*7、特徴量を絞りました。
特徴量選択は、Kernelでも様々な手法が紹介されていましたが*8、自分が試したのは以下です。
結局、1で相関係数>0.95のものは片方削除→4で重要度ベスト400,500,600をそれぞれ選び、モデルに学習させました。
パラメタチューニング① hyperopt/BayesianOptimization ( - )
LightGBMのパラメータの意味がわからなくとも自動的にパラメータチューニングしてくれるすごいライブラリの使い方がKernelに公開されていたので、試しました。
ただ、これらを使うには調整するパラメータの範囲や種類を指定しなければなりません。
- 範囲や種類が多いほど時間がかかる。
- 特徴量の数によっても、最適なパラメタは違う(らしい)。
しばらく試していたのですが、上記のような理由もあり、やっぱりある程度LightGBM、GBDTの簡単な理屈を知ったうえで手でチューニングしたほうがいいかなと思い、使うのをやめました。
実際、過去の似たようなコンペの上位解法を見ると、「パラメタチューニングは勘で決めました」であったり、「我々が時間を割くべきはパラメタチューニングではない」といった記述もありました。
パラメタチューニング② LightGBMの勉強 (150位 / 7200)
パラメタの意味を知るため、簡単にLightGBMの勉強をしました。
LightGBMを知るためにはxgboostを知ってる必要があり、xgboostを知るには勾配ブースティング木(GBDT)を知る必要があり、GBDTを知るには決定木を知る必要があるということがわかりました。
参考になった資料のリンクを貼っておきます。(自分が読んだ順)
概要
NIPS2017読み会 LightGBM: A Highly Efficient Gradient Boosting Decision T…
Overview of tree algorithms from decision tree to xgboost
大事なパラメタとその意味を調査
Complete Guide to Parameter Tuning in Gradient Boosting (GBM) in Python
XGBoostやパラメータチューニングの仕方に関する調査 | かものはしの分析ブログ
Santander Product RecommendationのアプローチとXGBoostの小ネタ - Speaker Deck
XGBoostにDart boosterを追加しました - お勉強メモ
Laurae++: xgboost / LightGBM - Parameters
LightGBM/Parameters.rst at master · Microsoft/LightGBM · GitHub
論文
xgboost http://www.kdd.org/kdd2016/papers/files/rfp0697-chenAemb.pdf
lightgbm https://papers.nips.cc/paper/6907-lightgbm-a-highly-efficient-gradient-boosting-decision-tree.pdf
dart https://arxiv.org/pdf/1505.01866.pdf
論文全然わかんなかったので参照
Gradient Boosting と XGBoost | ZABURO app
過去コンペのパラメタ参照
Kaggle Past Competitions
Porto Seguroとか見ました。
結局
いろいろ見て、(結局半分も理解できませんでしたが)以下の方針を立てました。
- モデルをロバストにする
- colsample_bytree 0.05-0.2くらい -> 0.05がよかった
- lamdba_l1とl2をそれぞれ0.1,1,5,10,20で組み合わせて試す -> l1=1, l2=20がよかった
- subsample 0.9で決め打ち
- 木の形はわからんからとりあえずいろいろ試す
- max_depthは-1とsqrt(max_leaves) * 2、max_leavesは30〜130くらいを試した -> max_depth=-1, max_leaves=30がよかった
- gbdt, dart, gossはわからんからそれぞれ試した -> dartメイン、gbdtもちょっと使った
- learning_rateは0.0025〜0.05まで試した -> アンサンブル考えると(後述)、以下がよかった。
- gbdt: 0.0025
- dart: 0.01
※この方針はあまりよくなかった気もしています。実際、2位のikiri_DSのonoderaさんのgithub見てみると*13、colsample_bytree 0.9くらいなので・・・
アンサンブル/スタッキング① アンサンブル ( 62位 / 7200 )
最後にアンサンブルをしました。
「◯×クイズに60%正解する人が3人。3人の答えがそれぞれ独立だとすると、3人のうち多数決を取ったときのクイズの正解率は?」
答え:
3人とも正解:0.6 * 3 = 0.216、2人が正解: 3 * 0.6 ^ 2 * 0.4 = 0.432なので、0.216 + 0.432 = 0.648。
正解率は一人の時より上がります!!
つまり、いろんなモデルを作って多数決をさせることで、それぞれのモデルよりよい性能が出る可能性があります。
ただし重要なのは、「モデル同士の予想の相関が低いこと」です。
モデル同士の予想の相関を低くするために、以下を行いました。
- 異なるアルゴリズムを使う(Neural Network) -> 精度が全く出ず断念
- 異なるパラメータを使う -> LightGBMのdart,gbdtを組み合わせることで精度が良くなった。
- 異なるseedを使ってK-Foldする -> 単一モデルより精度が良くなった
- 低すぎるlearning_rateを使わない(単一モデルの精度はよいが、異なるseedを使っても多様性がなく、アンサンブルの効果が薄くなった)
相関と精度を見て、最終的に、dart(500特徴量)・dart(400特徴量)・gbdt(500特徴量)のアンサンブルを組みました。(これが最終順位62位のサブミットでした)
アンサンブル/スタッキング② スタッキング ( - )
「スタッキング」とは、「複数のモデルが出した予想値」を入力にしてもう一度学習させ、最終的な予想を行うことです。
スタッキングもやったのですが、アンサンブル以上に精度が出なかったので断念しました。
結果
LB: 498位→PB:62位
LBの順位は低かったですが、公開されているソースよりもCVが高かったので、Trust Your CV*14の精神で耐えました。
CV0.794->LB0.804のようなLBにOverfitしてると思われるものもありましたが、自分はCV0.8018 -> LB0.802でした。
3. 感想
とても勉強になりましたし、楽しかったです。
データの分析の仕方もLightGBMもわからない状態でスタートしましたが、ほとんどKernelに助けられながら、最後までできました。
実際、Kernelを全く見ないでやったら、半分より上にも行けなかったと思います。
scikit-learnあたりの使い方が少しわかってきたけど次何すればよいか・・・という人には、Kaggleを強くおすすめします!
また、コンペ参加中は、データ分析をみんなで一緒にやってる気持ちでした。
discussionを読みながら、時々参加したり。
リーダーボード(ランキング)をみて、「自分はこのスコアが限界だと思ってたけど、もっと伸びるのか」と、分析を投げずに最後までがんばれたり。
中だるみもありましたが、充実した3ヶ月でした。
自分自身が、Kaggleに参加する前とした後で変わったことは以下かなと思っています。
- 特徴量作成が超大事だと分かった。(データを入れたら答えが帰ってくるわけじゃない)
- 「テーブルデータの分類モデルの構築をどこから始めたらよいか」が分かった。
- 特徴量作成の定石を少し覚えた。
- LightGBMの分類が使えるようになった。(ある程度パラメータも理解できた)
- GCPの使い方がわかった*15
泥臭い部分も含めたノウハウを、少しだけ、Kaggleを通じて習得できたかなと思います!
超長くなってしまいましたが、最後まで読んで頂きありがとうございましたm(_ _)m
*1:全部は覚えていないのですが以下が参考になりました
www.rco.recruit.co.jp
blog.kaggle.com
*2:データ項目, feature
*3:HomeCreditRisk : Extensive EDA + Baseline Model JP | Kaggle
*4:https://www.kaggle.com/jsaguiar/updated-0-792-lb-lightgbm-with-simple-features/code
*5:https://www.kaggle.com/jsaguiar/updated-0-792-lb-lightgbm-with-simple-features/code
*6:https://www.kaggle.com/c/home-credit-default-risk/discussion/58332
*7:上位の方は特徴あまり絞ってない方もいたので、間違ってるかもしれません
*8:https://www.kaggle.com/sz8416/6-ways-for-feature-selection
*9:https://www.kaggle.com/ogrellier/feature-selection-with-null-importances
*10:https://github.com/slundberg/shap
*11:https://www.kaggle.com/ogrellier/home-credit-hyperopt-optimization
*12:https://www.kaggle.com/sz8416/simple-bayesian-optimization-for-lightgbm
*13:https://github.com/KazukiOnodera/Home-Credit-Default-Risk
*14:https://www.kaggle.com/c/home-credit-default-risk/discussion/58332
<メモ>tensorflowのimport時のエラー(libcublas.so.9.0が見つからない)
tensorflowのimport時にlibcublas.so.9.0: cannot open shared object file: No such file or directory
と出る時の対処法メモ
環境
- CUDA 9.0
- tensorflow 1.8.0
- Pycharm community 2018.1
状況
解決方法
Pycharmを開き、[Run] => [Edit Configurations] => Python-[自分が動かしてるプログラム]を選択
Environment variablesに、LD_LIBRARY_PATH: /usr/local/cuda-9.0/lib64:$LD_LIBRARY_PATHを追加
defaults→python→Environment variablesにLD_LIBRARY_PATHをセットしても動かなかったので、自分のプログラムにパスを通しました。
CUDAの環境まわりなどちゃんと入れてるのに何故かちゃんと動いてくれない・・・というときは、開発環境を疑ってみるとよいと思います。
参考記事:
github.com
<実践>HomeCredit作業記録(6) - 進捗とメモ
いいところまで来ました。
淡々と、定石通りにやってるだけのつもりなんですが・・・
ただ、手法のPrivate shareが禁止ということで、規約にひっかかりそうなので過去の記事を非公開にしました。
いま使っているLightGBMがブラックボックスすぎるので、そこの勉強をしたいです。
ここまで作業時間40時間くらい、思ったことややったことメモ
Kaggleだと限界がわかる
自分が1位じゃないかぎり、スコアの伸びしろがわかるっていうのは大きいですね。
実務だと一人でやるので、どこまでできるのかが不透明です。
しばらく誰も100m10秒切れなかったのに切ったとたん、他の人も10秒切れるようになった、みたいな感じですね
1位じゃないからまだまだやれるんだなというのがわかるのは大きいです。
Discussionが参考になる
今私は特徴量作成をやっているのですが、それすらも自動化する「Featuretools」というツールが出てるみたいですね。
Kaggleのディスカッションは以下です。
https://www.kaggle.com/willkoehrsen/intro-to-tuning-automated-feature-engineering
まだ使っていないのですが、いつかは使ってみたいです。
仕事でもプライベートでも作れるEDAツールにする
多分次の分析でもやるだろうな〜と思ったデータ加工のプログラムは、再利用可能+テストを書いて多少の品質を確保しています。
データサイエンティストの人を片っ端からフォローした
議論を追ったりするとめっちゃ勉強になります!
<調査>Kaggle過去コンペ上位入賞者のSolutionから学ぶ(1)
blog.kaggle.com
この記事に、「コンペに優勝するためには過去のコンペから学ぶことが大事だ」と書いてあったので、過去のコンペを調べることにしました。
過去のコンペのDiscussionを見ると、上位入賞者が解法を公開してくれています。
HomeCreditと同じ「分類」タスクのコンペを探しました。
以下、自分のメモ書きです。
Porto Seguro’s Safe Driver Prediction 1st solution
Porto Seguro’s Safe Driver Prediction | Kaggle
1ファイルのコンペ(主キー1に対して、レコードがN行みたいなことがない)
手法
- Representation learning
- DAE(Deep Auto Encoder)による特徴抽出。
- Feature Engineeringが嫌い!それを自動化してこそAIだ!ということだそうです。
- Normalizationには"RankGauss"を使った
- RankGauss http://fastml.com/preparing-continuous-features-for-neural-networks-with-rankgauss/
- min/max scalingやmean/stdでの正規化より良いらしいです。
あまり記事がないので、あとで調べます・・・
- min/max scalingやmean/stdでの正規化より良いらしいです。
- RankGauss http://fastml.com/preparing-continuous-features-for-neural-networks-with-rankgauss/
inputSwapNoize
GANはやってみたけど失敗
- 連続値とカテゴリ変数が混在する場合のGANは難しい
- 疑問:GANを何に使おうとしたのでしょうか?
NN5個とLGBM1個をstacking(重みはすべて同じ)
感想
- DAEによる特徴抽出が強い!今のコンペでもやってみたいです。ただ、主キー1に対してN行データがある場合は入力が動的になるから難しいですね。この問題が解決できたら、いよいよFeature Engineeringが要らなくなる?
- inputSwapNoiseの考え方が個人的にとても面白かったです。