GPT2 Für die Textklassifizierung mit Hugging Face Transformers

Quellknoten: 809063

Textklassifizierung

Dieses Notebook wird zur Feinabstimmung des GPT2-Modells für die Textklassifizierung verwendet Gesicht umarmen Transformer Bibliothek für einen benutzerdefinierten Datensatz.

Hugging Face ist für uns sehr nett, da es alle Funktionen enthält, die für die Verwendung von GPT2 bei Klassifizierungsaufgaben erforderlich sind. Danke Hugging Face!

Ich konnte nicht viele Informationen darüber finden, wie man GPT2 zur Klassifizierung verwendet, daher habe ich beschlossen, dieses Tutorial mit einer ähnlichen Struktur wie bei anderen Transformatormodellen zu erstellen.

Wenn dieser ausführliche Bildungsinhalt für Sie nützlich ist, Abonnieren Sie unsere AI Research Mailingliste benachrichtigt werden, wenn wir neues Material veröffentlichen. 

Hauptidee: Da es sich bei GPT2 um einen Decoder-Transformer handelt, wird der letzte Token der Eingabesequenz verwendet, um Vorhersagen über den nächsten Token zu treffen, der der Eingabe folgen sollte. Das bedeutet, dass das letzte Token der Eingabesequenz alle für die Vorhersage erforderlichen Informationen enthält. Vor diesem Hintergrund können wir diese Informationen verwenden, um eine Vorhersage in einer Klassifizierungsaufgabe statt in einer Generierungsaufgabe zu treffen.

Mit anderen Worten: Anstatt die erste Token-Einbettung zu verwenden, um eine Vorhersage zu treffen, wie wir es in Bert tun, werden wir die letzte Token-Einbettung verwenden, um eine Vorhersage mit GPT2 zu treffen.

Da uns nur der erste Token in Bert interessierte, füllten wir nach rechts auf. Jetzt verwenden wir in GPT2 das letzte Token für die Vorhersage, daher müssen wir links auffüllen. Dank eines netten Upgrades auf HuggingFace Transformers sind wir in der Lage, den GPT2-Tokenizer genau dafür zu konfigurieren.

Was muss ich für dieses Notizbuch wissen?

Da ich PyTorch zur Feinabstimmung unserer Transformatormodelle verwende, ist jedes Wissen über PyTorch sehr nützlich.

Ein bisschen über das wissen Transformer Bibliothek hilft auch.

Wie benutze ich dieses Notebook?

Wie bei jedem Projekt habe ich auch bei der Erstellung dieses Notebooks auf Wiederverwendbarkeit geachtet.

Alle Änderungen werden im Datenverarbeitungsteil vorgenommen, wo Sie den PyTorch-Datensatz, den Data Collator und den DataLoader an Ihre eigenen Datenanforderungen anpassen müssen.

Alle Parameter, die geändert werden können, finden Sie unter Importe Sektion. Jeder Parameter ist gut kommentiert und strukturiert, um so intuitiv wie möglich zu sein.

Datensatz

In diesem Notizbuch werden Transformatoren vor dem Training in einem benutzerdefinierten Datensatz behandelt. Ich werde die bekannten Filmkritiken positiv - negativ beschriftet verwenden Großer Datensatz für Filmkritiken.

Die Beschreibung auf der Stanford-Website:

Dies ist ein Datensatz für die Klassifizierung der binären Stimmung, der wesentlich mehr Daten enthält als frühere Benchmark-Datensätze. Wir bieten 25,000 hochpolare Filmkritiken für das Training und 25,000 zum Testen. Es gibt zusätzliche unbeschriftete Daten zur Verwendung. Es werden Rohtext und bereits verarbeitete Wortformate bereitgestellt. Weitere Informationen finden Sie in der README-Datei in der Version.

Warum dieser Datensatz? Ich glaube, es ist ein leicht zu verstehender und zu verwendender Datensatz für die Klassifizierung. Ich denke, es macht immer Spaß, mit Stimmungsdaten zu arbeiten.

Programmierung

Jetzt lass uns etwas programmieren! Wir werden jede Codierungszelle im Notizbuch durchgehen und beschreiben, was sie tut, was der Code ist und wann sie relevant ist - zeigen Sie die Ausgabe.

Ich habe dieses Format so gestaltet, dass es einfach zu befolgen ist, wenn Sie sich entscheiden, jede Codezelle in Ihrem eigenen Python-Notizbuch auszuführen.

