GPT2 do klasyfikacji tekstu za pomocą przytulających transformatorów twarzy

Węzeł źródłowy: 809063

klasyfikacja tekstu

Ten notebook służy do dostrajania modelu GPT2 do klasyfikacji tekstu za pomocą Przytulanie Twarzy Transformatory biblioteka w niestandardowym zestawie danych.

Przytulanie twarzy jest dla nas bardzo miłe, ponieważ zawiera wszystkie funkcje potrzebne do wykorzystania GPT2 w zadaniach klasyfikacyjnych. Dziękuję Przytulająca Twarz!

Nie byłem w stanie znaleźć wielu informacji na temat używania GPT2 do klasyfikacji, więc postanowiłem zrobić ten samouczek przy użyciu podobnej struktury z innymi modelami transformatorów.

Jeśli te szczegółowe treści edukacyjne są dla Ciebie przydatne, zapisz się na naszą listę dyskusyjną AI zostać powiadomionym, gdy wydamy nowy materiał. 

Główny pomysł: Ponieważ GPT2 jest transformatorem dekodera, ostatni token sekwencji wejściowej jest używany do przewidywania następnego tokenu, który powinien następować po wejściu. Oznacza to, że ostatni token sekwencji wejściowej zawiera wszystkie informacje potrzebne w prognozie. Mając to na uwadze, możemy wykorzystać te informacje do prognozowania w zadaniu klasyfikacyjnym zamiast w zadaniu generowania.

Innymi słowy, zamiast używać osadzania pierwszego tokenu do prognozowania, tak jak robimy to w Bert, użyjemy osadzenia ostatniego tokenu do prognozowania za pomocą GPT2.

Ponieważ zależało nam tylko na pierwszym żetonie w Bert, skręcaliśmy w prawo. Teraz w GPT2 używamy ostatniego tokena do przewidywania, więc będziemy musieli dopełnić po lewej stronie. Dzięki fajnej aktualizacji do HuggingFace Transformers jesteśmy w stanie skonfigurować GPT2 Tokenizer, aby to zrobić.

Co powinienem wiedzieć o tym notatniku?

Ponieważ używam PyTorch do dostrajania naszych modeli transformatorów, każda wiedza na temat PyTorch jest bardzo przydatna.

Wiedząc trochę o Transformatory biblioteka też pomaga.

Jak korzystać z tego notatnika?

Jak w przypadku każdego projektu, stworzyłem ten notatnik z myślą o wielokrotnym użyciu.

Wszystkie zmiany nastąpią w części przetwarzania danych, w której musisz dostosować zestaw danych PyTorch, narzędzie Data Collator i DataLoader do własnych potrzeb.

Wszystkie parametry, które można zmienić, znajdują się w Import Sekcja. Każdy parametr jest ładnie skomentowany i ma strukturę tak, aby był jak najbardziej intuicyjny.

Dataset

Ten notatnik obejmie wstępne szkolenie transformatorów w niestandardowym zestawie danych. Skorzystam z dobrze znanych filmów z recenzji pozytywnych - z etykietą negatywną Duży zbiór danych recenzji filmów.

Opis podany na stronie Stanforda:

Jest to zbiór danych do binarnej klasyfikacji nastrojów zawierający znacznie więcej danych niż poprzednie zestawy danych wzorcowych. Udostępniamy zestaw 25,000 25,000 wysoce polarnych recenzji filmów na szkolenie i XNUMX XNUMX do testów. Istnieją również dodatkowe dane nieoznaczone do wykorzystania. Dostarczony jest surowy tekst i już przetworzony zbiór formatów słów. Więcej informacji można znaleźć w pliku README zawartym w wydaniu.

Dlaczego ten zbiór danych? Uważam, że jest to łatwy do zrozumienia i wykorzystania zbiór danych do klasyfikacji. Myślę, że praca z danymi o nastrojach jest zawsze przyjemna.

Kodowanie

Teraz zajmijmy się kodowaniem! Przejdziemy przez każdą komórkę kodującą w notatniku i opiszemy, co robi, jaki jest kod i kiedy ma znaczenie - pokażemy wynik.

Stworzyłem ten format tak, aby był łatwy do naśladowania, jeśli zdecydujesz się uruchomić każdą komórkę kodu w swoim własnym notatniku Pythona.

