Fine-Tuning di LLM nel 2026: Guida Completa con LoRA, QLoRA e Unsloth

Guida pratica al fine-tuning di Large Language Model nel 2026 con LoRA, QLoRA e il framework Unsloth. Dalla teoria alla pratica: preparazione del dataset, configurazione degli iperparametri, reinforcement learning con GRPO e deployment in produzione con codice funzionante.

Se avete costruito applicazioni con i Large Language Model, probabilmente vi siete già trovati davanti a un bivio: il modello generico funziona bene, ma non abbastanza bene per il vostro caso d'uso specifico. Le risposte sono vaghe, il tono è sbagliato, il formato di output non è quello che vi serve. Il prompt engineering vi ha portato fino a un certo punto, ma a un certo punto vi siete bloccati. È qui che entra in gioco il fine-tuning.

La buona notizia? Nel 2026, il fine-tuning di LLM è diventato radicalmente più accessibile. Grazie a tecniche come LoRA e QLoRA e a framework come Unsloth, potete addestrare modelli da 7-8 miliardi di parametri su una singola GPU consumer. Spendendo una frazione di quanto costava anche solo due anni fa. E i risultati, onestamente, sono impressionanti: modelli specializzati che superano GPT-4 su task specifici di dominio, con costi di inferenza vicini allo zero.

In questa guida vi accompagno attraverso l'intero processo — dalla teoria dietro LoRA e QLoRA alla preparazione del dataset, dalla configurazione degli iperparametri al reinforcement learning con GRPO, fino al deployment in produzione. Tutto con codice funzionante e consigli pratici che vengono da esperimenti reali (e da qualche errore di troppo).

Quando Conviene il Fine-Tuning (e Quando No)

Prima di investire tempo e risorse nel fine-tuning, è fondamentale capire se è davvero la soluzione giusta per il vostro problema. Nel 2026, le alternative principali sono tre: prompt engineering, RAG e fine-tuning. Ognuna ha il suo sweet spot.

Fine-Tuning vs RAG vs Prompt Engineering

Il prompt engineering è sempre il primo approccio da provare. Costa zero, non richiede dati di addestramento e — sorpresa — funziona sorprendentemente bene per molti casi d'uso. Se un sistema di prompt ben strutturato, con few-shot examples, chain-of-thought e output strutturati, risolve il vostro problema, fermatevi lì. Sul serio.

Il RAG (Retrieval-Augmented Generation) è la scelta giusta quando il vostro problema è legato alla conoscenza: il modello non ha le informazioni che vi servono, o le informazioni cambiano frequentemente. Il RAG mantiene il modello invariato ma gli fornisce contesto aggiornato al momento della query, con il vantaggio di poter citare le fonti.

Il fine-tuning, invece, è la scelta giusta quando il problema è legato al comportamento: il modello ha le informazioni giuste ma non le esprime nel modo corretto. Ecco quando dovreste seriamente considerarlo:

  • Stile e tono specifici — Il modello deve scrivere come un esperto legale, un medico, o con il tono del vostro brand aziendale.
  • Formato di output rigido — Dovete generare JSON strutturato, codice in un framework specifico o report con formato predefinito.
  • Terminologia di dominio — Il modello deve padroneggiare il gergo tecnico del vostro settore senza spiegazioni aggiuntive nel prompt.
  • Classificazione e task ripetitivi — Compiti come analisi del sentiment, categorizzazione di ticket o estrazione di entità, dove la consistenza è critica.
  • Riduzione dei costi di inferenza — Un modello piccolo fine-tuned può sostituire chiamate API costose a modelli giganti. Latenza inferiore, zero costi ricorrenti.

L'Approccio Ibrido: RAFT

Ecco, nel 2026 la scelta non è necessariamente binaria. L'approccio RAFT (Retrieval-Augmented Fine-Tuning) combina il meglio di entrambi i mondi: un modello fine-tuned su dati di dominio viene poi deployato in un'architettura RAG. Il risultato è un sistema che ha sia il comportamento specializzato del fine-tuning sia l'accesso a conoscenze aggiornate del RAG.

Il principio guida è semplice: mettete la conoscenza volatile nel retrieval, il comportamento stabile nel fine-tuning.

LoRA e QLoRA: La Teoria Essenziale

