先日、Riiid! Answer Correctness Predictionが終了し、結果発表がありました。

  • 順位:115th / 3395teams
  • スコア: Public:0.795 Private:0.795
  • メダル: ソロシルバー
  • 使用モデル: LightGBM + SAKT

また、本コンペでシルバーメダルを獲得したことでKaggle Masterになることができました。OSICコンペの諸々が落ち着いた頃、MoAとRiiidでシルバーを取りMasterになってやろうと意気込んでおり、無事達成することができたので良かったです。

1.Riiidコンペとは

開催概要

開催期間:2020/10/06~2021/01/08
形式:Code Competition
データ:テーブル
課題:TOEICアプリにおける各ユーザーのログが、時系列データとして与えられており、各問題に対する正解・不正解を予測する二値分類

所感

 最終的には3395チームが参加し、かなり盛り上がったコンペでした。テーブルコンペとはいえ、与えられた学習データが約1億行あり、テストデータについても約250万行と膨大な量のデータを処理する必要がありました。そのため、単に精度を上げるだけでなく、メモリ使用量や処理の軽量化といったソフトウェア・エンジニアリング的な側面での知識も問われるため、ハードな反面非常に学びのある楽しいコンペでもありました。また、独自のAPIを用いて推論結果を提出する必要があり、より実務に近い条件となるよう丁寧に作り込まれたコンペ設計でした。年末にPrivate Scoreが閲覧できるという致命的なKaggleのバグが発見されるというハプニングがありましたが、幸いにもデータ数の多さやコンペの形式からして大きなShakeは予想されないコンペであったため、事なきを得ました。私自身、これまで参加したコンペの中で最も楽しく、かつ学びのあるコンペであったように思います。

2.自身の取り組み

 各所で上位解法が閲覧できるため、そちらを閲覧していただいた方がためにはなるかと思いますが、ログとして自身の取り組みについても述べておきます。

使用モデル

 使用モデルについては冒頭でも述べたとおり、LightGBM(以下LGBM)とSAKTのアンサンブルです。より上位を目指すならSAKTやSAINTといったNN(Transformer)の改良がMUSTと感じましたが、今回はMasterになるべく確実にシルバーを取るために、自分にとってはより安牌なLGBMの育成に注力しました。最終的には3Folds分のSAKTとLGBMをアンサンブルしたものをFinal Submissionとしました。Private/Public Scoreはいずれも以下の通り。

LBGM: 0.790
SAKT: 0.777
LGBM + SAKT × 3: 0.795

効果のあった特徴量

LGBMについては31種類の特徴量を、SAKTについては['user_id', 'content_id', 'part']の3種類の特徴量を用いました。LGBMに用いた特徴量の中で効果のあったものをいくつかピックアップします。

  • ユーザーのインタラクション回数

  • bundle_idの正答率

  • ユーザーのパート別回答数、正解数

  • ユーザーのリスニング・リーディングの回答数

  • ユーザーのtagの正答率の最大値、最小値

  • timestampに関するラグ特徴量(正解・不正解・インタラクション × 3)

  • ユーザーのパート別lecture受講回数

正直、塵も積もれば山となる的な感じだったので、劇的に効いた特徴量があったわけでもなく、一つ一つが割りとオーソドックスなものばかりでした。lecture関係の特徴量は微妙に精度に貢献することもありましたが、上記のものも含め処理の問題で結局省きました。

やってよかったこと

特徴量そのもの以外でスコア向上に役立ったことについてもいくつか紹介します。

  • 回答結果が関連する特徴量の更新をtask_container_id単位で行う

  • 特徴量のpkl化

  • GCPを用いたフルデータ学習

  • num_leavesやlearning_rateの調整

 task_container_id単位での特徴量更新については、推論パートでユーザーの回答結果が得られるのは各バッチの処理が完了した後であるため、学習時にも同様の形で処理を行わなければリークになってしまうという事情によるものです。このリークがもたらす影響はモデルや特徴量によりけりで、実装も若干面倒であるため、費用対効果を考えた上で人によって実装したり、しなかったりだったかと思います。この改善について、完全に同じ条件や特徴量での比較は行っていませんが、似たような条件のsubmissionについて、実装前後で0.002ほどスコアが違いました。また、これを実装しないと、本来効くはずの特徴量(自分の場合はbundle_idの正答率等)が効かなかったりしたため、間接的にも精度向上に貢献しました。

 特徴量のpkl化は多くの人がやってたとは思いますが、これをしておかないとメモリの関係上、後述のクラウドで学習させたモデルと特徴量を用いて推論を行うことができません。また、ユーザーと問題の2つで決まる特徴量については、defaultdictのネスト構造を取るとpkl化する際に異常にメモリを食らう問題があり、極力リストやnumpyで持ちたいという気持ちがありました。しかしながら、user_idについては学習データに現れない新規のものがテストデータに含まれるため、content_idやtag等をindexとしたリストにdefaultdictを持たせ、強引にpkl化しました。ただ、テストデータの数からuser_idの上限を決め打ちして、多めにnumpy配列を確保すれば良かったのではないかとも思います。自分のやりたいことに対して、メモリが深刻な問題になることがなく、この辺の軽量化について詰めることはしませんでした。

 フルデータ学習についてはGoogle Cloud Platformの初回登録時に受け取ることができる無料クレジット300ドル分を活用して(150ドルほど余りましたが)、潤沢なメモリのもと学習を行うことができました。今回で絶対にMasterになってやるぞという思いで、コンペ終盤からクラウドを使い始め、「Kaggle Notebooks上で少量データ学習→CVとLBが良好ならばGCP上でフルデータ学習」といったサイクルを回しました。これがなかったらシルバーに届いてなかった気もしますし、逆に言えば少量データでも高い精度を誇る特徴量やモデルの作成ができるようになるべきですね。

 ハイパラチューニングについてはそこまで大きな効果があるわけではないのですが、最終モデル用のLGBMについてはlearning_rateを小さく取る等、最低限のことはしておきました。

効果の無かった特徴量や手法

 以下の特徴量や手法は試したもののうまくいかなかったものの一部です。

  • SAKTにおける['user_id', 'content_id', 'part']以外の特徴量

  • tagやlecture関係のほとんどの特徴量

  • CatBoost, XGBoost

SAKTについては正直ほとんど手を付けておらず、もう少しちゃんと取り組めば上記以外の特徴量も効かせることができたとは思います。CatBoostやXGBoostはLightGBMほどの精度が出せず、アンサンブルしても効果がなかったため、除外しました。tagやlecture関係の特徴量もほとんどうまくいきませんでしたね。特にlectureはうまくいかないねみたいな話がDiscussionでもあがってたと思います。

3.まとめ

 というわけで色々と手を動かしてLGBMを育て、なんとかシルバーメダルをもらうことができました。思いついたものはめんどくさくても全部やるという精神で、年末年始も時間のある限りKaggleに取り組み、なんとかMasterになることができたので良かったです。また、コンペそのものも今まで参加したものの中で最も楽しむことができ、これからコンペ後に公開されている各参加者のsolutionを読み、勉強するのが非常に楽しみです。自分がメインで進めていたGBDT系だけでなく、NN系の解法もしっかりと学ぶことで、次回以降のコンペで活かせるようにしておきたいですね。ちなみにKaggle参加からMasterになるまでの振り返り記事についても後日投稿する予定です。お疲れさまでした。