Vai al contenuto principale

RAG vs Fine-Tuning: un tutorial completo con esempi pratici

Scopri le differenze tra RAG e Fine-Tuning per personalizzare le prestazioni del modello e ridurre le allucinazioni negli LLM.
· 13 min leggi

Scopri con l'IA

La maggior parte dei Large Language Models (LLM), come GPT-4, sono addestrati su dataset generici, spesso obsoleti. Sebbene eccellano nel rispondere a domande generali, faticano con richieste su notizie recenti, sviluppi più recenti e argomenti di nicchia. In questi casi, possono allucinare o fornire risposte imprecise. 

Nonostante l’emergere di modelli più performanti come Claude 3.5 Sonnet, abbiamo ancora bisogno o di effettuare il fine-tuning del modello per generare risposte personalizzate, oppure di usare sistemi di Retrieval-Augmented Generation (RAG) per fornire contesto extra al modello di base.

In questo tutorial, esploreremo RAG e fine-tuning, due tecniche distinte usate per migliorare le risposte degli LLM. Esamineremo le differenze e passeremo dalla teoria alla pratica valutando i risultati. 

Inoltre, approfondiremo tecniche ibride che combinano modelli con fine-tuning e sistemi RAG per sfruttare il meglio di entrambi i mondi. Infine, vedremo come scegliere tra questi tre approcci in base a casi d’uso e requisiti specifici.

Panoramica di RAG e Fine-Tuning

RAG e fine-tuning migliorano la generazione di risposte per le richieste di dominio specifico, ma sono tecniche intrinsecamente molto diverse. Conosciamole meglio.

Retrieval-Augmented Generation (RAG)

La Retrieval-Augmented Generation è un processo in cui grandi modelli linguistici come GPT-4o diventano sensibili al contesto usando fonti di dati esterne. È una combinazione di un retriever e un generator. Il retriever recupera dati da Internet o da un database vettoriale e li fornisce al generator insieme alla query originale dell’utente. Il generator usa il contesto aggiuntivo per generare una risposta molto accurata e pertinente.  

Per saperne di più, leggi il nostro articolo  Cos’è la Retrieval Augmented Generation (RAG)? Guida alle basi e comprendi il funzionamento interno di un’app RAG e i vari casi d’uso. 

Fine-Tuning

Il fine-tuning è il processo di taratura di un modello pre-addestrato usando un dataset di dominio specifico. Il modello pre-addestrato è stato addestrato su vasti corpora di dataset generali raccolti da Internet. È bravo a rispondere a domande generiche, ma farà fatica o addirittura allucinerà quando risponde a domande di dominio specifico. 

Per esempio, un modello pre-addestrato può essere abile nella conversazione generale ma produrre risposte errate se interrogato su procedure mediche complesse o precedenti legali. 

Effettuare il fine-tuning su un dataset medico o legale permette al modello di comprendere e rispondere alle domande in quei campi con maggiore accuratezza e pertinenza.

Segui il tutorial Guida introduttiva al fine-tuning degli LLM per imparare a personalizzare un modello pre-addestrato con guide visive. 

RAG vs. Fine-Tuning

Abbiamo visto ciascuna metodologia per migliorare la generazione di risposte degli LLM. Esaminiamo ora le differenze per capirle meglio. 

1. Stile di apprendimento

RAG usa uno stile di apprendimento dinamico, che consente ai modelli linguistici di accedere e utilizzare i dati più recenti e accurati da database, Internet o persino API. Questo approccio garantisce che le risposte generate siano sempre aggiornate e rilevanti.

Il fine-tuning comporta un apprendimento statico, in cui il modello apprende tramite un nuovo dataset durante la fase di training. Pur consentendo al modello di adattarsi alla generazione di risposte specifiche di dominio, non può integrare nuove informazioni dopo il training senza riaddestramento.

2. Adattabilità

RAG è ideale per generalizzazioni. Usa il processo di retrieval per prelevare informazioni da diverse fonti dati. RAG non cambia il modo in cui il modello risponde; fornisce solo informazioni extra per guidarlo. 

