HuggingFace Transformers 4.29 : Tutorials : 事前訓練済みモデルの再調整

HuggingFace Transformers 4.29 : Tutorials : 事前訓練済みモデルの再調整 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/28/2023 (v4.29.1)

* 本ページは、HuggingFace Transformers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス

クラスキャット は人工知能・テレワークに関する各種サービスを提供しています。お気軽にご相談ください :

◆ 人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。スケジュール
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

  • 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
  • sales-info@classcat.com  ;  Web: www.classcat.com  ;   ClassCatJP

 

HuggingFace Transformers 4.29 : Tutorials : 事前訓練済みモデルの再調整

事前訓練モデルの使用は大きな利点があります。それは計算コスト、カーボン・フットプリントを削減して、ゼロから訓練する必要なく最先端のモデルを利用することを可能にします。🤗 Transformers は広範囲のタスクに対して数千の事前訓練済みモデルへのアクセスを提供しています。事前訓練済みモデルを使用するとき、貴方のタスクに固有のデータセット上でそれを訓練します。これは再調整 (= fine-tuning, 微調整) として知られる、非常に強力な訓練テクニックです。このチュートリアルでは、事前訓練済みモデルを貴方の選択した深層学習フレームワークで再調整します。

  • 🤗 Transformers Trainer で事前訓練済みモデルを再調整する。

  • TensorFlow with Keras で事前訓練済みモデルを再調整する。

  • native PyTorch で事前訓練済みモデルを再調整する。

 

データセットを準備する

事前訓練済みモデルを再調整する前に、データセットをダウンロードしてそれを訓練のために準備します。前のチュートリアルは訓練用データを処理する方法を紹介しましたが、今はそれらのスキルをテストする機会を得ています!

Yelp Reviews データセットをロードすることから始めます :

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!'}

ご存知のように、テキストを処理して、可変なシークエンス長を扱うためにパディングと切り捨てストラテジーを含めるためにはトークナイザーが必要です。データセットを 1 ステップで処理するため、データセット全体に対して前処理関数を適用する 🤗 Datasets map メソッドを使用します :

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)

望むのであれば、必要な時間を削減するために (その上で) 再調整する完全なデータセットの小さいサブセットを作成することができます :

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 はロギング, 勾配集積 (= accumulation), そして混合精度のような幅広い訓練オプションと機能をサポートします。

モデルをロードすることから始めて想定されるラベル数を指定します。Yelp Review dataset カード から、5 つのラベルがあることがわかります :

from transformers import AutoModelForSequenceClassification

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

Note : 事前訓練済み重みの一部は使用されずに幾つかの重みはランダムに初期化されたことについて警告を見るでしょう。心配しないでください、これはまったく普通のことです!BERT モデルの事前訓練済みヘッドは捨てられ、ランダムに初期化された分類ヘッドで置き換えられます。シークエンス分類タスクでこの新しいモデルヘッドを再調整し、事前訓練済みモデルの知識をそれに転移させます。

 

訓練ハイパーパラメータ

次に、TrainingArguments クラスを作成します、これは調整可能な総てのハイパーパラメータと、様々な訓練オプションを有効にするフラグを含みます。このチュートリアルのためにはデフォルトの訓練 ハイパーパラメータ で開始できますが、最適な設定を見つけるためにこれらで自由に実験してください。

訓練からチェックポイントをどこにセーブするか指定します :

from transformers import TrainingArguments

training_args = TrainingArguments(output_dir="test_trainer")

 

評価

Trainer は訓練中に自動的にはモデル性能を評価しません。メトリクスを計算して報告する関数を Trainer に渡す必要があります。🤗 Evaluate ライブラリ は、evaluate.load (詳細はこの quicktour 参照) でロード可能な単純な精度関数を提要しています :

import numpy as np
import evaluate

metric = evaluate.load("accuracy")

予測の精度を計算するためにメトリックの compute を呼び出します。予測を compute に渡す前に、予測をロジットに変換する必要があります (総ての 🤗 Transformers モデルはロジットを返すことを忘れないでください) :

def compute_metrics(eval_pred):
    logits, labels = eval_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", 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()

 

Keras で TensorFlow モデルを訓練する

Keras API により TensorFlow で 🤗 Transformers モデルを訓練することもできます!

 

Keras 用にデータをロード

