Allenare e Ottimizzare i Modelli di Sentence Transformers

'Allenamento e Ottimizzazione dei Modelli di Sentence Transformers'

Guarda questo tutorial con il Notebook Companion:



Allenare o affinare un modello Sentence Transformers dipende molto dai dati disponibili e dal compito da svolgere. La chiave è duplice:

  1. Capire come inserire i dati nel modello e preparare il tuo dataset di conseguenza.
  2. Conoscere le diverse funzioni di perdita e come sono correlate al dataset.

In questo tutorial, imparerai:

  1. Capire come funzionano i modelli Sentence Transformers creandone uno “da zero” o affinandone uno dallo Hugging Face Hub.
  2. Conoscere i diversi formati che il tuo dataset può avere.
  3. Rivedere le diverse funzioni di perdita che puoi scegliere in base al formato del tuo dataset.
  4. Allenare o affinare il tuo modello.
  5. Condividere il tuo modello con lo Hugging Face Hub.
  6. Capire quando i modelli Sentence Transformers potrebbero non essere la scelta migliore.

Come funzionano i modelli Sentence Transformers

In un modello Sentence Transformer, mappi un testo di lunghezza variabile (o pixel di immagine) in un embedding di dimensione fissa che rappresenta il significato di quell’input. Per iniziare con gli embeddings, guarda il nostro tutorial precedente. Questo post si concentra sul testo.

Ecco come funzionano i modelli Sentence Transformers:

  1. Layer 1 – Il testo di input viene passato attraverso un modello Transformer pre-addestrato che può essere ottenuto direttamente dallo Hugging Face Hub. Questo tutorial utilizzerà il modello “distilroberta-base”. Gli output del Transformer sono embedding di parole contestualizzati per tutti i token di input; immagina un embedding per ogni token del testo.
  2. Layer 2 – Gli embeddings passano attraverso un layer di pooling per ottenere un singolo embedding di dimensione fissa per tutto il testo. Ad esempio, il mean pooling calcola la media degli embeddings generati dal modello.

Questa figura riassume il processo:

Ricorda di installare la libreria Sentence Transformers con pip install -U sentence-transformers. Nel codice, questo processo a due fasi è semplice:

from sentence_transformers import SentenceTransformer, models

## Step 1: utilizza un modello di lingua esistente
word_embedding_model = models.Transformer('distilroberta-base')

## Step 2: utilizza una funzione di pooling sugli embeddings dei token
pooling_model = models.Pooling(word_embedding_model.get_word_embedding_dimension())

## Unisci i passaggi 1 e 2 utilizzando l'argomento modules
model = SentenceTransformer(modules=[word_embedding_model, pooling_model])

Dal codice sopra, puoi vedere che i modelli Sentence Transformers sono composti da moduli, ossia una lista di layer che vengono eseguiti consecutivamente. Il testo di input entra nel primo modulo e l’output finale proviene dall’ultimo componente. Per quanto semplice possa sembrare, il modello sopra è un’architettura tipica per i modelli Sentence Transformers. Se necessario, è possibile aggiungere ulteriori layer, ad esempio densi, bag of words e convoluzionali.

Perché non utilizzare un modello Transformer, come BERT o Roberta, direttamente per creare embeddings per intere frasi e testi? Ci sono almeno due motivi.

  1. I Transformer pre-addestrati richiedono un grande sforzo computazionale per svolgere compiti di ricerca semantica. Ad esempio, trovare la coppia più simile in una collezione di 10.000 frasi richiede circa 50 milioni di calcoli di inferenza (~65 ore) con BERT. In contrasto, un modello Sentence Transformers con BERT riduce il tempo a circa 5 secondi.
  2. Una volta addestrati, i Transformer creano rappresentazioni di frasi scadenti “out of the box”. Un modello BERT con gli embeddings dei token mediati per creare un embedding della frase ha prestazioni inferiori rispetto agli embeddings GloVe sviluppati nel 2014.

In questa sezione stiamo creando un modello Sentence Transformers da zero. Se vuoi affinare un modello Sentence Transformers esistente, puoi saltare i passaggi sopra e importarlo dallo Hugging Face Hub. La maggior parte dei modelli Sentence Transformers si trova nel compito “Sentence Similarity”. Qui carichiamo il modello “sentence-transformers/all-MiniLM-L6-v2”:

from sentence_transformers import SentenceTransformer