Il fine-tuning tradizionale (full fine-tuning) aggiorna tutti i parametri del modello ad ogni iterazione di backpropagation. Per un modello da 7 miliardi di parametri, questo richiede circa 28 GB di VRAM solo per i pesi del modello in FP32 — più i gradienti e gli stati dell'optimizer. In pratica, servono GPU da decine di migliaia di euro.

Non esattamente alla portata di tutti.

Come Funziona LoRA

LoRA (Low-Rank Adaptation, Hu et al. 2021) risolve questo problema con un'intuizione elegante: invece di aggiornare l'intera matrice dei pesi W₀ di dimensione N×N, LoRA congela i pesi originali e addestra solo due matrici molto più piccole, A e B, tali che W₀ + A×B approssima i pesi fine-tuned. In pratica, state addestrando meno dell'1% dei parametri totali — e i risultati sono comparabili al full fine-tuning.

I vantaggi sono enormi:

  • Riduzione drastica della VRAM — Da 28+ GB a 8-16 GB per un modello 7B.
  • Training più veloce — Meno parametri da aggiornare significa meno calcoli.
  • Niente forgetting catastrofico — I pesi originali rimangono congelati, preservando le capacità generali del modello.
  • Adapter componibili — Potete avere più adapter LoRA per task diversi e caricarli a runtime sullo stesso modello base. Questo è davvero comodo in produzione.

QLoRA: Fine-Tuning a 4-Bit

QLoRA (Dettmers et al. 2023) porta l'efficienza ancora oltre, combinando LoRA con la quantizzazione a 4 bit. Il modello base viene caricato in precisione 4-bit (usando il formato NormalFloat4), mentre gli adapter LoRA operano in 16 bit durante il training. L'idea si regge su tre innovazioni chiave:

  • 4-bit NormalFloat — Un formato di quantizzazione ottimizzato per le distribuzioni normali dei pesi dei transformer. Preserva meglio l'informazione rispetto alla quantizzazione uniforme.
  • Double Quantization — Quantizza anche le costanti di quantizzazione stesse, risparmiando ulteriore memoria (sì, è quantizzazione della quantizzazione).
  • Paged Optimizers — Gestisce i picchi di memoria durante il training spostando temporaneamente dati dalla GPU alla CPU.

Il risultato? QLoRA usa circa 4 volte meno VRAM rispetto a LoRA standard, permettendo di fine-tunare un modello 7B con soli 5-6 GB di VRAM. Una GPU consumer come la RTX 3060 è più che sufficiente.

Il compromesso c'è, va detto: un aumento del tempo di training di circa il 39% dovuto alle operazioni di quantizzazione/de-quantizzazione, e una lieve perdita di accuratezza. Ma nella mia esperienza, per la stragrande maggioranza dei casi d'uso, il trade-off è assolutamente favorevole.

LoRA vs QLoRA: Quale Scegliere

La regola pratica è semplice:

  • LoRA (16-bit) — Se il vostro modello base entra comodamente nella VRAM della GPU e volete massima accuratezza. Leggermente più veloce e preciso.
  • QLoRA (4-bit) — Se dovete lavorare con GPU consumer o volete fine-tunare modelli più grandi (13B, 30B) su hardware limitato. Il rapporto costi-benefici è quasi sempre vincente.

Setup Pratico con Unsloth

Unsloth è diventato il framework di riferimento per il fine-tuning efficiente di LLM nel 2026. E c'è un motivo. Sviluppato con ottimizzazioni a livello kernel — inclusa un'implementazione custom dell'attention in Triton — offre un training 2-3 volte più veloce con fino all'80% in meno di VRAM rispetto alle implementazioni standard di Hugging Face. Supporta LoRA, QLoRA, full fine-tuning e reinforcement learning, ed è compatibile con Ollama, llama.cpp e vLLM per il deployment.

Installazione e Configurazione

# Installazione di Unsloth (richiede Python 3.10+ e CUDA)
pip install unsloth

# Per Google Colab (gratuito)
pip install unsloth[colab]

# Verifica dell'installazione
python -c "from unsloth import FastLanguageModel; print('Unsloth installato correttamente')"

Caricamento del Modello con QLoRA

Allora, partiamo dal codice. Il primo passo è caricare il modello base e aggiungere gli adapter LoRA. Unsloth semplifica parecchio questa parte:

from unsloth import FastLanguageModel
import torch

# Configurazione del modello
max_seq_length = 2048
dtype = None  # Rilevamento automatico (Float16 per GPU vecchie, BFloat16 per Ampere+)
load_in_4bit = True  # QLoRA: carica il modello in 4-bit

# Caricamento del modello base con adapter LoRA
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="unsloth/Llama-3.1-8B-Instruct",
    max_seq_length=max_seq_length,
    dtype=dtype,
    load_in_4bit=load_in_4bit,
)

# Aggiunta degli adapter LoRA
model = FastLanguageModel.get_peft_model(
    model,
    r=16,                    # Rank di LoRA
    target_modules=[         # Moduli da addestrare
        "q_proj", "k_proj", "v_proj", "o_proj",  # Attention
        "gate_proj", "up_proj", "down_proj",       # MLP
    ],
    lora_alpha=16,           # Scaling factor
    lora_dropout=0,          # Unsloth ottimizza per dropout=0
    bias="none",
    use_gradient_checkpointing="unsloth",  # Riduce VRAM del 30%
    random_state=42,
)

# Verifica dei parametri addestrabili
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
total_params = sum(p.numel() for p in model.parameters())
print(f"Parametri addestrabili: {trainable_params:,} ({100*trainable_params/total_params:.2f}%)")
print(f"Parametri totali: {total_params:,}")

Notate come il numero di parametri addestrabili sia una percentuale minuscola del totale. È questa la magia di LoRA.

Preparazione del Dataset

La qualità del dataset è il fattore più importante per il successo del fine-tuning. Più importante della scelta del modello, degli iperparametri e del framework. Un dataset piccolo ma curato batte sempre un dataset grande ma rumoroso.

L'ho imparato a mie spese, dopo aver sprecato ore di GPU su dati mediocri.

Struttura del Dataset

Per il Supervised Fine-Tuning (SFT), il dataset deve seguire un formato conversazionale. Il formato più comune è quello di coppie istruzione-risposta, strutturate secondo il template di chat del modello:

from datasets import Dataset

# Esempio di dataset per un assistente legale italiano
dati_training = [
    {
        "instruction": "Qual è il termine di prescrizione per un credito commerciale in Italia?",
        "output": "Il termine di prescrizione ordinaria per i crediti commerciali in Italia è di 10 anni, ai sensi dell'art. 2946 del Codice Civile. Tuttavia, esistono termini più brevi per specifiche categorie..."
    },
    {
        "instruction": "Redigi una clausola di riservatezza per un contratto di consulenza.",
        "output": "CLAUSOLA DI RISERVATEZZA\n\n1. Definizione di Informazioni Riservate. Per \"Informazioni Riservate\" si intendono tutte le informazioni..."
    },
    # ... altri esempi
]

dataset = Dataset.from_list(dati_training)

# Formattazione nel template di chat del modello
def formatta_prompt(esempio):
    return {
        "text": f"""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Sei un assistente legale specializzato nel diritto commerciale italiano.<|eot_id|>
<|start_header_id|>user<|end_header_id|>
{esempio["instruction"]}<|eot_id|}
<|start_header_id|>assistant<|end_header_id|>
{esempio["output"]}<|eot_id|>"""
    }

dataset = dataset.map(formatta_prompt)
print(f"Dataset: {len(dataset)} esempi")
print(f"Esempio:\n{dataset[0]['text'][:300]}...")

Best Practice per la Qualità dei Dati

Ecco le regole che ho imparato a rispettare dopo numerosi esperimenti (e, devo ammetterlo, parecchi tentativi andati male):

  • Quantità minima — Per task semplici (classificazione, estrazione), 200-500 esempi possono bastare. Per task complessi (generazione di testo, ragionamento), servono 1.000-5.000 esempi di alta qualità.
  • Consistenza del formato — Ogni esempio deve seguire esattamente lo stesso formato. Anche piccole variazioni — spazi extra, punteggiatura inconsistente — degradano i risultati più di quanto immaginereste.
  • Diversità degli input — Coprite quante più variazioni possibili delle query che il modello riceverà in produzione. Un modello fine-tuned su un solo stile di domanda faticherà con formulazioni diverse.
  • Train solo sulle completions — Il paper originale di QLoRA dimostra che mascherare gli input e addestrare solo sulle risposte aumenta l'accuratezza di circa l'1%. Non è tanto, ma è gratis.
  • Generazione sintetica — Se non avete abbastanza dati reali, usate un modello più potente (GPT-4, Claude) per generare coppie domanda-risposta. Unsloth fornisce anche un notebook dedicato che parsifica documenti e genera automaticamente coppie QA.