Wenn ich aus einem Tutorial lerne, versuche ich immer, die Ergebnisse zu replizieren. Ich glaube, es ist einfach zu folgen, wenn Sie den Code neben den Erklärungen haben.

Downloads

Laden Sie die Großer Datensatz für Filmkritiken und entpacken Sie es lokal.

Download the dataset.
!wget -q -nc http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
Unzip the dataset.
!tar -zxf /content/aclImdb_v1.tar.gz

Installiert

  • Transformer Die Bibliothek muss installiert werden, um den großartigen Code von Hugging Face verwenden zu können. Um die neueste Version zu erhalten, werde ich sie direkt von GitHub installieren.
  • ml_dinge Bibliothek für verschiedene Aufgaben im Zusammenhang mit maschinellem Lernen. Ich habe diese Bibliothek erstellt, um die Menge an Code zu reduzieren, die ich für jedes maschinelle Lernprojekt schreiben muss.
# Install transformers library.
!pip install -q git+https://github.com/huggingface/transformers.git
# Install helper functions.
!pip install -q git+https://github.com/gmihaila/ml_things.git
Installing build dependencies ... done Getting requirements to build wheel ... done Preparing wheel metadata ... done |████████████████████████████████| 2.9MB 6.7MB/s |████████████████████████████████| 890kB 48.9MB/s |████████████████████████████████| 1.1MB 49.0MB/s Building wheelfor transformers (PEP 517) ... done Building wheel for sacremoses (setup.py) ... done |████████████████████████████████| 71kB 5.2MB/s Building wheel for ml-things (setup.py) ... done Building wheel for ftfy (setup.py) ... done

Importe

Importieren Sie alle benötigten Bibliotheken für dieses Notebook. Deklarieren Sie die für dieses Notebook verwendeten Parameter:

  • set_seed(123) - Es ist immer gut, einen festen Samen für die Reproduzierbarkeit zu setzen.
  • epochs - Anzahl der Trainingsepochen (Autoren empfehlen zwischen 2 und 4).
  • batch_size – Anzahl der Stapel – abhängig von der maximalen Sequenzlänge und dem GPU-Speicher. Bei einer Sequenzlänge von 512 funktioniert ein Stapel von 10 USUALY ohne Probleme mit dem Cuda-Speicher. Für eine kurze Sequenzlänge können Sie eine Charge von 32 oder mehr ausprobieren. max_length – Textsequenzen auf eine bestimmte Länge auffüllen oder abschneiden. Ich werde es auf 60 einstellen, um das Training zu beschleunigen.
  • device – Suchen Sie nach der zu verwendenden GPU. Wird standardmäßig die CPU verwenden, wenn keine GPU gefunden wird.
  • model_name_or_path – Name des Transformatormodells – verwendet bereits vorab trainiertes Modell. Pfad des Transformatormodells – lädt Ihr eigenes Modell von der lokalen Festplatte. In diesem Tutorial werde ich verwenden gpt2 Modell.
  • labels_ids - Wörterbuch der Beschriftungen und ihrer ID - Dies wird verwendet, um Zeichenfolgenbeschriftungen in Zahlen umzuwandeln.
  • n_labels - Wie viele Labels verwenden wir in diesem Datensatz? Dies wird verwendet, um die Größe des Klassifizierungskopfes zu bestimmen.
import io
import os
import torch
from tqdm.notebook import tqdm
from torch.utils.data import Dataset, DataLoader
from ml_things import plot_dict, plot_confusion_matrix, fix_text
from sklearn.metrics import classification_report, accuracy_score
from transformers import (set_seed, TrainingArguments, Trainer, GPT2Config, GPT2Tokenizer, AdamW, get_linear_schedule_with_warmup, GPT2ForSequenceClassification) # Set seed for reproducibility.
set_seed(123) # Number of training epochs (authors on fine-tuning Bert recommend between 2 and 4).
epochs = 4 # Number of batches - depending on the max sequence length and GPU memory.
# For 512 sequence length batch of 10 works without cuda memory issues.
# For small sequence length can try batch of 32 or higher.
batch_size = 32 # Pad or truncate text sequences to a specific length
# if `None` it will use maximum sequence of word piece tokens allowed by model.
max_length = 60 # Look for gpu to use. Will use `cpu` by default if no gpu found.
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # Name of transformers model - will use already pretrained model.
# Path of transformer model - will load your own model from local disk.
model_name_or_path = 'gpt2' # Dictionary of labels and their id - this will be used to convert.
# String labels to number ids.
labels_ids = {'neg': 0, 'pos': 1} # How many labels are we using in training.
# This is used to decide size of classification head.
n_labels = len(labels_ids)