model_id = "sentence-transformers/all-MiniLM-L6-v2"
model = SentenceTransformer(model_id)

Ora per la parte più critica: il formato del dataset.

Come preparare il tuo dataset per allenare un modello Sentence Transformers

Per allenare un modello di Sentence Transformers, è necessario informarlo in qualche modo che due frasi hanno un certo grado di similarità. Pertanto, ogni esempio nei dati richiede un’etichetta o una struttura che consenta al modello di capire se due frasi sono simili o diverse.

Purtroppo, non c’è un singolo modo per preparare i tuoi dati per addestrare un modello di Sentence Transformers. Dipende in gran parte dai tuoi obiettivi e dalla struttura dei tuoi dati. Se non hai un’etichetta esplicita, che è lo scenario più probabile, puoi derivarla dalla struttura dei documenti da cui hai ottenuto le frasi. Ad esempio, due frasi nello stesso report dovrebbero essere più confrontabili rispetto a due frasi in report diversi. Le frasi vicine potrebbero essere più confrontabili rispetto alle frasi non vicine.

Inoltre, la struttura dei tuoi dati influenzerà quale funzione di perdita puoi utilizzare. Questo verrà discusso nella sezione successiva.

Ricordati che il Notebook Companion per questo post ha già tutto il codice implementato.

La maggior parte delle configurazioni del dataset avrà una delle quattro forme seguenti (di seguito vedrai esempi di ogni caso):

  • Caso 1: L’esempio è una coppia di frasi e un’etichetta che indica quanto sono simili. L’etichetta può essere un numero intero o un numero decimale. Questo caso si applica ai dataset originariamente preparati per l’Inferenza del Linguaggio Naturale (NLI), poiché contengono coppie di frasi con un’etichetta che indica se si inferiscono reciprocamente o meno.
  • Caso 2: L’esempio è una coppia di frasi positive (simili) senza un’etichetta. Ad esempio, coppie di parafrasi, coppie di testi completi e i loro riassunti, coppie di domande duplicate, coppie di ( query , risposta ), o coppie di ( lingua_sorgente , lingua_destinazione ). Anche i dataset di Inferenza del Linguaggio Naturale possono essere formattati in questo modo accoppiando frasi che implicano l’una con l’altra. Avere i tuoi dati in questo formato può essere ottimo poiché puoi utilizzare la MultipleNegativesRankingLoss, una delle funzioni di perdita più utilizzate per i modelli di Sentence Transformers.
  • Caso 3: L’esempio è una frase con un’etichetta numerica. Questo formato dei dati viene facilmente convertito dalle funzioni di perdita in tre frasi (terne) in cui la prima è un “ancora”, la seconda un “positivo” della stessa classe dell’ancora e la terza un “negativo” di una classe diversa. Ogni frase ha un’etichetta numerica che indica la classe a cui appartiene.
  • Caso 4: L’esempio è una terna (ancora, positivo, negativo) senza classi o etichette per le frasi.

Ad esempio, in questo tutorial addestrerai un Sentence Transformer utilizzando un dataset nel quarto caso. Successivamente, lo affinerai utilizzando la configurazione del dataset del secondo caso (fare riferimento al Notebook Companion per questo blog).

Si noti che i modelli di Sentence Transformers possono essere addestrati con etichettatura umana (casi 1 e 3) o con etichette dedotte automaticamente dalla formattazione del testo (principalmente caso 2; sebbene il caso 4 non richieda etichette, è più difficile trovare dati in una terna a meno che non lo si elabori come fa la funzione MegaBatchMarginLoss).

Ci sono dataset su Hugging Face Hub per ciascuno dei casi sopra. Inoltre, i dataset nell’Hub hanno una funzionalità di anteprima del dataset che consente di visualizzare la struttura dei dataset prima di scaricarli. Ecco alcuni esempi di dataset per ciascuno di questi casi:

  • Caso 1: Puoi utilizzare la stessa configurazione dell’Inferenza del Linguaggio Naturale se hai (o crei) un’etichetta che indica il grado di similarità tra due frasi; ad esempio {0,1,2} dove 0 è contraddizione e 2 è implicazione. Controlla la struttura del dataset SNLI.

  • Caso 2: Il dataset di Compressione delle Frasi ha esempi composti da coppie positive. Se il tuo dataset ha più di due frasi positive per esempio, ad esempio quintetti come nei dataset COCO Captions o Flickr30k Captions, puoi formattare gli esempi in modo da avere diverse combinazioni di coppie positive.

  • Caso 3: Il dataset TREC ha etichette numeriche che indicano la classe di ciascuna frase. Ogni esempio nel dataset Yahoo Answers Topics contiene tre frasi e un’etichetta che indica l’argomento; quindi, ogni esempio può essere diviso in tre.

  • Caso 4: Il dataset di Triplette di Quora ha triple (ancora, positivo, negativo) senza etichette.

