Affinamento dei grandi modelli di linguaggio (LLM)

Fine-tuning of large language models (LLM)

Una panoramica concettuale con esempio di codice Python

Questo è il quinto articolo di una serie sull’utilizzo di modelli di linguaggio estesi (LLM) nella pratica. In questo post, discuteremo come adattare (FT) un LLM pre-addestrato. Inizieremo introducendo concetti e tecniche chiave per il FT, per poi concludere con un esempio concreto di come adattare un modello (in modo locale) utilizzando Python e l’ecosistema software di Hugging Face.

Adattamento di un modello di linguaggio. Immagine dell'autore.

Nell’articolo precedente di questa serie, abbiamo visto come poter costruire applicazioni pratiche basate su LLM integrando l’ingegneria delle prompt nel nostro codice Python. Per la maggior parte dei casi d’uso di LLM, questa è l’approccio iniziale che consiglio perché richiede significativamente meno risorse e competenze tecniche rispetto ad altri metodi, pur offrendo comunque molti vantaggi.

Tuttavia, ci sono situazioni in cui l’utilizzo di un LLM esistente “fuori dalla scatola” non è sufficiente e si richiede una soluzione più sofisticata. Qui entra in gioco l’adattamento del modello.

Cos’è l’Adattamento?

L’adattamento consiste nel prendere un modello pre-addestrato e addestrare almeno un parametro interno del modello (ad esempio i pesi). Nel contesto dei LLM, ciò che si ottiene tipicamente è la trasformazione di un modello di base a uso generico (ad esempio GPT-3) in un modello specializzato per un caso d’uso specifico (ad esempio ChatGPT) [1].

Il principale vantaggio di questo approccio è che i modelli possono ottenere prestazioni migliori richiedendo (molto) meno esempi etichettati manualmente rispetto ai modelli che si basano esclusivamente sull’addestramento supervisionato.

Anche se i modelli di base strettamente auto-supervisionati possono mostrare prestazioni impressionanti su una vasta gamma di compiti con l’aiuto dell’ingegneria delle prompt [2], essi rimangono comunque predittori di parole e potrebbero generare completamenti che non sono del tutto utili o accurati. Ad esempio, confrontiamo i completamenti di davinci (modello GPT-3 di base) e text-davinci-003 (un modello adattato).

Confronto dei completamenti di davinci (modello GPT-3 di base) e text-davinci-003 (un modello adattato). Immagine dell'autore.

Si noti che il modello di base sta semplicemente cercando di completare il testo elencando un insieme di domande come una ricerca di Google o un compito per i compiti a casa, mentre il modello adattato fornisce una risposta più utile. Il tipo di adattamento utilizzato per text-davinci-003 è l’adattamento dell’allineamento, che mira a rendere le risposte del LLM più utili, oneste e inoffensive, ma ne parleremo meglio più avanti [3,4].

Perché Adattare

L’adattamento non solo migliora le prestazioni di un modello di base, ma un modello più piccolo (adattato) può spesso superare in prestazioni modelli più grandi (più costosi) per l’insieme di compiti su cui è stato addestrato [4]. Questo è stato dimostrato da OpenAI con i loro modelli di prima generazione “InstructGPT”, in cui i completamenti del modello InstructGPT con 1,3 miliardi di parametri sono stati preferiti rispetto al modello di base GPT-3 con 175 miliardi di parametri, nonostante sia 100 volte più piccolo [4].

Anche se la maggior parte dei LLM con cui potremmo interagire in questi giorni non sono modelli strettamente auto-supervisionati come GPT-3, ci sono comunque svantaggi nell’utilizzare un modello adattato esistente per un caso d’uso specifico.

Uno dei principali è che i LLM hanno una finestra di contesto finita. Pertanto, il modello potrebbe avere prestazioni sub-ottimali su compiti che richiedono una grande base di conoscenze o informazioni specifiche del dominio [1]. I modelli adattati possono evitare questo problema “imparando” queste informazioni durante il processo di adattamento. Ciò evita anche la necessità di sovraccaricare le prompt con ulteriore contesto e può quindi ridurre i costi di inferenza.

3 Modi per Ottimizzare

Esistono 3 modi generici per ottimizzare un modello: auto-supervisionato, supervisionato e apprendimento per rinforzo. Questi non sono mutualmente esclusivi, in quanto è possibile utilizzare qualsiasi combinazione di questi tre approcci in successione per ottimizzare un singolo modello.