Hilfsfunktionen

Ich möchte alle Klassen und Funktionen behalten, die in diesem Notizbuch in diesem Abschnitt verwendet werden, um ein sauberes Erscheinungsbild des Notizbuchs zu gewährleisten:

MovieReviewsDataset(Datensatz)

Wenn Sie zuvor mit PyTorch gearbeitet haben, ist dies ziemlich normal. Wir benötigen diese Klasse, um unseren Datensatz einzulesen, ihn zu analysieren und Texte mit den zugehörigen Beschriftungen zurückzugeben.

In diesem Kurs muss ich nur den Inhalt jeder Datei einlesen, fix_text verwenden, um etwaige Unicode-Probleme zu beheben und positive und negative Gefühle im Auge zu behalten.

Ich werde alle Texte und Beschriftungen in Listen anhängen.

Es gibt drei Hauptteile dieser PyTorch-Dataset-Klasse:

  • drin() Hier lesen wir den Datensatz ein und wandeln Text und Beschriftungen in Zahlen um.
  • len () Dabei müssen wir die Anzahl der eingelesenen Beispiele zurückgeben. Dies wird beim Aufruf von len(MovieReviewsDataset()) verwendet.
  • getitem() Nimmt als Eingabe immer einen int-Wert, der angibt, welches Beispiel aus unseren Beispielen aus unserem Datensatz zurückgegeben werden soll. Wenn ein Wert von 3 übergeben wird, geben wir das Beispiel aus unserem Datensatz an Position 3 zurück.
class MovieReviewsDataset(Dataset): r"""PyTorch Dataset class for loading data. This is where the data parsing happens. This class is built with reusability in mind: it can be used as is as. Arguments: path (:obj:`str`): Path to the data partition. """ def __init__(self, path, use_tokenizer): # Check if path exists. if not os.path.isdir(path): # Raise error if path is invalid. raise ValueError('Invalid `path` variable! Needs to be a directory') self.texts = [] self.labels = [] # Since the labels are defined by folders with data we loop # through each label. for label in ['pos', 'neg']: sentiment_path = os.path.join(path, label) # Get all files from path. files_names = os.listdir(sentiment_path)#[:10] # Sample for debugging. # Go through each file and read its content. for file_name in tqdm(files_names, desc=f'{label} files'): file_path = os.path.join(sentiment_path, file_name) # Read content. content = io.open(file_path, mode='r', encoding='utf-8').read() # Fix any unicode issues. content = fix_text(content) # Save content. self.texts.append(content) # Save encode labels. self.labels.append(label) # Number of exmaples. self.n_examples = len(self.labels) return def __len__(self): r"""When used `len` return the number of examples. """ return self.n_examples def __getitem__(self, item): r"""Given an index return an example from the position. Arguments: item (:obj:`int`): Index position to pick an example to return. Returns: :obj:`Dict[str, str]`: Dictionary of inputs that contain text and asociated labels. """ return {'text':self.texts[item], 'label':self.labels[item]}

Gpt2ClassificationCollator

Ich verwende diese Klasse, um den Data Collator zu erstellen. Dies wird im DataLoader verwendet, um die Datenströme zu erstellen, die dem Modell zugeführt werden. Ich verwende den Tokenizer und den Label-Encoder für jede Sequenz, um Texte und Labels in Zahlen umzuwandeln.

Zu unserem Glück hat Hugging Face an alles gedacht und dem Tokenizer die ganze schwere Arbeit überlassen (Text in Token aufteilen, auffüllen, abschneiden, Text in Zahlen kodieren) und ist sehr einfach zu verwenden!

Es gibt zwei Hauptteile dieser Data Collator-Klasse:

  • drin() wo wir den Tokenizer initialisieren, den wir verwenden möchten, wie wir unsere Etiketten kodieren und ob wir die Sequenzlänge auf einen anderen Wert einstellen müssen.
  • Anruf() Wird als Funktionskollator verwendet, der eine Reihe von Datenbeispielen als Eingabe verwendet. Es muss ein Objekt mit dem Format zurückgeben, das unserem Modell zugeführt werden kann. Glücklicherweise erledigt das unser Tokenizer für uns und gibt ein Wörterbuch mit Variablen zurück, die auf folgende Weise in das Modell eingespeist werden können: model(**inputs). Da wir das Modell verfeinern, habe ich auch die Beschriftungen eingefügt.