Configurazione degli Iperparametri

Gli iperparametri di LoRA controllano il bilanciamento tra capacità di apprendimento, rischio di overfitting e costi computazionali. Scegliere i valori giusti è cruciale — e con milioni di combinazioni possibili, può sembrare scoraggiante. Ma in realtà, ci sono poche scelte che contano davvero.

Rank (r) e Alpha

Il rank (r) determina la dimensione delle matrici A e B di LoRA, e quindi quanti parametri addestrabili avete. Valori più alti significano più capacità espressiva, ma anche più rischio di overfitting e costi maggiori:

  • r=8 — Buon punto di partenza per task semplici (classificazione, sentiment analysis).
  • r=16 — Il default raccomandato per la maggior parte dei casi d'uso. Partite da qui.
  • r=32-64 — Per task complessi che richiedono un'ampia modifica del comportamento del modello.
  • r=128-256 — Solo per scenari avanzati dove la qualità è prioritaria rispetto ai costi. Raramente necessario, onestamente.

L'alpha è il fattore di scaling che controlla quanto gli adapter LoRA influenzano l'output finale. La regola standard è impostare alpha = rank (r=16, alpha=16), ma alcuni esperimenti suggeriscono che alpha = 2×rank possa dare risultati leggermente migliori con rank bassi.

Target Modules

La ricerca empirica — incluso il paper originale di QLoRA — dimostra che applicare LoRA sia ai layer di attention (q_proj, k_proj, v_proj, o_proj) sia ai layer MLP (gate_proj, up_proj, down_proj) produce i risultati migliori. Limitare LoRA ai soli layer di attention riduce le prestazioni in modo significativo. Non risparmiate qui.

Learning Rate e Scheduler

Questa è la parte dove si mettono insieme tutti i pezzi. Ecco una configurazione di training che funziona bene nella maggior parte degli scenari:

from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported

trainer = SFTTrainer(
    model=model,
    tokenizer=tokenizer,
    train_dataset=dataset,
    dataset_text_field="text",
    max_seq_length=max_seq_length,
    dataset_num_proc=2,
    packing=False,  # True per dataset con esempi brevi
    args=TrainingArguments(
        # Iperparametri fondamentali
        per_device_train_batch_size=2,
        gradient_accumulation_steps=4,  # Batch effettivo = 2 * 4 = 8
        warmup_steps=5,
        num_train_epochs=3,  # Non superare 3 epoche
        learning_rate=2e-4,  # Punto di partenza raccomandato

        # Precisione e ottimizzazione
        fp16=not is_bfloat16_supported(),
        bf16=is_bfloat16_supported(),
        optim="adamw_8bit",
        weight_decay=0.01,
        lr_scheduler_type="linear",
        seed=42,

        # Logging e salvataggio
        output_dir="output_finetuning",
        logging_steps=10,
        save_strategy="epoch",
    ),
)

# Avvio del training
trainer_stats = trainer.train()
print(f"Training completato in {trainer_stats.metrics['train_runtime']:.0f} secondi")
print(f"Loss finale: {trainer_stats.metrics['train_loss']:.4f}")

Errori Comuni da Evitare

Questi sono gli errori che vedo più spesso (e che ho fatto anche io, le prime volte):

  • Troppe epoche — Iterare sul dataset più di 3 volte spesso peggiora i risultati. Il modello inizia a memorizzare gli esempi invece di generalizzare. Tre epoche sono quasi sempre il massimo.
  • Learning rate troppo alto — Con LoRA, partite da 2e-4 e riducete se notate instabilità. Per modelli più grandi (>13B), provate 1e-4 o 5e-5.
  • Dropout con Unsloth — Unsloth è ottimizzato per dropout=0. Aggiungere dropout rallenta il training senza benefici significativi, grazie alle ottimizzazioni interne del framework.
  • Ignorare la validazione — Riservate sempre il 10-15% del dataset per la validazione. Monitorare la validation loss è l'unico modo affidabile per rilevare l'overfitting. Non saltatelo mai.

