Google QUESTコンペ振り返り

kaggleのGoogle QUEST Q&A Labelingコンペに参戦したので、振り返ります。

www.kaggle.com

はじめに

結果から言うと、個人的に目標にしていた銀メダルを獲得することができました。

コードの一部をGitHubに上げています。

github.com

コンペの概要

f:id:NmaViv:20200213214138p:plain

コンペ概要

質問タイトル・質問文・回答文から30種類のTARGETを予測するコンペで、TARGETには、例えば「その質問が他人の興味を引くものか」や「回答が質問に対して満足なものか」などがありました。

評価指標はmean column-wise Spearman's correlation coefficient(各TARGETの順位相関係数の平均)でした。

今回のコンペでやりたかったこと

  • オンプレ初陣
  • BERTに慣れる
  • データ量が少なく比較的実験の回しやすいコンペなので、Jigsawコンペの解法で気になっていたBERT+LGBを試す
  • 銀メダルを取る

解法に加えて、これらの結果についても振り返っていきます。

オンプレ初陣

オンプレ、kaggleのGPUと比較しても、とても高速で快適でした。

Google Colaboratoryも使おうとしていたのですが、途中で落ちたりしたので結局モデル訓練はオンプレで回して、kaggleのGPUは推論カーネルを作成するためのGPU Quota Limitを確保しておきたかったので、訓練には使いませんでした。

オンプレ、これからも積極的に使っていきたいです。

BERTに慣れる

こちらのPyTorch BERTのカーネルをベースラインに進めていきました。

(TFのやつは再現性取れないのが嫌なので...) 

www.kaggle.com

このカーネルモデリングではスコアが他のカーネルに比べてまだ改善の余地があったので、他のカーネルを参考にしつつ、最終的には以下のようなモデリングになりました。

 

f:id:NmaViv:20200213225925p:plain

モデリング

モデルはBert-base-uncasedとBert-base-casedとBert-large-uncasedを回してみましたが、Bert-largeはパラメータの調整が難しすぎてまともな精度が出ず(ここらへんのお気持ちを知りたい)、また自身が終盤から参加したため実験を回す時間も確保したかったので、Bert-largeは捨てる方針で進めていきました。

結果的に、Bert-base-uncasedのモデルを5種類、Bert-base-casedのモデルを1種類、そして後述するBert-base-uncased+LGBのモデルを1種類作成してアンサンブルしました。

Bert-base-uncasedのモデル5種類の内訳としては

  1. [Q_title+Q_body]と[Answer]をinputとしたもの
  2. [Q_title+Q_body]と[Q_title+Answer]をinputとしたもの
  3. 2からMAX_LENを小さくしたもの
  4. 2からTARGETをランク化してそのmaxで割った値に置き換えたもの
  5. 2からいくつかのTARGETをnp.log1pしたもの

みたいな感じです(head_tailの割合とdrop_rateもちょっといじったりした)。時間も少なかったので、実験的に回したものを混ぜる感じになりました。

Bert-base-casedは1と同じ条件で回したものだったはずです。

コンペ参加前に比べると(取っ付きやすさという観点において)Bertに少しは慣れた気がしますが、上位の方のsolutionを見ると、Bertのこと何もわかっていないことを実感させられるので、上位の方のsolutionはちゃんと確認して次回のコンペに活かしたいです。

BERT+LGB

過去コンペの解法を活かすというところでは、Jigsawコンペの解法で見たBERT+LGBを試すことができたのはとてもよかったです。

www.kaggle.com

MultilabelStratifiedKFoldでのCVの比較になりますが(なぜGroupKFoldにしなかったのかはこの際置いておいて)、ClassifierをLGBにする前と後で、CV0.39754 → CV0.40056と向上し、単純なアンサンブルでは、Bert+LGBのモデルが加わることでCV0.41593 → CV0.41848と、単純な差分をとったBertを混ぜるよりも伸び幅が大きかったです。

後処理

最初はQWKコンペでよく使われるOptimizedRounderを使っていましたが、最終的には小さい値(または大きい値)からどこで閾値を設定すればスコアが最大になるか探索して、それを繰り返すような形で閾値を求めていきました。閾値ごとのサンプル数とかも見ないとCVにフィットしすぎた閾値になっていた気もしましたが、結局そこはそんなに詰めませんでした。

試したけど上手くいかなかったこと

  • Focal loss
  • Bert-large
  • 特定TARGETに対してunder-samplingして学習

終わりに

短い期間でのチャレンジでしたが目標としていた銀メダルに届いたのと、過去コンペの気になっていた解法を試すことができたのでよかったです。

kaggleランクも自己ベスト更新してました(嬉しい)。

f:id:NmaViv:20200214001458p:plain