statsuのblog

愛知のデータサイエンティスト。自分の活動記録。主に機械学習やその周辺に技術について学んだことを記録していく予定。

Kaggle Google QUEST Q&Aコンペに参加した記録

Kaggle Google QUEST Q&Aコンペに参加して61位(1571チーム中)でソロ銀メダルを取れました。
以下ではその記録についてまとめます。

本記事の概要

  • コンペ概要
  • 私の取り組み
  • 興味深かった上位ソリューション
  • 感想

コンペ概要

f:id:st1990:20200212230955p:plain
Google QUEST Q&A Labeling | Kaggle

  • タスク概要

    • QAサイトの質問・回答に対して人が主観的にどう感じたかを予測するタスク。後述の入力データから予測対象データを予測する。
  • データ

    • 入力データ:stackoverflowなどのQAサイトの質問タイトル、質問文、回答文、カテゴリ、等
    • 予測対象データ:質問・回答に関する人の主観的な30個の項目について、0~1の得点が与えられる。例えば、answer_helpfulという項目は回答がhelpfulであるかどうかを表しており、得点0に近ければhelpfulではなく、1に近ければhelpfulである。得点はアノテータが付けたものらしい。
  • 予測性能の評価方法

    • 各予測対象項目の得点について、全サンプルで正解と予測のスピアマン順位相関係数を算出する。これを全項目で平均したものが評価指標。
    • 評価指標がMSEではなく順位相関係数なので、予測値の大小関係さえ正確に予測できれば良い。
  • スタンダードな解法

    • 予測モデル:Deep learningベースの自然言語処理モデル(Bertなど)を学習させ、入力(質問タイトル、質問文、回答文)から30項目の予測対象を回帰で推定。
    • 後処理:評価指標値を上げるために、予測モデルの出力を離散値とするような後処理を実施。正解値は0~1の範囲の離散値(例:0, 0.333, 0.666, ...,1.0)をとっていたため、予測値も離散値とすることでスピアマン順位相関係数が高くなる。

こちらのブログでわかりやすくまとめてありました。
Kaggle Google QUEST Q&A コンペ 振り返り - 機械学習 Memo φ(・ω・ )

私の解法

私の解法は以下のとおりです。流れは上記のスタンダードな解法と同じです。

  • コード
    github.com

  • 概要

    • Bert baseのpretrainedモデルを学習させ、入力(質問タイトル、質問文、回答文)から予測対象を回帰で予測。
    • Bert baseのpretrainedモデルを学習させ、入力(質問タイトル、質問文、回答文)から予測対象をクラス分類で予測。
    • 後処理は、予測値に対してある閾値以下を0、ある閾値以上を1にし、その間は何も処理しないというもの。
  • 使ったライブラリ

    • pytorch:kerasから乗り換えました。モデル構造や学習スキームをいじりたい凝り性の人にはpytorchおすすめ!自由度高くて使いやすい。あとgradient accumulate使いやすいのほんと良い。
    • hugging face:DL系の自然言語処理ライブラリ。初めて使ったけど、かなり使いやすい。
  • モデル構造

    • 下図の2つのモデルを使いました。Model1は回帰、Model2は回帰版とクラス分類版の2つを作りました。最終的な予測値には、基本的にはModel1とModel2の回帰版の平均を使い、一部の予測項目だけはModel2のクラス分類版を使いました。

f:id:st1990:20200213002707p:plain
モデル構造

  • データ前処理

    • 入力:特に特別な処理をしていない。
    • 出力:予測対象の値の偏りが大きかったので、ランク値にして最大ランクで割ることでスケーリングした。
  • モデルの学習

    • 3epoch(最初の1epochはwarmup)。
    • 学習率は1e-4。Bertは5e-5~2e-5が推奨されているらしいですが、1e-4でうまくいった。
    • 損失関数は、回帰ではBinary cross entropy、分類ではcategorical cross entropy。label smoothingも使った。
  • 後処理

    • 予測値に対してある閾値以下を0、ある閾値以上を1にし、その間は何も処理しないというもの。CVスコアを最大にするような閾値を探索して閾値を決定した。
  • うまくいかなかったこと

    • pair wiseのランク学習やってみたけど効果なかった。list wiseのランク学習であるListNetをやりたかったけど、メモリが足りなくて断念。バッチサイズをある程度大きくとれないと厳しい。ただ、ランク学習自体は面白い分野だと思うので今後活用していきたい。ランク学習に関して知りたい人は以下のサイトがおすすめ。かなり参考になりました。
      ランク学習(Learning to Rank) Advent Calendar 2018 - Adventar
    • 初手でAlbertを試したけどまったく精度でなかった。精度出なさ過ぎて自分のコードのどこかにバグがあるのか探し回ってしまったぐらい精度が出なかった。
    • Stochastic Weight Averagingは効果がなかった。でもいつか使いどころが出てくる気がする。

興味深かった上位のソリューション

  • 入力の工夫

    • Bertの入力にスペシャトークン[category]を追加して、[CLS]+[category]+question title+...としていた。Bertの出力にカテゴリ等の情報をくっつけても精度が上がらなかったという声が多かったが、これはうまくいったらしい。最初の入力に入れ込むことで、うまいこと情報を吸い上げてくれるんかな。
  • モデル構造の工夫

    • 最期の全結合層の入力として、Bertの最終層の出力だけでなく、中間層の出力も使っていた。具体的には、中間層Aの出力([CLS])、中間層Bの出力([CLS])、…、最終層([CLS])の出力を重み付き平均し、それを全結合層の入力としていた。重み付き平均の重みは学習パラメータとし、合計が1になるようにしていた。この発想めっちゃよさげ。どこかで使いたい。
    • 全結合層を2つ重ねていた。僕はBertは全結合層1つでいいものだという思い込みがあって、そこの工夫をまったくしていなかった。。
  • 学習の工夫

    • Bertの部分と全結合層で学習率を変えていた。

感想

  • 過去2回僅差で銅メダルを逃していたので、やっとメダルとれて純粋にうれしかった。
  • 自然言語処理を今まで敬遠してたけど、初めて真剣に取り組みました。やってみるとかなり面白いし、応用範囲も広そうだから身に着けていきたい。hugging faceには日本語版Bertもあるのでなんかやってみたいな。
  • pytorchは自由度が高くてかなり良い。構造や学習の工夫を柔軟に実装できる。もっと早くkerasから乗り換えればよかった。
  • ランク学習やってみたい。論文を漁ってたら、スピアマン順位相関係数を直接最適化できそうな手法があったりして面白そう。