Il passo successivo consiste nel convertire il dataset in un formato comprensibile dal modello di Sentence Transformers. Il modello non può accettare elenchi grezzi di stringhe. Ogni esempio deve essere convertito in una classe sentence_transformers.InputExample e quindi in una classe torch.utils.data.DataLoader per raggruppare e mescolare gli esempi.

Installa Hugging Face Datasets con pip install datasets. Quindi importa un dataset con la funzione load_dataset:

from datasets import load_dataset

dataset_id = "embedding-data/QQP_triplets"
dataset = load_dataset(dataset_id)

Questa guida utilizza un dataset di triplette senza etichetta, come nel quarto caso sopra.

Con la libreria datasets puoi esplorare il dataset:

print(f"- Il dataset {dataset_id} ha {dataset['train'].num_rows} esempi.")
print(f"- Ogni esempio è di tipo {type(dataset['train'][0])} con un valore di tipo {type(dataset['train'][0]['set'])}.")
print(f"- Gli esempi sono simili a questo: {dataset['train'][0]}")

Output:

- Il dataset embedding-data/QQP_triplets ha 101762 esempi.
- Ogni esempio è di tipo <class 'dict'> con un valore di tipo <class 'dict'>.
- Gli esempi sono simili a questo: {'set': {'query': 'Perché in India non abbiamo un dibattito politico uno contro uno come negli Stati Uniti?', 'pos': ['Perché non possiamo avere un dibattito pubblico tra politici in India come quello negli Stati Uniti?'], 'neg': ['Le persone su Quora possono smettere di fare dibattiti India Pakistan? Siamo stanchi di vederli tutti i giorni in quantità?']...]

Puoi vedere che query (l’ancora) ha una singola frase, pos (positivo) è una lista di frasi (quella che stampiamo ha solo una frase), e neg (negativo) è una lista di frasi multiple.

Converti gli esempi in oggetti InputExample. Per semplicità, (1) verrà utilizzato solo uno dei positivi e uno dei negativi nel dataset embedding-data/QQP_triplets. (2) Utilizzeremo solo la metà degli esempi disponibili. È possibile ottenere risultati molto migliori aumentando il numero di esempi.

from sentence_transformers import InputExample

train_examples = []
train_data = dataset['train']['set']
# Per agilità, utilizziamo solo la metà dei nostri dati disponibili
n_examples = dataset['train'].num_rows // 2

for i in range(n_examples):
  example = train_data[i]
  train_examples.append(InputExample(texts=[example['query'], example['pos'][0], example['neg'][0]]))

Converti gli esempi di allenamento in un Dataloader.

from torch.utils.data import DataLoader

train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=16)

Il passo successivo è scegliere una funzione di perdita adeguata che possa essere utilizzata con il formato dei dati.

Funzioni di perdita per l’allenamento di un modello Sentence Transformers

Ricorda i quattro diversi formati in cui i tuoi dati potrebbero trovarsi? Ognuno avrà una diversa funzione di perdita associata.

Caso 1: Coppia di frasi e un’etichetta che indica quanto sono simili. La funzione di perdita ottimizza affinché (1) le frasi con le etichette più vicine siano vicine nello spazio vettoriale e (2) le frasi con le etichette più lontane siano il più lontano possibile. La funzione di perdita dipende dal formato dell’etichetta. Se è un intero, utilizza ContrastiveLoss o SoftmaxLoss; se è un float, puoi utilizzare CosineSimilarityLoss.

Caso 2: Se hai solo due frasi simili (due positivi) senza etichette, puoi utilizzare la funzione MultipleNegativesRankingLoss. Può essere utilizzata anche la funzione MegaBatchMarginLoss, che convertirebbe i tuoi esempi in triplette (ancora_i, positivo_i, positivo_j) dove positivo_j funge da negativo.

