Sfruttare i superpoteri di NLP Un tutorial passo-passo per l’ottimizzazione di Hugging Face

Sfruttare i superpoteri di NLP Un tutorial passo-passo per ottimizzare Hugging Face

Introduzione

Perfezionare un modello di elaborazione del linguaggio naturale (NLP) implica la modifica degli iperparametri e dell’architettura del modello e l’adattamento del dataset per migliorarne le prestazioni in una determinata attività. Puoi ottenere ciò regolando il tasso di apprendimento, il numero di strati nel modello, le dimensioni degli embedding e altri parametri. La messa a punto è una procedura che richiede tempo e una solida comprensione del modello e del lavoro. Questo articolo illustra come perfezionare un modello Hugging Face.

Obiettivi di apprendimento

  • Comprendere la struttura del modello T5, inclusi i transformer e l’autoattenzione.
  • Apprendere come ottimizzare gli iperparametri per una migliore performance del modello.
  • Padroneggiare la preparazione dei dati testuali, inclusa la tokenizzazione e la formattazione.
  • Sapere come adattare modelli pre-addestrati a compiti specifici.
  • Apprendere come pulire, suddividere e creare dataset per l’addestramento.
  • Acquisire esperienza nell’addestramento e nella valutazione del modello utilizzando metriche come la perdita e l’accuratezza.
  • Esplorare le applicazioni del mondo reale del modello perfezionato per generare risposte o risposte.

Questo articolo è stato pubblicato come parte del Data Science Blogathon.

Informazioni sui modelli Hugging Face

Hugging Face è un’azienda che fornisce una piattaforma per l’addestramento e la distribuzione di modelli di elaborazione del linguaggio naturale (NLP). La piattaforma ospita una libreria di modelli adatta a vari compiti di NLP, tra cui traduzione del linguaggio, generazione di testo e risposte alle domande. Questi modelli vengono addestrati su ampi dataset e sono progettati per eccellere in una vasta gamma di attività di elaborazione del linguaggio naturale (NLP).

La piattaforma Hugging Face include anche strumenti per la messa a punto dei modelli pre-addestrati su dataset specifici, che possono aiutare ad adattare gli algoritmi a domini o linguaggi particolari. La piattaforma dispone anche di API per accedere e utilizzare modelli pre-addestrati in app e strumenti per la creazione di modelli personalizzati e la distribuzione nel cloud.

L’utilizzo della libreria Hugging Face per compiti di elaborazione del linguaggio naturale (NLP) ha vari vantaggi:

  1. Ampia selezione di modelli: La libreria Hugging Face offre una vasta gamma di modelli NLP pre-addestrati, inclusi modelli addestrati per traduzione del linguaggio, risposta alle domande e categorizzazione del testo. Questo rende semplice scegliere un modello che soddisfi le tue esigenze specifiche.
  2. Compatibilità tra piattaforme: La libreria Hugging Face è compatibile con sistemi standard di deep learning come TensorFlow, PyTorch e Keras, rendendo facile l’integrazione nel tuo workflow esistente.
  3. Semplice messa a punto: La libreria Hugging Face contiene strumenti per la messa a punto dei modelli pre-addestrati sul tuo dataset, risparmiando tempo ed sforzi rispetto all’addestramento di un modello da zero.
  4. Comunità attiva: La libreria Hugging Face ha una vasta e attiva comunità di utenti, il che significa che puoi ottenere assistenza e supporto e contribuire alla crescita della libreria stessa.
  5. Ben documentata: La libreria Hugging Face contiene una documentazione estesa, che facilita l’inizio e l’apprendimento di come utilizzarla in modo efficiente.

Importa librerie necessarie

Importare le librerie necessarie è analogo a creare un set di strumenti per una specifica attività di programmazione e analisi dei dati. Queste librerie, che sono spesso collezioni di codice predefinito, offrono una vasta gamma di funzioni e strumenti che aiutano a velocizzare lo sviluppo. Sviluppatori e data scientist possono accedere a nuove funzionalità, aumentare la produttività e utilizzare soluzioni esistenti importando le librerie appropriate.

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
import torch
from transformers import T5Tokenizer
from transformers import T5ForConditionalGeneration, AdamW
import pytorch_lightning as pl
from pytorch_lightning.callbacks import ModelCheckpoint
pl.seed_everything(100)
import warnings
warnings.filterwarnings("ignore")

Importa Dataset

L’importazione di un dataset è un passo iniziale cruciale nei progetti basati sui dati.

