GPT2 לסיווג טקסט באמצעות שנאי פנים מחבקים

צומת המקור: 809063

סיווג טקסט

מחברת זו משמשת לכוונון דגם GPT2 לסיווג טקסט באמצעות פנים מחבקות רוֹבּוֹטרִיקִים ספרייה במערך נתונים מותאם אישית.

חיבוק פנים מאוד נחמד בעינינו לכלול את כל הפונקציונליות הדרושה לשימוש ב- GPT2 במשימות סיווג. תודה לך מחבק פנים!

לא הצלחתי למצוא מידע רב על אופן השימוש ב- GPT2 לצורך סיווג, אז החלטתי להכין הדרכה זו תוך שימוש במבנה דומה למודלים של שנאים אחרים.

אם תוכן חינוכי מעמיק זה שימושי עבורך, הירשם לרשימת התפוצה של מחקרי AI שלנו להתריע כשאנחנו משחררים חומר חדש. 

רעיון מרכזי: מכיוון ש- GPT2 הוא שנאי מפענח, האסימון האחרון של רצף הקלט משמש לחיזוי לגבי האסימון הבא שצריך לעקוב אחר הקלט. משמעות הדבר היא שהאסימון האחרון של רצף הקלט מכיל את כל המידע הדרוש בחיזוי. עם זאת בחשבון אנו יכולים להשתמש במידע זה כדי לחזות במשימת סיווג במקום במשימת הדור.

במילים אחרות, במקום להשתמש בהטבעה של אסימונים ראשונים כדי לחזות כמו שאנחנו עושים בברט, נשתמש בהטבעה של אסימונים אחרונים כדי לחזות עם GPT2.

מכיוון שהיה אכפת לנו רק מהאסימון הראשון בברט, ריפדנו ימינה. כעת ב- GPT2 אנו משתמשים באסימון האחרון לחיזוי ולכן נצטרך לרפד בצד שמאל. בגלל שדרוג נחמד ל- HuggingFace Transformers אנו מסוגלים להגדיר את ה- GPT2 Tokenizer שיעשה בדיוק את זה.

מה עלי לדעת למחברת זו?

מכיוון שאני משתמש ב- PyTorch בכדי לכוונן את דגמי השנאים שלנו כל ידע ב- PyTorch הוא מאוד שימושי.

לדעת קצת על רוֹבּוֹטרִיקִים גם הספרייה עוזרת.

כיצד להשתמש במחברת זו?

כמו בכל פרויקט, בניתי את המחברת הזו עם יכולת שימוש חוזר.

כל השינויים יתרחשו בחלק של עיבוד הנתונים שבו אתה צריך להתאים אישית את מערך הנתונים PyTorch, Data Collector ו- DataLoader כך שיתאימו לצרכי הנתונים שלך.

כל הפרמטרים שניתן לשנות נמצאים תחת ה- יבוא סָעִיף. כל פרמטר מגיב יפה ומובנה כך שהוא יהיה אינטואיטיבי ככל האפשר.

מערך נתונים

מחברת זו תכסה שנאים לפני אימון במערך נתונים מותאם אישית. אשתמש בביקורות הסרטים הידועות כחיוביות - עם תווית שלילית מערך נתונים לסקירת סרטים גדולים.

התיאור המופיע באתר סטנפורד:

זהו מערך לסיווג סנטימנט בינארי המכיל נתונים משמעותיים יותר ממערכי נתונים של אמות מידה קודמות. אנו מספקים קבוצה של 25,000 ביקורות על סרטים קוטביים במיוחד לאימונים, ו- 25,000 לבדיקות. ישנם נתונים נוספים ללא תווית לשימוש גם כן. טקסט גולמי ושקית פורמטים של מילים מעובדים כבר מסופקים. לפרטים נוספים עיין בקובץ README הכלול במהדורה.

מדוע מערך הנתונים הזה? אני מאמין שהוא מערך נתונים קל להבנה ושימוש לצורך סיווג. אני חושב שנתוני סנטימנט תמיד כיף לעבוד איתם.

סִמוּל

עכשיו בואו נעשה קידוד! נעבור דרך כל תא קידוד במחברת ונתאר מה הוא עושה, מה הקוד ומתי הוא רלוונטי - נראה את הפלט.

