「AI王 〜クイズAI日本一決定戦〜」 第2回コンペティション参加＆振り返り

## はじめに
2022年3月11日開催のコンペティション「AI王 〜クイズAI日本一決定戦〜」に参加しました。本記事では取り組み内容の共有を通して、コンペティションの振り返りをしたいと思います。

## コンペティション概要

https://sites.google.com/view/project-aio/competition2

クイズを題材としたオープンドメイン質問応答のタスクに対してシステム性能を競うコンペティションです。計1,000問の問題に対する正答率を競います。[第1回](https://sites.google.com/view/project-aio/competition1)は20個の選択肢が設けられていましたが、第2回では選択肢が排除され、あらゆる解答がありえるという実践的な内容となっています。

評価は一次評価としてリーダーボード上で行われる自動評価と最終評価にあたる人手評価の2種類あり、開発した質問応答システムのDockerイメージをWebサイトを通じてアップロードすることにより行われます。
本コンペティションでは、以下の制約がありました。

- 投稿回数は**1日1投稿**
- gzip圧縮されたDockerイメージのサイズは**30GB以内**
    - Dockerイメージには質問応答システム（モデル）、文書集合、実行環境（ライブラリーなど）が含まれる
- 評価用サーバー上での実行時間は**6時間以内**
    - サーバースペックは`vCPU 12, Memory 78GB + Nvidia Tesla V100 (Memory 16GB)x1`
- 利用可能なデータは**一般公開されているもののみ**
    - ただし、無償で一般公開すれば利用可能
- インターネット検索などの外部リソースの利用は禁止

## コンペティション結果

コンペティションの最終投稿に対して行われた最終評価により、1,000問中633問正解し、全10チーム（内2チームはベースライン）中、6位という結果でした。
残念ながら上位に食い込むことができず、多くの課題を残す結果となりました。

## 取り組み内容

コンペティション参加に当たって、取り組んだ一連の内容は以下のとおりです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/c0adbd1f-e95b-fbcb-e43f-29ff9eae7a49.png)

## ベースラインモデルの理解

近年、質問応答タスクにて多く利用されているRetriever-Reader 方式を用いた[DPR[Karpukhin+, EMNLP 2020]](https://aclanthology.org/2020.emnlp-main.550/) を本コンペティションで利用できるよう改修したモデルをベースラインとして提供いただいているため、まずはじめにDPRについて理解を深めました。

https://github.com/cl-tohoku/AIO2_DPR_baseline

DPRで用いるRetriever-Reader 方式では2種類のモジュールを用います。

- 検索モジュール（Retriever）
    - 質問に関連する文書を大規模な文書集合から検索
- 読解モジュール（Reader）
    - 検索した関連文書を用いて質問の解答を抽出

コンペティションで公式配布されているデータの一つ、`『五輪書』という兵法書を著した、佐々木小次郎との巌流島（がんりゅうじま）の戦いで有名な、吉川英治の小説などでも知られる江戸時代の剣客は誰？`という質問を例に説明します。
この質問が与えられた場合、Retrieverモジュールによって、質問に関連する文書を文書集合から**検索**します。検索した結果はReaderモジュールに与えられます。
Readerモジュールは、複数の文書の中から`昭和の巌流島`という文書（※1）を選択します。続けてこの文書の中から`宮本武蔵`を解答として**抽出**します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/e06b3cf5-7078-ce66-d667-ba8a03beadc6.png)

DPRにおける各モジュールの中身についてもう少し踏み込んでみます。
Retrieverモジュールは、質問エンコーダと文書エンコーダの2つのエンコーダから構成されており、この2つのエンコーダを用いて質問と文書のベクトル表現を取得します。具体的には、質問に対する各文書の関連度は質問と文書のベクトルの内積により類似度を計算し、類似度が高い文書を質問文に関連する文書としています。

<div align="center">
<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/8437ee61-c023-d205-d422-3ffc2dcbcec9.png">
</div>