Da SFT a GRPO: Reinforcement Learning per il Ragionamento

Il Supervised Fine-Tuning insegna al modello cosa rispondere, ma non gli insegna come ragionare. Nel 2026, una delle novità più interessanti è l'applicazione del Reinforcement Learning — in particolare il GRPO (Group Relative Policy Optimization) sviluppato da DeepSeek — per insegnare ai modelli vere capacità di ragionamento.

Questa parte mi ha entusiasmato parecchio quando l'ho provata la prima volta.

Come Funziona GRPO

A differenza del SFT tradizionale che massimizza semplicemente la probabilità del prossimo token, GRPO ottimizza per una funzione di reward. Per ogni domanda, il modello genera molteplici risposte candidate (ad esempio 8), ciascuna viene valutata dalla funzione di reward, e il modello viene aggiornato per favorire le risposte con reward più alto.

L'aspetto davvero rivoluzionario? Non servono dataset di ragionamento annotati manualmente. Basta definire una funzione di reward — che può essere semplice come "la risposta è corretta sì/no?" — e il modello sviluppa autonomamente le sue strategie di ragionamento. È affascinante da osservare.

Implementazione con Unsloth

from unsloth import FastLanguageModel
from trl import GRPOConfig, GRPOTrainer
import re

# Caricamento del modello (dopo SFT o da base)
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="unsloth/Llama-3.1-8B-Instruct",
    max_seq_length=2048,
    load_in_4bit=True,
)

model = FastLanguageModel.get_peft_model(
    model,
    r=16,
    target_modules=[
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj",
    ],
    lora_alpha=16,
    lora_dropout=0,
    use_gradient_checkpointing="unsloth",
)

# Definizione delle funzioni di reward
def reward_formato_corretto(completions, **kwargs):
    """Premia risposte che seguono il formato ......"""
    pattern = r".*?\s*.*?"
    return [
        1.0 if re.search(pattern, c, re.DOTALL) else 0.0
        for c in completions
    ]

def reward_risposta_corretta(completions, answer, **kwargs):
    """Premia risposte che contengono la risposta corretta"""
    rewards = []
    for completion in completions:
        match = re.search(r"(.*?)", completion)
        if match and match.group(1).strip() == str(answer).strip():
            rewards.append(2.0)
        else:
            rewards.append(0.0)
    return rewards

def reward_lunghezza_ragionamento(completions, **kwargs):
    """Premia ragionamenti non troppo corti né troppo lunghi"""
    rewards = []
    for completion in completions:
        match = re.search(r"(.*?)", completion, re.DOTALL)
        if match:
            lunghezza = len(match.group(1).split())
            if 50 <= lunghezza <= 300:
                rewards.append(0.5)
            else:
                rewards.append(-0.5)
        else:
            rewards.append(-1.0)
    return rewards

# Configurazione GRPO
training_args = GRPOConfig(
    output_dir="output_grpo",
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=5e-6,       # LR più basso per RL
    num_generations=8,         # 8 risposte candidate per domanda
    max_prompt_length=512,
    max_completion_length=1024,
    num_train_epochs=2,
    logging_steps=5,
    save_strategy="steps",
    save_steps=100,
)

# Creazione del trainer con reward multipli
trainer = GRPOTrainer(
    model=model,
    processing_class=tokenizer,
    reward_funcs=[
        reward_formato_corretto,
        reward_risposta_corretta,
        reward_lunghezza_ragionamento,
    ],
    args=training_args,
    train_dataset=dataset_grpo,
)

trainer.train()

Una nota sulle prestazioni: Unsloth rende il GRPO particolarmente efficiente, usando l'80% in meno di VRAM rispetto ad altre implementazioni. Grazie a un nuovo algoritmo di batching, supporta contesti 7 volte più lunghi. Potete addestrare un modello di ragionamento con soli 5 GB di VRAM. Non male.

Salvataggio e Deployment in Produzione

Ok, il training è finito. Adesso viene la parte pratica: scegliere il formato di esportazione in base al vostro scenario di deployment.

Esportazione del Modello

# Salvataggio degli adapter LoRA (leggero, ~100-500 MB)
model.save_pretrained("modello_finetuned_lora")
tokenizer.save_pretrained("modello_finetuned_lora")