הכנתי את הפורמט הזה להיות קל למעקב אם תחליט להריץ כל תא קוד במחברת הפיתון שלך.

כשאני לומד מהדרכה אני תמיד מנסה לשכפל את התוצאות. אני מאמין שקל לעקוב אם יש לך את הקוד ליד ההסברים.

הורדות

הורד מערך נתונים לסקירת סרטים גדולים ולפתוח אותו באופן מקומי.

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

מותקן

  • רוֹבּוֹטרִיקִים יש להתקין את הספרייה כדי להשתמש בכל הקוד המדהים של Hugging Face. כדי לקבל את הגרסה האחרונה אני אתקין אותה ישירות מ- GitHub.
  • ml_things ספרייה המשמשת למשימות שונות הקשורות ללמידת מכונה. יצרתי ספרייה זו כדי להפחית את כמות הקוד שאני צריך לכתוב עבור כל פרויקט למידת מכונה.
# 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

יבוא

ייבא את כל הספריות הדרושות למחברת זו. הצהיר פרמטרים המשמשים למחברת זו:

  • set_seed(123) - תמיד טוב להגדיר זרע קבוע לשחזור.
  • epochs - מספר תקופות ההדרכה (מחברים ממליצים בין 2 ל -4).
  • batch_size - מספר קבוצות - תלוי באורך הרצף המרבי ובזיכרון ה- GPU. לאורך 512 רצפים, אצווה של 10 USUALY עובדת ללא בעיות זיכרון. עבור אורך רצף קטן יכול לנסות אצווה של 32 ומעלה. max_length - כרית או קטום רצפי טקסט לאורך ספציפי. אני אגדיר את זה ל 60 כדי להאיץ את האימונים.
  • device - חפש gpu לשימוש. ישתמש במעבד כברירת מחדל אם לא נמצא מעבד.
  • model_name_or_path - שם מודל השנאים - ישתמש במודל שכבר הוכשר מראש. נתיב מודל השנאי - יטען את המודל שלך מהדיסק המקומי. במדריך זה אשתמש gpt2 מודל.
  • labels_ids - מילון תוויות וזהותם - זה ישמש להמרת תוויות מחרוזות למספרים.
  • n_labels - כמה תוויות אנו משתמשים במערך הנתונים הזה. זה משמש להחלטת גודל ראש הסיווג.
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)

פונקציות עוזר

אני רוצה לשמור את כל השיעורים והפונקציות שישמשו במחברת זו תחת סעיף זה כדי לשמור על מראה נקי של המחשב הנייד:

MovieReviewsDataset (מערך נתונים)

אם עבדת עם PyTorch בעבר, זה די סטנדרטי. אנו זקוקים לשיעור זה כדי לקרוא במערך הנתונים שלנו, לנתח אותו ולהחזיר טקסטים עם התוויות המשויכות להם.

בשיעור זה עלי לקרוא רק את התוכן של כל קובץ, להשתמש ב- fix_text כדי לתקן את בעיות Unicode ולעקוב אחר רגשות חיוביים ושליליים.

אצרף את כל הטקסטים והתוויות ברשימות.

ישנם שלושה חלקים עיקריים בשיעור זה של מערך נתונים של PyTorch:

  • init () שם אנו קוראים במערך הנתונים והופכים טקסט ותוויות למספרים.
  • len () שם עלינו להחזיר את מספר הדוגמאות שקראנו בהן. זה משמש בעת קריאה ל- len (MovieReviewsDataset ()).
  • getitem () תמיד לוקח כקלט ערך int המייצג איזו דוגמה מהדוגמאות שלנו לחזור ממערך הנתונים שלנו. אם יועבר ערך 3, נחזיר את דוגמת טופס הנתונים שלנו במיקום 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]}

Gpt2ClassificationCollator

אני משתמש בכיתה זו כדי ליצור את Collector Data. זה ישמש ב- DataLoader כדי ליצור מרחצאות נתונים שמקבלים הזנה למודל. אני משתמש ב- tokenizer ובמקודד התוויות בכל רצף כדי להמיר טקסטים ותוויות למספר.

למזלנו, Face Hugging Face חשב על הכל וגרם לטוקנייזר לעשות את כל ההרמה הכבדה (פיצול טקסט לאסימונים, ריפוד, חתוך, קידוד טקסט למספרים) וקל מאוד לשימוש!

