2021年のKaggle NLPコンペソリューションの共通戦略から学ぶ

# この記事について

この記事は [Kaggleアドベントカレンダー](https://qiita.com/advent-calendar/2021/kaggle) の21日目の記事です。
昨日は u++ さんの[【Weekly Kaggle News 2周年】クリック記事ランキング2021](https://upura.hatenablog.com/entry/weekly-kaggle-news-2nd) でした。Transformer 強し。明日は @wokassis さんの[ワナビーからKagglerに〜コンペ所感〜](https://qiita.com/wokassis/items/b4abf896acc4e5a3dcc0)です。

本記事では、2021年の NLP コンペの上位ソリューションから共通して使われている手法や方針を紹介します。
**新しくNLPコンペに取り組む際に、基本的なことは試して次に何をすればよいかわからない、といった初学者向けに初期方針として参考になるかと思います。**ある程度コンペに参加している人には「当たり前やろ〜」といった内容かもしれません。

# 2021年 NLP コンペ概要

2021年に終了したコンペは [Coleridge Initiative - Show US the Data](https://www.kaggle.com/c/coleridgeinitiative-show-us-the-data), [CommonLit Readability Prize](https://www.kaggle.com/c/commonlitreadabilityprize), [chaii - Hindi and Tamil Question Answering](https://www.kaggle.com/c/chaii-hindi-and-tamil-question-answering) の3つがあります。
（全部 `c` から始まる...!）

現在開催中のコンペが2つ ([jigsaw-toxic-severity-rating](https://www.kaggle.com/c/jigsaw-toxic-severity-rating), [feedback-prize-2021](https://www.kaggle.com/c/feedback-prize-2021)) ありますが、こちらは終了はしてないので今回は対象外です（気が向いたら追記するかも...）。
それぞれ箇条書きレベルで概要を載せておきます。

- [Coleridge Initiative - Show US the Data](https://www.kaggle.com/c/coleridgeinitiative-show-us-the-data)（以下、Coleridge）
    - 科学論文内で参照されているデータセット名を抽出するコンペ。
    - データ数は train 14316件, test ~8000件の論文で、metric は Jaccard-based micro-F0.5 score.
    - NER が主流になるかと思いきや訓練データ内のラベルや外部データを用いた string matching や、正規表現によるアプローチなど機械学習モデル構築以外の方法でのスコア改善が目立った。
- [CommonLit Readability Prize](https://www.kaggle.com/c/commonlitreadabilityprize)（以下、CommonLit）
    - 与えられたテキストの「読みやすさ」を連続値で推定するコンペ。データは wikipedia や初等教育向けの教科書のテキストなどが使われている。
    - データ数は train 2834件、test は ~2000件で、metric は RMSE。
    - 入力文から回帰というシンプルな問題設定だったため最終 3,633 teams と多くの Kaggler が参加した。
- [chaii - Hindi and Tamil Question Answering](https://www.kaggle.com/c/chaii-hindi-and-tamil-question-answering)（以下、chaii）
    - ヒンディー語とタミル語による質問応答タスクのコンペ。英語ではなく、かつ2つの言語のデータを含んでいる。
    - データ数は train 1114件, test もだいたい同等とのこと。訓練データの言語は hindi:tamil = 6:4. metric は単語レベルの Jaccard Score.
    - 言語的な課題以外では、訓練データが少量かつラベルがノイジーである一方、テストデータのラベルはアノテーション方法の違いからある程度質が保証されているという特徴があった。


# 前提: NLP コンペの現状の取り組み方

Deep Learning モデルを活用できるコンペの場合、BERT をはじめとした多数の事前学習言語モデルが簡単に使えることから、多くの人が [huggingface/transformers](https://github.com/huggingface/transformers) ライブラリを中心に組み立てていくことになります。
その結果、次の理由から初動ではスコアに差が発生しづらい状況が発生します。

- 事前学習モデルの上にアーキテクチャ的な変更を加えてもスコアが改善されづらいことが経験的に知られている[要出典]。
- 画像コンペとは違い入力が離散値なので、data augmentation での工夫が難しい。
- 事前学習モデルを使うため、前処理でテキストのクレンジング・クリーニングなどの効果があまり期待できない。
- データ数が少ない場合は学習が不安定になりがちで、本質的な改善を確認しづらい。

他のコンペでは最初に思いつくようなモデル変更や前処理、data augmentation に頼れないため、初学者は躓きやすく、公開ノートブックなどでスコア改善のためハイパーパラメータを最適化しはじめてしまう例も見かけたりします。

本記事では、エレガントな発想の上位ソリューションを紹介するのではなく、序盤から中盤にかけてどのようなアプローチを取るべきかの方針について、2021年の NLP コンペのソリューションから良く使われていたものを紹介します。

# 主要なアプローチ

## 多様なモデルでのアンサンブル

「何を当たり前なことを」という感想を抱かれるかもしれませんが、基本にして最大の効果が得られます。
CommonLit, chaii では多様なモデルのアンサンブルがかなり効果的で、特に CommonLit では、あまり聞き慣れないような名前（当社比）の事前学習モデルがアンサンブルの要素として使われている例も上位ソリューションで多くありました。

- [CommonLit Readability Prize | 2nd place solution](https://www.kaggle.com/c/commonlitreadabilityprize/discussion/258328)
- [CommonLit Readability Prize | 🏅️ 4th Place Solution (0.447) 🏅️](https://www.kaggle.com/c/commonlitreadabilityprize/discussion/258148)

ここからはアンサンブルについて、ソリューションで明言されていたわけではありませんが、全体を眺めた結果ポイントとなりそうな部分をいくつか挙げておきます。

### さまざまな事前学習済みモデルを試す

上記の CommonLit のソリューション例のように、学習方法やデータを変えるだけではなく、いろんな事前学習モデルを使うことが効果的なようです。特に事前学習時の tokenizer が異なるものほどより異なる推論になっている気がします[要出典]。
また、アンサンブルとは異なりますが、多様な言語モデルを試すことでそのタスクに特化したものを見つけられる可能性もあります。例えば [Coleridge の 1st place solution](1st place solution: Metric learning and GPT) では、Roberta などの分類タスクに強い事前学習モデルをやめて GPT を試したところ効果的だったことが分かり、シングルモデルでも 3rd 相当のスコアが出ています。

### アンサンブルは序盤から組み込んでおく

人によってはアンサンブルを「最後の伸びしろ」として終盤まで取っておく人もいるかと思われますが、特にメリットはないので基本的な EDA や実験を行ったらさっさとアンサンブルもパイプライン化してすぐにサブミットを作れるようにしておきましょう。
アンサンブルといってもタスクによっては効かないケースもあれば、chaii のように工夫がいるケースもあります。また、初期に推論結果を目で見てヒューリスティックな後処理を加えていた場合、アンサンブルをした結果バイアスが減り後処理が効かなくなるようなことも考えられます。
アンサンブルの効果を検証する必要もあるので、特に理由がなければ早い段階で組んでおきましょう。早めに組んでおくことで、後半では「アンサンブルに効くようにモデルを作る」といった意識も強まると思います。

### 単純にアンサンブルできない場合は工夫が必要

単純な分類・回帰コンペであれば得られた推論結果を平均取るなりすれば良いですが、タスクによっては単純に行かないケースもあります。
例えば Coleridge や chaii ではテキスト内の一部単語を抽出するというタスクで、各トークンごとに得られる logits を基に単語を探すことになりますが、tokenizer が異なる場合単純にトークンごとに平均を取るといった方法が使えなくなります。

chaii では、次のような工夫が行われていました。

- 複数モデルの推論結果から hard voting で最終的な推論を決める
    - [chaii - Hindi and Tamil Question Answering | 1st Place Solution](https://www.kaggle.com/c/chaii-hindi-and-tamil-question-answering/discussion/287923) など
- トークンごとの logits を文字単位に展開し、文字単位でアンサンブルを行う
    - [chaii - Hindi and Tamil Question Answering | Tentative 2nd place solution](https://www.kaggle.com/c/chaii-hindi-and-tamil-question-answering/discussion/287917) など

こういった単純な足し合わせができない場合に備えるためにも、アンサンブルは早めに作っておきたいですね。

## 外部データの活用

前処理や data augmentation では差が付きづらい一方で、外部データを取り入れることでスコアを大きく改善したという報告も多いです（当然といえば当然なのですが...）。その中でも単純に混ぜるだけでなく、一工夫加えている例が上位には目立ちます。

### 同様のタスクのデータを収集し外部データとして活用する

chaii では、[MLQA](https://github.com/facebookresearch/MLQA) や [TyDi QA](https://ai.google.com/research/tydiqa/)、[XQuAD](https://github.com/deepmind/xquad) などの多言語質問応答データセットから、ターゲットとなる言語部分を抽出して多くの方が使用していました。こういったものは知らないとそもそも使えないので普段からアンテナを貼っておく必要があります。
とはいえ、たいていの場合は Kaggler が Discussion に有効そうなデータセットを投稿してくれるため、自信がない方は Discussion をしっかりチェックしましょう。

上位ソリューションを見ていると、どのデータがスコアに効いた/効かないという点はチームによってバラバラに見えました。おそらくそれまでの改善アイディア/実験の順序で効果の度合いが変わっているからだと思いますが、アンサンブルと同様で早い段階から実験しておいてその時のタスクに最適化できるよう経験値をためておく必要がありそうです。

### ラベルのない適当なテキストデータを外部データとして活用する

上記のようにコンペのタスクと同様のベンチマークデータセットがある例は珍しく、多くの場合はそのようなラベル付きデータセットは期待できません。その場合でもいくつかの方法で Wikipedia などのラベルのないテキストデータを活用することができます。例えば CommonLit では下記のように外部データが活用されていました。

- 事前学習として用いる。
    - コンペデータとドメインが近いデータを持ってきて、事前学習モデルを更に pre-training する。
- 何かしらのルールでラベルを適用して追加データとして使用する。
    - Wikipedia 記事と Simple Wikipedia 記事では Simple Wikipedia の方が読みやすいという前提でラベルを付与する。
- Pseudo Labeling を適用して追加データとして使用する。
    - 適当な学習済みモデルで推論を行い、推論結果をラベルとして付与する。

特に Pseudo Labeling は多くの上位ソリューションで使用されていますが、「単純に適用したものの効かなかった」という例も多く、一工夫加えることで効果を最大限に引き出している例もありました。

- [CommonLit Readability Prize | 1st place solution - external data, teacher/student and sentence transformers](https://www.kaggle.com/c/commonlitreadabilityprize/discussion/257844)
    - 外部データの候補から予めオリジナルデータに近いテキストを選出するなどフィルタリングを加えた上で使用し、大きくスコアを改善しています。
    - フィルタリングは Sentence Transformer を用いてテキストを embedding 化し類似度で行うなど、面白いアプローチでした。
- [CommonLit Readability Prize | 3rd place solution (0.447 private - 3 models simple average)](https://www.kaggle.com/c/commonlitreadabilityprize/discussion/258095)
    - pseudo labeling で加えたデータを用いて学習したモデルを再度 pseudo labeling に使用する、というサイクルを繰り返すことでスコアをあげています。
- [CommonLit Readability Prize | 12th (public 19th) place solution](https://www.kaggle.com/c/commonlitreadabilityprize/discussion/260325)
    - 自分のソリューションですが、pseudo labeling したデータについて、ノイズが増えないように推論結果が怪しいものをヒューリスティックなルールでフィルタリングしています。

## その他のテクニック

その他、複数コンペで使われていた、良さげなテクニックについて記載します。

### モデリング

- 事前学習モデルで抽出した特徴をタスクに落とす head の部分を工夫する
    - 通常は単なる Linear レイヤが使われますが、タスクによっては別の head が刺さることもあるようです。
        - [CommonLit Readability Prize | Utilizing Transformer Representations Efficiently](https://www.kaggle.com/rhtsingh/utilizing-transformer-representations-efficiently) で色々な head 例が紹介されています。
    - QAタスクでも、start/end を別々の Linear レイヤで予測するのではなく、end の予測に start の予測結果を利用する例がありました。
        - [Coleridge Initiative - Show US the Data | 1st place solution: Metric learning and GPT](https://www.kaggle.com/c/coleridgeinitiative-show-us-the-data/discussion/248251)

### 学習

- Layerwise Learning Rate で学習を安定化
    - データ数が少ない場合、大きな事前学習モデルだと fine-tuning が不安定になりがちです。CommonLit では、下記のようにレイヤをブロックで分けて学習率を変えることで改善したとの報告があります。画像モデルと同様に、head だけ高い学習率を使うケースも目立ちました。
    - 主に学習データの少なく学習が不安定だった CommonLit で使われていました。

```python:pseudo_code
# 12層のモデル想定、3つのグループに分けて lr を低レイヤに行くほど小さくする
group1 = ['layer.0.', 'layer.1.', 'layer.2.', 'layer.3.']
group2 = ['layer.4.', 'layer.5.', 'layer.6.', 'layer.7.']
group3 = ['layer.8.', 'layer.9.', 'layer.10.', 'layer.11.']
group_all = [
   'layer.0.', 'layer.1.', 'layer.2.', 'layer.3.',
   'layer.4.', 'layer.5.', 'layer.6.', 'layer.7.',
   'layer.8.', 'layer.9.', 'layer.10.', 'layer.11.'
]
optimizer_parameters = []
optimizer_parameters.append({
    'params': [
        p for n, p in model.model.named_parameters()
        if not any(nd in n for nd in group_all)
    ]
})
optimizer_parameters.append({
    'params': [
        p for n, p in model.named_parameters()
        if any(nd in n for nd in group1)
    ],
    'lr': lr
})
optimizer_parameters.append({
    'params': [
        p for n, p in model.named_parameters()
        if any(nd in n for nd in group2)
    ],
    'lr': lr * 2.5
})
optimizer_parameters.append({
    'params': [
        p for n, p in model.named_parameters()
        if any(nd in n for nd in group3)
    ],
    'lr': lr * 5.0
})
     
```

- Layer Re-Initialization
    - [Revisiting Few-sample BERT Fine-tuning](https://arxiv.org/abs/2006.05987) で紹介されている、学習を安定化するための手法の1つです。
    - fine-tuning を行う際に、head だけでなく上位Nレイヤ(n=1~6)も合わせてランダムに initialization を行うことで、学習の安定化だけでなく精度も改善することが報告されています。
    - CommonLit, chaii で使われていました。

```python:pseudo_code
class Model(nn.Module):
    ...
    def initialize(self):
        for i in range(self.num_reinit_layers):
            self.model.encoder.layer[-(1 + i)].apply(self._init_weight)

    def _init_weight(self, module):
        if isinstance(module, nn.Linear):
            module.weight.data.normal_(mean=0.0, std=self.model.config.initializer_range)
            if module.bias is not None:
                module.bias.data.zero_()
```

- Within-task pre-training (ITPT や DPTと呼ばれる?)
    - [Don't Stop Pretraining: Adapt Language Models to Domains and Tasks](https://arxiv.org/abs/2004.10964) の発展?
    - コンペの訓練データを使って事前学習モデルを MLM で更に pre-training を行った後、コンペのタスクで fine-tuning を行います。
    - 効果は人による（微増~効果なし程度）だが、行っている人は多い印象でした。コンペデータが特徴的なドメインであれば、とりあえずやってみて損はないかもしれません。
    - ラベルが必要ないため、コンペデータに近いデータで行うケースも CommonLit ではよく見られました。

とりあえずコンペ横断で使われてそうだったものを挙げました。他にも見つけたら追記します。

# 最後に

今年の3つのコンペのソリューションを色々読みました。巨大な事前学習モデルのコモディティ化が進んだ結果か、年々ソリューションの差別化が難しくなっているように感じました。ここで挙げたアンサンブル及び外部データの活用によるスコア改善が分かりやすく効果があるため、その他の細かい改善が見えづらいだけかもしれません。

一方で、CommonLit 1位のソリューション [1st place solution - external data, teacher/student and sentence transformers](https://www.kaggle.com/c/commonlitreadabilityprize/discussion/257844) や Coleridge 1位のソリューション [1st place solution: Metric learning and GPT](https://www.kaggle.com/c/coleridgeinitiative-show-us-the-data/discussion/248251) 及び [1st solution: Matching the <MASK>: Context Similarity via Deep Metric Learning and Beyond](https://www.kaggle.com/c/coleridgeinitiative-show-us-the-data/discussion/248253) は、他とは異なる面白いアプローチで優勝を勝ち取っており、めちゃくちゃかっこいいです。

来年は、この記事で取り上げた基本テクニックはちゃちゃっと終わらせて、よりタスクを理解したエレガントなアプローチを生み出せるよう頑張っていきたいと思います。