🤗 Transformers を Keras API で訓練したい場合、データセットを Keras が理解する形式に変換する必要があります。データセットが小さければ、全体を NumPy 配列に変換してそれを Keras に渡すだけです。より複雑なことをする前に最初にそれを試しましょう。

まずデータセットをロードします。単純な二値テキスト分類タスクなので、GLUE ベンチマーク から CoLA データセットを使用して、取り敢えず訓練分割を取ります。

from datasets import load_dataset

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

次に、トークナイザーをロードしてデータを 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

Finally, load, compile, and fit the model:

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)

You don’t have to pass a loss argument to your models when you compile() them! Hugging Face models automatically choose a loss that is appropriate for their task and model architecture if this argument is left blank. You can always override this by specifying a loss yourself if you want to!

このアプローチは小さいデータセットに対しては素晴らしく動作しますが、大規模なデータセットに対しては、問題になり始めることを見い出すかもしれません。何故でしょう?トークン化された配列とラベルはメモリに完全にロードされなければならないからです、そして NumPy は “jagged (ギザギザの)” 配列を扱えませんので、すべてのトークン化されたサンプルはデータセット全体の最長のサンプルの長さにパディングされる必要があるからです。それは配列をより大きくしていき、パディング・トークンのすべては訓練を遅くします!

 

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

訓練が遅くなることを回避したい場合、代わりにデータを tf.data.Dataset としてロードできます。望むなら tf.data パイプラインを書くこともできますが、これを行う 2 つの便利なメソッドがあります :

  • prepare_tf_dataset() : これは殆どの場合に勧められるメソッドです。それはモデル上のメソッドですので、モデルを検査することができて、どのカラムがモデル入力として利用可能であるかを自動的に見つけ出し、より単純でパフォーマンスが良いデータセットを作成するために他のカラムは捨てます。

  • to_tf_dataset: このメソッドはより低レベルで、どのカラムと label_cols を含めるかを正確に指定することでデータセットがどのように作成されるかを正確に制御したいときに有用です。

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

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 datasets はデフォルトではディスク上にストアされることを忘れないでください、これはメモリ使用量を膨張させません!カラムが追加されたならば、データセットからバッチをストリームして各バッチにパディングを追加することができます、これはデータセット全体をパディングすることと比較してパディングトークンの数を大幅に削減します。

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

上記のサンプルで、ロードされたときバッチを正しくパディングできるように、トークナイザーを prepare_tf_dataset に渡す必要があることに注意してください。データセット内のサンプルすべてが同じ長さでパディングが不要ならば、この引数はスキップできます。単にサンプルをパディングするよりも複雑なことを行う必要がある場合には (e.g. masked 言語モデリング用にトークンを破損)、代わりに collate_fn 引数を使用して、サンプルのリストをバッチに変換して望みの前処理を適用するために呼び出される関数を渡すことができます。このアプローチを実際に見るには サンプルノートブック をご覧ください。

Once you’ve created a tf.data.Dataset, you can compile and fit the model as before:

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

model.fit(tf_dataset)

 

native PyTorch で訓練

Trainer は訓練ループを処理してモデルを単一行のコードで再調整することを可能にします。独自の訓練ループを書くことを好むユーザについては、native PyTorch で 🤗 Transformers モデルを再調整することもできます。

この時点で、ノートブックを再起動するか、あるいは何某かのメモリを解放するために以下のコードを実行する必要があるかもしれません :

del model
del trainer
torch.cuda.empty_cache()

 
次に、tokenized_dataset を訓練用に準備するため手動で後処理します。

  1. text カラムを削除します、モデルは入力として raw テキストを受け取らないからです :
    tokenized_datasets = tokenized_datasets.remove_columns(["text"])
    

  2. label カラムを labels に名前変更します、モデルは引数が labels と命名されていることを想定しているからです :
    tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
    

  3. リストの代わりに 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

データのバッチに対してイテレートできるように、訓練とテストデータセット用 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)

 

Optimizer と学習率スケジューラ

モデルを再調整するために optimizer と学習率スケジューラを作成します。PyTorch からの AdamW optimizer を使用しましょう :

from torch.optim import AdamW

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

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

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 が持つならばそれを利用するように device を指定します。そうでないなら、CPU 上の訓練は数分ではなく数時間かかるかもしれません。

import torch

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

Great, now you are ready to train! 🥳

 

訓練ループ

訓練進捗を追跡するため、訓練ステップ数に渡るプログレスバーを追加する 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()

 

以上