[Huggingface シリーズの学習] 事前学習モデルの微調整

データの処理

ハブからデータセットをロードする

次のコードを使用してシーケンス分類器をトレーニングできます。

import torch
from transformers import AdamW, AutoTokenizer, AutoModelForSequenceClassification

# Same as before
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "This course is amazing!",
]
batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")

# This is new
batch["labels"] = torch.tensor([1, 1])

optimizer = AdamW(model.parameters())
loss = model(**batch).loss
loss.backward()
optimizer.step()

明らかに、2 つの文で良い結果を得るのは不可能なので、より大きなデータセットが必要です。

  • ハグフェイスは大量のデータセットも保存します。これらのデータセットは、次の方法でload_dataダウンロードできます。

    from datasets import load_dataset
    
    raw_datasets = load_dataset("glue", "mrpc") # GLUE benchmark 中的 MRPC数据集
    raw_datasets
    > DatasetDict({
          
          
        train: Dataset({
          
          
            features: ['sentence1', 'sentence2', 'label', 'idx'],
            num_rows: 3668
        })
        validation: Dataset({
          
          
            features: ['sentence1', 'sentence2', 'label', 'idx'],
            num_rows: 408
        })
        test: Dataset({
          
          
            features: ['sentence1', 'sentence2', 'label', 'idx'],
            num_rows: 1725
        })
    })
    
    • インデックスを作成することで各文のペアにアクセスできます

      raw_train_dataset = raw_datasets["train"]
      raw_train_dataset[0]
      
      >{
              
              'idx': 0,
       'label': 1,
       'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
       'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
      
    • データセットの各部分の意味を知りたい場合は、features属性を通じてそれを表示できます。

      raw_train_dataset.features
      {
              
              'sentence1': Value(dtype='string', id=None),
       'sentence2': Value(dtype='string', id=None),
       'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
       'idx': Value(dtype='int32', id=None)}
      

データセットの前処理

トークナイザーはペアになったデータを直接処理できるため、これがBERT期待されています。

inputs = tokenizer("This is the first sentence.", "This is the second one.")
inputs
>{
    
     
  'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
  'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
  'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
  • token_type_idsこれは最初の文と 2 番目の文を区別するために使用されますが、すべてのトークナイザーで利用できるわけではありません。これらは、事前トレーニング中に確認されているため、モデルがそれらをどう扱うべきかを知っている場合にのみ返されます。
    • ここでBERTtoken_type_dis は事前トレーニングで使用されます
    • デコードすると、[CLS] 文1 [SEP] 文2 [SEP] という形式であることがわかります。

最初の文のリストと 2 番目の文のリストを与えることで、トークナイザーに文のペアのリストを処理させることができます。

tokenized_dataset = tokenizer(
    raw_datasets["train"]["sentence1"],
    raw_datasets["train"]["sentence2"],
    padding=True,
    truncation=True,
)

これにより辞書が返され、この返された結果を元のデータセットに追加できます。

def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
  • Map を呼び出すときに、batched=True を使用します。これにより、関数が各要素に個別に適用されるのではなく、データセットの複数の要素に同時に適用されます (高速になります)。
  • 前処理関数がデータセット内の既存のキーの新しい値を返す場合は、既存のフィールドを変更することもできます。

動的パディング

サンプルをバッチに入れる役割を担う関数は次のとおりですcollate function(これは、デフォルトでサンプルを pytorch tensor に変換し、それらを接続する DataLoader のパラメーターです)

パディングを意図的に延期し、各バッチでのみ適用し、大量のパディングを伴う過度に長い入力を避けます。

  • これによりトレーニングがより速くなります
  • ただし、TPU には問題が発生します。TPU は固定形状を好みます。

これを実際に行うには、バッチ処理するデータセット内のアイテムに正しい量のパディングを適用する照合関数を定義する必要があります。

from transformers import DataCollatorWithPadding

data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
samples = tokenized_datasets["train"][:8]
batch = data_collator(samples)
  • どのトークンを入力するか、左側に入力するか右側に入力するかを知ることができるように、初期化中にトークン化を行う必要があります。

Trainer API を使用したモデルの微調整

Transformers は、独自のデータに基づいて事前トレーニング モデルを微調整するのに役立つ Trainer クラスを提供します。データ処理が完了したら、Trainer を定義する手順はわずかです。

これまでの操作をまとめます

from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)


tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

トレーニング

トレーナーを定義する前に、すべてのパラメーターを含む TrainingArguments クラスを定義する必要があります。モデルを保存する場所を指定するだけでよく、他のパラメーターはデフォルトを維持することで適切にトレーニングできます。

from transformers import TrainingArguments

training_args = TrainingArguments("test-trainer")

モデルを定義する

from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

トレーナーを定義する

from transformers import Trainer

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
)
  • tokenizer に渡すと、ここでの data_collat​​or は上で定義したものにデフォルト設定されるため、この手順は省略できます。