Auto-supervisione

L’auto-supervisione consiste nell’allenare un modello basato sulla struttura intrinseca dei dati di addestramento. Nel contesto dei LLM (Language Model), questo di solito significa che, dati una sequenza di parole (o token, per essere più precisi), si prevede la prossima parola (token).

Anche se questo è il modo in cui molti modelli di linguaggio pre-addestrati sono sviluppati in questi giorni, può anche essere utilizzato per l’ottimizzazione del modello. Un possibile caso d’uso di questo è sviluppare un modello che può imitare lo stile di scrittura di una persona dato un insieme di testi di esempio.

Apprendimento Supervisionato

Il modo successivo, e forse il più popolare, per ottimizzare un modello è attraverso l’apprendimento supervisionato. Ciò comporta l’addestramento di un modello su coppie di input-output per una particolare attività. Un esempio è l’ottimizzazione delle istruzioni, che mira a migliorare le prestazioni del modello nel rispondere a domande o nel rispondere a richieste dell’utente [1,3].

Il passo chiave nell’apprendimento supervisionato è curare un set di dati di addestramento. Un modo semplice per farlo è creare coppie di domande-risposte e integrarle in un modello di input [1,3]. Ad esempio, la coppia domanda-risposta: Chi è stato il 35° Presidente degli Stati Uniti? – John F. Kennedy potrebbe essere inserita nel modello di input sottostante. Altri esempi di modelli di input sono disponibili nella sezione A.2.1 del riferimento [4].

"""Si prega di rispondere alla seguente domanda. D: {Domanda} R: {Risposta}"""

Utilizzare un modello di input è importante perché i modelli di base come GPT-3 sono essenzialmente “completatori di documenti”. Ciò significa che, dato un certo testo, il modello genera altro testo che (statisticamente) ha senso in quel contesto. Questo fa riferimento al blog precedente di questa serie e all’idea di “ingannare” un modello di linguaggio per risolvere il tuo problema tramite l’ingegneria del modello di input.

Ingegneria del Modello di Input – Come ingannare l’IA nel risolvere i tuoi problemi

7 trucchi di input, Langchain e codice di esempio Python

towardsdatascience.com

Apprendimento per Rinforzo

Infine, si può utilizzare l’apprendimento per rinforzo (RL) per ottimizzare i modelli. L’RL utilizza un modello di ricompensa per guidare l’addestramento del modello di base. Ciò può assumere molte forme diverse, ma l’idea di base è addestrare il modello di ricompensa per valutare le completazioni del modello di linguaggio in modo da riflettere le preferenze degli etichettatori umani [3,4]. Il modello di ricompensa può quindi essere combinato con un algoritmo di apprendimento per rinforzo (ad es. Proximal Policy Optimization (PPO)) per ottimizzare ulteriormente il modello pre-addestrato.

Un esempio di come l’RL può essere utilizzato per ottimizzare il modello è dimostrato dai modelli InstructGPT di OpenAI, che sono stati sviluppati attraverso 3 passaggi chiave [4].

  1. Generare coppie prompt-risposta di alta qualità e ottimizzare un modello pre-addestrato utilizzando l’apprendimento supervisionato. (~13k prompt di addestramento) Nota: Si può (in alternativa) passare al passaggio 2 con il modello pre-addestrato [3].
  2. Utilizzare il modello ottimizzato per generare completamenti e far valutare le risposte dagli etichettatori umani in base alle loro preferenze. Utilizzare queste preferenze per addestrare il modello di ricompensa. (~33k prompt di addestramento)
  3. Utilizzare il modello di ricompensa e un algoritmo RL (ad es. PPO) per ottimizzare ulteriormente il modello. (~31k prompt di addestramento)

Anche se la strategia sopra generalmente porta a completamenti di LLM significativamente più preferibili rispetto al modello di base, può anche comportare una diminuzione delle prestazioni in un sottoinsieme di attività. Questa diminuzione delle prestazioni è anche nota come tassa di allineamento [3,4].

Passaggi di Ottimizzazione Supervisionata (Alto Livello)