ישנם שני חלקים עיקריים במחלקה זו של Collector Data:

  • init () היכן אנו מאתחלים את ה- tokenizer שאנו מתכננים להשתמש בו, כיצד לקודד את התוויות שלנו ואם עלינו להגדיר את אורך הרצף לערך אחר.
  • שִׂיחָה() משמש כמקבץ פונקציות שלוקח כקלט אצווה של דוגמאות נתונים. הוא צריך להחזיר אובייקט עם הפורמט שניתן להזין למודל שלנו. למרבה המזל האסימון שלנו עושה זאת עבורנו ומחזיר מילון של משתנים שמוכנים להזנה למודל בצורה כזו: model(**inputs). מכיוון שאנחנו מכוונים את המודל, כללתי גם את התוויות.
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

רכבת (מטען נתונים, אופטימיזציה_, מתזמן_, מכשיר_)

יצרתי פונקציה זו כדי לבצע מעבר מלא דרך האובייקט DataLoader (האובייקט DataLoader נוצר מהאובייקט מסוג Dataset * שלנו באמצעות המחלקה ** MovieReviewsDataset). זו בעצם רכבת עידן אחת בכל מערך הנתונים.

מטען הנתונים נוצר מ- PyTorch DataLoader שלוקח את האובייקט שנוצר מהמחלקה MovieReviewsDataset ומכניס כל דוגמה לקבוצות. בדרך זו אנו יכולים להזין את קבוצות הנתונים של הדגם שלנו!

אופטימיזציה_ ומתזמן_ נפוצים מאוד ב- PyTorch. הם נדרשים לעדכן את הפרמטרים של המודל שלנו ולעדכן את קצב הלמידה שלנו במהלך האימון. יש הרבה יותר מזה אבל אני לא אפרט. זה יכול למעשה להיות חור ארנבות ענק שכן הרבה קורה מאחורי הפונקציות האלה שאנחנו לא צריכים לדאוג. תודה לך PyTorch!

בתהליך אנו עוקבים אחר התוויות בפועל והתוויות החזויות יחד עם האובדן.

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

אימות (מטען נתונים, מכשיר_)

יישמתי פונקציה זו בצורה דומה מאוד לרכבת אך ללא עדכון הפרמטרים, מעבר לאחור וחלק הגון שיפוע. איננו צריכים לבצע את כל אותן משימות אינטנסיביות מאוד מבחינה חישובית כי אנו דואגים רק לחיזויים של המודל שלנו.

אני משתמש ב- DataLoader בצורה דומה כמו ברכבת כדי להוציא קבוצות להאכיל למודל שלנו.

בתהליך אני עוקב אחר התוויות בפועל והתוויות החזויות יחד עם האובדן.

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

טען מודל וטוקניזר

טוען את שלושת החלקים החיוניים של שנאי ה- GPT2 המאומנים מראש: תצורה, טוקנייזר ודגם

לדוגמא זו אשתמש gpt2 מבית HuggingFace שנאים מאומנים מראש. אתה יכול להשתמש בכל וריאציות של GP2 שאתה רוצה.

ביצירת ה model_config אזכיר את מספר התוויות שאני זקוק למשימת הסיווג שלי. מכיוון שאני מנבא רק שני סנטימנטים: חיובי ושלילי אצטרך רק שתי תוויות num_labels.

יצירת tokenizer הוא די סטנדרטי בשימוש בספריית הרובוטריקים. לאחר יצירת ה- tokenizer חשוב כי הדרכה זו תקבע ריפוד שמאלה tokenizer.padding_side = "left" לאתחל את אסימון הריפוד ל- tokenizer.eos_token שהוא אסימון הרצף המקורי של ה- GPT2. זהו החלק המהותי ביותר במדריך זה מכיוון ש- GPT2 משתמש באסימון האחרון לחיזוי ולכן עלינו לרפד שמאלה.

HuggingFace כבר עשתה עבורנו את רוב העבודה והוסיפה שכבת סיווג למודל GPT2. ביצירת המודל בו השתמשתי GPT2ForSequenceClassification. מכיוון שיש לנו אסימון ריפוד מותאם אישית עלינו לאתחל אותו עבור הדגם באמצעות model.config.pad_token_id. לבסוף נצטרך להעביר את הדגם למכשיר שהגדרנו קודם.

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

מערך נתונים ואספן