df = pd.read_csv("/kaggle/input/queestion-answer-dataset-qa/train.csv")
df.columns

df = df[['context','question', 'text']]
print("Numero di record: ", df.shape[0])

Problema

“Creare un modello capace di generare risposte basate sul contesto e sulle domande.”

Ad esempio,

Contesto = “La creazione di gruppi di casi simili mediante il clustering, ad esempio, può individuare pazienti simili o essere utilizzato per la segmentazione dei clienti nel settore bancario. La tecnica dell’associazione viene utilizzata per trovare elementi o eventi che spesso si verificano insieme, ad esempio i prodotti di un supermercato che un determinato cliente acquista di solito insieme. La rilevazione delle anomalie viene utilizzata per scoprire casi anomali e insoliti; ad esempio, il rilevamento delle frodi con le carte di credito.”

Domanda = “Qual è un esempio di rilevazione delle anomalie?”

Risposta = ????????????????????????????????

df["context"] = df["context"].str.lower()
df["question"] = df["question"].str.lower()
df["text"] = df["text"].str.lower()
df.head()

Inizializza Parametri

  • Lunghezza di input: Durante l’addestramento, ci si riferisce al numero di token di input (ad esempio, parole o caratteri) in un singolo esempio alimentato al modello come lunghezza di input. Se stai addestrando un modello di linguaggio per prevedere la prossima parola in una frase, la lunghezza di input sarebbe il numero di parole nella frase.
  • Lunghezza di output: Durante l’addestramento, ci si aspetta che il modello generi una quantità specifica di token di output, come parole o caratteri, in un singolo campione. La lunghezza di output corrisponde al numero di parole che il modello prevede nella frase.
  • Dimensione del batch di addestramento: Durante l’addestramento, il modello elabora diversi campioni contemporaneamente. Se si imposta la dimensione del batch di addestramento su 32, il modello gestisce 32 istanze, ad esempio 32 frasi, contemporaneamente prima di aggiornare i suoi pesi del modello.
  • Dimensione del batch di convalida: Simile alla dimensione del batch di addestramento, questo parametro indica il numero di istanze che il modello elabora durante la fase di convalida. In altre parole, rappresenta il volume dei dati che il modello elabora quando viene testato su un dataset di convalida.
  • Epoche: Un’epoca è un singolo passaggio attraverso l’intero dataset di addestramento. Quindi, se il dataset di addestramento comprende 1000 istanze e la dimensione del batch di addestramento è di 32, un’epoca richiederà 32 passi di addestramento. Se il modello viene addestrato per dieci epoche, avrà elaborato diecimila istanze (10 * 1000 = diecimila).
DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
INPUT_MAX_LEN = 512 # Lunghezza di input
OUT_MAX_LEN = 128 # Lunghezza di output
TRAIN_BATCH_SIZE = 8 # Dimensione del batch di addestramento
VALID_BATCH_SIZE = 2 # Dimensione del batch di convalida
EPOCHS = 5 # Numero di iterazioni

T5 Transformer

Il modello T5 si basa sull’architettura del Transformer, una rete neurale progettata per gestire efficacemente i dati di input sequenziali. Comprende un codificatore e un decodificatore, che includono una sequenza di “strati” interconnessi.

Le sezioni di encoder e decoder comprendono vari meccanismi di “attenzione” e reti “feedforward”. I meccanismi di attenzione consentono al modello di concentrarsi su diverse sezioni della sequenza di input in momenti diversi. Allo stesso tempo, le reti feedforward modificano i dati di input utilizzando un insieme di pesi e bias.

Il modello T5 utilizza anche l'”auto-attenzione”, che consente a ciascun elemento nella sequenza di input di prestare attenzione a ogni altro elemento. Ciò consente al modello di riconoscere i collegamenti tra parole e frasi nei dati di input, il che è fondamentale per molte applicazioni di elaborazione del linguaggio naturale.

Oltre all’encoder e al decoder, il modello T5 contiene anche una “testa del modello di linguaggio”, che prevede la prossima parola in una sequenza in base alle parole precedenti. Questo è fondamentale per lavori di traduzione e produzione di testo, in cui il modello deve fornire un’uscita coesa e dall’aspetto naturale.

Il modello T5 rappresenta una rete neurale grande e sofisticata progettata per un elaborazione sequenziale altamente efficiente e accurata dell’input. È stato sottoposto a un’ampia formazione su un set di dati di testo diversificato e può svolgere efficientemente un’ampia gamma di compiti di elaborazione del linguaggio naturale.

T5Tokenizer