# Merge completo e salvataggio in formato Hugging Face
model.save_pretrained_merged(
    "modello_finetuned_merged",
    tokenizer,
    save_method="merged_16bit",  # oppure "merged_4bit"
)

# Esportazione in formato GGUF per llama.cpp / Ollama
model.save_pretrained_gguf(
    "modello_finetuned_gguf",
    tokenizer,
    quantization_method="q4_k_m",  # Buon bilanciamento qualità/dimensione
)

# Esportazione per vLLM (produzione ad alte prestazioni)
model.save_pretrained_merged(
    "modello_finetuned_vllm",
    tokenizer,
    save_method="merged_16bit",
)

Guida alla Scelta del Formato

  • Solo adapter LoRA — Per sviluppo e sperimentazione. Leggero e componibile: potete caricare diversi adapter sullo stesso modello base a runtime.
  • GGUF (llama.cpp / Ollama) — Per inferenza locale su hardware consumer. Supporta quantizzazione avanzata (Q4_K_M, Q5_K_M, Q8_0) per bilanciare qualità e velocità.
  • vLLM / SGLang — Per deployment in produzione con alto throughput. Se deployate un LLM per uso enterprise o multi-utente, questa è la scelta raccomandata. Supporta batching continuo, PagedAttention e servizio multi-utente.
  • Hugging Face Hub — Per condividere il modello con la comunità o deployare tramite Inference Endpoints.

Deployment Rapido con Ollama

Se volete testare il vostro modello in locale (e fidatevi, lo vorrete), Ollama è il modo più rapido:

# Dopo l'esportazione in GGUF, create un Modelfile per Ollama
cat > Modelfile << EOF
FROM ./modello_finetuned_gguf/unsloth.Q4_K_M.gguf

PARAMETER temperature 0.7
PARAMETER top_p 0.9

SYSTEM """Sei un assistente specializzato nel dominio legale italiano."""
EOF

# Creazione e test del modello in Ollama
ollama create assistente-legale -f Modelfile
ollama run assistente-legale "Qual è la differenza tra rescissione e risoluzione contrattuale?"

Quanto Costa il Fine-Tuning nel 2026

Uno degli aspetti che trovo più interessanti del fine-tuning nel 2026 è il crollo dei costi. Grazie a QLoRA e Unsloth, i numeri sono cambiati radicalmente. Ecco un confronto realistico:

  • Google Colab gratuito — GPU T4 (16 GB VRAM). Sufficiente per fine-tunare modelli fino a 8B con QLoRA. Costo: 0 €. Sì, zero.
  • GPU consumer RTX 4090 — 24 GB VRAM. Fine-tuning di modelli fino a 13B. Investimento una tantum: ~1.800 €.
  • Cloud GPU A10G (AWS) — 24 GB VRAM. Ideale per training più lunghi. Costo: circa 1,50 €/ora. Con Unsloth, un training completo su 8B richiede ~3 ore, quindi circa 4,50 € per run.
  • Cloud GPU A100 (AWS/GCP) — 80 GB VRAM. Per modelli 30-70B o training su dataset molto grandi. Costo: circa 4 €/ora.

Per dare un po' di contesto: senza le ottimizzazioni di Unsloth, lo stesso training su AWS richiederebbe un'istanza con 4× A10G GPU a ~9 €/ora. Una riduzione dei costi del 78% grazie al framework. Non è poca cosa.

Valutazione del Modello Fine-Tuned

Il fine-tuning non finisce con il training. La valutazione è altrettanto cruciale — e troppo spesso viene sottovalutata. Un modello che performa bene sulla training loss potrebbe comunque fallire in produzione se ha fatto overfitting sugli esempi di training.

Metriche e Strategie di Valutazione

  • Validation loss — Monitorate la loss su un holdout set durante il training. Se la validation loss inizia a salire mentre la training loss scende, state facendo overfitting. Fermatevi.
  • Task-specific benchmarks — Create un set di test con 50-100 esempi rappresentativi del vostro caso d'uso reale. Valutate manualmente o con un modello giudice (LLM-as-a-Judge).
  • A/B testing — Prima del deployment completo, confrontate il modello fine-tuned con il modello base sulle stesse query di produzione. I numeri parlano più delle sensazioni.
  • Benchmark standard — Per validazione generale, usate benchmark come MMLU, HellaSwag o ARC per verificare che il modello non abbia perso capacità generali.
