たかいとの備忘録

自然言語処理や機械学習のことについて学んだことを忘れないように書いていけたらと思います.

atmaCup#8参加レポート

はじめに

2020年12月4日(金)〜12月13日(日)に開催されたatmaCup#8に参加してました.

atmaCup#5に参加し学びが多かったので,それ以降のatmaCup#6,atmaCup#7も参加したかったのですが,atmaCup#6は抽選落ち,atmaCup#7は社会人向けだったことや忙しかったこともあり参加できませんでした.

今回は初心者向けコンペとのことで気軽に参加しましたが,初心者向けとは?と思うくらいレベルが高く,とても勉強になりました.

博士論文の執筆に追われており,やったことや思ったことを全部書くのは難しいので,記事書くかどうかも迷ったのですが,簡潔にでもまとめておきたいなと思い記事作成に至りました.

使用した特徴量は,他の方のsolutionに記載されているので,それ以外の部分を中心にまとめることにしました.

開会式

開会式は12月4日の夕方でした.

コーヒーコンペと事前情報が書いてあったのですが,どんなタスクなのか気になっていましたが,フタを開けたらゲームの売上予測でした.

初心者向けコンペなので,タスクを簡単なものに変えたとのことでした.(コーヒーコンペ楽しみにしております.)

序盤

初日submitすることがひとつの目標でしたが,条件付き採録通った論文の回答書作成とかに追われていて,23時過ぎくらいからコンペスタート(汗)

とりあえず,テーブルデータのコンペとのことでlightGBM使って1st submit.

この時点での工夫としては,YouTube動画視聴回数予測のコンペと評価指標が一緒だったので,こちらのトピック参考にしてコードを作成しました.

RMSLE関係のディスカッション作ろうかなと思っていましたが,TawaraさんがRMSLE を最適化する小技というタイトルであげていたので断念.(速さが足りない)

次の日,チュートリアルの視聴と, takapyさんのカテゴリ値を持つカラムのベン図のディスカッションを読んで,trainとtestが'Publisher'によって分割されていることに気づくことができました.

この特徴から,CVのデータ分割を工夫して過学習防止&特徴量の選択に生かそうと,とりあえずtrainとvalidの分割をPublisherで行うことにしました.

ただ,分割によって学習が安定しないこともあり,いろいろ相談したいなと思い,Cross Validationについてのディスカッションを投稿しました.

さらに,次の日にチュートリアル2日目があり,後から視聴したのですが,簡潔にコメントをまとめると,

  • 'Publisher'でのGroup K Foldは,他のK Foldに比べ,validの予測が難しくなる.
  • 'Publisher'でのGroup K Foldにすると,分割によって結果のスコアが大きく異なる.
  • 分割により,学習が難しいものと,学習がそれなりにできているものがある場合,学習が難しいものは,ほとんど学習がまともにできていない可能性があり,結果の平均をとっていいか悩ましいところ.

みたいなコメントでした.

そこからしばらくは,完全ランダムにデータを分割する方法をしばらくは使用していました.

中盤

e-toppoさんやcha_kabuさんのディスカッションを参考にして,特徴量を増やしていきました.

それに伴い,CVはガンガン下がりましたが,LBとの乖離が目立ち始めました.

ここで改めて,CVのデータ分割を再検討しました.

多くのトピックでシリーズものの特定が,良好な結果につながるのではと考察されていました.

そこで自分は特定したシリーズで分割を行うことにしました.

シリーズの特定には,主に特徴量を作るためのものと,CVを決めるためのものの二種類を作成しました.

CVを決めるためのシリーズ特定方法は,'Name'同士のレーベンシュタイン距離を長さで0から1の範囲に正規化したものを使い,0.5を超えるものを同一シリーズとしてまとめていくことにしました.

事前学習済みモデルのembeddingを利用しなかったのは固有名詞が多いので,今回は古典的な手法を使用することにしました.

