lukeとdeberta-v2のアンサンブルモデルを作成したら最高の精度を達成した件

こんにちにゃんです。
水色桜（みずいろさくら）です。
今回は前回までの記事でJCommonsenseQAを解くために作った[luke-japanese-baseのファインチューニングモデル](https://huggingface.co/Mizuiro-sakura/luke-japanese-base-commonsenseqa)と、[deberta-v2-japanese-baseのファインチューニングモデル](https://huggingface.co/Mizuiro-sakura/deberta-v2-japanese-base-finetuned-commonsenseqa)を利用して、アンサンブルモデルを作成してみました。
手法としては単純にvoting（多数決や学習器の平均をとる手法）を用いました。
アンサンブルモデルの精度は、以下のようになりました。
```
0.8480786416443361
```
以下の表からもわかる通り、今回作成したアンサンブルbaseモデルは既存のbaseモデルのSOTAを超える性能となりました（個々のモデルの精度は約0.800だったため、まさかSOTAを超えられるとは夢にも思っていませんでした！！）。
参考：既存のbaseモデルの性能（studio ousiaさんのホームページ（　https://www.ousia.jp/ja/page/ja/2022/11/17/luke-japanese/ ）
より引用）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2847349/d6de6468-b54d-8cdd-de8d-b01961b610f5.png)

では使用したコードなどについてみていきます（実装はかなり簡単です）

# 環境
pandas 1.4.4
numpy 1.23.4
torch 1.12.1
transformer 4.24.0
Python 3.9.13
sentencepiece 0.1.97


# LUKE
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2847349/99114ada-239d-9577-e773-b8f789d23b7d.png)


2020年4月当時、5つのタスクで世界最高精度を達成した新しい言語モデル。
日本語バージョンのLUKE-baseモデルは執筆現在（2022年12月）も4つのタスク(MARC-ja, JSTS, JNLI, JCommonsenseQA)で最高スコアを有しています。LUKEはRoBERTaを元として構成され、entity-aware self-attentionという独自のメカニズムを用いています。LUKEに関して詳しくは下記記事をご覧ください。

https://qiita.com/Mizuiro__sakura/items/9ccbd655501e78df5cc6

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2847349/b09faef2-10d8-5052-b3b1-730cf1901792.png)

# deberta-v2-japanese
日本語Wikipedeia（3.2GB）および、cc100(85GB)、oscar(54GB)を用いて訓練されたモデルです。京都大学黒橋研究室が公表されました。


# 検証に使用したコード
以下のようなコードで精度の検証を行いました。
accuracyは0.848であり、既存のSOTAを超える値となりました（個々のモデルのハイパーパラメータを工夫するともう少し上がるかもしれません）


<details><summary>ensemble.py</summary>

```python:ensemble.py
from transformers import AutoTokenizer, AutoModelForMultipleChoice
import torch

tokenizer1=AutoTokenizer.from_pretrained('Mizuiro-sakura/deberta-v2-japanese-base-finetuned-commonsenseqa')
model1=AutoModelForMultipleChoice.from_pretrained('Mizuiro-sakura/deberta-v2-japanese-base-finetuned-commonsenseqa')

import pandas as pd
import json
import os
import numpy as np
# JCommonsenseQAの読み込み
dataset_v = [json.loads(line)
        for line in open("CommonsenseQA_valid.json", 'r', encoding='utf-8')]

tokenizer2 = AutoTokenizer.from_pretrained('Mizuiro-sakura/luke-japanese-base-commonsenseqa')
model2 = AutoModelForMultipleChoice.from_pretrained('Mizuiro-sakura/luke-japanese-base-commonsenseqa')

acc=0
for i in range(len(dataset_v)):
    question=dataset_v[i]['question']
    x1=tokenizer1([question,question,question,question,question],[dataset_v[i]['choice0'],dataset_v[i]['choice1'],dataset_v[i]['choice2'],dataset_v[i]['choice3'],dataset_v[i]['choice4']]
    ,return_tensors='pt',padding=True)
    leng=len(x1['input_ids'][0])

    X1 = np.empty(shape=(1, 5, leng))
    X2 = np.empty(shape=(1, 5, leng))
    X1[0, :, :] = x1['input_ids']
    X2[0, :, :] = x1['attention_mask']
    result_deberta = model1(torch.tensor(X1).to(torch.int64),torch.tensor(X2).to(torch.int64))


    question=dataset_v[i]['question']
    x1=tokenizer2([question,question,question,question,question],[dataset_v[i]['choice0'],dataset_v[i]['choice1'],dataset_v[i]['choice2'],dataset_v[i]['choice3'],dataset_v[i]['choice4']]
    ,return_tensors='pt',padding=True)
    leng=len(x1['input_ids'][0])

    X1 = np.empty(shape=(1, 5, leng))
    X2 = np.empty(shape=(1, 5, leng))
    X1[0, :, :] = x1['input_ids']
    X2[0, :, :] = x1['attention_mask']
    result_luke = model2(torch.tensor(X1).to(torch.int64),torch.tensor(X2).to(torch.int64))

    results=torch.tensor(result_deberta.logits)+torch.tensor(result_luke.logits)
    max_r=torch.argmax(results)
    if max_r==dataset_v[i]['label']:
        acc=acc+1/len(dataset_v)
print(acc)
```