T5Tokenizer viene utilizzato per trasformare un testo in una lista di token, ognuno che rappresenta una singola parola o segno di punteggiatura. Il tokenizer inserisce inoltre token unici nel testo di input per indicare l’inizio e la fine del testo e per distinguere varie frasi.

Il T5Tokenizer utilizza una combinazione di tokenizzazione a livello di carattere e a livello di parola e una strategia di tokenizzazione a livello di sotto-parola simile al tokenizer SentencePiece. Sotto-tokens il testo di input in base alla frequenza di ciascun carattere o sequenza di caratteri nei dati di formazione. Ciò aiuta il tokenizer nel trattamento di termini out-of-vocabulary (OOV) che non si verificano nei dati di formazione ma compaiono nei dati di test.

Il T5Tokenizer inserisce inoltre token unici nel testo per indicare l’inizio e la fine delle frasi e per dividerle. Ad esempio, aggiunge i token s> e /s> per indicare l’inizio e la fine di una frase, e pad> per indicare il riempimento.

MODEL_NAME = "t5-base"tokenizer = T5Tokenizer.from_pretrained(MODEL_NAME, model_max_length= INPUT_MAX_LEN)

print("eos_token: {} and id: {}".format(tokenizer.eos_token,                   tokenizer.eos_token_id)) # Token di fine (eos_token)print("unk_token: {} and id: {}".format(tokenizer.unk_token,                   tokenizer.eos_token_id)) # Token sconosciuto (unk_token)print("pad_token: {} and id: {}".format(tokenizer.pad_token,                 tokenizer.eos_token_id)) # Token di riempimento (pad_token)

Preparazione del dataset

Quando si lavora con PyTorch, di solito si preparano i dati per l’uso con il modello utilizzando una classe di dataset. La classe del dataset è responsabile del caricamento dei dati da disco e dell’esecuzione delle procedure di preparazione richieste, come la tokenizzazione e la numerazione. La classe dovrebbe inoltre implementare la funzione getitem, che viene utilizzata per ottenere un singolo elemento dal dataset tramite l’indice.

Il metodo init popola il dataset con la lista di testo, la lista di etichette e il tokenizer. La funzione len restituisce il numero di campioni nel dataset. La funzione get item restituisce un singolo elemento da un dataset tramite l’indice. Accetta un indice idx e restituisce l’input e le etichette tokenizzate.

È anche consuetudine includere varie fasi di preprocessing, come il padding e il troncamento degli input tokenizzati. È inoltre possibile convertire le etichette in tensori.

class T5Dataset:    def __init__(self, context, question, target):        self.context = context        self.question = question        self.target = target        self.tokenizer = tokenizer        self.input_max_len = INPUT_MAX_LEN        self.out_max_len = OUT_MAX_LEN    def __len__(self):        return len(self.context)    def __getitem__(self, item):        context = str(self.context[item])        context = " ".join(context.split())        question = str(self.question[item])        question = " ".join(question.split())        target = str(self.target[item])        target = " ".join(target.split())                        inputs_encoding = self.tokenizer(            context,            question,            add_special_tokens=True,            max_length=self.input_max_len,            padding = 'max_length',            truncation='only_first',            return_attention_mask=True,            return_tensors="pt"        )                output_encoding = self.tokenizer(            target,            None,            add_special_tokens=True,            max_length=self.out_max_len,            padding = 'max_length',            truncation= True,            return_attention_mask=True,            return_tensors="pt"        )        inputs_ids = inputs_encoding["input_ids"].flatten()        attention_mask = inputs_encoding["attention_mask"].flatten()        labels = output_encoding["input_ids"]        labels[labels == 0] = -100  # Come indicato nella documentazione di T5        labels = labels.flatten()        out = {            "context": context,            "question": question,            "answer": target,            "inputs_ids": inputs_ids,            "attention_mask": attention_mask,            "targets": labels        }        return out

DataLoader

La classe DataLoader carica i dati in parallelo e in batch, rendendo possibile lavorare con grandi dataset che altrimenti sarebbero troppo vasti da conservare in memoria. Combinando la classe DataLoader con una classe dataset contenente i dati da caricare.

Il dataloader si occupa di iterare sul dataset e restituire un batch di dati al modello per l’addestramento o la valutazione durante l’addestramento di un modello di trasformazione. La classe DataLoader offre vari parametri per controllare il caricamento e la pre-elaborazione dei dati, inclusi la dimensione del batch, il numero di thread lavoratori e se mescolare i dati prima di ogni epoca.

