Metriche ROUGE Valutazione delle Sommari in Grandi Modelli di Linguaggio.

Metriche ROUGE per valutare sommari in modelli di linguaggio di grandi dimensioni.

L’incremento delle applicazioni commerciali basate su grandi modelli di linguaggio ha portato alla necessità di misurare la qualità delle soluzioni fornite da queste applicazioni. Qui entra in gioco la metrica ROUGE.

Immagine generata dall'Autore usando Dall-e2

Le metriche che abbiamo utilizzato fino ad ora con modelli più tradizionali, come Accuracy, F1 Score o Recall, non ci aiutano a valutare i risultati dei modelli generativi.

Con questi modelli, stiamo iniziando a utilizzare metriche come BLEU, ROUGE o METEOR. Metriche adattate all’obiettivo del modello.

In questo articolo, voglio spiegare come utilizzare le metriche ROUGE per misurare la qualità dei riassunti prodotti dai nuovi grandi modelli di linguaggio.

Cos’è ROUGE?

ROUGE consiste in un insieme di metriche utilizzate per confrontare la qualità di un testo generato rispetto a un testo di riferimento.

Può essere utilizzato ogni volta che abbiamo a disposizione un testo di riferimento. Alcune delle applicazioni più comuni sono: traduzioni, riassunti di testo o estrazione di entità tra gli altri.

È importante notare che ROUGE è affidabile solo quanto la qualità del testo di riferimento. Se hanno una qualità scadente, i risultati forniti da ROUGE potrebbero non riflettere correttamente la qualità del testo generato.

Un’altra utilità di ROUGE è misurare quanto differiscono gli output prodotti da due modelli diversi. Forse vogliamo vedere se ci sono differenze significative tra i testi prodotti dai due modelli.

Inoltre, possiamo utilizzare ROUGE per stimare se processi come il potatura o la quantizzazione (riduzione del peso dei modelli) hanno alterato significativamente i risultati prodotti dal nostro modello per il compito specifico in cui vogliamo utilizzarlo.

Le metriche fornite sono:

  • ROUGE-1: Indica la corrispondenza tra il testo generato e il testo di riferimento utilizzando 1-grammi o singole parole.
  • ROUGE-2: La stessa cosa di ROUGE-1, ma considera insiemi di 2-grammi.
  • ROUGE-L: Questa metrica valuta la corrispondenza della sottosequenza comune più lunga di parole tra i due testi. Le parole non devono essere nell’ordine esatto.

Maggiori informazioni sulle metriche ROUGE possono essere trovate su: https://pypi.org/project/rouge-score/

Cosa stiamo per misurare nel nostro Notebook?

Misureremo un modello di base T5 rispetto a un modello T5 sintonizzato per la generazione di riassunti.

Inizialmente, analizzeremo le differenze tra i due modelli per determinare se il processo di sintonizzazione ha prodotto un modello con un comportamento distinto.

In seguito, valuteremo entrambi i modelli utilizzando i testi di riferimento forniti nel dataset CNN-DailyMail, per vedere quale produce i migliori riassunti.

Il codice sorgente per questo articolo, così come l’intero corso sui grandi modelli di linguaggio, può essere trovato nel mio repository GitHub insieme agli articoli corrispondenti in inglese. Il notebook utilizzato per questo articolo è accessibile su: https://github.com/peremartra/Large-Language-Model-Notebooks-Course/blob/main/rouge-evaluation-untrained-vs-trained-llm.ipynb

GitHub – peremartra/Large-Language-Model-Notebooks-Course

Contribuisci allo sviluppo di peremartra/Large-Language-Model-Notebooks-Course creando un account su GitHub.

github.com

I modelli utilizzati possono essere trovati su Hugging Face.

  • Sintonizzato: t5-Base: https://huggingface.co/flax-community/t5-base-cnn-dm
  • Base: t5-Base: https://huggingface.co/t5-base

Caricamento dei dati.

Nella prima parte dell’articolo, utilizzeremo un dataset disponibile su Kaggle contenente notizie del MIT che non hanno riassunti generati. Questo dataset verrà utilizzato per verificare che entrambi i modelli caricati generino riassunti sufficientemente differenti.

Link al dataset: https://www.kaggle.com/datasets/deepanshudalal09/mit-ai-news-published-till-2023

Iniziamo importando le librerie Python tipiche:

#Importa librerie genericheimport numpy as np import pandas as pdimport torch

Tutte queste sono librerie ben conosciute nell’universo Python. L’importazione di torch è necessaria perché ROUGE lo richiede.

Carichiamo il dataset.

news = pd.read_csv('/kaggle/input/mit-ai-news-published-till-2023/articles.csv')DOCUMENTO="Corpo dell'articolo"#Poiché è solo un corso, selezioniamo una piccola porzione delle notizie.MAX_NEWS = 3subset_news = news.head(MAX_NEWS)subset_news.head()

Il contenuto completo dell’articolo è contenuto nella colonna Corpo dell’articolo. Questo è l’unico campo del dataset di cui abbiamo bisogno.

Stiamo selezionando solo tre articoli per accelerare l’esecuzione del notebook.

articles = subset_news[DOCUMENTO].tolist()

Abbiamo memorizzato il testo completo dei tre articoli caricati nella variabile chiamata articles. Se vogliamo lavorare con più o meno articoli di notizie, è sufficiente modificare il valore della variabile MAX_NEWS.

Carica i modelli e crea i riassunti.

I due modelli utilizzati sono della famiglia T5. Uno di essi è il modello fondamentale T5-Base, mentre l’altro è un modello derivato da T5-Base ma messo a punto con il dataset CNN-DailyMail.

Dato che entrambi i modelli sono disponibili su Hugging Face, lavoreremo con la libreria transformers.

import transformersfrom transformers import AutoTokenizer, AutoModelForSeq2SeqLM#I nomi dei modelli sono memorizzati nelle variabili model_name_small e model_name_reference.model_name_small = "t5-base"model_name_reference = "flax-community/t5-base-cnn-dm"

Per ottenere i tokenizzatori e i modelli, ho creato la seguente funzione:

#Questa funzione restituisce il tokenizzatore e il modello. def get_model(model_id):    tokenizer = AutoTokenizer.from_pretrained(model_id)    model = AutoModelForSeq2SeqLM.from_pretrained(model_id)        return tokenizer, model

Questa funzione riceve in input il model_id e restituisce il modello caricato insieme al tokenizzatore.

Il tokenizzatore viene utilizzato per convertire il testo in sequenze di token che possono essere elaborati dal modello.

Utilizzando la funzione get_model, possiamo ottenere i due tokenizzatori e i modelli:

tokenizer_small, model_small = get_model(model_name_small)tokenizer_reference, model_reference = get_model(model_name_reference)

Per generare i riassunti, creeremo una funzione che prende quattro parametri:

  • Una lista di testi da riassumere.
  • Il tokenizzatore.
  • Il modello.
  • La lunghezza massima per il riassunto ottenuto.

La funzione dovrebbe formattare leggermente il testo degli articoli da riassumere. Ciò comporta l’introduzione di un prefisso prima di ciascun articolo. Questo prefisso consisterà in istruzioni per il modello. Nel nostro caso, vogliamo che il modello faccia un riassunto degli articoli, quindi il prefisso da aggiungere sarà: “Riassumi questa notizia:“.

Utilizzando il tokenizzatore, ogni articolo verrà trasformato in codifiche, che verranno inviate al modello. Il modello restituirà un riassunto, che deve essere decodificato e convertito da codifiche a testo.

def create_summaries(texts_list, tokenizer, model, max_l=125):        # Aggiungiamo un prefisso a ciascun articolo da riassumere     # in modo che il modello sappia cosa deve fare    prefisso = "Riassumi questa notizia: "      elenco_riassunti = [] #Contiene tutti i riassunti    texts_list = [prefisso + text for text in texts_list]        for text in texts_list:                riassunto=""                #calcola le codifiche        input_encodings = tokenizer(text,                                     max_length=1024,                                     return_tensors='pt',                                     padding=True,                                     truncation=True)        # Genera i riassunti        with torch.no_grad():            output = model.generate(                input_ids=input_encodings.input_ids,                attention_mask=input_encodings.attention_mask,                max_length=max_l,  # Imposta la lunghezza massima del riassunto generato                num_beams=2,     # Imposta il numero di fasci per la ricerca a fascio                early_stopping=True            )                    #Decodifica per ottenere il testo        riassunto = tokenizer.batch_decode(output, skip_special_tokens=True)                #Aggiungi il riassunto all'elenco dei riassunti         elenco_riassunti += riassunto    return elenco_riassunti 