</details>

# 実際にモデルを使用してみる
以下のコードを用いることで、アンサンブルモデルによる解析が行えます。

<details><summary>exe.py</summary>

```python:exe.py
from transformers import AutoTokenizer, AutoModelForMultipleChoice
import torch
import numpy as np

# modelのロード
tokenizer1=AutoTokenizer.from_pretrained('Mizuiro-sakura/deberta-v2-japanese-base-finetuned-commonsenseqa')#('Mizuiro-sakura/deberta-v2-base-juman-finetuned-commonsenseqa')
model1=AutoModelForMultipleChoice.from_pretrained('Mizuiro-sakura/deberta-v2-japanese-base-finetuned-commonsenseqa')

tokenizer2 = AutoTokenizer.from_pretrained('Mizuiro-sakura/luke-large-commonsenseqa-japanese')
model2 = AutoModelForMultipleChoice.from_pretrained('Mizuiro-sakura/luke-large-commonsenseqa-japanese')

# 質問と選択肢の代入
question = '電子機器で使用される最も主要な電子回路基板の事をなんと言う？'
choice1 = '掲示板'
choice2 = 'パソコン'
choice3 = 'マザーボード'
choice4 = 'ハードディスク'
choice5 = 'まな板'

# トークン化（エンコーディング・形態素解析）する
token = tokenizer1([question,question,question,question,question],[choice1,choice2,choice3,choice4,choice5],return_tensors='pt',padding=True)
leng=len(token['input_ids'][0])

# modelに入力するための下準備
X1 = np.empty(shape=(1, 5, leng))
X2 = np.empty(shape=(1, 5, leng))
X1[0, :, :] = token['input_ids']
X2[0, :, :] = token['attention_mask']

# modelにトークンを入力する
result_deberta = model1(torch.tensor(X1).to(torch.int64),torch.tensor(X2).to(torch.int64))

token = tokenizer2([question,question,question,question,question],[choice1,choice2,choice3,choice4,choice5],return_tensors='pt',padding=True)
leng=len(token['input_ids'][0])

# modelに入力するための下準備
X1 = np.empty(shape=(1, 5, leng))
X2 = np.empty(shape=(1, 5, leng))
X1[0, :, :] = token['input_ids']
X2[0, :, :] = token['attention_mask']

# modelにトークンを入力する
result_luke = model2(torch.tensor(X1).to(torch.int64),torch.tensor(X2).to(torch.int64))

results=result_deberta.logits+result_luke.logits

# 最も高い値のインデックスを取得する
max_result=torch.argmax(results)
print(max_result)
```

</details>

# 終わりに
アンサンブルモデルという存在は知っていましたが、ここまで精度が向上するものだとは、驚きました。
アンサンブルモデルの作成方法としてはほかにブースティングやスタッキングなどがあるため、まだ精度向上の余地があるかもしれません。
ぜひ皆さんもいろいろ試してみてください。
今回はここまでにしようと思います。
では、ばいにゃん～。