4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[翻訳] Hugging Face transformersにおける事前学習済みモデルのファインチューン

Last updated at Posted at 2023-05-13

Fine-tune a pretrained modelの翻訳です。

本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。

事前学習済みモデルを用いるメリットは非常に大きなものです。計算コストやカーボンフットプリントを削減し、スクラッチからトレーニングすることなしに最先端のモデルを活用することができます。🤗 Transformesはさまざまなタスク向けに事前トレーニングされた数千のモデルへのアクセスを提供します。事前学習モデルを使用する際、ご自身のタスク固有のデータセットでトレーニングを行います。これは、非常にパワフルなトレーニングテクニックであるファインチューニングと呼ばれるものです。このチュートリアルでは、お好きなディープラーニングフレームワークを用いて事前学習済みモデルをファインチューンします。

  • 🤗 Transformers Trainerによる事前学習済みモデルのファインチューン。
  • Kerasを用いたTensorFlowにおける事前学習済みモデルのファインチューン。
  • ネイティブPyTorchにおける事前学習済みモデルのファインチューン。

データセットの準備

事前学習済みモデルをファインチューンする前に、トレーニングためのデータセットをダウンロードして準備を行います。以前のチュートリアルでは、トレーニングのためのデータをどのように処理するのかを説明しているので、今がこのテストのためにそれらのスキルを活用する機会です!

Yelp Reviewsデータセットのロードからスタートします:

Python
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...

すでにみなさんが知っている通り、テキストを処理し、すべての変動するシーケンス長をハンドリングするためのトークナイザが必要となります。一つのステップでデータセットを処理するには、データセット全体に前処理関数を適用するための🤗 Datasetsのmapメソッドを使用します:

Python
from transformers import AutoTokenizer

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


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


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

必要であれば、処理時間を削減するためにファインチューンする全体のデータセットから小規模なサブセットを作成することができます。

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

トレーニング

この時点から、使用したいフレームワークに対応するセクションに進んでください。好きなところにジャンプするために右側のリンクを使用することができます。特定のフレームワークのコンテンツを非表示にしたい場合には、フレームワークブロックの右上のボタンを使用してください!(原文の話です。)

PyTorch Trainerによるトレーニング

🤗 Transformersは🤗 Transformersモデルトレーイングに最適化されたTrainerクラスを提供しており、マニュアルで自分のトレーニングループを記述することなしに、より簡単にトレーニングをスタートできます。Trainer APIはさまざまなトレーニングオプションや、ロギング、勾配集積、混成精度のような機能をサポートしています。

モデルをロードし、期待するラベルの数を指定することからスタートします。Yelp Reviewのdataset cardからは、5つのラベルがあることがわかります:

Python
from transformers import AutoModelForSequenceClassification

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

事前学習された重みのいくつかが使用されず、いくつかの重みがランダムに初期化された旨の警告が表示されます。心配しないでください、これは完全に正常です!BERTモデルの事前学習済みヘッドは除外され、ランダムに初期化された分類ヘッドで置き換えられます。ご自身のシーケンス分類タスクでこの新たなモデルヘッドをファインチューンし、事前学習済みモデルの知識を転送します。

トレーニングのハイパーパラメーター

次に、あなたがチューニングできるすべてのハイパーパラメーターと異なるトレーニングオプションを有効にするフラグを格納するTrainingArgumentsクラスを作成します。このチュートリアルでは、デフォルトのトレーニングハイパーパラメーターでスタートすることができますが、ご自身の最適の設定を見つけ出すために自由に実験しても構いません。

ご自身のトレーニングでチェックポイントを保存する場所を指定します:

Python
from transformers import TrainingArguments

training_args = TrainingArguments(output_dir="test_trainer")

評価

Trainerはトレーニング期間において自動ではモデルのパフォーマンスを評価しません。メトリクスを計算、レポートする関数をTrainerに引き渡す必要があります。🤗 Evaluateライブラリは、evaluate.load関数でロードできるシンプルなaccuracy関数を提供します(詳細はquicktourをご覧ください):

Python
import numpy as np
import evaluate

metric = evaluate.load("accuracy")

ご自身の予測精度を計算するためにmetriccomputeを呼び出します。computeに予測結果を渡す前に、予測結果をlogitsに変換する必要があります(すべての🤗 Transformersモデルはlogitsを返却することを思い出してください):

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

ファインチューニング中に評価メトリクスを監視したいのであれば、それぞれのエポックの最後に評価メトリックをレポートするようにトレーニングの引数にevaluation_strategyパラメーターを指定します:

Python
from transformers import TrainingArguments, Trainer

training_args = TrainingArguments(output_dir="test_trainer", evaluation_strategy="epoch")

トレーナー

モデル、トレーニングの引数、トレーニング・テスとデータセット、評価関数を用いてTrainerオブジェクトを作成します:

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

そして、train()を呼び出すことでモデルをファインチューンします:

Python
trainer.train()

KerasによるTensorFlowモデルのトレーニング

TensorFlowとKeras APIで🤗 Transformersモデルをトレーニングすることもできます!

Kelasのデータロード

Keras APIで🤗 Transformersモデルをトレーニングする際には、ご自身のデータセットをKerasが理解できるフォーマットに変換する必要があります。データセットが小さい場合、すべてをNumPy配列に変換してKerasに渡すことができます。より複雑なことを行う前に最初にこれをトライしてみましょう。

最初にデータセットをロードします。GLUE benchmarkはシンプルな二値のテキスト分類タスクなので、これのCoLaデータセットを使用して、ここではトレーニングデータのみを使います。

Python
from datasets import load_dataset

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

次に、トークナイザーをロードし、データをNumPy配列としてトークナイズします。すでにラベルは0と1のリストなので、トークナイズなしに直接NumPy配列に変換できることに注意してください!