Analizziamo una parte specifica del codice della funzione: l’invocazione del modello per generare i riepiloghi:

   # Genera i riepiloghi    with torch.no_grad():        output = model.generate(            input_ids=input_encodings.input_ids,            attention_mask=input_encodings.attention_mask,            max_length=max_l,  # Imposta la lunghezza massima del riepilogo generato            num_beams=2,     # Imposta il numero di beams per la ricerca di beam            early_stopping=True        )

Come puoi vedere, iniziamo il blocco con la riga: with torch.no_grad() che indica che non vogliamo calcolare i gradienti all’interno del blocco. Questo migliora la velocità del codice.

Poi chiamiamo model.generate con i seguenti parametri:

  • input_ids: Le codifiche che rappresentano il testo di input tokenizzato.
  • attention_mask: La maschera di attenzione, fornita dal tokenizer.
  • max_length: La lunghezza massima della risposta del modello.
  • beams: Maggiore è il numero di beams, maggiore è la diversità nella risposta generata dal modello. Lo manteniamo a 2 per introdurre un po’ di varietà.
  • early_stopping: questo parametro consente al modello di smettere di generare testo prima che la risposta raggiunga la lunghezza massima. È una buona pratica impostare questo valore su True.

Vediamo come chiamare la funzione create_summaries per ottenere i riepiloghi:

# Creazione dei riepiloghi per entrambi i modelli. summaries_small = create_summaries(articles,                                   tokenizer_small,                                   model_small)summaries_reference = create_summaries(articles,                                       tokenizer_reference,                                       model_reference)

Come puoi vedere, è semplice. Devi solo passare i testi da riassumere insieme ai tokenizer e ai modelli.

Diamo un’occhiata ai riepiloghi generati dai due modelli:

