Transformers 日本語BERT クラス分類モデルのファインチューニング

Python

Transformersで日本語BERTのクラス分類モデルのファインチューニングを行いました。備忘録として投稿します。

Transformers公式ドキュメントはこちら

🤗 Transformers
We’re on a journey to advance and democratize artificial intelligence through open source and open science.

使用したライブラリバージョンは、
・python=3.10.5
・torch==1.12.1+cu116
・transformers==4.21.1

使用する学習済みモデルは、
cl-tohoku/bert-base-japanese-v2

データ準備

以下からlivedoorニュースコーパスのデータをダウンロードします。
https://www.rondhuit.com/download.html
「ldcc-20140209.tar.gz」のファイルです。

コマンドプロンプトで解凍します。

$ tar xvf ldcc-20140209.tar.gz

テキストファイルからデータを読み込んで、DataFrameに成形します。

import os
from glob import glob
import pandas as pd
import linecache
from sklearn.metrics import classification_report


# カテゴリを配列で取得
categories = [name for name in os.listdir("text") if os.path.isdir("text/" + name)]
print(categories)

datasets = pd.DataFrame(columns=["text", "category"])
for cat in categories:
    path = "text/" + cat + "/*.txt"
    files = glob(path)
    for text_name in files:
        body = ''
        for i in range(3,1000):
            text=linecache.getline(text_name, i)
            if text=='':
                break
            body+=text

        datasets.loc[len(datasets)]=[body,cat]

# データフレームシャッフル、先頭の200データだけ抽出
datasets = datasets.sample(frac=1,random_state=0).reset_index(drop=True)
datasets = datasets.iloc[:200]
print(datasets)
['dokujo-tsushin', 'it-life-hack', 'kaden-channel', 'livedoor-homme', 'movie-enter', 'peachy', 'smax', 'sports-watch', 'topic-news']

ラベルを数値に変換します。

# 正解ラベル(カテゴリー)をデータセットから取得
categories = list(set(datasets['category']))
print(categories)

# カテゴリーのID辞書を作成
id2cat = dict(zip(list(range(len(categories))), categories))
cat2id = dict(zip(categories, list(range(len(categories)))))
print(id2cat)
print(cat2id)

# DataFrameにカテゴリーID列を追加
datasets['label'] = datasets['category'].map(cat2id)

# データセットを本文とカテゴリーID列だけにする
datasets = datasets[['text', 'label']]
display(datasets.head())
['topic-news', 'smax', 'livedoor-homme', 'peachy', 'movie-enter', 'kaden-channel', 'sports-watch', 'it-life-hack', 'dokujo-tsushin']
{0: 'topic-news', 1: 'smax', 2: 'livedoor-homme', 3: 'peachy', 4: 'movie-enter', 5: 'kaden-channel', 6: 'sports-watch', 7: 'it-life-hack', 8: 'dokujo-tsushin'}
{'topic-news': 0, 'smax': 1, 'livedoor-homme': 2, 'peachy': 3, 'movie-enter': 4, 'kaden-channel': 5, 'sports-watch': 6, 'it-life-hack': 7, 'dokujo-tsushin': 8}

Hugging Faceのdatasetsライブラリを使用して、transfomersで扱えるデータ構造に変換します。train/testの分割もここで行います。

from datasets import Dataset

dataset_packed = Dataset.from_pandas(datasets)
dataset_split = dataset_packed.train_test_split(test_size=0.2, seed=0)
print(dataset_split)
DatasetDict({
    train: Dataset({
        features: ['text', 'label'],
        num_rows: 160
    })
    test: Dataset({
        features: ['text', 'label'],
        num_rows: 40
    })
})

tokenizer、モデル準備

tokenizerを用意し、データセットに適用します。

from transformers import AutoTokenizer

tokenizer= AutoTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-v2')

def preprocess_function(examples):
    MAX_LENGTH = 512
    return tokenizer(examples["text"], max_length=MAX_LENGTH, truncation=True)

tokenized_dataset = dataset_split.map(preprocess_function, batched=True)

DataCollatorの定義、モデルの定義をします。

from transformers import DataCollatorWithPadding
from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainer

data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
model = AutoModelForSequenceClassification.from_pretrained("cl-tohoku/bert-base-japanese-v2", num_labels=9)

評価用関数の定義。

from sklearn.metrics import accuracy_score, f1_score

def compute_metrics(pred):
    labels = pred.label_ids
    preds = pred.predictions.argmax(-1)
    f1 = f1_score(labels, preds, average='weighted')
    acc = accuracy_score(labels, preds)
    return {'accuracy':acc, 'f1':f1}

学習(ファインチューニング)

TrainingArguments、Trainerを定義して学習を実行します。今回は、GPU環境が用意できなかったので、CPUで実行するため、no_cuda=Trueとしています(デフォルトではFalse)。

training_args = TrainingArguments(
    output_dir="./results",
    evaluation_strategy='epoch',
    logging_strategy='epoch',
    save_strategy='epoch',
    save_total_limit=1,
    learning_rate=2e-5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    num_train_epochs=5,
    weight_decay=0.01,
    no_cuda=True, # GPUを使用する場合はFalse
)

trainer = Trainer(
    model=model,
    args=training_args,
    compute_metrics=compute_metrics,
    train_dataset=tokenized_dataset['train'],
    eval_dataset=tokenized_dataset['test'],
    tokenizer=tokenizer,
    data_collator=data_collator,
)

trainer.train()
Epoch	Training Loss	Validation Loss	Accuracy	F1
1	2.166400	1.942927	0.375000	0.293243
2	1.800700	1.806098	0.600000	0.576190
3	1.570400	1.625521	0.650000	0.595616
4	1.391600	1.512957	0.650000	0.602818
5	1.306500	1.481055	0.675000	0.644277

CPUで実行する都合で学習データを少なくしているので、学習結果はあまり良くなさそうです。ただ、Lossはどちらも減少しているので、学習は進んでそうです。

学習させたモデルを保存する。

trainer.save_state()
trainer.save_model()

学習させたモデルで、testデータの予測を出力する。

pred_result = trainer.predict(tokenized_dataset['test'], ignore_keys=['loss', 'last_hidden_state', 'hidden_states', 'attentions'])
pred_label= pred_result.predictions.argmax(axis=1).tolist()
print(pred_label)
[4, 0, 8, 0, 7, 4, 4, 1, 8, 3, 1, 7, 4, 8, 7, 5, 5, 8, 1, 0, 0, 5, 7, 8, 8, 2, 0, 0, 2, 0, 5, 7, 5, 5, 8, 0, 2, 0, 8, 0]

sklearnのclassification_reportで予測結果のレポートを表示する。

from sklearn.metrics import classification_report
print(classification_report(tokenized_dataset['test']['label'], pred_label, target_names=categories))
                precision    recall  f1-score   support

dokujo-tsushin       0.50      1.00      0.67         5
  it-life-hack       1.00      1.00      1.00         3
 kaden-channel       1.00      1.00      1.00         3
livedoor-homme       1.00      0.33      0.50         3
   movie-enter       0.50      0.25      0.33         8
        peachy       1.00      0.86      0.92         7
          smax       0.00      0.00      0.00         2
  sports-watch       0.40      1.00      0.57         2
    topic-news       0.62      0.71      0.67         7

      accuracy                           0.68        40
     macro avg       0.67      0.68      0.63        40
  weighted avg       0.69      0.68      0.64        40

正解率が0.68ということで性能はそこまで良くないですが、ランダム予測よりは十分に高いので一応学習はできていることがうかがえます。

まとめ

Transformersで日本語BERTのクラス分類モデルのファインチューニングを行いました。CPU環境だったので、ほぼ動作確認程度しかできてませんが、参考になれば幸いです。

参考

機械学習エンジニアのためのTransformers ―最先端の自然言語処理ライブラリによるモデル開発

Bitly

Transformers Text classification

class transformers.Trainer

Dataset.from_pandas

Data Collator

PyTorchを使ってLSTMで文章分類を実装してみた

huggingfaceのTrainerクラスを使えばFineTuningの学習コードがスッキリ書けてめちゃくちゃ便利です

【sklearn】Classification_reportの使い方を丁寧に

コメント

タイトルとURLをコピーしました