# Esempio di valutazione con LLM-as-a-Judge
from openai import OpenAI

client = OpenAI()

def valuta_risposta(domanda, risposta_modello, risposta_gold):
    """Usa GPT-4 come giudice per valutare la qualità della risposta."""
    prompt_giudice = f"""Valuta la qualità della risposta del modello rispetto alla risposta di riferimento.

Domanda: {domanda}
Risposta del modello: {risposta_modello}
Risposta di riferimento: {risposta_gold}

Assegna un punteggio da 1 a 5:
1 = Completamente errata
2 = Parzialmente corretta ma con errori significativi
3 = Corretta ma incompleta
4 = Buona, con dettagli rilevanti
5 = Eccellente, completa e accurata

Rispondi SOLO con il punteggio numerico."""

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt_giudice}],
        temperature=0,
    )
    return int(response.choices[0].message.content.strip())

# Valutazione su un set di test
punteggi = []
for esempio in test_set:
    output = genera_risposta(model, tokenizer, esempio["instruction"])
    punteggio = valuta_risposta(
        esempio["instruction"],
        output,
        esempio["output"]
    )
    punteggi.append(punteggio)

print(f"Punteggio medio: {sum(punteggi)/len(punteggi):.2f}/5.0")

Domande Frequenti

Quanti dati servono per il fine-tuning di un LLM?

Dipende dalla complessità del task — ma meno di quanto pensiate. Per task semplici come classificazione o estrazione di entità, 200-500 esempi di alta qualità possono essere sufficienti. Per task più complessi come la generazione di testo specializzato, servono 1.000-5.000 esempi. La regola d'oro è che la qualità dei dati conta molto più della quantità: 500 esempi curati battono 10.000 esempi rumorosi, sempre. Potete anche generare dati sintetici usando modelli più potenti come GPT-4 o Claude.

Quanto tempo richiede il fine-tuning di un LLM con LoRA?

Con Unsloth e QLoRA su una GPU consumer (RTX 3060/4090), il fine-tuning di un modello 8B su 5.000 esempi richiede circa 2-4 ore. Su GPU cloud come A100, i tempi si riducono a 1-2 ore. Il full fine-tuning, in confronto, richiederebbe giorni e hardware molto più costoso. I fattori che influenzano di più il tempo sono la dimensione del modello, la lunghezza delle sequenze e il numero di epoche.

Fine-tuning o RAG: qual è meglio per il mio caso d'uso?

Se il vostro problema è che il modello non ha le informazioni giuste (conoscenze aziendali, dati aggiornati, documenti specifici), usate RAG. Se il problema è che il modello non si comporta come volete (stile sbagliato, formato errato, terminologia imprecisa), usate il fine-tuning. Nel 2026, l'approccio più efficace è spesso l'ibrido RAFT: fine-tuning per il comportamento e RAG per la conoscenza dinamica.

Posso fare fine-tuning gratis?

Assolutamente sì. Google Colab offre GPU T4 gratuite con 16 GB di VRAM, sufficienti per il fine-tuning con QLoRA di modelli fino a 8B parametri. Anche Kaggle offre GPU gratuite. Unsloth è progettato specificamente per rendere il fine-tuning accessibile su hardware limitato — con QLoRA potete iniziare con soli 5 GB di VRAM. Il costo reale, a dirla tutta, non è la GPU, ma il tempo necessario per preparare un dataset di qualità.

Cosa succede se il modello perde le sue capacità generali dopo il fine-tuning?

Questo fenomeno si chiama "catastrophic forgetting" ed è uno dei rischi principali del fine-tuning tradizionale. La buona notizia è che LoRA mitiga significativamente questo problema: i pesi originali del modello restano congelati, e state solo aggiungendo dei piccoli adapter. Per minimizzare ulteriormente il rischio, evitate di addestrare per troppe epoche (massimo 3), usate un learning rate conservativo e verificate le capacità generali del modello su benchmark standard come MMLU dopo il training. In pratica, con LoRA, il catastrophic forgetting è molto meno preoccupante di quanto fosse con il full fine-tuning.

Sull'Autore Editorial Team

Our team of expert writers and editors.