Traffine I/O

日本語

2023-02-03

Hugging Face Transformers:ファインチューニング

ファインチューニング

Hugging Face Transformersは幅広いタスクのための何千もの事前学習済みモデルへのアクセスを提供します。事前学習済みモデルを使用する場合、目的のタスクに特化したデータセットで学習させます。これはファインチューニングと呼ばれ、非常に強力な学習技術です。

事前学習済みモデルは次の方法でファインチューニングすることができます。

  • Transformers Trainerで事前学習されたモデルをファインチューニング
  • TensorFlowで学習済みモデルをファインチューニング
  • ネイティブのPyTorchで学習済みモデルをファインチューニング

データセットの準備

事前学習済みモデルをファインチューニングする前に、データセットをダウンロードし、トレーニングのための準備をします。

Yelp Reviews というデータセットをロードします。

$ pip install transformers datasets
from datasets import load_dataset

dataset = load_dataset("yelp_review_full")
dataset["train"][100]
{'label': 0,
 'text': 'My expectations for McDonalds are t rarely high. But for one to still fail so spectacularly...that takes something special!\\nThe cashier took my friends\'s order, then promptly ignored me. I had to force myself in front of a cashier who opened his register to wait on the person BEHIND me. I waited over five minutes for a gigantic order that included precisely one kid\'s meal. After watching two people who ordered after me be handed their food, I asked where mine was. The manager started yelling at the cashiers for \\"serving off their orders\\" when they didn\'t have their food. But neither cashier was anywhere near those controls, and the manager was the one serving food to customers and clearing the boards.\\nThe manager was rude when giving me my order. She didn\'t make sure that I had everything ON MY RECEIPT, and never even had the decency to apologize that I felt I was getting poor service.\\nI\'ve eaten at various McDonalds restaurants for over 30 years. I\'ve worked at more than one location. I expect bad days, bad moods, and the occasional mistake. But I have yet to have a decent experience at this store. It will remain a place I avoid unless someone in my party needs to avoid illness from low blood sugar. Perhaps I should go back to the racially biased service of Steak n Shake instead!'}

Tokenizerを使って、可変長のシーケンスを処理します。データセットをバッチ処理するには、Datasetsのmapメソッドを使ってデータセット全体に前処理関数を適用します。

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

def tokenize_function(ds):
    return tokenizer(ds["text"], padding="max_length", truncation=True)

tokenized_datasets = dataset.map(tokenize_function, batched=True)

今回は時間を短縮するために、全データセットの小さなサブセットを作成してファインチューニングを行います。

small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))

Transformers Trainer

Hugging Face Transformersはモデルのトレーニングに最適化されたTrainerクラスを提供し、独自のトレーニングループを手動で書くことなく簡単にトレーニングを開始することができます。Trainer APIはロギング、勾配累積、混合精度などの幅広いトレーニングオプションと機能をサポートしています。

まずはモデルをロードし、予想されるラベルの数を指定します。Yelp Reviewのデータセットを確認すると、5つのラベルがあることが分かります。

from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", num_labels=5)

TrainingArguments

次に、TrainingArgumentsクラスを作成します。このクラスには、チューニング可能な全てのハイパーパラメータと、トレーニングオプションを設定するフラグが含まれています。

from transformers import TrainingArguments

training_args = TrainingArguments(
    output_dir='test_trainer',       # output directory
    num_train_epochs=3,              # total # 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
)

TrainingArgumentsの詳細については次のリンクから確認することができます。

https://huggingface.co/docs/transformers/v4.26.1/en/main_classes/trainer#transformers.TrainingArguments

Evaluate

Trainerはトレーニング中に自動的にモデルのパフォーマンスを評価することはありません。Trainerにメトリクスを計算しレポートするための関数を渡す必要があります。Evaluateライブラリは evaluate.load 関数でロードできるシンプルな精度関数を提供しています。

import numpy as np
import evaluate

metric = evaluate.load("accuracy")

予測の精度を計算するために、metriccomputeを呼び出します。computeに予測を渡す前に、予測をlogitsに変換する必要があります。

def compute_metrics(pred):
    logits, labels = pred
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)

ファインチューニング中に評価指標を監視したい場合は、学習引数にevaluation_strategyパラメータを指定し、各エポック終了時に評価指標を報告するようにします。

from transformers import TrainingArguments, Trainer

training_args = TrainingArguments(
    output_dir='test_trainer',       # output directory
    num_train_epochs=3,              # total # 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
    evaluation_strategy="epoch"
)

Trainer

モデル、引数、学習およびテストデータセット、評価関数を含むTrainerオブジェクトを作成します。

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=small_train_dataset,
    eval_dataset=small_eval_dataset,
    compute_metrics=compute_metrics,
)