Kiedy uczę się z samouczka, zawsze staram się powtórzyć wyniki. Uważam, że łatwo jest postępować zgodnie z instrukcjami, jeśli obok objaśnień znajduje się kod.

Pliki do Pobrania

Pobierz Duży zbiór danych recenzji filmów i rozpakuj lokalnie.

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

Instaluje

  • Transformatory Aby korzystać z całego niesamowitego kodu z Hugging Face, należy zainstalować bibliotekę. Aby uzyskać najnowszą wersję, zainstaluję ją bezpośrednio z GitHub.
  • ml_rzeczy biblioteka używana do różnych zadań związanych z uczeniem maszynowym. Stworzyłem tę bibliotekę, aby zmniejszyć ilość kodu, który muszę napisać dla każdego projektu uczenia maszynowego.
# 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

Import

Zaimportuj wszystkie potrzebne biblioteki do tego notatnika. Zadeklaruj parametry używane dla tego notatnika:

  • set_seed(123) - Zawsze dobrze jest ustawić utrwalony materiał siewny w celu zapewnienia powtarzalności.
  • epochs - Liczba okresów szkoleniowych (autorzy zalecają od 2 do 4).
  • batch_size - Liczba wsadów - w zależności od maksymalnej długości sekwencji i pamięci GPU. Dla długości sekwencji 512 partia 10 USUALY działa bez problemów z pamięcią CUDA. W przypadku małej długości sekwencji można spróbować partii 32 lub wyższej. max_length - dopełnianie lub przycinanie sekwencji tekstowych do określonej długości. Ustawię to na 60, aby przyspieszyć trening.
  • device - Poszukaj procesora graficznego do użycia. Domyślnie użyje procesora, jeśli nie zostanie znaleziony procesor.
  • model_name_or_path - Nazwa modelu transformatora - użyje już wstępnie wytrenowanego modelu. Ścieżka modelu transformatora - załaduje Twój własny model z dysku lokalnego. W tym samouczku użyję gpt2 model.
  • labels_ids - Słownik etykiet i ich identyfikatorów - posłuży do konwersji etykiet łańcuchowych na liczby.
  • n_labels - Ile etykiet używamy w tym zbiorze danych. Służy do decydowania o wielkości głowy klasyfikacyjnej.
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)

Funkcje pomocnicze

Chciałbym zachować wszystkie klasy i funkcje, które będą używane w tym notebooku w tej sekcji, aby pomóc w utrzymaniu czystego wyglądu notebooka:

MovieReviewsDataset (Dataset)

Jeśli wcześniej pracowałeś z PyTorch, jest to dość standardowe. Potrzebujemy tej klasy, aby czytać nasz zbiór danych, analizować go i zwracać teksty z powiązanymi etykietami.

W tej klasie muszę tylko czytać zawartość każdego pliku, używać fix_text, aby naprawić wszelkie problemy z Unicode i śledzić pozytywne i negatywne odczucia.

Dołączę wszystkie teksty i etykiety do list.

Istnieją trzy główne części tej klasy PyTorch Dataset:

  • w tym() gdzie czytamy w zbiorze danych i przekształcamy tekst i etykiety w liczby.
  • len () gdzie musimy zwrócić liczbę przykładów, które wczytaliśmy. Jest to używane podczas wywoływania len (MovieReviewsDataset ()).
  • Zdobądź przedmiot() jako dane wejściowe zawsze przyjmuje wartość int, która reprezentuje przykład z naszych przykładów do zwrócenia z naszego zestawu danych. Jeśli zostanie przekazana wartość 3, zwrócimy przykład z naszego zbioru danych na pozycji 3.
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]}

Kolektor klasyfikacji Gpt2

Używam tej klasy do tworzenia Data Collator. Zostanie to użyte w module DataLoader do utworzenia pakietów danych, które są przekazywane do modelu. Używam tokenizera i kodera etykiet w każdej sekwencji, aby konwertować teksty i etykiety na liczby.

Na szczęście dla nas, Hugging Face pomyślał o wszystkim i sprawił, że tokenizer wykonał wszystkie ciężkie czynności (podzielenie tekstu na tokeny, dopełnienie, obcinanie, zakodowanie tekstu na liczby) i jest bardzo łatwy w użyciu!

