Kaggle Google QUEST Q&A コンペ 振り返り

はじめに

Kaggleで開催されていた Google QUEST Q&A Labeling Competition 、通称 QUEST コンペ、QA コンペに参加したので、コンペの概要を記載します。また、このコンペで、 78位 / 1579チーム中でギリギリ銀メダルを獲得できたので、取り組んだことを記載します。


f:id:YukoIshizaki:20200202165548p:plain

コンペの概要

英文による質問と回答のペアが与えられており、そのペアに対する30項目における評価値 (  \in [0,1] )を予測します。
質問タイトルや質問者・回答者の名前、サイトURLやカテゴリーもデータとして提供されていました。

この30項目はとても主観的な内容であり、コンピュータでは評価が困難なQAに対する主観的評価を行うことが今回のコンペの意義のようです。

url http://stats.stackexchange.com/questions/125/what-is-the-best-introductory-bayesian-statistics-textbook
カテゴリ SCIENCE
質問者 Shane
回答者 gappy
質問 タイトル What is the best introductory Bayesian statistics textbook?
質問 本文 Which is the best introductory textbook for Bayesian statistics? One book per answer, please.
回答 "Bayesian Core: A Practical Approach to Computational Bayesian Statistics" by Marin and Robert, Springer-Verlag (2007)
評価項目 1
質問の意図が理解できる
 1.0
 \vdots   \vdots
評価項目 30
回答が上手く書かれてる
 1.0

Notebook Competition

QUEST コンペは Kaggle Notebook のみのコンペでした。予測結果を submission.csv というファイルで出力するようなコードを Notebook に記載して提出します。トレーニング済みのモデルをデータセットとしてアップロードして使うことが許可されていたため、実質は推論のみを Kaggle Notebook 上で実行すればOKでした。

また、Internet は Off でないといけないため、外からデータをダウンロードすることはできませんでした。
他のNotebook コンペと同様、GPUの場合は2時間、CPUの場合は9時間の時間制限があります。

しかし、時間制限よりもソロ参加者にとっては、Private Datasetの容量制限が 20GBというのがちょっと苦しかったです。

評価関数

このコンペの評価関数はスピアマン順位相関係数でした。
正解データと予測値のランクの類似度を表した値になります。

 r_s = 1 - \dfrac{6\displaystyle \sum_{i = 1}^n {d_i}^2}{n(n^2 - 1)}

 d は正解データと予測データのランキングの差になります。 (あるデータで、正解が10位、予測が9位なら  d は 1)

Private Dataset

テストデータの 13%が Public Leaderboard に反映されます。コンペ中に与えられる test.csvはその13%のデータで、実際の Private Leaderboard の値は、Kaggle Notebook の test.csv が別のものに差し代わって残りの87%のデータで再度実行されて出力した submission.csv の結果になります。

データの特徴

Training データの数は6079, PublicになってるTest データの数は476です。
Training データと Test データのデータ提供元 (host名) の割合です。
両方とも20%強が stack overflowからで、それ以外はどれも5%未満です。

f:id:YukoIshizaki:20200202221052p:plain:w400f:id:YukoIshizaki:20200202221236p:plain:w400

カテゴリの割合は Training データと Test データはほぼ同じです。

f:id:YukoIshizaki:20200202221539p:plain:w400f:id:YukoIshizaki:20200202221543p:plain:w400

また、正解データを見ると離散値であることが確認できます。
(30項目中10個をピックアップ)
f:id:YukoIshizaki:20200202222340p:plain

Start From Here : QUEST Complete EDA + FE ✓✓ | Kaggle

正解データが離散値であり、ほとんどが循環小数であることから何人かのアノテータが各項目を0, 1で評価し、その平均値を正解データとしたようです。また、その循環小数を見ることで何人のアノテータがいたかが、おおよそわかります。

取り組んだこと

モデル

BERT, XL-Net, RoBERTa の平均です。
Transformers の BertForSequenceClassification はヘッドが以下のようになっているので、config の num_labels にクラス数 ( 評価項目の数 ) を指定して、事前学習済みモデルを Fine-tuning して利用しました。
( XL-Net, RoBERTaも同じ)