class Gpt2ClassificationCollator(object): r""" Data Collator used for GPT2 in a classificaiton rask. It uses a given tokenizer and label encoder to convert any text and labels to numbers that can go straight into a GPT2 model. This class is built with reusability in mind: it can be used as is as long as the `dataloader` outputs a batch in dictionary format that can be passed straight into the model - `model(**batch)`. Arguments: use_tokenizer (:obj:`transformers.tokenization_?`): Transformer type tokenizer used to process raw text into numbers. labels_ids (:obj:`dict`): Dictionary to encode any labels names into numbers. Keys map to labels names and Values map to number associated to those labels. max_sequence_len (:obj:`int`, `optional`) Value to indicate the maximum desired sequence to truncate or pad text sequences. If no value is passed it will used maximum sequence size supported by the tokenizer and model. """ def __init__(self, use_tokenizer, labels_encoder, max_sequence_len=None): # Tokenizer to be used inside the class. self.use_tokenizer = use_tokenizer # Check max sequence length. self.max_sequence_len = use_tokenizer.model_max_length if max_sequence_len is None else max_sequence_len # Label encoder used inside the class. self.labels_encoder = labels_encoder return def __call__(self, sequences): r""" This function allowes the class objesct to be used as a function call. Sine the PyTorch DataLoader needs a collator function, I can use this class as a function. Arguments: item (:obj:`list`): List of texts and labels. Returns: :obj:`Dict[str, object]`: Dictionary of inputs that feed into the model. It holddes the statement `model(**Returned Dictionary)`. """ # Get all texts from sequences list. texts = [sequence['text'] for sequence in sequences] # Get all labels from sequences list. labels = [sequence['label'] for sequence in sequences] # Encode all labels using label encoder. labels = [self.labels_encoder[label] for label in labels] # Call tokenizer on all texts to convert into tensors of numbers with # appropriate padding. inputs = self.use_tokenizer(text=texts, return_tensors="pt", padding=True, truncation=True, max_length=self.max_sequence_len) # Update the inputs with the associated encoded labels as tensor. inputs.update({'labels':torch.tensor(labels)}) return inputs

Zug (Datenlader, Optimierer_, Scheduler_, Gerät_)

Ich habe diese Funktion erstellt, um einen vollständigen Durchlauf durch das DataLoader-Objekt durchzuführen (das DataLoader-Objekt wird aus unserem Objekt vom Typ Dataset* mithilfe der Klasse **MovieReviewsDataset erstellt). Dies ist im Grunde ein Epochenzug durch den gesamten Datensatz.

Der Datenlader wird aus PyTorch DataLoader erstellt, der das aus der MovieReviewsDataset-Klasse erstellte Objekt übernimmt und jedes Beispiel in Stapeln ablegt. Auf diese Weise können wir unsere Modelldatenstapel füttern!

„optimierer_“ und „scheduler_“ sind in PyTorch sehr verbreitet. Sie sind erforderlich, um die Parameter unseres Modells und unsere Lernrate während des Trainings zu aktualisieren. Es gibt noch viel mehr als das, aber ich werde nicht ins Detail gehen. Das kann tatsächlich ein riesiges Kaninchenloch sein, da hinter diesen Funktionen VIEL passiert, worüber wir uns keine Sorgen machen müssen. Danke PyTorch!

Dabei verfolgen wir die tatsächlichen Etiketten und die vorhergesagten Etiketten sowie den Verlust.