Il fine-tuning personalizza l’output del modello e ne migliora le prestazioni su uno specifico dominio strettamente associato al dataset di training. Cambia anche lo stile di generazione della risposta e talvolta fornisce risposte più pertinenti rispetto ai sistemi RAG. 

3. Intensità di risorse

RAG è intensivo in risorse perché viene eseguito durante l’inferenza del modello. Rispetto a semplici LLM senza RAG, richiede più memoria e calcolo. 

Il fine-tuning è intensivo in calcolo, ma si esegue una volta sola. Richiede più GPU e molta memoria durante il training, ma dopo è piuttosto leggero in termini di risorse rispetto ai sistemi RAG. 

4. Costi

RAG richiede modelli di embedding e LLM di fascia alta per una migliore generazione di risposte. Serve anche un database vettoriale veloce. I costi di API e operatività possono salire rapidamente.

Il fine-tuning comporta un costo elevato principalmente durante il training, ma dopo pagherai l’inferenza del modello, che è decisamente più economica rispetto a RAG.   

In generale, in media, considerando tutto, il fine-tuning costa più di RAG. 

5. Complessità di implementazione

I sistemi RAG possono essere sviluppati da software engineer e richiedono una competenza tecnica media. Devi conoscere design degli LLM, database vettoriali, embedding, prompt engineering e altro: serve tempo, ma si può imparare in un mese. 

Il fine-tuning del modello richiede alta competenza tecnica. Dalla preparazione del dataset alla definizione dei parametri di tuning fino al monitoraggio delle prestazioni, servono anni di esperienza nel campo dell’elaborazione del linguaggio naturale. 

Dalla teoria alla pratica con esempi concreti

Mettiamo alla prova la teoria fornendo lo stesso prompt a un modello con fine-tuning, a un’app RAG e a un approccio ibrido, quindi valutiamo i risultati. L’approccio ibrido combina il modello con fine-tuning con l’app RAG. Per questo esempio useremo il dataset ruslanmv/ai-medical-chatbot di Hugging Face, che contiene conversazioni tra pazienti e medici su varie condizioni di salute.

Creare un’app RAG con Llama 3

Inizieremo creando l’app RAG usando Llama 3 e l’ecosistema LangChain. 

Puoi anche imparare a creare un’app RAG usando LlamaIndex seguendo il code along, Retrieval Augmented Generation con LlamaIndex.

1. Installa tutti i pacchetti Python necessari.

%%capture
%pip install -U langchain langchainhub langchain_community langchain-huggingface faiss-gpu transformers accelerate

2. Carica le funzioni necessarie dalle librerie LangChain e Transformers.

from langchain.document_loaders import HuggingFaceDatasetLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from transformers import AutoTokenizer, AutoModelForCausalLM,pipeline
from langchain_huggingface import HuggingFacePipeline
from langchain.chains import RetrievalQA

3. Per accedere a modelli e dataset con restrizioni, è consigliabile effettuare l’accesso all’hub di Hugging Face usando la chiave API.

from huggingface_hub import login
from kaggle_secrets import UserSecretsClient
user_secrets = UserSecretsClient()

hf_token = user_secrets.get_secret("HUGGINGFACE_TOKEN")
login(token = hf_token)

4. Carica il dataset fornendo il nome del dataset e il nome della colonna a HuggingFaceDatasetLoader. La colonna “Doctor” sarà il nostro documento principale, e il resto delle colonne saranno i metadati. 

5. Limitiamo il dataset alle prime 1000 righe. Ridurre il dataset ci aiuterà a ridurre i tempi di archiviazione dei dati nel database vettoriale. 

# Specify the dataset name
dataset_name = "ruslanmv/ai-medical-chatbot"


# Create a loader instance using dataset columns
loader_doctor = HuggingFaceDatasetLoader(dataset_name,"Doctor")

# Load the data
doctor_data = loader_doctor.load()

# Select the first 1000 entries
doctor_data = doctor_data[:1000]

doctor_data[:2]

Come possiamo vedere, la colonna “Doctor” è il contenuto della pagina e il resto è considerato metadati. 

output of HuggingFaceDatasetLoader dataset

6. Carica il modello di embedding da Hugging Face usando parametri specifici, come l’abilitazione dell’accelerazione GPU.

