Huggingface TransformersでBERTをFine Tuningしてみる
Huggingface TransformersでBERTをFine Tuningしてみる
TL;DR
様々な自然言語処理モデルをお手軽に使えるHuggingface Transformersを利用し、日本語の事前学習済みBERTモデルのFine Tuningを試してみました。
例によってはテストで利用したデータセットは京都大学情報学研究科–NTTコミュニケーション科学基礎研究所 共同研究ユニットが提供するブログの記事に関するデータセットを利用しました。
Transformers 4.5.1時点では、公式ドキュメントのままでは上手くいなかったので注意事項を含めて記載しています。
ベンチマーク用データ
京都大学情報学研究科–NTTコミュニケーション科学基礎研究所 共同研究ユニットが提供するブログの記事に関するデータセットを利用しました。 このデータセットでは、ブログの記事に対して以下の4つの分類がされています。
- グルメ
- 携帯電話
- 京都
- スポーツ
事前準備
以下のようにデータを準備します。
!mkdir data
!wget http://nlp.ist.i.kyoto-u.ac.jp/kuntt/KNBC_v1.0_090925_utf8.tar.bz2 -O data/KNBC_v1.0_090925_utf8.tar.bz2
%cd data
!tar xvf KNBC_v1.0_090925_utf8.tar.bz2
%cd ..
import re
def get_sentences_from_text(filename):
sentences = []
with open(filename, 'r') as f:
for i, line in enumerate(f):
sentence = line.split('\t')[1].strip()
if sentence == '': # 空文字を除去。
continue
if re.match('^http.*$', sentence): # URLを除去。
continue
sentences.append(sentence)
return sentences
import os
import pandas as pd
root_dir = 'data/KNBC_v1.0_090925_utf8/corpus2'
targets = ['Gourmet', 'Keitai', 'Kyoto', 'Sports']
original_data = []
for target in targets:
filename = os.path.join(root_dir, f'{target}.tsv')
sentences = get_sentences_from_text(filename)
for sentence in sentences:
original_data.append([target, sentence])
original_df = pd.DataFrame(original_data, columns=['target', 'sentence'])
display(original_df.head())
display(original_df.tail())
display(pd.DataFrame(original_df['target'].value_counts()))
from sklearn.model_selection import train_test_split
train_df, test_df = train_test_split(original_df, test_size=0.1)
ソースコード
Google Colabを利用することを前提としています。
モジュールのインストール
Transformersと今回使用する学習済みBERTモデルが要求するモジュールをインストールします。
!pip install transformers
!pip install fugashi
!pip install ipadic
データのロード
import pandas as pd
data_root = 'Your Own Drive/data'
train_df = pd.read_csv(f'{data_root}/knbc-train.csv', index_col=0)
test_df = pd.read_csv(f'{data_root}/knbc-test.csv', index_col=0)
データの変換
from transformers import pipeline
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
train_encodings = tokenizer(train_df['sentence'].to_list(), truncation=True, padding=True)
test_encodings = tokenizer(test_df['sentence'].to_list(), truncation=True, padding=True)
import tensorflow as tf
label2index = {
'Kyoto': 0,
'Keitai': 1,
'Gourmet': 2,
'Sports': 3,
}
train_dataset = tf.data.Dataset.from_tensor_slices((
dict(train_encodings),
train_df['target'].map(lambda x: label2index[x]).to_list()
))
test_dataset = tf.data.Dataset.from_tensor_slices((
dict(test_encodings),
test_df['target'].map(lambda x: label2index[x]).to_list()
))
学習の実行
Transformersが用意するTrainer(TensorFlow用はTFTrainer)を利用して学習を実行します。
compute_metrics
は必須ではありません。追加のMetricsを取得するために設定しています。
注意すべきはmodel.classifier
の設定です。公式ドキュメント上は不要なはずですが、損失関数で期待する活性化関数が設定されていないため、設定しないと学習が進みませんでした。今後改善される可能性もあるため、4.5.1時点でのTipsとなります。
from transformers import TFTrainer, TFTrainingArguments, TFBertForSequenceClassification
from sklearn.metrics import accuracy_score, precision_recall_fscore_support
import tensorflow as tf
def compute_metrics(pred):
labels = pred.label_ids
preds = pred.predictions.argmax(-1)
precision, recall, f1, _ = precision_recall_fscore_support(labels, preds, average='weighted')
acc = accuracy_score(labels, preds)
return {
'accuracy': acc,
'f1': f1,
'precision': precision,
'recall': recall
}
training_args = TFTrainingArguments(
output_dir='./results', # output directory
num_train_epochs=3, # total number of training epochs
per_device_train_batch_size=16, # batch size per device during training
per_device_eval_batch_size=64, # batch size for evaluation
warmup_steps=500, # number of warmup steps for learning rate scheduler
weight_decay=0.01, # strength of weight decay
logging_dir='./logs', # directory for storing logs
logging_steps=10
)
with training_args.strategy.scope():
model = TFBertForSequenceClassification.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
# Transformers 4.5.1のTFBertForSequenceClassificationでは
# classifierにactivationが設定されておらず、多項分類でlossが計算出来ないため、
# 差し替えています。
model.classifier = tf.keras.layers.Dense(
units=4, # クラス数を指定します。
activation='softmax',
name='classifier',
)
trainer = TFTrainer(
model=model, # the instantiated 🤗 Transformers model to be trained
args=training_args, # training arguments, defined above
train_dataset=train_dataset, # training dataset
eval_dataset=test_dataset, # evaluation dataset
compute_metrics=compute_metrics
)
trainer.train()
評価
Trainerのevaluateメソッドで評価を実行出来ます。
trainer.evaluate()
以下は実行結果の例です。
{'eval_accuracy': 0.8370535714285714,
'eval_f1': 0.8375614865485349,
'eval_loss': 0.4905424118041992,
'eval_precision': 0.8416657684509984,
'eval_recall': 0.8370535714285714}
以下を実行するとTensorBoardでも確認ができます。
%load_ext tensorboard
%tensorboard --logdir logs
まとめ
Huggingface Transformersを使う事で、データ以外の準備が不要になり、かなり簡単に最新モデルを使った自然言語処理を行うことができます。
TrainerのようにTransfomersが用意する抽象化レイヤーを利用することで、事前学習モデルを切り替えるだけで精度の確認を行うことができると思いますので、BERT以外にALBERTやT5との比較なども試してみたいと思います。