Python
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

最後に、モデルをロードして、compileし、fitします:

Python
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)

モデルをcompile()する際にloss引数を渡すべきではありません!この引数が空の場合、Hugging Faceモデルは自分のタスクとモデルアーキテクチャに適切なlossを自動で選択します。必要であれば、自分でlossを指定することで常に上書きすることができます!

このアプローチは小規模なデータセットでは問題なく動作しますが、すぐに問題になることでしょう。なぜ?トークナイズされた配列とラベルはメモリーに完全にロードされる必要があり、NumPyは「でこぼこの」配列を取り扱えないため、データセット全体において最長のサンプルの長さにすべてのトークナイズされたサンプルをパディングしなくてはなりません。これは、配列をさらに大きなものとし、パディングされたすべてのトークンはトレーニングも遅くしてしまうことでしょう!

tf.data.Datasetとしてデータをロード

トレーニングのスローダウンを避けたいのであれば、代わりにtf.data.Datasetとしてデータをロードすることができます。必要であれば自分でtf.dataパイプラインを記述することができますが、これを行うための2つの便利なメソッドを提供しています:

  • prepare_tf_dataset(): これは、多くの場合で推奨されるメソッドです。ご自身のモデルのメソッドなので、モデル入力としてどのカラムが使用できるのかをモデルが自動で判別するために調査を行うことができ、よりシンプルかつより性能のでるデータセットにするためにほかのカラムを排除することができます。
  • to_tf_dataset: このメソッドはより低レベルで、どのcolumnslabel_colsを含めるのかを正確に指定することで、データセットをどのように作成するのかを正確にコントロールしたい際には有用です。

prepare_tf_dataset()を使用する前に、以下のコードサンプルのように、自分のデータセットにカラムとしてトークナイザのアウトプットを追加する必要があります:

Python
def tokenize_dataset(data):
    # Keys of the returned dictionary will be added to the dataset as columns
    return tokenizer(data["text"])


dataset = dataset.map(tokenize_dataset)

Hugging Faceデータセットはデフォルトでディスクに保存され、メモリー使用量を増大させないことを思い出してください!カラムが追加されると、データセットからバッチをストリーミングし、それぞれのバッチにパディングを追加することができるので、データセット全体にパディングするのと比べて、パディングするトークンの数を大きく削減することができます。

Python
tf_dataset = model.prepare_tf_dataset(dataset["train"], batch_size=16, shuffle=True, tokenizer=tokenizer)

上のコードサンプルではprepare_tf_datasetにトークナイザーを引き渡しているので、ロードされるバッチに適切にパディングされることに注意してください。ご自身のデータセットの全てのサンプルが同じ長さで、パディングが不要である場合には、この引数をスキップすることができます。サンプルをパディングする以上の複雑なことを行う必要がある場合には、サンプルのリストをバッチに変換し、任意の前処理を適用するために呼び出される関数を指定するためにcollate_fn引数を活用することができます。このアプローチの実例を見るには、examplesnotebooksをご覧ください。

tf.data.Datasetを作成すると、上述の通りモデルのコンパイルとフィットを行うことができます。

Python
model.compile(optimizer=Adam(3e-5))

model.fit(tf_dataset)

ネイティブPyTorchによるトレーニング

Trainerはトレーニングループのケアを行うので、一行のコードでモデルをファンチューンすることができます。自分でトレーニングループを記述したいユーザーは、ネイティブPyTorchで🤗 Transformersモデルをファインチューンすることもできます。

この際には、ある程度のメモリーを解放するために、ノートブックを再起動するか以下のコードを実行します:

Python
del model
del trainer
torch.cuda.empty_cache()

次に、トレーニングのための準備としてtokenized_datasetを手動で後処理します。

  1. モデルは入力として生のテキストを受け付けないのでtextカラムを削除します:

    Python
    tokenized_datasets = tokenized_datasets.remove_columns(["text"])
    
  2. モデルはlabelsという引数を期待するので、labelカラムをlabelsに変更します:

    Python
    tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
    
  3. リストではなくPyTorch tensorsを返却するようにデータセットのフォーマットを設定します:

    Python
    tokenized_datasets.set_format("torch")
    

ファインチューニングをスピードアップするために上述した通り、小規模なサブセットデータセットを作成します。

Python
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

データバッチにイテレーションを行えるように、トレーニングデータセットとテストデータセットのDataLoaderを作成します。

Python
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)

期待されるラベルの数を指定してモデルをロードします:

Python
from transformers import AutoModelForSequenceClassification

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

オプティマイザと学習率スケジューラ

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

Python
from torch.optim import AdamW

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

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

Python
from transformers import get_scheduler

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を使うようにdeviceを指定します。そうでない場合、CPUでのトレーニングは数分ではなく数時間かかることがあります。

Python
import torch

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

手元にGPUがない場合には、ColaboratorySageMaker StudioLabのようなホスティングされているノートブックでクラウドGPUのフリーアクセスを活用しましょう。

すばらしい、トレーニングの準備ができました!🥳

トレーニングループ

トレーニングの進捗を追跡し続けるために、トレーニングステップの数のプログレスバーを追加するためにtqdmライブラリを活用します:

Python
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を用いて全てのバッチの積算値を計算し、一番最後にメトリックを計算します。

Python
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()

その他のリソース

より多くのファインチューニングのサンプルに関しては、以下をご覧ください:

  • 🤗 Transformers Examplesには、PyTorchとTensorFlowで一般的なNLPタスクをトレーニングするためのスクリプトが含まれています。
  • 🤗 Transformers Notebooksには、PyTorchとTensorFlowで固有のタスクにモデルをファインチューニングする方法を記したさまざまなノートブックが含まれています。
4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?