7. Metti alla prova il modello di embedding fornendogli un testo di esempio.

# Define the path to the embedding model
modelPath = "sentence-transformers/all-MiniLM-L12-v2"

# GPU acceleration
model_kwargs = {'device':'cuda'}

# Create a dictionary with encoding options
encode_kwargs = {'normalize_embeddings': False}

# Initialize an instance of HuggingFaceEmbeddings with the specified parameters
embeddings = HuggingFaceEmbeddings(
    model_name=modelPath,     
    model_kwargs=model_kwargs, 
    encode_kwargs=encode_kwargs
)
text = "Why are you a doctor?"
query_result = embeddings.embed_query(text)
query_result[:3]
[-0.059351932257413864, 0.08008933067321777, 0.040729623287916183]

8. Converti i dati in embedding e salvali nel database vettoriale.

9. Salva il database vettoriale nella directory locale.

10. Esegui una ricerca per similarità usando il prompt di esempio.

vector_db = FAISS.from_documents(doctor_data, embeddings)
vector_db.save_local("/kaggle/working/faiss_doctor_index")
question = "Hi Doctor, I have a headache, help me."
searchDocs = vector_db.similarity_search(question)
print(searchDocs[0].page_content)

similarity search result.

11. Converti l’istanza del database vettoriale in un retriever. Questo ci aiuterà a creare la catena RAG.

retriever = vector_db.as_retriever()

12. Carica il tokenizer e il modello usando il modello Llama 3 8B Chat.

13. Usali per creare la pipeline di generazione di test.

14. Converte la pipeline nel client LLM di LangChain.

import torch
base_model = "/kaggle/input/llama-3/transformers/8b-chat-hf/1"

tokenizer = AutoTokenizer.from_pretrained(base_model)

model = AutoModelForCausalLM.from_pretrained(
        base_model,
        return_dict=True,
        low_cpu_mem_usage=True,
        torch_dtype=torch.float16,
        device_map="auto",
        trust_remote_code=True,
)

pipe = pipeline(
    "text-generation", 
    model=model, 
    tokenizer=tokenizer,
    max_new_tokens=120
)

llm = HuggingFacePipeline(pipeline=pipe)

15. Crea una catena di domanda e risposta usando il retriever, la query dell’utente, il prompt RAG e l’LLM.

from langchain import hub
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough


rag_prompt = hub.pull("rlm/rag-prompt")




qa_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | rag_prompt
    | llm
    | StrOutputParser()
)

16. Metti alla prova la catena Q&A facendo domande al medico.

question = "Hi Doctor, I have a headache, help me."
result = qa_chain.invoke(question)
print(result.split("Answer: ")[1])

È piuttosto simile al dataset, ma non riprende lo stile. Ha compreso il contesto e lo ha usato per scrivere la risposta nel proprio stile. 

RAG chain result

Proviamo di nuovo con un’altra domanda. 

question = "Hello doctor, I have bad acne. How do I get rid of it?"
result = qa_chain.invoke(question)
print(result.split("Answer: ")[1])

Questa è una risposta molto diretta. Forse per un chatbot medico-paziente serve fare fine-tuning del modello invece di usare l’approccio RAG. 

RAG chain result

Se riscontri difficoltà nell’esecuzione del codice, consulta il notebook Kaggle: Building RAG Application using Llama 3.

Scopri come migliorare le prestazioni dei sistemi RAG con tecniche come Chunking, Reranking e Query Transformations seguendo il tutorial Come migliorare le prestazioni RAG: 5 tecniche chiave con esempi.

Fine-tuning di Llama 3 su dati medici

Non effettueremo il fine-tuning del modello sul dataset medico-paziente perché lo abbiamo già fatto in un tutorial precedente: Fine-Tuning di Llama 3 e utilizzo in locale: guida passo passo. Caricheremo invece il modello con fine-tuning e porremo le stesse domande per valutarne i risultati. Il modello con fine-tuning è disponibile su Hugging Face e Kaggle.

Se ti interessa fare il fine-tuning del modello GPT-4 usando l’API di OpenAI, puoi fare riferimento al tutorial DataCamp, semplice da seguire, Fine-Tuning di OpenAI GPT-4: guida passo passo.