Istnieją dwie główne części tej klasy Data Collator:

  • w tym() gdzie inicjalizujemy tokenizer, którego planujemy użyć, jak zakodować nasze etykiety i czy musimy ustawić długość sekwencji na inną wartość.
  • połączenie() używany jako zbieracz funkcji, który przyjmuje jako dane wejściowe zestaw przykładów danych. Musi zwrócić obiekt w formacie, który może zostać przekazany do naszego modelu. Na szczęście nasz tokenizer robi to za nas i zwraca słownik zmiennych gotowych do wprowadzenia do modelu w następujący sposób: model(**inputs). Ponieważ dopracowujemy model, dołączyłem również etykiety.
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

pociąg (dataloader, optymalizator_, harmonogram_, urządzenie_)

Utworzyłem tę funkcję, aby wykonać pełne przejście przez obiekt DataLoader (obiekt DataLoader jest tworzony z naszego obiektu typu Dataset * przy użyciu klasy ** MovieReviewsDataset). Zasadniczo jest to ciąg jednej epoki przez cały zbiór danych.

Dataloader jest tworzony z PyTorch DataLoader, który pobiera obiekt utworzony z klasy MovieReviewsDataset i umieszcza każdy przykład w partiach. W ten sposób możemy zasilać nasze wzorcowe partie danych!

Optymalizator_ i harmonogram_ są bardzo powszechne w PyTorch. Są zobowiązani do aktualizacji parametrów naszego modelu i zaktualizowania naszego wskaźnika uczenia się podczas treningu. Jest o wiele więcej, ale nie będę wchodził w szczegóły. W rzeczywistości może to być ogromna królicza nora, ponieważ za tymi funkcjami dzieje się DUŻO, o które nie musimy się martwić. Dziękuję PyTorch!

W trakcie tego procesu śledzimy faktyczne etykiety i przewidywane etykiety wraz ze stratami.

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

walidacja (dataloader, device_)

Zaimplementowałem tę funkcję w bardzo podobny sposób jak pociąg, ale bez aktualizacji parametrów, przejazdu wstecz i części przyzwoitej gradientu. Nie musimy wykonywać tych wszystkich BARDZO intensywnych obliczeniowo zadań, ponieważ zależy nam tylko na przewidywaniach naszego modelu.

Używam DataLoadera w podobny sposób jak w pociągu, aby pobrać partie do podania do naszego modelu.

W trakcie tego procesu śledzę faktyczne etykiety i przewidywane etykiety wraz ze stratami.

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

Załaduj model i tokenizator

Ładowanie trzech podstawowych części wstępnie wyszkolonego transformatora GPT2: konfiguracja, tokenizer i model.

W tym przykładzie użyję gpt2 z transformatorów wstępnie wytrenowanych HuggingFace. Możesz użyć dowolnej wersji GP2.

W tworzeniu model_config Wspomnę o liczbie etykiet, których potrzebuję do mojego zadania klasyfikacyjnego. Ponieważ przewiduję tylko dwa nastroje: pozytywne i negatywne, będę potrzebować tylko dwóch etykiet num_labels.

Tworzenie tokenizer jest dość standardowe podczas korzystania z biblioteki Transformers. Po utworzeniu tokenizera ważne jest, aby w tym samouczku ustawiono dopełnienie w lewo tokenizer.padding_side = "left" i zainicjuj token wypełnienia tokenizer.eos_token który jest oryginalnym tokenem końca sekwencji GPT2. Jest to najważniejsza część tego samouczka, ponieważ GPT2 używa ostatniego tokenu do przewidywania, więc musimy przejść do lewej strony.

HuggingFace wykonał już większość pracy za nas i dodał warstwę klasyfikacji do modelu GPT2. Tworząc model użyłem GPT2ForSequenceClassification. Ponieważ mamy niestandardowy token wypełnienia, musimy go zainicjować dla modelu przy użyciu model.config.pad_token_id. Na koniec będziemy musieli przenieść model na zdefiniowane wcześniej urządzenie.

# 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`

Zestaw danych i zbieracz