זה המקום בו אני יוצר את מערך הנתונים PyTorch ומעמיס הנתונים עם אובייקטים של Collector Data שישמשו להזנת נתונים למודל שלנו.

זה המקום בו אני משתמש ב- סרטים ביקורות נתונים בכיתה ליצירת מערך הנתונים PyTorch שיחזיר טקסטים ותוויות.

מכיוון שעלינו להזין מספרים למודל שלנו עלינו להמיר את הטקסטים והתוויות למספרים. זו מטרתו של אספן! נדרשים נתונים שהופקו על ידי מערך הנתונים PyTorch ועברו דרך פונקציית Collector Data כדי להפיק את הרצף עבור המודל שלנו.

אני מרחיק את ה- tokenizer ממערך הנתונים של PyTorch כדי להפוך את הקוד לנקי יותר ומובנה יותר. אתה יכול כמובן להשתמש באסימון בתוך מערך הנתונים PyTorch ולהפיק רצפי פלט שניתן להשתמש בהם ישירות למודל מבלי להשתמש באוסף נתונים.

אני ממליץ בחום להשתמש בקובץ טקסט אימות על מנת לקבוע כמה הכשרה נדרשת על מנת למנוע התאמה יתרה. לאחר שתבין אילו פרמטרים מניבים את התוצאות הטובות ביותר, ניתן לשלב את קובץ האימות ברכבת ולהפעיל רכבת סופית עם כל מערך הנתונים.

אספן הנתונים משמש לעיצוב יציאות PyTorch Dataset כך שתתאים לתשומות הדרושות ל- 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!

רכבת

יצרתי שימוש באופטימיזציה ומתזמן על ידי PyTorch באימונים. השתמשתי בפרמטרים הנפוצים ביותר המשמשים מודלים של שנאים.

דללתי במספר התקופות שהוגדרו וקראתי ל רכבת ו אימות פונקציות.

אני מנסה להעביר מידע דומה לאחר כל תקופה כמו Keras: train_loss: - val_loss: - train_acc: - valid_acc.

לאחר האימון, עקומות הרכבת העלילה ואיבוד האימות והדיוק כדי לבדוק כיצד התנהל האימון.

הערה: עלילות האימון עשויות להיראות קצת מוזרות: דיוק האימות מתחיל יותר מדיוק האימון ואובדן האימות מתחיל נמוך יותר מאובדן האימונים. בדרך כלל זה יהיה ההפך. אני מניח שפיצול הנתונים פשוט יותר קל לחלק האימות או קשה מדי לאימון בחלק או שניהם. מכיוון שמדריך זה עוסק בשימוש ב- GPT2 לסיווג אני לא אדאג לתוצאות המודל יותר מדי.

# 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
אובדן רכבת ואימות.
דיוק באימון ובאימות.

להעריך

כאשר מתמודדים עם סיווג שימושי לבחון זיכרון דיוק וציון F1.

מד טוב שיש בעת הערכת מודל הוא מטריצת הבלבול.

# 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
מטריקס בלבול מנורמל.

הערה אחרונה

אם הצלחתם להגיע עד הלום מזל טוב! 🎊 ו תודה רבה לך! 🙏 להתעניינות בהדרכה שלי!

אני משתמש בקוד זה זמן מה ואני מרגיש שהוא הגיע למצב שהוא מתועד יפה וקל לביצוע.

כמובן שקל לי לעקוב מכיוון שבניתי אותו. לכן כל משוב יתקבל בברכה וזה עוזר לי לשפר את ההדרכות העתידיות שלי!

אם אתה רואה משהו לא בסדר, אנא יידע אותי על ידי פתיחת בעיה בנושא שלי מאגר GitHub ml_things!

הרבה מדריכים שם הם בעיקר דבר חד פעמי ואינם מתוחזקים. אני מתכנן לשמור על ההדרכות עד כמה שאוכל.

מאמר זה פורסם במקור ב האתר האישי של ג'ורג 'מיכאילה  ופורסם מחדש ל- TOPBOTS באישור המחבר.

נהנה ממאמר זה? הירשם לעוד עדכוני AI.

נודיע לך כשנפרסם חינוך טכני נוסף.

מקור: https://www.topbots.com/gpt2-text-classification-using-hugging-face-transformers/

בול זמן:

עוד מ עליון