finetuned model on Hugging face

Fonte: kingabzpro/llama-3-8b-chat-doctor

1. Carica il tokenizer e il modello usando la libreria transformers.

2. Assicurati di usare i parametri corretti per caricare il modello nell’ambiente Kaggle GPU T4 x2.

from transformers import AutoTokenizer,AutoModelForCausalLM,pipeline
import torch

base_model = "/kaggle/input/fine-tuned-adapter-to-full-model/llama-3-8b-chat-doctor/"

tokenizer = AutoTokenizer.from_pretrained(base_model)

model = AutoModelForCausalLM.from_pretrained(
        base_model,
        return_dict=True,
        low_cpu_mem_usage=True,
        torch_dtype=torch.float16,
        device_map="auto",
        trust_remote_code=True,
)

3. Applica il template di chat ai messaggi.

4. Crea una pipeline di generazione del testo usando modello e tokenizer.

5. Fornisci alla pipeline un prompt e genera la risposta.

messages = [{"role": "user", "content": "Hi Doctor, I have a headache, help me."}]

prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
pipe = pipeline(
    "text-generation",
    model=model,
    tokenizer=tokenizer,
    torch_dtype=torch.float16,
    device_map="auto",
)

outputs = pipe(prompt, max_new_tokens=120, do_sample=True)
print(outputs[0]["generated_text"])

La risposta è molto simile al dataset. Lo stile è lo stesso, ma invece di dare una risposta diretta, suggerisce al paziente di effettuare ulteriori esami.

generated result from finetuned Llama3 model

6. Facciamo la seconda domanda.

messages = [{"role": "user", "content": "Hello doctor, I have bad acne. How do I get rid of it?"}]

prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
outputs = pipe(prompt, max_new_tokens=120, do_sample=True)
print(outputs[0]["generated_text"])

Lo stile è lo stesso e la risposta è piuttosto empatica ed esplicativa. 

generated result from finetuned Llama3 model

Se riscontri difficoltà nell’esecuzione del codice, consulta il notebook Kaggle: Fine-tuned Llama 3 HF Inference.

Approccio ibrido (RAG + Fine-tuning)

Ora forniremo al modello con fine-tuning un contesto aggiuntivo per affinare ulteriormente la risposta e trovare il giusto equilibrio. 

Invece di riscrivere tutto il codice, passeremo direttamente alla generazione delle risposte usando la catena Q&A. Se vuoi vedere il codice completo su come abbiamo combinato un modello con fine-tuning con una catena Q&A RAG, dai un’occhiata al notebook Kaggle Hybrid Approach (RAG + Fine-tuning). 

Fornisci alla catena le stesse domande che abbiamo posto al modello RAG e a quello con fine-tuning.

question = "Hi Doctor, I have a headache, help me."
result = qa_chain.invoke(question)
print(result.split("Answer: ")[1])

La risposta è piuttosto accurata e lo stile è quello del medico. 

generated result from hybrid approach (RAG + Fine-tuning)

Facciamo la seconda domanda.

question = "Hello doctor, I have bad acne. How do I get rid of it?"
result = qa_chain.invoke(question)
print(result.split("Answer: ")[1])

Questo è strano. Non abbiamo mai fornito un contesto aggiuntivo su eventuali brufoli con pus o meno. Forse l’approccio ibrido non si applica ad alcune query. 

generated result from hybrid approach (RAG + Fine-tuning)

Nel caso di un chatbot medico-paziente, il modello con fine-tuning eccelle nell’adozione dello stile e nell’accuratezza. Tuttavia, questo può variare per altri casi d’uso: ecco perché è importante condurre test approfonditi per individuare il metodo migliore per il tuo specifico caso.

Il termine ufficiale per l’approccio ibrido è RAFT (Retrieval Augmented Fine-Tuning). Scopri di più leggendo il post Cos’è RAFT? Combinare RAG e Fine-Tuning per adattare gli LLM a domini specializzati.

Come scegliere tra RAG, Fine-tuning e RAFT