class BertForSequenceClassification(BertPreTrainedModel):
    def __init__(self, config):
        super().__init__(config)
        self.num_labels = config.num_labels
        self.bert = BertModel(config)
        self.dropout = nn.Dropout(config.hidden_dropout_prob)
        self.classifier = nn.Linear(config.hidden_size, self.config.num_labels)
        self.init_weights()

transformers/modeling_bert.py at master · huggingface/transformers · GitHub

モデルを上手に改良してスコアを上げられたら良かったのですが、色々試みたものの全く良くなりませんでした。

評価項目ごとにモデルを作成

評価項目を以下の3つに主観で分類しました。

  • 1. 質問文のみで評価できる (例えば「質問の意図が理解できる」)
  • 2. 回答文のみで評価できる (例えば「回答が構造的に書かれているか」)
  • 3. 質問文と回答文揃ってないと評価ができない(例えば「回答が尤もらしいか」)

また、ぞれぞれ以下のように学習しました。

  • 1. に分類される評価項目は、インプットを質問文と質問タイトルを結合したもので学習
  • 2. に分類される評価項目は、インプットを回答文のみで学習
  • 3. に分類される評価項目は、インプットを質問文と質問タイトルと回答文を結合したもので学習

前処理

基本的にTransformersのTokenizerがよしなにやってくれるので、stop word や省略形などの対応はしませんでした。
BERTを使う上で必要な special token に変換される、[CLS]や[SEP]などを結合することや attention mask を作成することなどはしました。

後処理

データの特徴で記載したとおり、正解データが離散値で評価関数がスピアマン順位相関係数であることから出力された値を、クラスタリングしてまとめて同じ値に変換する後処理をしました。

例えば、[question_type_instructions] という評価項目なら、ヒストグラムを描くとこのような予測値なのですが

f:id:YukoIshizaki:20200210010548p:plain:w300

以下のように近い値を同じ値に変換するということです。

f:id:YukoIshizaki:20200210010533p:plain:w300

クラスタリングは scikit-learn の BayesianGaussianMixture を使いました。

from sklearn.mixture import BayesianGaussianMixture

dp = BayesianGaussianMixture(18)
pred = dp.fit_predict(sub[col].values.reshape(-1, 1))

CV結果をみて、実際に変換する評価項目を以下の 3 つにしました。

  • question_has_commonly_accepted_answer
  • question_type_consequence
  • answer_plausible

また、Training データからアノテーターの人数を推測し、想定される離散値に予測値を寄せるという対応も行いました。離散値どうしの中間の値を閾値にして、どちらに近いかで寄せる値を決めました。

CV結果をみて、実際に変換する評価項目を以下の 4 つにしました。

  • question_conversational
  • question_type_compare
  • question_type_definition
  • question_type_entity

学習時のパラメータ

  • loss: BCEWithLogitsLoss
  • optimizer: AdamW
  • lr: 3e-5
  • Batch Size: 8
  • 5-fold

うまくいかなかったこと

  • model: ALBELT, GTP, GTP2, XLM, Distilbert
  • loss: MSE+BCE
  • Optimizer: AdaMod, BertAdam
  • Tokenの追加 (stackoverflowのdatasetから最頻名詞100個ほど)
  • pseudo label
  • カテゴリと host名 を one-hot で BERT の出力に結合して MLP で学習
  • その他、BERTモデルのHeadを公開 kernel を参考に色々変えてみたモデル

その他

BERTとNLPライブラリのTransformersの使い方を学びました。
( BERTは完全理解というより、お気持ち理解程度です。)

Transformerの論文 : [1706.03762] Attention Is All You Need
BERTの論文 : [1810.04805] BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
Transformersのライブラリ: Transformers — transformers 2.4.1 documentation
まつけん (@Kenmatsu4) | Twitterさんの資料 : BERT入門
Ryobotさんのブログ : 論文解説 Attention Is All You Need (Transformer) - ディープラーニングブログ

おわり

初めてのNLPコンペでしたが、モデリング部分はほとんど工夫をすることが出来ず、NLP独自のテクニックなどは全く使いませんでした。上位陣の解法から学んでいきたいです。