class T5DatasetModule(pl.LightningDataModule):    def __init__(self, df_train, df_valid):        super().__init__()        self.df_train = df_train        self.df_valid = df_valid        self.tokenizer = tokenizer        self.input_max_len = INPUT_MAX_LEN        self.out_max_len = OUT_MAX_LEN    def setup(self, stage=None):        self.train_dataset = T5Dataset(        context=self.df_train.context.values,        question=self.df_train.question.values,        target=self.df_train.text.values        )        self.valid_dataset = T5Dataset(        context=self.df_valid.context.values,        question=self.df_valid.question.values,        target=self.df_valid.text.values        )    def train_dataloader(self):        return torch.utils.data.DataLoader(         self.train_dataset,         batch_size= TRAIN_BATCH_SIZE,         shuffle=True,          num_workers=4        )    def val_dataloader(self):        return torch.utils.data.DataLoader(         self.valid_dataset,         batch_size= VALID_BATCH_SIZE,         num_workers=1        )

Costruzione del modello

Quando si crea un modello di trasformazione in PyTorch, di solito si inizia creando una nuova classe derivata da torch.nn.Module. Questa classe descrive l’architettura del modello, inclusi i livelli e la funzione forward. La funzione init della classe definisce l’architettura del modello, spesso istanziando i diversi livelli del modello e assegnandoli come attributi di classe.

Il metodo forward si occupa di far passare i dati attraverso il modello in avanti. Questo metodo accetta i dati di input e applica i livelli del modello per creare l’output. Il metodo forward dovrebbe implementare la logica del modello, come il passaggio dell’input attraverso una sequenza di livelli e il restituzione del risultato.

La funzione init della classe crea un livello di embedding, un livello di trasformazione e un livello completamente connesso e li assegna come attributi di classe. Il metodo forward accetta i dati entranti x, li elabora tramite le fasi specificate e restituisce il risultato. Durante l’addestramento di un modello di trasformazione, il processo di addestramento di solito prevede due fasi: addestramento e validazione.

Il metodo training_step specifica il motivo per cui viene eseguito un singolo passaggio di addestramento, che generalmente include:

  • passaggio in avanti attraverso il modello
  • calcolo della perdita
  • calcolo dei gradienti
  • aggiornamento dei parametri del modello

Il metodo val_step, come il metodo training_step, viene utilizzato per valutare il modello su un set di validazione. Di solito include:

  • passaggio in avanti attraverso il modello
  • calcolo delle metriche di valutazione
class T5Model(pl.LightningModule):        def __init__(self):        super().__init__()        self.model = T5ForConditionalGeneration.from_pretrained(MODEL_NAME, return_dict=True)    def forward(self, input_ids, attention_mask, labels=None):        output = self.model(            input_ids=input_ids,             attention_mask=attention_mask,             labels=labels        )        return output.loss, output.logits    def training_step(self, batch, batch_idx):        input_ids = batch["inputs_ids"]        attention_mask = batch["attention_mask"]        labels= batch["targets"]        loss, outputs = self(input_ids, attention_mask, labels)                self.log("train_loss", loss, prog_bar=True, logger=True)        return loss    def validation_step(self, batch, batch_idx):        input_ids = batch["inputs_ids"]        attention_mask = batch["attention_mask"]        labels= batch["targets"]        loss, outputs = self(input_ids, attention_mask, labels)        self.log("val_loss", loss, prog_bar=True, logger=True)                return loss    def configure_optimizers(self):        return AdamW(self.parameters(), lr=0.0001)

Addestramento del modello

Iterare sul dataset a blocchi, inviare l’input attraverso il modello e modificare i parametri del modello in base ai gradienti calcolati e a un insieme di criteri di ottimizzazione è comune per l’addestramento di un modello di trasformazione.

def run():        df_train, df_valid = train_test_split(        df[0:10000], test_size=0.2, random_state=101    )        df_train = df_train.fillna("none")    df_valid = df_valid.fillna("none")        df_train['context'] = df_train['context'].apply(lambda x: " ".join(x.split()))    df_valid['context'] = df_valid['context'].apply(lambda x: " ".join(x.split()))        df_train['text'] = df_train['text'].apply(lambda x: " ".join(x.split()))    df_valid['text'] = df_valid['text'].apply(lambda x: " ".join(x.split()))        df_train['question'] = df_train['question'].apply(lambda x: " ".join(x.split()))    df_valid['question'] = df_valid['question'].apply(lambda x: " ".join(x.split()))       df_train = df_train.reset_index(drop=True)    df_valid = df_valid.reset_index(drop=True)        dataModule = T5DatasetModule(df_train, df_valid)    dataModule.setup()    device = DEVICE    models = T5Model()    models.to(device)    checkpoint_callback  = ModelCheckpoint(        dirpath="/kaggle/working",        filename="best_checkpoint",        save_top_k=2,        verbose=True,        monitor="val_loss",        mode="min"    )    trainer = pl.Trainer(        callbacks = checkpoint_callback,        max_epochs= EPOCHS,        gpus=1,        accelerator="gpu"    )    trainer.fit(models, dataModule)run()