Dipende tutto dal tuo caso d’uso e dalle risorse disponibili. Se sei una startup con risorse limitate, prova a costruire un proof of concept RAG usando le API di OpenAI e il framework LangChain. Per questo ti serviranno risorse, competenze e dataset limitati. 

Se sei un’azienda di medie dimensioni e vuoi fare fine-tuning per migliorare l’accuratezza delle risposte e distribuire un modello open source sul cloud, dovrai assumere esperti come data scientist e ingegneri di machine learning operations. Il fine-tuning richiede GPU di fascia alta, molta memoria, un dataset pulito e un team tecnico che conosca gli LLM. 

Una soluzione ibrida è intensiva sia in risorse sia in calcolo. Richiede anche un LLMOps engineer in grado di bilanciare fine-tuning e RAG. Considerala quando vuoi migliorare ulteriormente la generazione di risposte sfruttando i punti di forza di RAG e di un modello con fine-tuning. 

Fai riferimento alla tabella qui sotto per una panoramica delle soluzioni RAG, fine-tuning e RAFT.

 

RAG

Fine-Tuning

RAFT

Vantaggi

Comprensione contestuale, minimizza le allucinazioni, si adatta facilmente a nuovi dati, conveniente.

Competenza specifica per il task, personalizzazione, maggiore accuratezza, maggiore robustezza.

Combina i punti di forza di RAG e fine-tuning, comprensione e contesto più profondi.

Svantaggi

Gestione delle fonti dati, complessità.

Bias nei dati, intensivo in risorse, alti costi computazionali, notevoli requisiti di memoria, richiede tempo e competenze.

Complessità di implementazione, richiede bilanciamento tra processi di retrieval e fine-tuning.

Complessità di implementazione

Superiore al prompt engineering.

Maggiore di RAG. Richiede esperti altamente tecnici.

La più complessa delle tre.

Stile di apprendimento

Dinamico

Statico

Dinamico + Statico

Adattabilità

Si adatta facilmente a nuovi dati e fatti in evoluzione.

Personalizza gli output per task e domini specifici.

Si adatta sia ai dati in tempo reale sia a task specifici.

Costo

Basso

Moderato

Alto

Intensità di risorse

Bassa. Le risorse sono usate durante l’inferenza. 

Moderata. Le risorse sono usate durante il fine-tuning. 

Alta

Conclusione

I Large Language Models sono al centro dello sviluppo dell’IA di oggi. Le aziende cercano vari modi per migliorare e personalizzare questi modelli senza spendere milioni in training. Si parte dall’ottimizzazione dei parametri e dal prompt engineering. Poi si sceglie tra RAG o fine-tuning del modello per ottenere risposte ancora migliori e ridurre le allucinazioni. Sebbene esistano altre tecniche per migliorare le risposte, queste sono le opzioni più diffuse.

In questo tutorial, abbiamo imparato le differenze tra RAG e fine-tuning sia in teoria sia con esempi pratici. Abbiamo anche esplorato i modelli ibridi e confrontato quale metodo potrebbe funzionare meglio per te.

Per saperne di più sul deployment degli LLM e sulle varie tecniche coinvolte, dai un’occhiata al nostro code-along su RAG con LLaMAIndex e al nostro corso su sviluppare applicazioni LLM con LangChain.

Abid Ali Awan

Autore

Abid Ali Awan

In quanto data scientist certificato, sono appassionato di sfruttare tecnologie all’avanguardia per creare applicazioni di machine learning innovative. Con una solida esperienza in riconoscimento vocale, analisi e reportistica dei dati, MLOps, AI conversazionale e NLP, ho affinato le mie competenze nello sviluppo di sistemi intelligenti in grado di avere un impatto concreto. Oltre alla mia expertise tecnica, sono anche un comunicatore efficace, con il talento di rendere chiari e sintetici concetti complessi. Di conseguenza, sono diventato un blogger molto seguito in ambito data science, condividendo idee ed esperienze con una community in crescita di professionisti dei dati. Attualmente mi concentro sulla creazione e sull’editing di contenuti, lavorando con large language model per sviluppare contenuti potenti e coinvolgenti che possano aiutare aziende e singoli a valorizzare al meglio i propri dati.

Argomenti

Correlato