電車

trainer.train()
  • 500 ステップごとにトレーニング損失が出力されますが、モデルがどの程度優れているかはわかりません。
    • トレーニング中にトレーナーに評価させませんでした (evaluation_strategy をステップ/エポックに設定することにより)
    • compute_metrics()評価中にメトリックを計算するために「いいえ」が提供されますが、リターン・ロスだけが提供されるため、直感的ではありません。

評価

Trainer.predict() を使用してモデルを予測させることができます。

predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)
>(408, 2) (408,)
  • 出力は 3 つのフィールド (予測、label_id、メトリクス) を含むタプルです。
  • ここでメトリクスには損失とある程度の実行時間があります。独自のcompute_metrics()関数を定義してTrainerに渡すと、このフィールドにはcompute_metrics()の結果も含まれます。
  • 予測はデータセットの各要素のロジットです

予測値を真のラベルと比較するには、2 番目の軸の最大値のインデックスが必要です。

import numpy as np

preds = np.argmax(predictions.predictions, axis=-1)

compute_metraic() を構築するには、Evaluate ライブラリを使用できます。

import evaluate

metric = evaluate.load("glue", "mrpc")
metric.compute(predictions=preds, references=predictions.label_ids)
> {
    
    'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}

最後にすべてをまとめると、compute_metrics() 関数が得られます。

def compute_metrics(eval_preds):
    metric = evaluate.load("glue", "mrpc")
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)

この時点で、新しいトレーナーを定義できます。

training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
  • 評価はエポックごとに実行されることに注意してください。今回は、トレーニング損失に加えて、各エポックの終了時の検証損失とメトリクスを出力します。

トレーナーの背後にあるプロセス

データ処理の簡単な概要

from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

トレーニング前の準備

tokenized_datasets に対していくつかの処理を行う必要があります。具体的には次のとおりです。

  • モデルが予期しない値に対応する列 (列sentence1sentence2列など)を削除します。
  • 列名labelを に変更しますlabels(モデルではパラメーターが であると想定されているためlabels)。
  • リストの代わりに PyTorch テンソルを返すようにデータセットをフォーマットします。
tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
tokenized_datasets.set_format("torch")
tokenized_datasets["train"].column_names

データローダーのロード

from torch.utils.data import DataLoader

train_dataloader = DataLoader(
    tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
)

ロードモデル

from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
  • モデルはGPUにロードされます

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

ロードオプティマイザ

from transformers import AdamW

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

ロードスケジューラ

from transformers import get_scheduler

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

トレーニングループ

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)

ループを評価する

import evaluate

metric = evaluate.load("glue", "mrpc")
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()
  • このインジケーターは、予測ループのメソッドを使用するadd_batch()ときに実際にすべての結果を蓄積しますbatchすべてを蓄積したらbatch、次を使用してmetric.compute()最終結果を取得できます。

Accelerate を使用したサーキット トレーニング:完全なワークアウト - ハグフェイス コース

おすすめ

転載: blog.csdn.net/qq_52852138/article/details/128997766