def train(dataloader, optimizer_, scheduler_, device_): r""" Train pytorch model on a single pass through the data loader. It will use the global variable `model` which is the transformer model loaded on `_device` that we want to train on. This function is built with reusability in mind: it can be used as is as long as the `dataloader` outputs a batch in dictionary format that can be passed straight into the model - `model(**batch)`. Arguments: dataloader (:obj:`torch.utils.data.dataloader.DataLoader`): Parsed data into batches of tensors. optimizer_ (:obj:`transformers.optimization.AdamW`): Optimizer used for training. scheduler_ (:obj:`torch.optim.lr_scheduler.LambdaLR`): PyTorch scheduler. device_ (:obj:`torch.device`): Device used to load tensors before feeding to model. Returns: :obj:`List[List[int], List[int], float]`: List of [True Labels, Predicted Labels, Train Average Loss]. """ # Use global variable for model. global model # Tracking variables. predictions_labels = [] true_labels = [] # Total loss for this epoch. total_loss = 0 # Put the model into training mode. model.train() # For each batch of training data... for batch in tqdm(dataloader, total=len(dataloader)): # Add original labels - use later for evaluation. true_labels += batch['labels'].numpy().flatten().tolist() # move batch to device batch = {k:v.type(torch.long).to(device_) for k,v in batch.items()} # Always clear any previously calculated gradients before performing a # backward pass. model.zero_grad() # Perform a forward pass (evaluate the model on this training batch). # This will return the loss (rather than the model output) because we # have provided the `labels`. # The documentation for this a bert model function is here: # https://huggingface.co/transformers/v2.2.0/model_doc/bert.html#transformers.BertForSequenceClassification outputs = model(**batch) # The call to `model` always returns a tuple, so we need to pull the # loss value out of the tuple along with the logits. We will use logits # later to calculate training accuracy. loss, logits = outputs[:2] # Accumulate the training loss over all of the batches so that we can # calculate the average loss at the end. `loss` is a Tensor containing a # single value; the `.item()` function just returns the Python value # from the tensor. total_loss += loss.item() # Perform a backward pass to calculate the gradients. loss.backward() # Clip the norm of the gradients to 1.0. # This is to help prevent the "exploding gradients" problem. torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) # Update parameters and take a step using the computed gradient. # The optimizer dictates the "update rule"--how the parameters are # modified based on their gradients, the learning rate, etc. optimizer.step() # Update the learning rate. scheduler.step() # Move logits and labels to CPU logits = logits.detach().cpu().numpy() # Convert these logits to list of predicted labels values. predictions_labels += logits.argmax(axis=-1).flatten().tolist() # Calculate the average loss over the training data. avg_epoch_loss = total_loss / len(dataloader) # Return all true labels and prediction for future evaluations. return true_labels, predictions_labels, avg_epoch_loss

Validierung (Datenlader, device_)

Ich habe diese Funktion auf sehr ähnliche Weise wie Train implementiert, jedoch ohne die Parameter Update, Rückwärtsdurchlauf und Gradiententeil. Wir müssen nicht alle diese SEHR rechenintensiven Aufgaben erledigen, da uns nur die Vorhersagen unseres Modells wichtig sind.

Ich verwende den DataLoader auf ähnliche Weise wie im Zug, um Stapel herauszuholen und sie unserem Modell zuzuführen.

Dabei verfolge ich die tatsächlichen Etiketten und die vorhergesagten Etiketten sowie den Verlust.

def validation(dataloader, device_): r"""Validation function to evaluate model performance on a separate set of data. This function will return the true and predicted labels so we can use later to evaluate the model's performance. This function is built with reusability in mind: it can be used as is as long as the `dataloader` outputs a batch in dictionary format that can be passed straight into the model - `model(**batch)`. Arguments: dataloader (:obj:`torch.utils.data.dataloader.DataLoader`): Parsed data into batches of tensors. device_ (:obj:`torch.device`): Device used to load tensors before feeding to model. Returns: :obj:`List[List[int], List[int], float]`: List of [True Labels, Predicted Labels, Train Average Loss] """ # Use global variable for model. global model # Tracking variables predictions_labels = [] true_labels = [] #total loss for this epoch. total_loss = 0 # Put the model in evaluation mode--the dropout layers behave differently # during evaluation. model.eval() # Evaluate data for one epoch for batch in tqdm(dataloader, total=len(dataloader)): # add original labels true_labels += batch['labels'].numpy().flatten().tolist() # move batch to device batch = {k:v.type(torch.long).to(device_) for k,v in batch.items()} # Telling the model not to compute or store gradients, saving memory and # speeding up validation with torch.no_grad(): # Forward pass, calculate logit predictions. # This will return the logits rather than the loss because we have # not provided labels. # token_type_ids is the same as the "segment ids", which # differentiates sentence 1 and 2 in 2-sentence tasks. # The documentation for this `model` function is here: # https://huggingface.co/transformers/v2.2.0/model_doc/bert.html#transformers.BertForSequenceClassification outputs = model(**batch) # The call to `model` always returns a tuple, so we need to pull the # loss value out of the tuple along with the logits. We will use logits # later to to calculate training accuracy. loss, logits = outputs[:2] # Move logits and labels to CPU logits = logits.detach().cpu().numpy() # Accumulate the training loss over all of the batches so that we can # calculate the average loss at the end. `loss` is a Tensor containing a # single value; the `.item()` function just returns the Python value # from the tensor. total_loss += loss.item() # get predicitons to list predict_content = logits.argmax(axis=-1).flatten().tolist() # update list predictions_labels += predict_content # Calculate the average loss over the training data. avg_epoch_loss = total_loss / len(dataloader) # Return all true labels and prediciton for future evaluations. return true_labels, predictions_labels, avg_epoch_loss