Come abbiamo visto in precedenza, ci sono molti modi in cui è possibile ottimizzare un modello di linguaggio esistente. Tuttavia, per il resto di questo articolo, ci concentreremo sull’ottimizzazione tramite apprendimento supervisionato. Di seguito è riportata una procedura di alto livello per l’ottimizzazione del modello supervisore [1].

  1. Scegliere il compito di ottimizzazione (ad esempio, riassunto, risposta alle domande, classificazione del testo)
  2. Preparare il set di dati di addestramento, cioè creare coppie input-output (100-10k) e preprocessare i dati (cioè tokenizzare, troncare e riempire il testo).
  3. Scegliere un modello di base (sperimentare con modelli diversi e scegliere quello che si comporta meglio nel compito desiderato).
  4. Ottimizzare il modello tramite apprendimento supervisionato
  5. Valutare le prestazioni del modello

Anche se ciascuno di questi passaggi potrebbe essere un articolo a sé stante, voglio concentrarmi sul passaggio 4 e discutere come possiamo addestrare il modello ottimizzato.

3 Opzioni per l’Addestramento dei Parametri

Quando si tratta di ottimizzare un modello con ~100M-100B di parametri, è necessario tener conto dei costi computazionali. A tal fine, una domanda importante è: quali parametri dobbiamo (ri)addestrare?

Con la quantità di parametri in gioco, abbiamo innumerevoli scelte su quali addestrare. Qui, mi concentrerò su tre opzioni generiche tra cui scegliere.

Opzione 1: Riaddestrare tutti i parametri

La prima opzione è quella di riaddestrare tutti i parametri interni del modello (chiamato ottimizzazione completa dei parametri) [3]. Sebbene questa opzione sia semplice (concettualmente), è la più costosa dal punto di vista computazionale. Inoltre, un problema noto dell’ottimizzazione completa dei parametri è il fenomeno dell’oblio catastrofico. Questo è quando il modello “dimentica” le informazioni utili che ha “imparato” nel suo addestramento iniziale [3].

Un modo per mitigare gli svantaggi dell’Opzione 1 è congelare una grande parte dei parametri del modello, il che ci porta all’Opzione 2.

Opzione 2: Transfer Learning

L’idea principale del transfer learning (TL) è preservare le rappresentazioni/caratteristiche utili che il modello ha appreso dall’addestramento precedente quando si applica il modello a un nuovo compito. Questo consiste generalmente nel rimuovere “la testa” di una rete neurale (NN) e sostituirla con una nuova (ad esempio, aggiungendo nuovi strati con pesi casuali). Nota: La testa di una NN include i suoi strati finali, che traducono le rappresentazioni interne del modello in valori di output.

Pur lasciando la maggior parte dei parametri intatti, il trasferimento di apprendimento potrebbe non risolvere necessariamente il problema dell’oblio catastrofico. Per gestire meglio entrambi questi problemi, possiamo rivolgerci a un diverso insieme di approcci.

Opzione 3: Ottimizzazione Efficiente dei Parametri (PEFT)

PEFT coinvolge l’aggiunta di un numero relativamente piccolo di parametri addestrabili a un modello di base. Il risultato chiave di ciò è una metodologia di ottimizzazione che dimostra prestazioni comparabili all’ottimizzazione completa dei parametri a una frazione molto ridotta dei costi computazionali e di archiviazione [5].

PEFT comprende una famiglia di tecniche, una delle quali è il popolare metodo LoRA (Low-Rank Adaptation) [6]. L’idea di base di LoRA è selezionare un sottoinsieme di strati in un modello esistente e modificare i loro pesi secondo l’equazione seguente.

Equazione che mostra come le matrici dei pesi vengono modificate per l'ottimizzazione tramite LoRA [6]. Immagine dell'autore.

Dove h() = uno strato nascosto che verrà addestrato, x = l’input per h(), W₀ = la matrice dei pesi originale per h e ΔW = una matrice di parametri addestrabili iniettati in h. ΔW viene scomposta secondo ΔW=BA, dove ΔW è una matrice d per k, B è una matrice d per r e A è una matrice r per k. r è l'”intrinsic rank” (rango intrinseco) stimato di ΔW (che può essere piccolo come 1 o 2) [6].

Spiacente per tutta questa matematica, ma il punto chiave è che i pesi (d * k) in W₀ sono congelati e, quindi, non vengono inclusi nell’ottimizzazione. Invece, i pesi ((d * r) + (r * k)) che compongono le matrici B e A sono gli unici che vengono allenati.

Inserendo dei numeri inventati per d=1000, k=1000 e r=2 per avere un’idea dei vantaggi in termini di efficienza, il numero dei parametri allenabili passa da 1.000.000 a 4.000 in quel livello. Nella pratica, gli autori del paper LoRA hanno citato una riduzione del 10.000 volte nella dimensione del checkpoint dei parametri utilizzando LoRA per il fine-tuning di GPT-3 rispetto all’ottimizzazione completa dei parametri [6].

