RoBERTa: Un metodo ottimizzato per il pre-addestramento di sistemi NLP auto-supervisionati
I modelli linguistici pre-addestrati hanno ottenuto un successo notevole nell'elaborazione del linguaggio naturale (NLP), portando a un cambio di paradigma dall'apprendimento supervisionato al pre-addestramento seguito dal fine-tuning. L'obiettivo della modellazione linguistica è prevedere il token successivo in una sequenza, data una cronologia di testi non annotati, ovvero grandi quantità di dati testuali grezzi che non sono stati etichettati con informazioni specifiche. BERT (Bidirectional Encoder Representations from Transformers) stabilisce nuovi standard per questo concetto consentendo una comprensione bidirezionale del linguaggio, poiché esamina l'intera frase, non solo una parola alla volta. Considera le parole prima e dopo per comprenderne meglio il significato.
Tuttavia, il problema principale riscontrato con BERT era il suo sotto-addestramento, che ne limitava le prestazioni nei task di NLP. Questa limitazione ha portato i ricercatori a esplorare metodologie di addestramento migliorate nei modelli successivi, come RoBERTa.
RoBERTa (A Robustly Optimized BERT Pretraining Approach) è una versione migliorata di BERT progettata per affrontarne i limiti. RoBERTa ha introdotto diversi miglioramenti chiave che ne potenziano le prestazioni in vari problemi di NLP.
Questo articolo discuterà le metodologie e le tecnologie utilizzate nello sviluppo di RoBERTa, incluso il suo approccio di addestramento, i risultati che ha ottenuto e le potenziali opportunità di ricerca future che apre.
Una breve introduzione a BERT (Bidirectional Encoder Representations from Transformers)
Per comprendere RoBERTa, è essenziale comprendere il suo predecessore, BERT, e le sfide che ha affrontato. BERT ha introdotto il concetto di transformer bidirezionali, consentendo ai modelli di deep learning di comprendere il contesto in un modo precedentemente irraggiungibile.
Ma, per capire davvero come funziona BERT, dobbiamo scomporlo. Quindi, diamo un'occhiata più da vicino alla sua architettura e a come è stato addestrato.
L'architettura di BERT e il processo di pre-addestramento
L'architettura di BERT si basa sui transformer, introdotti da Devlin et al. nel 2018. Utilizza un encoder bidirezionale multistrato (da sinistra a destra e da destra a sinistra) per ponderare l'importanza delle diverse parole in una frase.
Architettura di BERT
Figura 1: Architettura di BERT
Una parola inizia con la sua rappresentazione di embedding proveniente dal livello di embedding; un embedding è un vettore denso (rappresentazione numerica) di una parola o di un token in uno spazio ad alta dimensionalità. Il livello di embedding è il primo livello in una rete neurale che converte le parole di input in questi vettori densi. Ogni livello esegue calcoli di attenzione multi-head sulla rappresentazione della parola del livello precedente per generare una nuova rappresentazione intermedia. Tutte queste rappresentazioni intermedie hanno la stessa dimensione. Nella figura sopra, E1 è la rappresentazione di embedding, T1 è l'output finale e Trm è la rappresentazione intermedia del token.
Il processo di pre-addestramento di BERT include due task principali:
Masked Language Modeling (MLM)
Next-Sentence Prediction (NSP)
Il language modeling prevede la parola successiva data una sequenza di parole. Nel compito MLM, però, invece di prevedere ogni token successivo, una percentuale di token di input viene mascherata casualmente, e vengono previsti solo quei token mascherati. Il compito NSP, d’altra parte, è un compito di classificazione binaria che prevede se due frasi date si susseguono nel testo originale. Questo approccio aiuta il modello a comprendere le relazioni tra le frasi.
Next Sentence Prediction
Figura 2: Next Sentence Prediction
Il processo di pre-addestramento di BERT aveva limitazioni che gli impedivano di raggiungere il suo pieno potenziale. Uno dei problemi principali era l’uso di uno schema di mascheramento statico nel compito MLM. In BERT, una volta che una parola veniva mascherata durante l’addestramento, le stesse parole venivano mascherate in tutte le iterazioni. Questo approccio statico limitava il modello a scenari di mascheramento diversificati, riducendo la sua capacità di generalizzazione. Inoltre, anche la dimensione e la durata dei dati di addestramento erano limitate.
Queste limitazioni hanno portato a incoerenze nelle prestazioni di BERT su compiti specifici di dominio, come l’analisi di testi legali o medici, e in scenari complessi di comprensione del linguaggio, come l’analisi del sentiment o il ragionamento multi-step. Affrontare queste carenze richiede ulteriori attività di ricerca e sviluppo.
L’emergere di RoBERTa
La motivazione principale alla base dello sviluppo di RoBERTa era affrontare le sfide osservate nel processo di pre-addestramento di BERT. Tuttavia, RoBERTa mantiene la stessa architettura di base di Bert Large, che consiste in 24 livelli, 1024 unità nascoste e 16 teste di attenzione, per un totale di 355 milioni di parametri.
Le sfide che RoBERTa mirava ad affrontare erano:
Schema di mascheramento statico: Il mascheramento statico di BERT portava a scenari di addestramento limitati, causando overfitting. L’overfitting si verifica quando un modello funziona molto bene sui dati di addestramento ma ha prestazioni scarse con i dati di test (nuovi dati). RoBERTa ha introdotto il mascheramento dinamico, generando nuove maschere per ogni epoca e migliorando la sua capacità di gestire nuovi dati.
Dati di addestramento insufficienti: L’addestramento di BERT si basava su un dataset di circa 16 GB di testo, insufficiente per catturare l’intera diversità del linguaggio. RoBERTa ha ampliato i dati di addestramento, utilizzando oltre 160 GB di testo da varie fonti.
Breve durata dell’addestramento: Il modello BERT originale è stato addestrato per una durata relativamente breve, ma RoBERTa ha esteso significativamente la durata dell’addestramento, eseguendolo per più epoche e iterazioni.
Dimensioni dei batch ridotte e tassi di apprendimento conservativi: Il pre-addestramento di BERT utilizzava dimensioni dei batch relativamente ridotte e tassi di apprendimento conservativi, un approccio sicuro ma limitante. D’altra parte, RoBERTa ha sperimentato dimensioni dei batch più grandi e tassi di apprendimento più aggressivi, portando a migliori prestazioni del modello.
Che cos’è RoBERTa e come funziona?
RoBERTa (A Robustly Optimized BERT Pretraining Approach) è una versione migliorata di BERT progettata per affrontarne le limitazioni e ottenere prestazioni migliori. Ha introdotto modifiche al processo di addestramento di BERT che ne migliorano le prestazioni nei compiti di natural language processing.
Queste modifiche includono:
Mascheramento dinamico
Rimozione del NSP
Dati di addestramento più ampi e durata estesa
Aumento delle dimensioni dei batch
Codifica del testo a byte
Mascheramento dinamico
Dall’architettura di BERT, ricordiamo che durante il pre-addestramento, BERT eseguiva il Masked Language Modeling (MLM) cercando di prevedere una certa percentuale di token mascherati. Questi token mascherati scelti erano gli stessi (statici) in ogni iterazione.
RoBERTa ha affrontato questo problema implementando un approccio leggermente migliore chiamato masking dinamico. Questo approccio cambia lo schema di masking ogni volta che una sequenza viene passata a BERT durante l’addestramento. Questa modifica ha reso i dati di addestramento più vari, permettendo al modello di apprendere una gamma più ampia di pattern linguistici. Di conseguenza, RoBERTa ha sviluppato una comprensione più profonda del contesto, migliorando le prestazioni nei task downstream.
Masking statico vs. masking dinamico
Figura 3: Masking statico vs. masking dinamico
I ricercatori che hanno sviluppato RoBERT hanno anche reimplementato l’approccio di masking statico duplicando i dati di addestramento 10 volte. Questa duplicazione ha permesso di mascherare ogni sequenza in dieci modi diversi nel corso delle 40 epoche di addestramento. Pertanto, ogni sequenza di addestramento è stata vista con la stessa maschera quattro volte durante l’addestramento. Le prestazioni erano simili confrontando la reimplementazione del masking statico con il modello BERT originale. Tuttavia, il masking dinamico mostra prestazioni leggermente migliori rispetto al masking statico.
Confronto tra masking statico e dinamico
Figura 4: Confronto tra masking statico e dinamico
Rimozione della next sentence prediction (NSP)
Un’altra modifica chiave in RoBERTa è stata la rimozione del task NSP. In NSP, al modello venivano fornite coppie di frasi e doveva prevedere se la seconda frase seguiva la prima nel testo originale. Tuttavia, lo sviluppo di RoBERTa ha riscontrato che il task NSP contribuiva in modo minimo alle prestazioni del modello nei task downstream.
RoBERTa utilizza un approccio FULL-SENTENCES (impacchettando sequenze da più documenti). Ogni input al modello viene impacchettato con frasi complete campionate in modo contiguo da uno o più documenti fino alla lunghezza massima della sequenza di 512 token. Questo permette al modello di apprendere in modo più efficace le dipendenze a lungo raggio.
BERT con e senza NSP
Figura 5: BERT con e senza NSP
Inoltre, i ricercatori hanno esaminato quattro modi diversi per formattare i dati di input per un modello linguistico. Hanno confrontato due formati principali: l’originale SEGMENT-PAIR e SENTENCE-PAIR. Entrambi i formati utilizzano NSP, ma il formato SENTENCE-PAIR utilizza frasi singole invece di coppie. I ricercatori hanno scoperto che l’uso di frasi singole peggiorava le prestazioni del modello nei task perché faticava a comprendere le connessioni tra parole molto distanti nel testo.
Successivamente, hanno testato il modello addestrandolo senza il metodo NSP e utilizzando blocchi di testo da un documento chiamato DOC-SENTENCES. Nella maggior parte dei casi, hanno riscontrato che funzionava meglio del BERT originale e che non utilizzare la loss NSP non danneggiava le prestazioni del modello.
Il metodo basato sul testo da un singolo documento (DOC-SENTENCES) era leggermente migliore rispetto a quello basato su più documenti (FULL-SENTENCES). Tuttavia, poiché DOC-SENTENCES portava a batch size diversi durante l’addestramento, hanno deciso di utilizzare FULL-SENTENCES per il resto dei loro esperimenti. Questa scelta ha reso più facile confrontare i loro risultati con quelli di altri studi correlati.
Risultati per modelli base pre-addestrati su BOOK CORPUS e WIKIPEDIA
Figura 6: Risultati per modelli base pre-addestrati su BOOK CORPUS e WIKIPEDIA
Dati di addestramento più ampi e durata estesa
Forse l’ottimizzazione più semplice, ma non meno importante, è semplicemente addestrare su più dati per un periodo più lungo. BERT è stato addestrato su un dataset di circa 16 GB di testo, mentre RoBERTa ha utilizzato oltre 160 GB di testo.
Dataset di RoBERTa:
BOOK-CORPUS e WIKIPEDIA: Uguale a BERT.
CC-NEWS: RoBERTa introduce un nuovo dataset chiamato CC-NEWS, un dataset di notizie su larga scala con linguaggio contemporaneo e un’ampia gamma di argomenti, stili e prospettive.
Dataset aggiuntivi: RoBERTa combina i dataset sopra citati con altri tre dataset privati descritti nel paper, migliorando le prestazioni sui compiti downstream.
Utilizzando dati di addestramento più grandi, estendendo la durata dell’addestramento ed eseguendo più epoche e iterazioni, RoBERTa mostra un grande miglioramento del 4-6% sui compiti downstream rispetto ai risultati BERT originariamente riportati (Devlin et al. nel 2018).
Risultati di RoBERTa rispetto a BERT
Figura 7: Risultati di RoBERTa rispetto a BERT
Aumento delle dimensioni dei batch
Lavori precedenti nella traduzione automatica neurale hanno mostrato che l’addestramento con mini-batch molto grandi può migliorare la perplessità per il masked language modeling. Questo approccio migliora le prestazioni nei compiti finali quando il learning rate viene aumentato in modo appropriato (Ott et al., 2018). La perplessità è una metrica utilizzata per misurare le prestazioni dei modelli linguistici. Quantifica l’incertezza (distribuzione di probabilità) che un modello ha quando predice la parola successiva in una sequenza. Ad esempio, minore è la perplessità, più accurate sono le predizioni.
Basandosi su questo concetto, RoBERTa utilizza mini-batch molto più grandi durante l’addestramento. Mentre BERT utilizzava una dimensione del batch di 256 sequenze per un milione di step, RoBERTa l’ha aumentata a 8k sequenze per 31k step con un valore di learning rate pari a 1e-3. Tuttavia, le prestazioni ottimali sono state osservate con 2k sequenze per 125k step.
Confronto tra perplessità e prestazioni nei compiti finali di BERT vs. RoBERTa
Figura 8: Confronto tra perplessità e prestazioni nei compiti finali di BERT vs. RoBERTa
Codifica del testo a byte
RoBERTa ha anche introdotto un metodo alternativo per gestire le parole fuori vocabolario (OOV) impiegando la Byte Pair Encoding (BPE) a livello di byte. BERT utilizzava un vocabolario Byte-Pair Encoding (BPE) a livello di carattere di 30K unità. RoBERTa lo espande a un vocabolario BPE a livello di byte di 50K unità subword senza ulteriore preprocessing o tokenizzazione dell’input. Rispetto a BERT, questo metodo ha permesso a RoBERTa di codificare quasi qualsiasi parola o subword senza utilizzare il token sconosciuto.
Codifica del testo a byte
Figura 9: Codifica del testo a byte
Risultati degli esperimenti
Dopo aver modificato BERT(Devlin et al. 2019), RoBERTa è stato testato su benchmark NLP. Discuteremo le sue prestazioni su tre benchmark: GLUE, SQuAD, e RACE.
GLUE (General Language Understanding Evaluation): Una raccolta di 9 dataset per compiti di NLU (natural language understanding).
SQuAD (Stanford Question Answering Dataset): Un dataset progettato per addestrare e valutare sistemi di question answering. È composto da domande reali poste da esseri umani su un insieme di articoli di Wikipedia, dove la risposta a ciascuna domanda è uno specifico intervallo di testo all’interno dell’articolo corrispondente.
RACE (Reading Comprehension from Examinations): Un dataset di reading comprehension su larga scala raccolto da esami di inglese.
Risultati dei test sul dataset GLUE
RoBERTa è stato testato utilizzando due impostazioni di fine-tuning. Nella prima impostazione (single-task), RoBERTa è stato sottoposto separatamente a fine-tuning per 10 epoche per ciascun GLUE task, utilizzando solo i dati di addestramento per il task corrispondente. Ha ottenuto risultati allo stato dell’arte su tutti e nove i task GLUE, superando sia BERT sia XLNet. Nella seconda impostazione (ensembles), RoBERTa ha ottenuto un punteggio di 88.5 sulla GLUE leaderboard, eguagliando il precedente stato dell’arte stabilito da XLNet. RoBERTa ha stabilito nuovi risultati allo stato dell’arte su 4 dei 9 task GLUE: MNLI, QNLI, RTE e STS-B.
Risultati su GLUE
Figura 10: Risultati su GLUE
SQuAD
RoBERTa è stato sottoposto a fine-tuning utilizzando i dati di addestramento di SQuAD. Su SQuAD v1.1, RoBERTa eguaglia lo stato dell’arte stabilito da XLNet. Su SQuAD v2.0, RoBERTa ha raggiunto un punteggio F1 di 89.4, stabilendo un nuovo stato dell’arte per modelli singoli senza data augmentation. Questo è particolarmente notevole considerando che RoBERTa non utilizza alcuna tecnica specifica per SQuAD ed è sottoposto a fine-tuning solo sui dati SQuAD.
Risultati su SQuAD
Figura 11: Risultati su SQuAD
Risultati dei test sul dataset RACE
RACE è un benchmark impegnativo di comprensione della lettura che richiede ai modelli di rispondere a domande a scelta multipla basate su brani tratti da esami di inglese. Questo benchmark è particolarmente difficile perché richiede ai modelli di comprendere brani complessi e dedurre le risposte corrette in base al contesto. RoBERTa ha raggiunto un’accuratezza del 83.2% sul benchmark RACE e ha stabilito nuovi standard allo stato dell’arte.
Risultati sul set di test RACE
Figura 12: Risultati sul set di test RACE
Fine-tuning di RoBERTa per l’analisi del sentiment
Oltre alle sue prestazioni sui benchmark standard, RoBERTa si è dimostrato anche altamente efficace per il fine-tuning di task specifici, come l’analisi del sentiment. Il fine-tuning consiste nel prendere un modello pre-addestrato e adattarlo a un task specifico addestrandolo su un dataset più piccolo e specifico per il task. Il codice sorgente utilizzato in questo articolo proviene da Hugging Face.
Importazione e preparazione del dataset
Utilizzeremo il dataset disponibile alla Kaggle Competition e faremo riferimento solo al primo file csv del dump dei dati: train.tsv.
Importazione delle librerie Python:
!pip install transformers==3.0.2
Importa le librerie e i moduli necessari per eseguire il nostro script in questo passaggio.
# Importing the libraries needed
import pandas as pd
import numpy as np
import torch
import transformers
import json
from tqdm import tqdm
from torch.utils.data import Dataset, DataLoader
from transformers import RobertaModel, RobertaTokenizer
import logging
logging.basicConfig(level=logging.ERROR)
# Setting up the device for GPU usage
from torch import cuda
device = 'cuda' if cuda.is_available() else 'cpu'
Preparazione del dataset
Per prima cosa, carica il dataset e leggilo utilizzando il pacchetto Pandas.
train = pd.read_csv('train.tsv', delimiter='\t')
new_df = train[['Phrase', 'Sentiment']]
Definiamo alcune variabili:
MAX_LEN: La lunghezza massima del testo di input (256).
TRAIN_BATCH_SIZE and VALID_BATCH_SIZE: Le dimensioni dei batch per addestramento e validazione (rispettivamente 8 e 4).
LEARNING_RATE: Il tasso di apprendimento per il modello (1e-05).
Tokenizer: Viene utilizzata un'istanza di RobertaTokenizer per pre-elaborare i dati testuali.
La classe SentimentData implementa una classe dataset personalizzata, prendendo come input un data frame, un tokenizer e una lunghezza massima. Pre-elabora i dati testuali, restituendo un dizionario contenente ID di input, maschere di attenzione, ID dei tipi di token ed etichette di sentiment target.
La classe DataLoader crea data loader per i set di training e di test, iterando sui dati in batch durante il training e la validazione.
# Defining some key variables that will be used later on in the training
MAX_LEN = 256
TRAIN_BATCH_SIZE = 8
VALID_BATCH_SIZE = 4
LEARNING_RATE = 1e-05
tokenizer = RobertaTokenizer.from_pretrained('roberta-base', truncation=True, do_lower_case=True)
class SentimentData(Dataset):
def __init__(self, dataframe, tokenizer, max_len):
self.tokenizer = tokenizer
self.data = dataframe
self.text = dataframe.Phrase
self.targets = self.data.Sentiment
self.max_len = max_len
def __len__(self):
return len(self.text)
def __getitem__(self, index):
text = str(self.text[index])
text = " ".join(text.split())
inputs = self.tokenizer.encode_plus(
text,
None,
add_special_tokens=True,
max_length=self.max_len,
pad_to_max_length=True,
return_token_type_ids=True
)
ids = inputs['input_ids']
mask = inputs['attention_mask']
token_type_ids = inputs["token_type_ids"]
return {
'ids': torch.tensor(ids, dtype=torch.long),
'mask': torch.tensor(mask, dtype=torch.long),
'token_type_ids': torch.tensor(token_type_ids, dtype=torch.long),
'targets': torch.tensor(self.targets[index], dtype=torch.float)
}
train_size = 0.8
train_data=new_df.sample(frac=train_size,random_state=200)
test_data=new_df.drop(train_data.index).reset_index(drop=True)
train_data = train_data.reset_index(drop=True)
print("FULL Dataset: {}".format(new_df.shape))
print("TRAIN Dataset: {}".format(train_data.shape))
print("TEST Dataset: {}".format(test_data.shape))
training_set = SentimentData(train_data, tokenizer, MAX_LEN)
testing_set = SentimentData(test_data, tokenizer, MAX_LEN)
train_params = {'batch_size': TRAIN_BATCH_SIZE,
'shuffle': True,
'num_workers': 0
}
test_params = {'batch_size': VALID_BATCH_SIZE,
'shuffle': True,
'num_workers': 0
}
training_loader = DataLoader(training_set, **train_params)
testing_loader = DataLoader(testing_set, **test_params)
Creazione della rete neurale per il fine-tuning
Creeremo una rete neurale con RobertaClass.
Questa rete avrà il modello linguistico Roberta, seguito da un dropout e infine da un layer lineare per ottenere gli output finali.
I dati verranno forniti al modello linguistico Roberta come definito nel dataset.
L'output del layer finale verrà confrontato con la categoria di sentiment per determinare l'accuratezza della previsione del modello.
Inizializzeremo un'istanza della rete chiamata Model. Questa istanza verrà utilizzata per il training.
class RobertaClass(torch.nn.Module):
def __init__(self):
super(RobertaClass, self).__init__()
self.l1 = RobertaModel.from_pretrained("roberta-base")
self.pre_classifier = torch.nn.Linear(768, 768)
self.dropout = torch.nn.Dropout(0.3)
self.classifier = torch.nn.Linear(768, 5)
def forward(self, input_ids, attention_mask, token_type_ids):
output_1 = self.l1(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
hidden_state = output_1[0]
pooler = hidden_state[:, 0]
pooler = self.pre_classifier(pooler)
pooler = torch.nn.ReLU()(pooler)
pooler = self.dropout(pooler)
output = self.classifier(pooler)
return output
model = RobertaClass()
model.to(device)
Fine-tuning del modello
Dopo tutto lo sforzo di caricamento e preparazione del dataset e di creazione del modello. Questo è probabilmente il passaggio più semplice del processo.
Qui definiamo una funzione di training che addestra il modello per un numero specificato di epoche sul dataset di training sopra indicato. Un'epoca definisce quante volte i dati completi verranno passati attraverso la rete.
# Creating the loss function and optimizer
loss_function = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(params = model.parameters(), lr=LEARNING_RATE)
def calcuate_accuracy(preds, targets):
n_correct = (preds==targets).sum().item()
return n_correct
# Defining the training function on the 80% of the dataset for tuning the distilbert model
def train(epoch):
tr_loss = 0
n_correct = 0
nb_tr_steps = 0
nb_tr_examples = 0
model.train()
for _,data in tqdm(enumerate(training_loader, 0)):
ids = data['ids'].to(device, dtype = torch.long)
mask = data['mask'].to(device, dtype = torch.long)
token_type_ids = data['token_type_ids'].to(device, dtype = torch.long)
targets = data['targets'].to(device, dtype = torch.long)
outputs = model(ids, mask, token_type_ids)
loss = loss_function(outputs, targets)
tr_loss += loss.item()
big_val, big_idx = torch.max(outputs.data, dim=1)
n_correct += calcuate_accuracy(big_idx, targets)
nb_tr_steps += 1
nb_tr_examples+=targets.size(0)
if _%5000==0:
loss_step = tr_loss/nb_tr_steps
accu_step = (n_correct*100)/nb_tr_examples
print(f"Training Loss per 5000 steps: {loss_step}")
print(f"Training Accuracy per 5000 steps: {accu_step}")
optimizer.zero_grad()
loss.backward()
# # When using GPU
optimizer.step()
print(f'The Total Accuracy for Epoch {epoch}: {(n_correct*100)/nb_tr_examples}')
epoch_loss = tr_loss/nb_tr_steps
epoch_accu = (n_correct*100)/nb_tr_examples
print(f"Training Loss Epoch: {epoch_loss}")
print(f"Training Accuracy Epoch: {epoch_accu}")
return
EPOCHS = 1
for epoch in range(EPOCHS):
train(epoch)
Output:
Figura 13- Dettagli del training del modello
Validazione delle prestazioni del modello
Durante la fase di validazione, passiamo al modello i dati non visti (il dataset di test). Questo passaggio determina quanto bene il modello si comporta sui dati non visti, che costituiscono il 20% di train.tsv, separato durante la fase di creazione del dataset.
La fase di validazione verifica la capacità di generalizzazione del modello su dati nuovi e non visti. I pesi del modello non sono stati aggiornati. Solo l'output finale viene confrontato con il valore effettivo, che viene poi utilizzato per calcolare l'accuratezza del modello.
La funzione valid implementa la fase di validazione, prendendo come input il modello e il testing loader. Imposta il modello in modalità di valutazione, inizializza le variabili per tenere traccia delle previsioni corrette e incorrette e itera sul testing loader. Per ogni batch, calcola la perdita e l'accuratezza, stampando la perdita e l'accuratezza di validazione ogni 5000 passaggi e alla fine dell'epoca. Infine, restituisce l'accuratezza dell'epoca.
def valid(model, testing_loader):
model.eval()
n_correct = 0; n_wrong = 0; total = 0; tr_loss=0; nb_tr_steps=0; nb_tr_examples=0
with torch.no_grad():
for _, data in tqdm(enumerate(testing_loader, 0)):
ids = data['ids'].to(device, dtype = torch.long)
mask = data['mask'].to(device, dtype = torch.long)
token_type_ids = data['token_type_ids'].to(device, dtype=torch.long)
targets = data['targets'].to(device, dtype = torch.long)
outputs = model(ids, mask, token_type_ids).squeeze()
loss = loss_function(outputs, targets)
tr_loss += loss.item()
big_val, big_idx = torch.max(outputs.data, dim=1)
n_correct += calcuate_accuracy(big_idx, targets)
nb_tr_steps += 1
nb_tr_examples+=targets.size(0)
if _%5000==0:
loss_step = tr_loss/nb_tr_steps
accu_step = (n_correct*100)/nb_tr_examples
print(f"Validation Loss per 100 steps: {loss_step}")
print(f"Validation Accuracy per 100 steps: {accu_step}")
epoch_loss = tr_loss/nb_tr_steps
epoch_accu = (n_correct*100)/nb_tr_examples
print(f"Validation Loss Epoch: {epoch_loss}")
print(f"Validation Accuracy Epoch: {epoch_accu}")
return epoch_accu
acc = valid(model, testing_loader)
print("Accuracy on test data = %0.2f%%" % acc)
Output:
Figura 14- Dettagli della validazione del modello
Come puoi vedere, il modello prevede la categoria corretta di un dato campione con un'accuratezza del 69,47%, che può essere ulteriormente migliorata con più addestramento.
Opportunità di ricerca future
Il successo di RoBERTa ha aperto nuove porte alla ricerca nell'NLP e nell'ottimizzazione dei modelli. Ecco alcune potenziali opportunità di ricerca:
Scalabilità dei dataset
Dataset più grandi e tempi di addestramento più lunghi hanno migliorato RoBERTa. La ricerca futura potrebbe indagare la scalabilità dinamica dei dataset, in cui i modelli vengono addestrati su dataset progressivamente più grandi nel tempo. Questo approccio potrebbe aiutarci a capire come il modello apprende da nuovi dati e se può continuare a migliorare man mano che i suoi dati aumentano.
Fine-tuning multi-task
Le prestazioni di RoBERTa potrebbero essere ulteriormente migliorate esplorando procedure avanzate di fine-tuning multi-task. Al momento, è valido in molte cose senza essere addestrato su più compiti contemporaneamente. Tuttavia, i ricercatori potrebbero indagare come l'integrazione di più compiti durante la fase di fine-tuning possa migliorare le prestazioni su diversi benchmark.
Esplorazione degli obiettivi di pre-training
La rimozione dell'obiettivo NSP e l'uso di un approccio di masking dinamico rispetto al masking statico in RoBERTa sollevano interrogativi sul fatto che esistano modi ancora migliori per insegnare ai modelli linguistici. La ricerca futura può esplorare diversi metodi di pre-training che influenzano quanto bene RoBERTa comprende e completa i compiti.
Conclusione
Il paper di RoBERTa offre diversi contributi importanti al campo dell'NLP. Gli autori scoprono che modificare dinamicamente il pattern di masking nei dati di addestramento, addestrare su sequenze più lunghe e rimuovere il compito NSP può migliorare le prestazioni sui compiti downstream.
Inoltre, anche l'uso di una dimensione del batch e di un dataset di addestramento più grandi contribuisce a questo miglioramento. RoBERTa raggiunge risultati allo stato dell'arte su GLUE, RACE e SQuAD senza fine-tuning multi-task per GLUE o dati aggiuntivi per SQuAD.
Inoltre, il dibattito tra RoBERTa e BERT non riguarda solo quale modello sia migliore. Riguarda la comprensione dei compromessi tra risorse computazionali, prestazioni del modello e implicazioni etiche della distribuzione di questi modelli. I progressi di RoBERTa rispetto a BERT mostrano che il campo dell'NLP è tutt'altro che statico e aprono la porta a un percorso verso capacità di elaborazione del linguaggio più avanzate e complesse.
Ulteriori risorse
Continua a leggere

Build Multimodal Search for 3D Assets with Tripo and Zilliz Cloud
Generate 3D assets with Tripo, then search them by text, image, and metadata with multimodal embeddings and Zilliz Cloud.

Context Engineering Strategies for AI Agents: A Developer’s Guide
Learn practical context engineering strategies for AI agents. Explore frameworks, tools, and techniques to improve reliability, efficiency, and cost.

OpenAI o1: What Developers Need to Know
In this article, we will talk about the o1 series from a developer's perspective, exploring how these models can be implemented for sophisticated use cases.