Modell und Tokenizer laden

Laden der drei wesentlichen Teile des vorab trainierten GPT2-Transformators: Konfiguration, Tokenizer und Modell.

Für dieses Beispiel werde ich verwenden gpt2 von vortrainierten HuggingFace-Transformatoren. Sie können jede gewünschte Variante von GP2 verwenden.

Bei der Erstellung der model_config Ich werde die Anzahl der Etiketten angeben, die ich für meine Klassifizierungsaufgabe benötige. Da ich nur zwei Stimmungen vorhersage: positiv und negativ, benötige ich dafür nur zwei Bezeichnungen num_labels.

Erstellen der tokenizer ist bei Verwendung der Transformers-Bibliothek ziemlich Standard. Nach dem Erstellen des Tokenizers ist es für dieses Tutorial wichtig, den Abstand links festzulegen tokenizer.padding_side = "left" und initialisieren Sie das Fülltoken mit tokenizer.eos_token Dabei handelt es sich um das ursprüngliche Ende-der-Sequenz-Token des GPT2. Dies ist der wichtigste Teil dieses Tutorials, da GPT2 das letzte Token für die Vorhersage verwendet und wir daher nach links auffüllen müssen.

HuggingFace hat bereits die meiste Arbeit für uns erledigt und dem GPT2-Modell eine Klassifizierungsebene hinzugefügt. Bei der Erstellung des von mir verwendeten Modells GPT2ForSequenceClassification. Da wir ein benutzerdefiniertes Fülltoken haben, müssen wir es für das verwendete Modell initialisieren model.config.pad_token_id. Schließlich müssen wir das Modell auf das zuvor definierte Gerät verschieben.

# Get model configuration.
print('Loading configuraiton...')
model_config = GPT2Config.from_pretrained(pretrained_model_name_or_path=model_name_or_path, num_labels=n_labels) # Get model's tokenizer.
print('Loading tokenizer...')
tokenizer = GPT2Tokenizer.from_pretrained(pretrained_model_name_or_path=model_name_or_path)
# default to left padding
tokenizer.padding_side = "left"
# Define PAD Token = EOS Token = 50256
tokenizer.pad_token = tokenizer.eos_token # Get the actual model.
print('Loading model...')
model = GPT2ForSequenceClassification.from_pretrained(pretrained_model_name_or_path=model_name_or_path, config=model_config) # resize model embedding to match new tokenizer
model.resize_token_embeddings(len(tokenizer)) # fix model padding token id
model.config.pad_token_id = model.config.eos_token_id # Load model to defined device.
model.to(device)
print('Model loaded to `%s`'%device)
Loading configuraiton... Loading tokenizer... Loading model... Some weights of GPT2ForSequenceClassification were not initialized from the model checkpoint at gpt2 and are newly initialized: ['score.weight'] You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference. Model loaded to `cuda`

Datensatz und Collator

Hier erstelle ich den PyTorch-Datensatz und den Data Loader mit Data Collator-Objekten, die zum Einspeisen von Daten in unser Modell verwendet werden.

Hier benutze ich die FilmRezensionenDataset Klasse zum Erstellen des PyTorch-Datensatzes, der Texte und Beschriftungen zurückgibt.

Da wir Zahlen in unser Modell eingeben müssen, müssen wir die Texte und Beschriftungen in Zahlen umwandeln. Dies ist der Zweck eines Collators! Es nimmt vom PyTorch-Datensatz ausgegebene Daten und leitet sie durch die Data Collator-Funktion, um die Sequenz für unser Modell auszugeben.

Ich halte den Tokenizer vom PyTorch-Datensatz fern, um den Code sauberer und besser strukturiert zu machen. Sie können den Tokenizer natürlich im PyTorch-Datensatz verwenden und Sequenzen ausgeben, die direkt im Modell verwendet werden können, ohne einen Datensammler zu verwenden.

Ich empfehle dringend, eine Validierungstextdatei zu verwenden, um festzustellen, wie viel Schulung erforderlich ist, um eine Überanpassung zu vermeiden. Nachdem Sie herausgefunden haben, welche Parameter die besten Ergebnisse liefern, kann die Validierungsdatei in den Zug integriert werden und einen endgültigen Zug mit dem gesamten Datensatz ausführen.

Der Datensammler wird verwendet, um die PyTorch-Dataset-Ausgaben so zu formatieren, dass sie mit den für GPT2 benötigten Eingaben übereinstimmen.