Per rendere tutto ciò più concreto, vediamo come possiamo utilizzare LoRA per il fine-tuning di un modello di linguaggio in modo efficiente abbastanza da eseguirlo su un computer personale.

Codice di Esempio: Fine-tuning di un LLM utilizzando LoRA

In questo esempio, utilizzeremo l’ecosistema di Hugging Face per il fine-tuning di un modello di linguaggio per classificare il testo come ‘positivo’ o ‘negativo’. Qui, effettuiamo il fine-tuning di distilbert-base-uncased, un modello di ~70M di parametri basato su BERT. Poiché questo modello di base è stato allenato per il language modeling e non per la classificazione, utilizziamo il transfer learning per sostituire la testa del modello di base con una testa di classificazione. Inoltre, utilizziamo LoRA per il fine-tuning del modello in modo sufficientemente efficiente da poterlo eseguire sul mio Mac Mini (chip M1 con 16GB di memoria) in un tempo ragionevole (~20 minuti).

Il codice, insieme ai file dell’ambiente conda, è disponibile nel repository di GitHub. Il modello finale e il dataset [7] sono disponibili su Hugging Face.

YouTube-Blog/LLMs/fine-tuning su main · ShawhinT/YouTube-Blog

Codici per integrare i video di YouTube e gli articoli del blog su VoAGI. – YouTube-Blog/LLMs/fine-tuning su main ·…

github.com

Importazioni

Iniziamo importando librerie e moduli utili. Datasets, transformers, peft e evaluate sono tutte librerie di Hugging Face (HF).

from datasets import load_dataset, DatasetDict, Datasetfrom transformers import (    AutoTokenizer,    AutoConfig,     AutoModelForSequenceClassification,    DataCollatorWithPadding,    TrainingArguments,    Trainer)from peft import PeftModel, PeftConfig, get_peft_model, LoraConfigimport evaluateimport torchimport numpy as np

Modello di base

Successivamente, carichiamo il nostro modello di base. Il modello di base qui è relativamente piccolo, ma ci sono diversi altri (più grandi) che avremmo potuto utilizzare (ad esempio roberta-base, llama2, gpt2). Una lista completa è disponibile qui.

model_checkpoint = 'distilbert-base-uncased'# definiamo le mappature dei labelid2label = {0: "Negativo", 1: "Positivo"}label2id = {"Negativo":0, "Positivo":1}# generiamo un modello di classificazione dal modello di checkpointmodel = AutoModelForSequenceClassification.from_pretrained(    model_checkpoint, num_labels=2, id2label=id2label, label2id=label2id)

Caricamento dati

Possiamo quindi caricare i dati di addestramento e di validazione dalla libreria datasets di HF. Si tratta di un dataset di 2000 recensioni di film (1000 per l’addestramento e 1000 per la validazione) con etichette binarie che indicano se la recensione è positiva (o meno).

# carica il datasetdataset = load_dataset("shawhin/imdb-truncated")dataset# dataset = # DatasetDict({#     train: Dataset({#         features: ['label', 'text'],#         num_rows: 1000#     })#     validation: Dataset({#         features: ['label', 'text'],#         num_rows: 1000#     })# }) 

Pre-elaborazione dei dati

Successivamente, dobbiamo preprocessare i nostri dati in modo che possano essere utilizzati per il training. Questo consiste nell’utilizzare un tokenizer per convertire il testo in una rappresentazione intera comprensibile dal modello di base.

# creare il tokenizertokenizer = AutoTokenizer.from_pretrained(model_checkpoint, add_prefix_space=True)

Per applicare il tokenizer al dataset, utilizziamo il metodo .map(). Questo prende in input una funzione personalizzata che specifica come il testo deve essere preprocessato. In questo caso, quella funzione si chiama tokenize_function(). Oltre a tradurre il testo in interi, questa funzione tronca le sequenze di interi in modo che non superino i 512 numeri per conformarsi alla lunghezza massima di input del modello di base.