train()を呼び出してモデルをファインチューニングします。

trainer.train()

Tensorflow

TensorFlowでKeras APIを使ってHugging face Transformersモデルを学習させることも可能です。

Keras APIでHugging Face Transformersモデルを学習させたい場合、データセットをKerasが理解できる形式に変換する必要があります。データセットが小さい場合は、全体をNumPyの配列に変換してKerasに渡します。

まず、データセットをロードします。単純な2値テキストの分類タスクなので、GLUEベンチマークのCoLAデータセットを使い、とりあえずトレーニングスプリットを取るだけにしておきます。

from datasets import load_dataset

dataset = load_dataset("glue", "cola")
dataset = dataset["train"]  # Just take the training split for now

次にTokenizerをロードし、データをNumPyの配列としてトークン化します。ラベルはすでに0と1のリストなので、トークン化せずに直接NumPyの配列に変換します。

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
tokenized_data = tokenizer(dataset["sentence"], return_tensors="np", padding=True)
# Tokenizer returns a BatchEncoding, but we convert that to a dict for Keras
tokenized_data = dict(tokenized_data)

labels = np.array(dataset["label"])  # Label is already an array of 0 and 1

最後に、モデルをロードし、コンパイルし、フィットさせます。

from transformers import TFAutoModelForSequenceClassification
from tensorflow.keras.optimizers import Adam

# Load and compile our model
model = TFAutoModelForSequenceClassification.from_pretrained("bert-base-cased")
# Lower learning rates are often better for fine-tuning transformers
model.compile(optimizer=Adam(3e-5))

model.fit(tokenized_data, labels)

PyTorch

ネイティブのPyTorchでHugging Face Transformersのモデルをファインチューニングすることができます。

まずはtokenized_datasetを手動で後処理し、学習用に準備します。

モデルは生のテキストを入力として受け取ることができないため、textカラムを削除します。

tokenized_datasets = tokenized_datasets.remove_columns(["text"])

モデルは引数がlabelsという名前であることを期待しているので、labelカラムの名前をlabelsに変更します。

tokenized_datasets = tokenized_datasets.rename_column("label", "labels")

リストではなくPyTorchテンソルを返すようにデータセットの形式を設定します。

tokenized_datasets.set_format("torch")

次に、時間を短縮するためにデータセットの小さいサブセットを作成します。

small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))

トレーニングデータセットとテストデータセット用のDataLoaderを作成し、データのバッチを反復処理できるようにします。

from torch.utils.data import DataLoader

train_dataloader = DataLoader(small_train_dataset, shuffle=True, batch_size=8)
eval_dataloader = DataLoader(small_eval_dataset, batch_size=8)

ラベルの数をモデルに読み込ませます。

from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", num_labels=5)

モデルをファインチューニングするためのオプティマイザと学習率スケジューラーを作成します。今回はPyTorchのAdamWオプティマイザを使います。

from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=5e-5)

Trainerからデフォルトの学習率スケジューラーを作成します。

num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    name="linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps
)

GPUが利用できる場合は、GPUを使用するデバイスを指定します。

import torch

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)

トレーニングの進捗状況を把握するために、tqdmライブラリを使って、トレーニングのステップ数に応じたプログレスバーを追加します。

from tqdm.auto import tqdm

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

Trainerに評価関数を追加したのと同様に、自分で学習ループを書くときも同じことをする必要があります。しかし、各エポックの終わりに評価値を計算して報告するのではなく、今回はadd_batchで全てのバッチを蓄積し、一番最後に評価値を計算することになります。

import evaluate

metric = evaluate.load("accuracy")
model.eval()
for batch in eval_dataloader:
    batch = {k: v.to(device) for k, v in batch.items()}
    with torch.no_grad():
        outputs = model(**batch)

    logits = outputs.logits
    predictions = torch.argmax(logits, dim=-1)
    metric.add_batch(predictions=predictions, references=batch["labels"])

metric.compute()

その他の資料

より詳細のチューニングの例については以下をご参照ください。

https://github.com/huggingface/transformers/tree/main/examples

  • PyTorchとTensorFlowで一般的なNLPタスクを学習するためのスクリプト
    https://huggingface.co/docs/transformers/notebooks
  • PyTorchとTensorFlowで特定のタスクのためにモデルをファインチューニングする方法に関する様々なノートブック

参考

https://huggingface.co/docs/transformers/training
https://huggingface.co/transformers/v3.2.0/training.html
https://huggingface.co/docs/transformers/v4.26.1/en/main_classes/trainer#transformers.TrainingArguments
https://github.com/huggingface/transformers/tree/main/examples
https://huggingface.co/docs/transformers/notebooks

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!