# Create data collator to encode text and labels into numbers.
gpt2_classificaiton_collator = Gpt2ClassificationCollator(use_tokenizer=tokenizer, labels_encoder=labels_ids, max_sequence_len=max_length) print('Dealing with Train...')
# Create pytorch dataset.
train_dataset = MovieReviewsDataset(path='/content/aclImdb/train', use_tokenizer=tokenizer)
print('Created `train_dataset` with %d examples!'%len(train_dataset)) # Move pytorch dataset into dataloader.
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, collate_fn=gpt2_classificaiton_collator)
print('Created `train_dataloader` with %d batches!'%len(train_dataloader)) print() print('Dealing with Validation...')
# Create pytorch dataset.
valid_dataset = MovieReviewsDataset(path='/content/aclImdb/test', use_tokenizer=tokenizer)
print('Created `valid_dataset` with %d examples!'%len(valid_dataset)) # Move pytorch dataset into dataloader.
valid_dataloader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False, collate_fn=gpt2_classificaiton_collator)
print('Created `eval_dataloader` with %d batches!'%len(valid_dataloader))
Dealing with Train... pos files: 100%|████████████████████████████████|12500/12500 [01:17<00:00, 161.19it/s] neg files: 100%|████████████████████████████████|12500/12500 [01:05<00:00, 190.72it/s] Created `train_dataset` with 25000 examples! Created `train_dataloader` with 782 batches! Reading pos files... pos files: 100%|████████████████████████████████|12500/12500 [00:54<00:00, 230.93it/s] neg files: 100%|████████████████████████████████|12500/12500 [00:42<00:00, 291.07it/s] Created `valid_dataset` with 25000 examples! Created `eval_dataloader` with 782 batches!

Training

Ich habe im Training die Verwendung von Optimierern und Schedulern durch PyTorch erstellt. Ich habe die gängigsten Parameter verwendet, die von Transformatormodellen verwendet werden.

Ich habe die Anzahl der definierten Epochen durchlaufen und aufgerufen Zug und Bestätigung Funktionen.

Ich versuche, nach jeder Epoche ähnliche Informationen wie Keras auszugeben: train_loss: - val_loss: - train_acc: - valid_acc.

Zeichnen Sie nach dem Training die Verlust- und Genauigkeitskurven für Zug und Validierung auf, um zu überprüfen, wie das Training verlaufen ist.

Hinweis: Die Trainingsdiagramme sehen möglicherweise etwas seltsam aus: Die Validierungsgenauigkeit beginnt höher als die Trainingsgenauigkeit und der Validierungsverlust beginnt niedriger als der Trainingsverlust. Normalerweise wird das Gegenteil der Fall sein. Ich gehe davon aus, dass die Datenaufteilung für den Validierungsteil einfach einfacher und für den Trainingsteil oder beides zu schwierig ist. Da es in diesem Tutorial um die Verwendung von GPT2 zur Klassifizierung geht, werde ich mir über die Ergebnisse des Modells keine allzu großen Sorgen machen.