# creare la funzione di tokenizzazionedef tokenize_function(examples):    # estrarre il testo    text = examples["text"]    # tokenizzare e troncare il testo    tokenizer.truncation_side = "left"    tokenized_inputs = tokenizer(        text,        return_tensors="np",        truncation=True,        max_length=512    )    return tokenized_inputs# aggiungere il token di padding se non esisteif tokenizer.pad_token is None:    tokenizer.add_special_tokens({'pad_token': '[PAD]'})    model.resize_token_embeddings(len(tokenizer))# tokenizzare i dataset di training e validazionetokenized_dataset = dataset.map(tokenize_function, batched=True)tokenized_dataset# tokenized_dataset = # DatasetDict({#     train: Dataset({#        features: ['label', 'text', 'input_ids', 'attention_mask'],#         num_rows: 1000#     })#     validation: Dataset({#         features: ['label', 'text', 'input_ids', 'attention_mask'],#         num_rows: 1000#     })# })

A questo punto, possiamo anche creare un data collator, che effettuerà il padding dinamico degli esempi in ogni batch durante il training in modo che abbiano tutti la stessa lunghezza. Questo è computazionalmente più efficiente che effettuare il padding di tutti gli esempi in modo che abbiano la stessa lunghezza in tutto il dataset.

# creare il data collatordata_collator = DataCollatorWithPadding(tokenizer=tokenizer)

Metriche di valutazione

Possiamo definire come vogliamo valutare il nostro modello sintonizzato tramite una funzione personalizzata. Qui definiamo la funzione compute_metrics() per calcolare l’accuratezza del modello.

# importare la metrica di valutazione dell'accuratezzaaccuracy = evaluate.load("accuracy")# definire una funzione di valutazione da passare successivamente al trainerdef compute_metrics(p):    predictions, labels = p    predictions = np.argmax(predictions, axis=1)    return {"accuracy": accuracy.compute(predictions=predictions, references=labels)}

Prestazioni del modello non addestrato

Prima di addestrare il nostro modello, possiamo valutare le prestazioni del modello di base con una testa di classificazione inizializzata casualmente su alcuni input di esempio.

# definire una lista di esempi text_list = ["Era buono.", "Non mi piace, non lo consiglio.", "Meglio del primo.", "Non vale la pena guardarlo neanche una volta.", "Questo è da evitare."]print("Predizioni del modello non addestrato:")print("----------------------------")for text in text_list:    # tokenizzare il testo    inputs = tokenizer.encode(text, return_tensors="pt")    # calcolare i logit    logits = model(inputs).logits    # convertire i logit in etichetta    predictions = torch.argmax(logits)    print(text + " - " + id2label[predictions.tolist()])# Output:# Predizioni del modello non addestrato:# ----------------------------# Era buono. - Negativo# Non mi piace, non lo consiglio. - Negativo# Meglio del primo. - Negativo# Non vale la pena guardarlo neanche una volta. - Negativo# Questo è da evitare. - Negativo

Come previsto, le prestazioni del modello sono equivalenti a un’indovinatura casuale. Vediamo come possiamo migliorare questo con il fine-tuning.

Fine-tuning con LoRA

Per utilizzare LoRA per il fine-tuning, abbiamo prima bisogno di un file di configurazione. Questo imposta tutti i parametri per l’algoritmo LoRA. Consultare i commenti nel blocco di codice per ulteriori dettagli.

