Annuaire d'articles
Traitement des données
Charger un ensemble de données depuis le Hub
Nous pouvons utiliser le code suivant pour former un classificateur de séquence
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()
Évidemment, il est impossible d’obtenir de bons résultats avec deux phrases, nous avons donc besoin d’un ensemble de données plus important.
-
huggingface enregistre également de nombreux ensembles de données, qui peuvent être
load_data
téléchargés viafrom 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 }) })
-
Nous pouvons accéder à chaque paire de phrases en indexant
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 .'}
-
Si vous souhaitez connaître la signification de chaque partie de l'ensemble de données, vous pouvez
features
la visualiser via l'attributraw_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)}
-
Prétraiter un ensemble de données
Le tokenizer peut traiter directement les données appariées, ce qui BERT
est espéré
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
Il est utilisé pour distinguer la première phrase de la deuxième phrase, qui peut ne pas être disponible dans tous les tokenizer. Ils ne sont restitués que si le modèle sait quoi en faire, puisqu'il les a vus lors du pré-entraînement.- Ici,
BERT
token_type_dis est utilisé en pré-formation - Si nous décodons, nous constaterons que le format est [CLS] phrase1 [SEP] phrase2 [SEP]
- Ici,
Nous pouvons laisser le tokenizer traiter une liste de paires de phrases en lui donnant une liste de la première phrase et une liste de la deuxième phrase.
tokenized_dataset = tokenizer(
raw_datasets["train"]["sentence1"],
raw_datasets["train"]["sentence2"],
padding=True,
truncation=True,
)
Cela renvoie un dictionnaire, et nous pouvons ajouter ce résultat renvoyé à l'ensemble de données d'origine
def tokenize_function(example):
return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
- Nous utilisons batched=True lors de l'appel de map, afin que la fonction soit appliquée à plusieurs éléments de l'ensemble de données en même temps, au lieu de s'appliquer à chaque élément séparément (plus rapide)
- Nous pouvons également modifier les champs existants si la fonction de prétraitement renvoie une nouvelle valeur pour une clé existante dans l'ensemble de données.
Rembourrage dynamique
La fonction chargée de mettre les échantillons dans un lot est collate function
(c'est un paramètre de DataLoader, qui convertit les échantillons en tenseur pytorch par défaut et les connecte)
Nous différons délibérément le remplissage, en l'appliquant uniquement dans chaque lot, et évitons les entrées trop longues avec beaucoup de remplissage.
- Cela rendra la formation plus rapide
- Mais il y aura des problèmes sur le TPU, le TPU préfère une forme fixe
Pour ce faire en pratique, nous devons définir une fonction d'assemblage qui appliquera la quantité correcte de remplissage aux éléments de l'ensemble de données que nous souhaitons regrouper.
from transformers import DataCollatorWithPadding
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
samples = tokenized_datasets["train"][:8]
batch = data_collator(samples)
- Un tokenize doit être donné lors de l'initialisation, afin qu'il sache quel token remplir, s'il doit remplir à gauche ou à droite
Affiner un modèle avec l'API Trainer
Transformers fournit une classe Trainer pour aider à affiner le modèle de pré-formation sur ses propres données. Une fois le traitement des données terminé, il n'y a que quelques étapes pour définir le Trainer.
Nous résumons les opérations précédentes
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)
Entraînement
Avant de définir le formateur, vous devez définir la classe TrainingArguments, qui inclut tous les paramètres. Il nous suffit de fournir l'emplacement où le modèle doit être enregistré, et d'autres paramètres peuvent être bien entraînés en conservant la valeur par défaut.
from transformers import TrainingArguments
training_args = TrainingArguments("test-trainer")
définir un modèle
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
Définir un formateur
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,
)
- Lorsque nous passons le tokenizer, le data_collator ici prendra par défaut ce que nous avons défini ci-dessus, nous pouvons donc omettre cette étape
former
trainer.train()
- affichera une perte d'entraînement toutes les 500 étapes, mais ne nous dira pas à quel point le modèle est bon :
- N'a pas laissé le formateur évaluer pendant la formation (en définissant évaluation_strategy sur steps/epoch)
- Aucun moyen n'est fourni
compute_metrics()
pour calculer une métrique lors de l'évaluation, juste une perte de retour, ce qui n'est pas intuitif
Évaluation
Nous pouvons utiliser Trainer.predict() pour faire prédire notre modèle
predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)
>(408, 2) (408,)
- La sortie est un tuple avec trois champs (prédictions, label_ids, métriques)
- Ici, les métriques n'ont que des pertes et du temps d'exécution, si nous définissons notre propre fonction calculate_metrics() et la transmettons à Trainer , ce champ contiendra également le résultat de computation_metrics()
- les prédictions sont des logits pour chaque élément de l'ensemble de données
Pour comparer notre prédiction à la vraie étiquette, nous avons besoin de l'indice de la valeur maximale sur le deuxième axe :
import numpy as np
preds = np.argmax(predictions.predictions, axis=-1)
Pour construire computing_metraic(), nous pouvons utiliser la bibliothèque Evaluate
import evaluate
metric = evaluate.load("glue", "mrpc")
metric.compute(predictions=preds, references=predictions.label_ids)
> {
'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
Finalement, en mettant le tout ensemble, nous obtenons la fonction calculate_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)
À ce stade, nous pouvons définir un nouveau formateur
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,
)
- Notez que l’évaluation sera effectuée à chaque époque. Cette fois, il affichera la perte de validation et les métriques à la fin de chaque époque en plus de la perte de formation.
Le processus derrière Trainer
Un bref résumé du traitement des données
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)
préparation avant l'entraînement
Nous devons effectuer certains traitements sur tokenized_datasets, en particulier :
sentence1
Supprimez les colonnes (telles que les colonnes etsentence2
) qui correspondent aux valeurs que le modèle n'attend pas .- Renommez le nom de la colonne
label
enlabels
(car le modèle s'attend à ce que le paramètre soitlabels
). - Formatez l'ensemble de données afin qu'il renvoie des tenseurs PyTorch au lieu de listes.
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
Charger le chargeur de données
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
)
modèle de charge
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-
Le modèle est chargé sur le GPU
import torch device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") model.to(device)
optimiseur de charge
from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=5e-5)
planificateur de chargement
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,
)
boucle d'entraînement
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)
évaluer la boucle
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()
Cet indicateur cumule en fait pour nous tous les résultats lorsque nous utilisons la méthode de la boucle de prévisionbatch
. Une fois que nous avons tout accumulébatch
, nous pouvonsmetric.compute()
obtenir le résultat final en utilisant
Entraînement en circuit avec Accelerate : un entraînement complet - Cours Hugging Face