ただ,'Name'だけで距離を全組み合わせ計算するのは時間がかかることや,「スーパーマリオゴルフ」と「みんなのゴルフ」みたいなゲームが同じグループになったり,「スーパーマリオゴルフ」と「スーパーマリオ64」みたいなゲームはあえて違うシリーズを割り当てたいと思ったので,'Name'の距離を計算する対象は,'Publisher'と'Genre'を文字列連結して同一のもの同士のみでシリーズ特定を行いました.(これがいいかどうかはわかりませんが,結果として良好な結果でした)

この特定したシリーズをもとにデータを分割することで,CVが一気に安定しました.

CV:0.796, LB:0.8828 → CV:0.871, LB:0.8823

特に,lightGBMでは,LBに差異が出なかったのですが,CatBoostはカテゴリカル変数をTarget Encodingしていることもあり,ランダムにデータを分割するものよりもLBが高くなる傾向がありました.

個人的に今回の最終順位は,CV戦略によるtrainへの過学習防止と無駄なsubmitを控えることができたことが大きく貢献しています.

終盤

上位の圧倒的なスコアとデータの特徴からリークには少し気が付いていましたが,時間がなかったので愚直にできることをやることにしました.

後半はコードが複雑になってしまい,自身でも何の特徴量を作成しているかわからなくなりつつあったため,新たな特徴量作成は断念し,特徴量をCVとLBを参考によかったものを全ツッコミ.(正直他の人がsolutionに書いている画期的な特徴量は自身ではひとつも作れず…)

LightGBMとCatBoostの結果がそれなりに良かったので,終わりにしようと思いましたが,submitが20近く余っていたので,時間かけずにできることをすることに.

結果のアンサンブルも考えたのですが,lightGBMとCatBoostの結果が両方とも良好だが,微妙に異なっていたので,CatBoostのtestデータの出力を疑似的な目的変数として,LightGBMでtrainとtestを7:3くらいで重み付けしてモデルを学習させたところLBが気持ち上がり,このlightGBMのtestデータの出力を再び疑似的な目的変数として,CatBoostを学習させることで,気持ちスコアが上がりました.

あとはもともとのLightGBMとCatBoostの出力と,testデータ混ぜて学習させたlightGBMとCatBoostの出力を平均した結果をsubmitしたところスコアがさらに良くなりました.

ダメ押しで,適当に作ったNNモデルに上記のsubmitでtestデータに疑似的な目的変数を追加したものを加えて学習させてみました.

ここで,10fold分学習させるはずが,時間が足りず...

時間内に学習させることができた5つのNNモデルで,目的変数を予測し,submitしたところあまり良いスコアではなかったのですが,アンサンブルは多様性が大事かなと思って,この結果も最後はアンサンブルに追加し,submitすることにしました.

結果

最後にLBは上がっていたものの,test全体が良くなっている保証もなかったので,最終submitのひとつは保険をかけて,CVとLBのよかったCatBoostのシングルモデルを選択.

もうひとつのsubmitは,もともとのLightGBMとCatBoostの出力と,testデータ混ぜて学習させたlightGBMとCatBoostの出力を平均した結果に,気持ち上記のNNの結果を混ぜたもの(0.9:0.1)を選びました.

その結果…

敢闘賞10位,全体12位でした!

感想

正直,ディスカッションに助けられた感じが強く,特徴量作成能力が根本的に足りていないなと感じました.(ディスカッションなかったら0.87切れていないかも…)

ただ,コンペを通してどういう風に特徴量を作っていけばいいのかを,今回のコンペでは参加しながら実際に経験することができたので,今後のコンペに生かしていきたいなと思います.

自分自身の取組みとして,CVに工夫ができた初めてのコンペでした.

最初から良いスコアが出せてないのもありますが,41回のsubmit中,16回も緑の画面(LBスコア更新時に見れる画面)が見れたのは嬉しかったです.

データ解析コンペに参加し始めて,もう数ヵ月で一年になりますが,少しずつ結果につながり始めているので,今後も積極的に参加していけたらと思っています.

次回のatmaCupも楽しみにしています!

コンペの開催ありがとうございました.