質問に対して適切な文書を関連文書と認識させる（＝類似度を高くする）ためにRetriverの学習には正例文書および負例文書を用います。
質問の解答の文字列を含んだ文書を正例文書、含んでいない文書を負例文書とします。この例では質問に対する答えである「宮本武蔵」を含む文書が正例文書になります。正例文書および負例文書の作成方法については[データセット](#データセット)に記載しています。
また、正例文書は類似度が高くなるように、反対に負例文書は類似度が低くなるように学習を行います。すなわち学習に用いるロス値は、正例文書との関連度が高いほど小さく、負例文書との関連度が高いほど大きくなるように設定します。
ロス関数$L$は以下のようになります。

<div align="center">
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/b97f4d4b-8685-c3cc-4482-79ed6d54bd23.png">
</div>

```math
\require{enclose}
L(q_i,p_i^+,p_{i,1}^-,…,p_{i,m}^-)=-log\frac{\enclose{roundedbox}[mathcolor="green"]{\color{black}{e^{sim(q_i,p_i^+)}}}}{\enclose{roundedbox}[mathcolor="green"]{\color{black}{e^{sim(q_i,p_i^+)}}}+\enclose{roundedbox}[mathcolor="red"]{\color{black}{{Σ^n_{j=1}e^{sim(q_i,p_{i,j}^-)}}}}} \\
```

こうして作成したRetrieverによって大量の文書の中から質問に対して関連する文書を検索することができます。

続いて、Readerモジュールです。
質問文と合わせてRetrieverが抽出した複数の関連文書がReaderモジュールに与えられます。Readerモジュールは複数の関連文書から対象の文書を選択し、選択した文書の中から答えに該当する場所を特定していきます。

<div align="center">
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/c51d80ae-12b0-420b-6d8e-784be6fb520f.png">
</div>


上記を実現するためにReaderの学習では1つのエンコーダと3つのヘッドを用います。

- 読解エンコーダ：質問文と関連文書を連結したベクトルを入力として与えることでCLSトークンや関連文書のベクトル表現を出力します。
- 文書選択ヘッド：CLSトークンのベクトル表現を用いてsoftmax関数により、答えを含む文書を抽出します。
- 開始（終了）予測ヘッド：関連文書のベクトル表現を用いてsoftmax関数により、答えの開始および終了位置を予測します。

<div align="center">
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/bc2cfa8e-659a-baeb-39d0-4975716230ae.png">
</div>


## 開発環境構築

- 社内計算機サーバーを使用
    - 使用するGPUのスペックは`Quadro P6000x4(24GBx4)`
- Dockerを用いて仮想環境内で開発
    - ゲストOSは`Ubuntu 20.04`
        - Dockerイメージ`11.4.2-cudnn8-devel-ubuntu20.04`を使用
    - DPRの学習データ作成用にElasticSearchをインストール
        - Dockerイメージ`docker.elastic.co/elasticsearch/elasticsearch:6.5.4`を使用

## モデル設計

### Retriverモジュール

- 2つのエンコーダは[東北大BERT-Baseモデル](https://huggingface.co/cl-tohoku/bert-base-japanese-whole-word-masking "cl-tohoku/bert-base-japanese-whole-word-masking")を使用
    - 巨大なモデルである[東北大BERT-Largeモデル](https://huggingface.co/cl-tohoku/bert-large-japanese "cl-tohoku/bert-large-japanese")を用いたRetrieverも作成したものの、Retrieverモジュール単体でDockerイメージサイズの上限である30GBをオーバーしてしまったため、断念しました。
- 1つの質問文に対して、最大100件のWikipedia記事を抽出
- [Faiss](https://github.com/facebookresearch/faiss)を用いてあらかじめベクトル表現を計算しておくことで、高速化および消費メモリー削減

<div align="center">
<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/776439ab-2963-a7a9-abcd-4d881eec310e.png">
</div>

### Readerモジュール

- 抽出型Readerを使用
    - 選択肢として生成型Readerもありましたが、開発環境のスペックや開発期間の関係で見送りました。
- 読解エンコーダは[東北大BERT-Baseモデル](https://huggingface.co/cl-tohoku/bert-base-japanese-whole-word-masking "cl-tohoku/bert-base-japanese-whole-word-masking")を使用
    - [東北大BERT-Largeモデル](https://huggingface.co/cl-tohoku/bert-large-japanese "cl-tohoku/bert-large-japanese")はGPUメモリーの不足により断念しました。

### データセット

DPRの学習データセットは以下のようなフォーマットに準ずる必要があります。
https://github.com/facebookresearch/DPR#retriever-input-data-format

- ```question```：質問
- ```answers```：解答
- ```positive_ctxs```：正例文書（質問の解答を含む文書）
- ```id```：文書インデックス
- ```title```：文書のタイトル
- ```text```：文書本文
- ```negative_ctxs```：負例文書（ミニバッチ内のほかの質問に対する正例文書）
- ```hard_negative_ctxs```: ハード負例文書（質問の解答を含まない文書）

```json
[
  {
	"question": "『五輪書』という兵法書を著した、佐々木小次郎との巌流島の戦いで有名な、吉川英治の小説などでも知られる江戸時代の剣客は誰？
",
	"answers": ["宮本武蔵"],
	"positive_ctxs": [{
        "id": 2929214,
		"title": "昭和の巌流島",
		"text": "そういう意味で宮本武蔵と佐々木小次郎の巌流島の戦いになぞらえると、あくまで結果だけから判断すると、力道山の方が兵法に長けた宮本武蔵的、木村政彦の方は佐々木小次郎的であった。この試合が「昭和の巌流島」と呼ばれる以上、力道山は「昭和の武蔵」であり木村政彦は「昭和の小次郎」と言ってもいいだろう。"
	}],
	"negative_ctxs": [""],
	"hard_negative_ctxs": [{
        "id": 5845260,
		"title": "武州伝来記",
		"text": "『武州伝来記』では、巌流島の決闘の相手は佐々木小次郎ではなく、長門国長府に住む国人津田小次郎という巖流の兵法者で、武蔵の父無二に勝負を挑んだところ無二が断り、「巖流の仕込み剣を恐れて無二辞退に及ぶ」と風評が立った。そのころ弁之助(武蔵)は摂津国(大坂)で弟子を取って兵法を教えていたが、これを聞いてただちに長門へ下り、小次郎と下関で勝負をしようとしたが許されず、巌流島の決闘にいたったと書かれている。『二天記』に伝える熊本藩公認試合ではなく私闘であり、武蔵のほうが先着して小次郎を待つなど、10を越える通説との相違が見られる逸話となっている。"
	}]
  },
  ...
]
```

データセットに含まれる正例文書とハード負例文書（難しい負例文書）に関してはElasticSearchを用いて生成します。
具体的にはクイズの質問文をクエリ文字列として、日本語Wikipediaデータを投入したElasticSearchに対して検索を行います。検索結果上位のうち、解答を含むWikipedia文書を正例文書、解答を含まないWikipedia文書をハード負例文書としています。また、学習時のミニバッチ内に含まれるほかの正例文書を負例文書とします。

実装はベースラインに含まれる下記資料を参考にしました。

https://github.com/cl-tohoku/AIO2_DPR_baseline/blob/master/data/README.md

また、使用したデータは以下のとおりです。
- 日本語Wikipediaデータ：当時最新の[Wikipedia Cirrussearch](https://dumps.wikimedia.org/other/cirrussearch/)のダンプデータ（jawiki-20220117-cirrussearch-content.json.gz）を使用
- 学習データ：[第2回コンペティション学習用データおよび開発用データv1.0](https://sites.google.com/view/project-aio/competition2#h.i5iz9xhxq2us)を使用

### アンサンブル学習

DPRモデル単体ではうまく解答を導き出せない質問に対応するため、DPRモデルと特徴の異なる2つのモデルを取り入れました。今回追加したモデルは、文書中に含まれる単語の重要度を評価する手法の1つであるtf-idfモデルと文章生成言語モデルであるGPTモデルです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/504164c6-a89a-2377-dff8-8587c2a880f3.png)


DPRはTransformerを用いたニューラルネットワークであるのに対し、tf-idfは非ニューラルネットワークの手法です。また、DPRで用いているReaderは抽出型であるのに対し、GPTモデルは生成型です。

- tf-idfモデル：scikit-learnライブラリーの[TfidfVectorizer](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html?highlight=labelencoder#sklearn.preprocessing.LabelEncoder)を使用
- GPTモデル：rinna株式会社が公開している日本語GPTモデル [rinna/japanese-gpt-1b](https://huggingface.co/rinna/japanese-gpt-1b)を使用

GPTは、単語の確率の組み合わせから次の単語を推定するモデルであり、本来会話文の生成などに用いられますが、今回はクイズの解答生成に適用しました。
質問文をOne-Shotと呼ばれる手法で与え、生成された文章を解答としました。

<div align="center">
<img width="90%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/769cd131-cec4-e409-c144-07d687c99717.png">
</div>

### 「ですが問題」の対策

クイズ特有の対応として、以下の処理を追加しました。

- ですが問題（AといえばBですが、Cといえば何？）に対して、ノイズとなっている文章を除去
- 文頭から続いている場合、文頭から「ですが、」の部分を除去
    - 例：`オーストラリアの公用語は英語ですが、オーストリアの公用語は何語？`の場合、`オーストリアの公用語は何語？`となります。
- 句点で囲まれている場合、「～ですが、」を「、」に置換
    - 例：`東北地方の郷土富士で、津軽富士といえば岩木山ですが、出羽富士といえば何山？`の場合、`東北地方の郷土富士で、出羽富士といえば何山？`となります。
- 句点なしで文頭から続いている場合、文頭から「ですが、」の部分を除去
    - 例：`アメリカの州の愛称で「ゴールデンステート」と呼ばれるのはカリフォルニア州ですが、「シルバーステート」と呼ばれるのは何州？`の場合、`シルバーステートと呼ばれるのは何州？`となります。

### 明らかな誤り解答の削除

- 解答の文字列が明らかに誤りの場合、置換または別のモデルでの解答とする
    - 空白（0文字）解答
        - 最終解答の対象から除外
    - 不自然な記号を含む解答
        - 不自然な記号を含まない解答に置換
    - 長文解答
        - 後半を削り、名詞での解答に置換

## 投稿結果

正答率の推移は以下のとおりでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609733/8cac6249-52e4-918a-f4fd-b6c02e250e67.png)

いくつかの改良を加えたものの精度改善に貢献したのは、[tf-idfモデルの追加](#アンサンブル学習)や開発データを加えたモデルの学習でした。

ほかの対応は誤差の範囲内であったり、[「ですが問題」の対策](#ですが問題の対策)に関しては、むしろ精度が落ちてしまいました。理由については推測ですが、ノイズとはいえ、質問文を短くしてしまうため、情報量が減ってしまい、Retrieverによる関連文書の抽出がうまくいかなかったのではないかと考えられます。

## 苦労した点

- DPRの学習から評価実行までの実行時間が数日から1週間近く必要
    - モデルを設計してから結果が出るまでにかなり時間がかかり、満足のいく試行回数だけモデルの調整ができませんでした。
- 投稿用Dockerイメージの作成
    - はじめのうちは、うまく作成することができず、そもそも動作しませんでした。
    - 実行手順を誤っていたり、1つのパッケージでも不足していた場合、動作確認（推論実施）の段階で発覚し、Dockerイメージを再構築する必要があり、手間でした。
- GPUメモリー不足
    - ベースラインモデルと同等のデータ量や標準のBERTでは何とか動作しましたが、学習データを多少増やすだけでメモリー不足に陥り、バッチサイズを減らす必要がありました。


## おわりに

ここまでお読みいただきありがとうございました。
結果は伴わなかったものの、コンペティションを通して最新のNLP技術に触れられたことで、多くのことを学ぶことができました。
第3回も開催予定とのことで、次回は上位に食い込めるよう日々邁進していきたいと思います。
コンペティションの運営を行っていただいた実行委員の方々、参加者の方々に深く感謝いたします。

## 引用元・ライセンスなど
※1　引用元：「昭和の巌流島」「ウィキペディア（Wikipedia）: フリー百科事典」 2022年5月5日（木）11:29 UTC URL: https://ja.wikipedia.org/wiki/昭和の巌流島
※2　引用元：「武州伝来記」「ウィキペディア（Wikipedia）: フリー百科事典」2021年5月16日（日）16:13 UTC URL: https://ja.wikipedia.org/wiki/武州伝来記
本ページは、CirrusSearchのデータのほか、DPRデータフォーマットのソースコード表記を一部改変し、それぞれ記載しております。そのため、本ページも「[CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/)」と「[CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en)」にて公開します。