記事ディレクトリ
データの処理
ハブからデータセットをロードする
次のコードを使用してシーケンス分類器をトレーニングできます。
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 番目の文を区別するために使用されますが、すべてのトークナイザーで利用できるわけではありません。これらは、事前トレーニング中に確認されているため、モデルがそれらをどう扱うべきかを知っている場合にのみ返されます。- ここで
BERT
token_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_collator は上で定義したものにデフォルト設定されるため、この手順は省略できます。
電車
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 に対していくつかの処理を行う必要があります。具体的には次のとおりです。
- モデルが予期しない値に対応する列 (列
sentence1
やsentence2
列など)を削除します。 - 列名
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 を使用したサーキット トレーニング:完全なワークアウト - ハグフェイス コース