Predizione del modello

Per fare previsioni con un modello NLP fine-tuned come T5 utilizzando un nuovo input, è possibile seguire questi passaggi:

  • Preprocessare il nuovo input: Tokenizzare e preprocessare il testo del nuovo input per farlo corrispondere alla pre-elaborazione applicata ai dati di addestramento. Assicurarsi che sia nel formato corretto atteso dal modello.
  • Utilizzare il modello fine-tuned per l’inference: Caricare il modello T5 fine-tuned, precedentemente addestrato o caricato da un checkpoint.
  • Generare previsioni: Passare il nuovo input pre-elaborato al modello per fare previsioni. Nel caso di T5, è possibile utilizzare il metodo generate per generare risposte.
train_model = T5Model.load_from_checkpoint("/kaggle/working/best_checkpoint-v1.ckpt")train_model.freeze()def generate_question(context, question):    inputs_encoding =  tokenizer(        context,        question,        add_special_tokens=True,        max_length= INPUT_MAX_LEN,        padding = 'max_length',        truncation='only_first',        return_attention_mask=True,        return_tensors="pt"        )        generate_ids = train_model.model.generate(        input_ids = inputs_encoding["input_ids"],        attention_mask = inputs_encoding["attention_mask"],        max_length = INPUT_MAX_LEN,        num_beams = 4,        num_return_sequences = 1,        no_repeat_ngram_size=2,        early_stopping=True,        )    preds = [        tokenizer.decode(gen_id,        skip_special_tokens=True,         clean_up_tokenization_spaces=True)        for gen_id in generate_ids    ]    return "".join(preds)

Previsione

Eseguiamo una previsione utilizzando il modello T5 fine-tuned con un nuovo input:

context = “Clustering groups of similar cases, for example, \can find similar patients, or use for customer segmentation in the \banking field. Using association technique for finding items or events that \often co-occur, for example, grocery items that are usually bought together\by a particular customer. Using anomaly detection to discover abnormal \and unusual cases, for example, credit card fraud detection.”

que = “Qual è un esempio di rilevamento di anomalie?”

print(generate_question(context, que))

context = "La classificazione viene utilizzata quando il tuo obiettivo è categorico,\ mentre la regressione viene utilizzata quando la tua variabile di target\è continua. Sia la classificazione che la regressione appartengono alla categoria\degli algoritmi di apprendimento automatico supervisionato."que = "Quando viene utilizzata la classificazione?"print(generate_question(context, que))

Conclusioni

In questo articolo, ci siamo imbarcati in un viaggio per il fine-tuning di un modello di elaborazione del linguaggio naturale (NLP), nello specifico il modello T5, per un compito di domanda-risposta. Durante questo processo, abbiamo esplorato vari aspetti dello sviluppo e del deployment del modello NLP.

Punti chiave:

  • Abbiamo esplorato la struttura encoder-decoder e i meccanismi di self-attention che ne sottendono le capacità.
  • L’arte della messa a punto degli iperparametri è una competenza essenziale per ottimizzare le prestazioni del modello.
  • Sperimentando con learning rate, dimensioni del batch e dimensioni del modello, siamo stati in grado di fare un fine-tuning efficace del modello.
  • Competenti nella tokenizzazione, nella padding e nella conversione dei dati di testo grezzi in un formato adatto per l’input del modello.
  • Abbiamo approfondito il fine-tuning, inclusi il caricamento dei pesi pre-addestrati, la modifica dei livelli del modello e l’adattamento agli specifici compiti.
  • Abbiamo imparato come pulire e strutturare i dati, dividerli in set di addestramento e di validazione.
  • Abbiamo dimostrato come il modello possa generare risposte o soluzioni basate sul contesto di input e sulle domande, mostrando la sua utilità nel mondo reale.

Domande frequenti

I media mostrati in questo articolo non sono di proprietà di Analytics Vidhya e sono utilizzati a discrezione dell’autore.