# Note: AdamW is a class from the huggingface library (as opposed to pytorch) # I believe the 'W' stands for 'Weight Decay fix"
optimizer = AdamW(model.parameters(), lr = 2e-5, # default is 5e-5, our notebook had 2e-5 eps = 1e-8 # default is 1e-8. ) # Total number of training steps is number of batches * number of epochs.
# `train_dataloader` contains batched data so `len(train_dataloader)` gives # us the number of batches.
total_steps = len(train_dataloader) * epochs # Create the learning rate scheduler.
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps = 0, # Default value in run_glue.py num_training_steps = total_steps) # Store the average loss after each epoch so we can plot them.
all_loss = {'train_loss':[], 'val_loss':[]}
all_acc = {'train_acc':[], 'val_acc':[]} # Loop through each epoch.
print('Epoch')
for epoch in tqdm(range(epochs)): print() print('Training on batches...') # Perform one full pass over the training set. train_labels, train_predict, train_loss = train(train_dataloader, optimizer, scheduler, device) train_acc = accuracy_score(train_labels, train_predict) # Get prediction form model on validation data. print('Validation on batches...') valid_labels, valid_predict, val_loss = validation(valid_dataloader, device) val_acc = accuracy_score(valid_labels, valid_predict) # Print loss and accuracy values to see how training evolves. print(" train_loss: %.5f - val_loss: %.5f - train_acc: %.5f - valid_acc: %.5f"%(train_loss, val_loss, train_acc, val_acc)) print() # Store the loss value for plotting the learning curve. all_loss['train_loss'].append(train_loss) all_loss['val_loss'].append(val_loss) all_acc['train_acc'].append(train_acc) all_acc['val_acc'].append(val_acc) # Plot loss curves.
plot_dict(all_loss, use_xlabel='Epochs', use_ylabel='Value', use_linestyles=['-', '--']) # Plot accuracy curves.
plot_dict(all_acc, use_xlabel='Epochs', use_ylabel='Value', use_linestyles=['-', '--'])
Epoch 100%|████████████████████████████████|4/4 [15:11<00:00, 227.96s/it] Training on batches... 100%|████████████████████████████████|782/782 [02:42<00:00, 4.82it/s] Validation on batches... 100%|████████████████████████████████|782/782 [02:07<00:00, 6.13it/s] train_loss: 0.54128 - val_loss: 0.38758 - train_acc: 0.75288 - valid_acc: 0.81904 Training on batches... 100%|████████████████████████████████|782/782 [02:36<00:00, 5.00it/s] Validation on batches... 100%|████████████████████████████████|782/782 [01:41<00:00, 7.68it/s] train_loss: 0.36716 - val_loss: 0.37620 - train_acc: 0.83288 -valid_acc: 0.82912 Training on batches... 100%|████████████████████████████████|782/782 [02:36<00:00, 5.00it/s] Validation on batches... 100%|████████████████████████████████|782/782 [01:24<00:00, 9.24it/s] train_loss: 0.31409 - val_loss: 0.39384 - train_acc: 0.86304 - valid_acc: 0.83044 Training on batches... 100%|████████████████████████████████|782/782 [02:36<00:00, 4.99it/s] Validation on batches... 100%|████████████████████████████████|782/782 [01:09<00:00, 11.29it/s] train_loss: 0.27358 - val_loss: 0.39798 - train_acc: 0.88432 - valid_acc: 0.83292
Zug- und Validierungsverlust.
Trainings- und Validierungsgenauigkeit.

Bewerten

Bei der Klassifizierung ist es hilfreich, sich die Präzisionsrückrufe und den F1-Score anzusehen.

Ein gutes Maß für die Bewertung eines Modells ist die Verwirrungsmatrix.

# Get prediction form model on validation data. This is where you should use
# your test data.
true_labels, predictions_labels, avg_epoch_loss = validation(valid_dataloader, device) # Create the evaluation report.
evaluation_report = classification_report(true_labels, predictions_labels, labels=list(labels_ids.values()), target_names=list(labels_ids.keys()))
# Show the evaluation report.
print(evaluation_report) # Plot confusion matrix.
plot_confusion_matrix(y_true=true_labels, y_pred=predictions_labels, classes=list(labels_ids.keys()), normalize=True, magnify=0.1, );
Training on batches... 100%|████████████████████████████████|782/782 [01:09<00:00, 11.24it/s] precision recall f1-score support neg 0.84 0.83 0.83 12500 pos 0.83 0.84 0.83 12500 accuracy 0.83 25000 macro avg 0.83 0.83 0.83 25000 weighted avg 0.83 0.83 0.83 25000
Die Verwirrungsmatrix wurde normalisiert.

Schlussbemerkung

Wenn du es so weit geschafft hast Herzlichen Glückwunsch! 🎊 und Vielen Dank! 🙏 für Ihr Interesse an meinem Tutorial!

Ich benutze diesen Code jetzt schon eine Weile und habe das Gefühl, dass er an einem Punkt angelangt ist, an dem er gut dokumentiert und leicht zu befolgen ist.

Natürlich fällt es mir leicht zu folgen, weil ich es gebaut habe. Deshalb ist jedes Feedback willkommen und hilft mir, meine zukünftigen Tutorials zu verbessern!

Wenn Sie etwas falsch sehen, lassen Sie es mich bitte wissen, indem Sie eine Ausgabe auf meiner Seite öffnen ml_things GitHub-Repository!

Viele Tutorials sind meistens einmalig und werden nicht gepflegt. Ich habe vor, meine Tutorials so aktuell wie möglich zu halten.

Dieser Artikel wurde ursprünglich veröffentlicht am Die persönliche Website von George Mihaila  und mit Genehmigung des Autors erneut auf TOPBOTS veröffentlicht.

Genießen Sie diesen Artikel? Melden Sie sich für weitere AI-Updates an.

Wir werden Sie informieren, wenn wir mehr technische Ausbildung veröffentlichen.

Quelle: https://www.topbots.com/gpt2-text-classification-using-hugging-face-transformers/

Zeitstempel:

Mehr von TOPBOTS