peft_config = LoraConfig(task_type="SEQ_CLS", # classificazione di sequenze                        r=4, # rango intrinseco della matrice dei pesi addestrabili                        lora_alpha=32, # questo è come un tasso di apprendimento                        lora_dropout=0.01, # probabilità di dropout                        target_modules = ['q_lin']) # applichiamo LoRA solo al livello di query

Possiamo quindi creare una nuova versione del nostro modello che può essere addestrata tramite PEFT. Si noti che la scala dei parametri addestrabili è stata ridotta di circa 100 volte.

modello = get_peft_model(modello, peft_config)
modello.print_trainable_parameters()
# parametri addestrabili: 1.221.124 || tutti i parametri: 67.584.004 || % addestrabili: 1.8068239934408148

Successivamente, definiamo gli iperparametri per l’addestramento del modello.

# iperparametrilr = 1e-3 # dimensione del passo di ottimizzazione batch_size = 4 # numero di esempi processati per passo di ottimizzazione num_epochs = 10 # numero di volte in cui il modello viene eseguito sui dati di addestramento# definizione degli argomenti di addestramentotraining_args = TrainingArguments(    output_dir= model_checkpoint + "-lora-text-classification",    learning_rate=lr,    per_device_train_batch_size=batch_size,     per_device_eval_batch_size=batch_size,    num_train_epochs=num_epochs,    weight_decay=0.01,    evaluation_strategy="epoch",    save_strategy="epoch",    load_best_model_at_end=True,)

Infine, creiamo un oggetto trainer() e affiniamo il modello!

# creazione dell'oggetto trainertrainer = Trainer(    model=modello, # il nostro modello peft    args=training_args, # iperparametri    train_dataset=tokenized_dataset["train"], # dati di addestramento    eval_dataset=tokenized_dataset["validation"], # dati di validazione    tokenizer=tokenizer, # definizione del tokenizer    data_collator=data_collator, # ciò permetterà di riempire dinamicamente gli esempi in ogni batch per renderli della stessa lunghezza    compute_metrics=compute_metrics, # valuta il modello utilizzando la funzione compute_metrics() definita precedentemente)# addestramento del modellotrainer.train()

Il codice sopra genererà la seguente tabella di metriche durante l’addestramento.

Metriche di addestramento del modello. Immagine dell'autore.

Prestazioni del modello addestrato

Per vedere come sono migliorate le prestazioni del modello, applichiamolo agli stessi 5 esempi precedenti.

modello.to('mps') # passaggio a mps per Mac (è possibile utilizzare anche 'cpu')print("Predizioni del modello addestrato:")print("--------------------------")for testo in elenco_testi:    input = tokenizer.encode(testo, return_tensors="pt").to("mps") # passaggio a mps per Mac (è possibile utilizzare anche 'cpu')    logits = modello(input).logits    predizioni = torch.max(logits,1).indices    print(testo + " - " + id2label[predizioni.tolist()[0]])# Output:# Predizioni del modello addestrato:# ----------------------------# Era buono. - Positivo# Non mi piace, non lo raccomando. - Negativo# Meglio del primo. - Positivo# Non vale nemmeno la pena guardarlo una volta. - Negativo# Questo è da evitare. - Positivo # questo è difficile

Il modello affinato è migliorato notevolmente rispetto all’indovinare casuale precedente, classificando correttamente tutti gli esempi nel codice sopra, ad eccezione di uno. Ciò corrisponde alla metrica di accuratezza del ~90% che abbiamo osservato durante l’addestramento.

Link: Repo del codice | Modello | Dataset

Conclusioni

Sebbene l’affinamento di un modello esistente richieda più risorse computazionali e competenze tecniche rispetto all’utilizzo di un modello standard, i modelli affinati (più piccoli) possono superare i modelli di base pre-addestrati (più grandi) per un caso d’uso specifico, anche quando si utilizzano strategie di ingegneria dei prompt intelligenti. Inoltre, con tutte le risorse open-source LLM disponibili, non è mai stato così facile affinare un modello per un’applicazione personalizzata.

Il prossimo (e ultimo) articolo di questa serie andrà oltre l’affinamento del modello e discuterà come addestrare un modello di linguaggio da zero.

👉 Ulteriori informazioni su LLM: Introduzione | OpenAI API | Hugging Face Transformers | Ingegneria dei Prompt

Risorse

Contatti: Il mio sito web | Prenota una chiamata | Chiedimi qualcosa

Social: YouTube 🎥 | LinkedIn | Twitter

Supporto: Offrimi un caffè ☕️

Gli imprenditori dei dati

Una community per imprenditori nel settore dei dati. 👉 Unisciti al Discord!

VoAGI.com

[1] Deeplearning.ai Finetuning Large Langauge Models Short Course: https://www.deeplearning.ai/short-courses/finetuning-large-language-models/

[2] arXiv:2005.14165 [cs.CL] (GPT-3 Paper)

[3] arXiv:2303.18223 [cs.CL] (Survey of LLMs)

[4] arXiv:2203.02155 [cs.CL] (InstructGPT paper)

[5] 🤗 PEFT: Fine-Tuning Parametri-Efficient di Modelli su Scala Miliardaria su Hardware a Basso Risorsa: https://huggingface.co/blog/peft

[6] arXiv:2106.09685 [cs.CL] (LoRA paper)

[7] Fonte originale del dataset — Andrew L. Maas, Raymond E. Daly, Peter T. Pham, Dan Huang, Andrew Y. Ng e Christopher Potts. 2011. Learning Word Vectors for Sentiment Analysis. In Proceedings of the 49th Annual Meeting of the Association for Computational Linguistics: Human Language Technologies, pages 142–150, Portland, Oregon, USA. Association for Computational Linguistics.