BERTの解説とText Classificationタスクの実装

# BERT

最近プロジェクトでBERTシリーズのモデルを使用しているため、継続的に整理することにしました。

## 1.0 イントロダクション

BERT（Bidirectional Encoder Representations from Transformers）は、Transformersを基にした事前学習型の深層学習モデルで、双方向を通じて言語表現を生成します。自然言語処理（NLP）において、タスクは一般的にテキスト理解、生成、分類などに分けられ、感情分析、機械翻訳、質問応答システムなどが含まれます。BERTの汎用性は、その事前学習とファインチューニング（fine-tune）の方法からです。まず、Masked Language Model (MLM) と Next Sentence Prediction (NSP) を用いて大規模なコーパス上で事前学習を行い、豊富な言語知識を獲得します。次に、特定の下流タスクにおいて少量のファインチューニングを行うことで、迅速に適応し、優れたパフォーマンスを発揮します。この設計により、BERTは各種テキスト理解タスクを効果的に処理できるだけでなく、後の大規模言語モデルも似たようなやり方を採用しています。

## 1.1 アーキテクチャ

BERTの主要な構造は、Transformerのエンコーダを積み重ねたものです。Transformerエンコーダ内のアテンションはマスクされていないため、最終的に左右のコンテキスト情報を統合した深層の双方向言語表現を生成することができます。
![截屏2024-05-03 11.59.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006814/0647a968-526f-ed9b-81d9-96c8337fbaeb.png)

## 1.2 BERTの入力部分

テキストからトークンへのプロセス：

- トークンは文を分割する最小単位です。
- WordPieceアルゴリズムを使用して単語をトークンに分割します（例：embedding → em、##be、##dd、##ing。ここで「##」は単語の一部を表します）。
- 入力シーケンスの始まりには特定の分類トークン（[CLS]）が存在し、この分類トークンに対応する最後のTransformer層の出力は、入力シーケンス全体の特徴情報を集約する役割を果たします。

テキスト要約、自然言語推論などの下流タスクで複数の文の入力が必要な場合、トークンには以下が追加されます：

- [CLS]：**分類タスクの特別なトークンを表します**
- [SEP]：区切り符
- 長文の入力は、[CLS] + 文A（+[SEP]+ 文B + [SEP] …）となります。

Transformer自体は入力の位置情報を持たないため、BERTにはPosition Encodingが含まれており、区切り符にも文に対応する埋め込み情報があります。入力層の情報は以下の通りで、最終的なモデルの入力はこれら三つの情報を合わせたものになります。
![2024-05-03 12.28.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006814/61168aaa-69d9-aa0c-b4d9-63fb1eb3335b.png)
## 1.3 BERTの出力部分

BERTの出力は以下のようになります。[CLS]からの出力Cは、分類用のトークンとして使用されます。その他の入力トークンに対応する出力はTです。

- C：自然言語推論や感情分類タスクなどの分類タスクに使用できます。
- T：シーケンスタグ付けや質問応答などのシーケンスタスクに使用できます。

![2024-05-03 12.34.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006814/fc1dbf7d-7ac6-93c1-5386-83cecb09a301.png)
## 1.4 BERTは双方向の情報を考慮する能力を持つ

NLP分野では高品質の人手によるアノテーションデータが不足していますが、BERTの事前学習は教師なし学習を活用して事前学習タスクを構築しています。従来の言語モデルは文脈から次の単語を予測するもので、モデルの能力が制限されがちです。左から右へ、そして右から左へと順序で二つのモデルを訓練し組み合わせる方法では、双方向の情報を考慮できますが、**パラメータ量が2倍**になる、**QAタスクでの非合理性**などの問題が伴います。これらの問題を解決するために、BERTでは**Masked Language Model**（MLM）**とNext Sentence Prediction**（NSP）の二つのPreTrainingタスクを採用しています。

- **Mask Language Model（MLM）は穴埋め問題のようなタスク**
    - 入力シーケンスの一部の位置をランダムにマスキングし、その部分をモデルによって予測させます。
- **Next Sentence Prediction（NSP）は二つの文が連続するかどうかを予測**
    - 二つの文をモデルに入力し、2番目の文が1番目の文の次の文であるかどうかを予測します。

これら二つのタスクの事前学習プロセスにより、訓練されたモデルは強力な表現力を持つようになります。

# Text Classification
日本語のニュースデータセットを使用して、テキスト分類タスクのデモを作成しました。
## 2.0 BERTにより日本語ニュース記事をカテゴリに分類のDemo実装