Caso 3: Quando i tuoi campioni sono triplette della forma [ancora, positivo, negativo] e hai un’etichetta intera per ciascuno, una funzione di perdita ottimizza il modello affinché l’ancora e le frasi positive siano più vicine nello spazio vettoriale rispetto all’ancora e alle frasi negative. Puoi utilizzare BatchHardTripletLoss, che richiede che i dati siano etichettati con interi (ad esempio, etichette 1, 2, 3) assumendo che i campioni con la stessa etichetta siano simili. Pertanto, le ancora e i positivi devono avere la stessa etichetta, mentre i negativi devono avere una diversa. In alternativa, puoi utilizzare BatchAllTripletLoss, BatchHardSoftMarginTripletLoss o BatchSemiHardTripletLoss. Le differenze tra di esse sono al di fuori dello scopo di questo tutorial, ma possono essere consultate nella documentazione di Sentence Transformers.

Caso 4: Se non hai un’etichetta per ogni frase nelle triplette, dovresti utilizzare TripletLoss. Questa funzione di perdita minimizza la distanza tra l’ancora e le frasi positive, mentre massimizza la distanza tra l’ancora e le frasi negative.

Questa figura riassume i diversi formati dei dataset, gli esempi di dataset nell’Hub e le loro funzioni di perdita adeguate.

La parte più difficile è scegliere una funzione di perdita adatta concettualmente. Nel codice, ci sono solo due righe:

from sentence_transformers import losses

train_loss = losses.TripletLoss(model=model)

Una volta che il dataset ha il formato desiderato e una funzione di perdita adatta è al suo posto, addestrare un Sentence Transformer è semplice.

Come addestrare o affinare un modello di Sentence Transformer

“SentenceTransformers è stato progettato in modo che affinare i tuoi modelli di incorporamento di frasi/testo sia facile. Fornisce la maggior parte dei blocchi di costruzione che puoi combinare per ottimizzare gli incorporamenti per il tuo compito specifico.” – Documentazione di Sentence Transformers.

Ecco come appare l’addestramento o il fine-tuning:

model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=10) 

Ricorda che se stai affinando un modello Sentence Transformers esistente (vedi Notebook Companion), puoi chiamare direttamente il metodo fit su di esso. Se si tratta di un nuovo modello Sentence Transformers, è necessario definirlo prima, come hai fatto nella sezione “Come funzionano i modelli di Sentence Transformers”.

Ecco tutto; hai un nuovo o migliorato modello di Sentence Transformers! Vuoi condividerlo nell’Hugging Face Hub?

Prima, accedi all’Hugging Face Hub. Dovrai creare un token write nelle impostazioni del tuo account. Ci sono poi due opzioni per accedere:

  1. Digita huggingface-cli login nel tuo terminale e inserisci il tuo token.

  2. Se stai usando un notebook Python, puoi usare notebook_login.

from huggingface_hub import notebook_login

notebook_login()

Quindi, puoi condividere i tuoi modelli chiamando il metodo save_to_hub del modello addestrato. Per impostazione predefinita, il modello verrà caricato nel tuo account, ma puoi caricarlo in un’organizzazione specifica passandola come parametro organization. save_to_hub genera automaticamente una scheda del modello, un widget di inferenza, frammenti di codice di esempio e ulteriori dettagli. Puoi aggiungere automaticamente alla scheda del modello nell’Hub un elenco di dataset utilizzati per addestrare il modello con l’argomento train_datasets:

model.save_to_hub(
    "distilroberta-base-sentence-transformer", 
    organization= # Aggiungi il tuo nome utente
    train_datasets=["embedding-data/QQP_triplets"],
    )

Nel Notebook Companion ho affinato lo stesso modello utilizzando il dataset embedding-data/sentence-compression e la funzione di perdita MultipleNegativesRankingLoss.

Quali sono i limiti di Sentence Transformers?

I modelli di Sentence Transformers funzionano molto meglio dei semplici modelli Transformers per la ricerca semantica. Tuttavia, in quali casi i modelli di Sentence Transformers non funzionano bene? Se il tuo compito è la classificazione, allora utilizzare gli incorporamenti delle frasi è un approccio sbagliato. In quel caso, sarebbe meglio utilizzare la libreria 🤗 Transformers.

Risorse aggiuntive

  • Iniziare con gli incorporamenti.
  • Comprendere la ricerca semantica.
  • Inizia con il tuo primo modello di Sentence Transformers.
  • Generare playlist utilizzando Sentence Transformers.
  • Documentazione di Hugging Face + Sentence Transformers.

Grazie per la lettura! Buon lavoro con gli incorporamenti.