summaries_small: [‘MIT e MIT-Watson AI Lab hanno sviluppato un framework unificato. Il sistema può prevedere contemporaneamente le proprietà molecolari e generare nuove molecole. Usa questa grammatica per costruire molecole valide e prevederne le proprietà.’, ‘BioAutoMATED è un sistema di apprendimento automatico automatizzato che può selezionare e costruire un modello appropriato per un determinato set di dati. Può anche occuparsi del laborioso compito di preelaborazione dei dati, riducendo un processo di mesi a poche ore. “Vogliamo abbassare queste barriere per molte persone che vogliono usare l’apprendimento automatico o la biologia”, dice la prima coautrice Jacqueline Valeri.’, “I ricercatori del MIT e di IBM hanno reso più robusto un modello di visione artificiale addestrandolo a funzionare come una parte del cervello su cui si basano gli esseri umani e altri primati per il riconoscimento degli oggetti. ‘Abbiamo chiesto alla rete neurale artificiale di rendere la funzione di uno dei tuoi strati ‘neurali’ simulati il più simile possibile a quello corrispondente nel cervello biologico'”, dice il professor MIT.]

summaries_reference: [‘I ricercatori hanno creato un sistema di apprendimento automatico che impara automaticamente il “linguaggio” delle molecole utilizzando solo un piccolo set di dati specifico del dominio. Il sistema impara a costruire molecole valide e prevederne le proprietà. Il Computational design and Fabrication Group sarà presentato alla International Conference for Machine Learning.’, “Il sistema di apprendimento automatico automatico può selezionare e costruire un modello appropriato per un determinato set di dati. ‘BioAutoMATED’ è un sistema di apprendimento automatico automatico. Lo strumento include modelli di classificazione binaria, modelli di classificazione multiclasse e reti neurali più complesse.”, “I ricercatori del MIT e di IBM hanno scoperto che le reti neurali artificiali assomigliano ai circuiti cerebrali multistrato che elaborano le informazioni visive negli esseri umani e negli altri primati. ‘Abbiamo chiesto alla rete di fare entrambe queste cose così come l’approccio standard di visione artificiale'”, ha detto un esperto. La rete è stata trovata più robusta addestrandola a funzionare come una parte del cervello su cui gli esseri umani si basano per il riconoscimento degli oggetti.]

A prima vista, si può già osservare che i riepiloghi generati sono diversi. Tuttavia, è difficile determinare quale dei due sia migliore per le nostre esigenze semplicemente guardando i riepiloghi.

Per determinare se i riepiloghi sono significativamente diversi, useremo ROUGE. Quando si confrontano due riepiloghi generati, in cui nessuno di essi funge da riepilogo di riferimento, non stiamo ottenendo una comprensione della qualità dei riepiloghi generati di per sé. Invece, otteniamo un’indicazione di quanto i risultati siano diversi l’uno dall’altro. Consentendoci di identificare se il processo di messa a punto ha avuto un impatto sugli esiti dei riepiloghi.

Calcolo del ROUGE

Installiamo e carichiamo le librerie necessarie per eseguire le metriche ROUGE.

Anche se ci sono diverse librerie che implementano il calcolo del ROUGE, ho deciso di utilizzare la libreria evaluate. A volte la scelta è basata sulla familiarità, ed è la libreria che sono abituato a usare.

!pip install evaluate import evaluatefrom nltk.tokenize import sent_tokenize#Con la funzione load della libreria evaluate #creiamo un oggetto rouge_scorerouge_scorer = evaluate.load("rouge")

Una volta importate le librerie, calcolare la metrica ROUGE è semplice come una singola chiamata alla funzione compute dell’oggetto rouge_scorer appena creato.

I testi vengono passati a questa funzione, insieme a un terzo parametro che indica se si desidera utilizzare le radici delle parole (lemmi) o parole intere per i confronti. È comune confrontare solo le radici in modo che parole come “jump” e “jumped” siano considerate uguali.

Ecco alcuni esempi di lemmi:

  • Jumping → Jump.
  • Running → Run.
  • Cats → Cat.

Quando si utilizza True per il parametro steamer, le parole con la stessa radice sono considerate uguali, mentre utilizzando False vengono considerate come parole distinte.

Tuttavia, è importante preparare un po’ il testo aggiungendo interruzioni di riga all’inizio di ogni riga e rimuovendo i caratteri vuoti. Per fare questo, creiamo la funzione compute_punteggio_rouge:

def compute_punteggio_rouge(generato, riferimento):        #Dobbiamo aggiungere '\n' a ogni riga prima di inviarla a ROUGE    generato_con_interruzioni = ["\n".join(sent_tokenize(s.strip())) for s in generato]    riferimento_con_interruzioni = ["\n".join(sent_tokenize(s.strip())) for s in riferimento]        return rouge_scorer.compute(        predictions=generato_con_interruzioni,        references=riferimento_con_interruzioni,        use_stemmer=True,            )

Come puoi vedere, questa funzione modifica i testi ricevuti come parametri aggiungendo interruzioni di riga ad ogni frase. Successivamente, fa solo una singola chiamata alla funzione rouge_score.compute.

 compute_punteggio_rouge(sommari_piccoli, sommari_riferimento)

In questa chiamata, stiamo passando il testo generato utilizzando sia il modello T5 Base che il modello T5 sintonizzato. Il risultato restituito è:

{‘rouge1’: 0.47018752391886715, ‘rouge2’: 0.3209013209013209, ‘rougeL’: 0.34330271718331423, ‘rougeLsum’: 0.44692881745120555}

Possiamo vedere che il grado di similarità nel ROUGE-1 è del 47%, mentre nel ROUGE-2 è del 32%. Questo indica chiaramente che i sommari generati da entrambi i modelli sono distinti. Con alcune somiglianze ma differenze significative, suggerendo che il processo di sintonizzazione abbia influenzato il modo in cui il modello genera i sommari.

Tuttavia, non possiamo ancora determinare quale modello sia migliore, poiché non abbiamo confrontato con alcun testo di riferimento.

Confronto con un testo di riferimento

Ora utilizzeremo un secondo dataset: il CNN_Dailymail, un dataset ben noto che fa parte della libreria Datasets.

Oltre agli articoli, questo dataset contiene sommari generati dall’uomo che utilizzeremo come testi di riferimento.

Confronteremo i sommari generati dai due modelli con i sommari di riferimento nel dataset. Questo ci aiuterà a determinare quale modello produce sommari più simili a quelli forniti come testi di riferimento.

from datasets import load_datasetcnn_dataset = load_dataset(    "cnn_dailymail", version="3.0.0")#Prendi solo alcune notizie per il testsample_cnn = cnn_dataset["test"].select(range(MAX_NEWS))sample_cnn

Come nella prima parte dell’articolo, caricheremo solo un numero limitato di notizie. E ciò aiuterà a mantenere sotto controllo il tempo di esecuzione.

Otteniamo la lunghezza massima dei sommari selezionati del dataset, per indicare la lunghezza massima che vogliamo che abbia la risposta generata dai modelli.

max_length = max(len(item['highlights']) for item in sample_cnn)max_length = max_length + 10

Ora abbiamo tutto il necessario per generare i riassunti.

summaries_t5_base = create_summaries(sample_cnn["article"],                                       tokenizer_small,                                       model_small,                                       max_l=max_length)summaries_t5_finetuned = create_summaries(sample_cnn["article"],                                       tokenizer_reference,                                       model_reference,                                       max_l=max_length)#Ottieni i veri riassunti dal dataset di CNNreal_summaries = sample_cnn['highlights']

Con i tre riassunti nelle rispettive variabili. Ora analizziamo il contenuto.

summaries = pd.DataFrame.from_dict(        {            "base": summaries_t5_base,             "finetuned": summaries_t5_finetuned,            "reference": real_summaries,        }    )summaries.head()

In realtà, a prima vista, è davvero difficile sapere quale dei due modelli crei riassunti migliori.

Calcoliamo le metriche ROUGE per entrambi i modelli.

compute_rouge_score(summaries_t5_base, real_summaries)

summaries_t5_base: {‘rouge1’: 0.3050834824090638, ‘rouge2’: 0.07211128178870115,‘rougeL’: 0.2095520274299344, ‘rougeLsum’: 0.2662418008348241}

compute_rouge_score(summaries_t5_finetuned, real_summaries)

summaries_t5_finetuned: {‘rouge1’: 0.31659149328289443, ‘rouge2’: 0.11065084340946411, ‘rougeL’: 0.22002036956205442, ‘rougeLsum’: 0.24877540132887144}

Con questi risultati, direi che il modello fine-tuned crea riassunti migliori rispetto al modello T5-Base. Questo si basa sul fatto che ottiene punteggi più alti in tutte le metriche, tranne LSUM, dove la differenza è minima.

È importante notare che le metriche ROUGE sono altamente interpretabili e non forniscono una verità assoluta. In altre parole, un modello non è necessariamente migliore di un altro semplicemente perché i suoi punteggi ROUGE sono più alti. Questi punteggi indicano solo che i testi che genera hanno maggiore somiglianza con i testi di riferimento rispetto a quelli prodotti da un altro modello.

Entrambi i modelli hanno risultati molto simili, ma il modello fine-tuned ottiene punteggi più alti in tutte le metriche, soprattutto in ROUGE-2 e ROUGE-L, ad eccezione di ROUGE-LSUM. Ciò implica che il modello Base potrebbe generare testi più simili, mentre il modello fine-tuned utilizza un vocabolario più simile ai testi di riferimento.

In ogni caso, non possiamo trarre conclusioni chiare poiché abbiamo analizzato solo alcuni riassunti. Per determinare quale modello sia il migliore, dovremmo sviluppare una strategia diversa. Un buon approccio potrebbe essere: raggruppare le notizie per argomento ed esaminare se ci sono differenze significative nei risultati.

Conclusioni.

Le metriche da sole non sono sufficienti, anche ROUGE non è esaustivo per noi, poiché dipende in gran parte dalla qualità dei testi che prendiamo come riferimento.

Il fattore umano è ancora molto importante per verificare l’adeguatezza delle risposte. Ecco perché i modelli che sono stati addestrati con la mediazione umana stanno avendo successo.

Almeno abbiamo una buona notizia: è abbastanza facile ottenere metriche come ROUGE, che possono aiutarci nel processo decisionale, e questo ci aiuta a fare giudizi migliori sui modelli.

L’intero corso su Large Language Models è disponibile su Github. Per rimanere aggiornato su nuovi articoli, considera di seguire il repository o di metterlo tra i preferiti. In questo modo, riceverai notifiche ogni volta che verrà aggiunto nuovo contenuto.

GitHub – peremartra/Large-Language-Model-Notebooks-Course

Contribuisci allo sviluppo di peremartra/Large-Language-Model-Notebooks-Course creando un account su GitHub.

github.com

Scrivo regolarmente su Deep Learning e AI. Ti consiglio di seguirmi su VoAGI per ricevere aggiornamenti su nuovi articoli. E, naturalmente, sei il benvenuto a connetterti con me su LinkedIn.