- 今回のデモで使用したコードは以下のGitHubページにアップロードされました。    
[GitHubのページ](https://github.com/yh00722/Demo_DL/tree/main/TC)

- コードでは、HuggingFaceのTransformersライブラリと東北大学乾研究室が提供する日本語の事前訓練済みBERTモデルを使用し、ローカルデータでファインチューニングを行いました。
[HuggingFaceのページ](https://huggingface.co/tohoku-nlp/bert-base-japanese-whole-word-masking)

## 2.1 データセットの準備

RONDHUIT社の[ダウンロードページ](https://www.rondhuit.com/download.html#ldcc)からダウンロードしたファイルを解凍します。

そして、以下のプログラムを通してニュースの各ファイルのタイトルを抽出し、カテゴリとタイトルの内容を出力します。

```python:dataset.py
import os
import pandas as pd
from pathlib import Path

def create_dataset_df(directory, skip_filename="LICENSE.txt"):
    path = Path(directory)
    title_list = []
    data = []
    for subdir in path.glob('*'):
        if subdir.is_dir():
            title_list.append(subdir.name)
            for file in subdir.glob('*.txt'):
                if file.name == skip_filename:
                    continue
                else:
                    with open(file, "r", encoding='utf-8') as f:
                        sentence = f.readlines()[2].strip()
                        data.append((subdir.name, sentence))
    return title_list, data
```
以下のコードを通して、学習用とテスト用ファイルを作成して保存します。
```python:dataset.py
# dataset
df = pd.DataFrame(data, columns=['label', 'sentence'])
df['label'] = df['label'].map(title_dict)

df = df.sample(frac=1)
print(df)
# make dataset
num = len(df)
df[:int(num*0.8)].to_csv('train.csv', sep=',', index=False)
df[int(num*0.8):].to_csv('dev.csv', sep=',', index=False)
```
## 2.2 モデルの学習

以下のクラスではそれぞれ学習のパラメータやモデルのパラメータを定義し、main関数にコマンドラインからの入力を検出できるようにしています。

```python:run.py
@dataclass
class DataTrainingArguments:
```

```python:run.py
@dataclass
class ModelArguments:
```

ここで、それぞれの定義したパラメータを代入しモデルをインスタンス化します。

```python:run.py
    config = AutoConfig.from_pretrained(
        model_args.config_name if model_args.config_name else model_args.model_name_or_path,
        num_labels=num_labels,
        finetuning_task=data_args.task_name,
        cache_dir=model_args.cache_dir,
        revision=model_args.model_revision,
        token=model_args.token,
        trust_remote_code=model_args.trust_remote_code,
    )
    tokenizer = AutoTokenizer.from_pretrained(
        model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path,
        cache_dir=model_args.cache_dir,
        use_fast=model_args.use_fast_tokenizer,
        revision=model_args.model_revision,
        token=model_args.token,
        trust_remote_code=model_args.trust_remote_code,
    )
    model = AutoModelForSequenceClassification.from_pretrained(
        model_args.model_name_or_path,
        from_tf=bool(".ckpt" in model_args.model_name_or_path),
        config=config,
        cache_dir=model_args.cache_dir,
        revision=model_args.model_revision,
        token=model_args.token,
        trust_remote_code=model_args.trust_remote_code,
        ignore_mismatched_sizes=model_args.ignore_mismatched_sizes,
    )
```

コマンドラインからの入力は以下になっています。wandbなどのツールを使って学習過程を可視化できます。

```terminal
python run.py \
--model_name_or_path=cl-tohoku/bert-base-japanese-whole-word-masking \
--do_train \
--do_eval \
--max_seq_length=128 \
--per_device_train_batch_size=32 \
--use_fast_tokenizer=False \
--learning_rate=2e-5 \
--num_train_epochs= \
--output_dir=output/ \
--overwrite_output_dir \
--train_file=train.csv \
--validation_file=dev.csv \
--report_to="wandb" \
--logging_steps=100 \                                                                                                                                                          
--evaluation_strategy="epoch”
```
## 2.3 結果

10Epochsで学習Lossから収束を確認でき、テスト結果からAccuracyが90％以上になっています。
- Train Loss:
![2024-05-03 16.01.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006814/2734d3e5-1446-0fc5-5cc2-b9dbe32dca9b.png)
- Test Acc:
![2024-05-03 16.01.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006814/51f1ba84-73fe-0495-2785-3091acb6a706.png)
## まとめ

BERTは数年前のモデルであり、学術界での注目度はほぼなくなりました。しかし、工学や実用アプリケーションでは、どのモデルを使用するかを決定する際に、タスクの要件とリソースの可用性を考慮することが重要です。