Tutaj tworzę zestaw danych PyTorch i moduł ładujący dane z obiektami Data Collator, które będą używane do dostarczania danych do naszego modelu.

Tutaj używam FilmRecenzjeZbiór danych klasy, aby utworzyć PyTorch Dataset, który zwróci teksty i etykiety.

Ponieważ musimy wprowadzić liczby do naszego modelu, musimy przekonwertować teksty i etykiety na liczby. Taki jest cel zbieracza! Pobiera dane wyprowadzone przez PyTorch Dataset i przepuszczane przez funkcję Data Collator w celu wyprowadzenia sekwencji dla naszego modelu.

Trzymam tokenizer z dala od PyTorch Dataset, aby kod był czystszy i lepiej zorganizowany. Możesz oczywiście użyć tokenizera w zestawie danych PyTorch i sekwencji wyjściowych, których można użyć bezpośrednio w modelu bez użycia Data Collator.

Zdecydowanie zalecam użycie pliku tekstowego do walidacji, aby określić, ile treningu jest potrzebne, aby uniknąć nadmiernego dopasowania. Po ustaleniu, które parametry dają najlepsze wyniki, plik walidacji można włączyć do pociągu i uruchomić końcowy pociąg z całym zestawem danych.

Moduł gromadzący dane służy do formatowania danych wyjściowych zestawu PyTorch Dataset w celu dopasowania ich do danych wejściowych wymaganych przez GPT2.

# 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!

Pociąg

Stworzyłem optymalizator i harmonogram wykorzystania przez PyTorch w szkoleniu. Użyłem najczęściej używanych parametrów stosowanych w modelach transformatorów.

Przejrzałem liczbę zdefiniowanych epok i zadzwoniłem do pociąg i uprawomocnienie funkcje.

Próbuję uzyskać podobne informacje po każdej epoce jak Keras: train_loss: - val_loss: - train_acc: - valid_acc.

Po przeszkoleniu narysuj pociąg i krzywe utraty walidacji i dokładności, aby sprawdzić, jak poszło szkolenie.

Uwaga: Wykresy treningowe mogą wyglądać trochę dziwnie: dokładność walidacji zaczyna się od dokładności treningu, a strata walidacji zaczyna się niższa niż strata treningu. Zwykle będzie odwrotnie. Zakładam, że podział danych jest po prostu łatwiejszy dla części walidacyjnej lub zbyt trudny dla części treningowej lub obu. Ponieważ ten samouczek dotyczy używania GPT2 do klasyfikacji, nie będę się zbytnio przejmować wynikami modelu.

# 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
Szkolenie i utrata walidacji.
Trenuj i poprawiaj dokładność.

Oceniać

Podczas klasyfikacji warto przyjrzeć się dokładności i wynikowi F1.

Dobrym miernikiem do oceny modelu jest macierz pomyłki.

# 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
Macierz zamieszania znormalizowana.

Ostatnia uwaga

Jeśli dotarłeś tak daleko Gratulacje! 🎊 i Dziękuję Ci! 🙏 za zainteresowanie moim samouczkiem!

Używam tego kodu już od jakiegoś czasu i czuję, że doszło do punktu, w którym jest ładnie udokumentowany i łatwy do naśladowania.

Oczywiście jest to dla mnie łatwe do naśladowania, ponieważ go zbudowałem. Dlatego każda opinia jest mile widziana i pomaga mi ulepszyć moje przyszłe samouczki!

Jeśli zauważysz, że coś jest nie tak, daj mi znać, otwierając problem na moim ml_things Repozytorium GitHub!

Wiele samouczków jest w większości jednorazowych i nie jest aktualizowanych. Planuję aktualizować moje samouczki tak często, jak to tylko możliwe.

Ten artykuł został pierwotnie opublikowany w Osobista strona internetowa George'a Mihaila  i ponownie opublikowane w TOPBOTS za zgodą autora.

Podoba Ci się ten artykuł? Zarejestruj się, aby otrzymywać więcej aktualizacji AI.

Damy Ci znać, kiedy wydamy więcej edukacji technicznej.

Źródło: https://www.topbots.com/gpt2-text-classification-using-hugging-face-transformers/

Znak czasu:

Więcej z TOPBOTY