Personalizzare un modello linguistico sui propri dati - il cosiddetto fine-tuning - un tempo richiedeva cluster di GPU costosissime. Oggi, grazie alla tecnica LoRA e a librerie come Unsloth, si puo' fare gratuitamente sulla GPU messa a disposizione da Google Colab. In questa guida vedremo, passo dopo passo, come addestrare un piccolo modello open perche' risponda nello stile e con le conoscenze che vuoi tu, per poi usarlo in locale con Ollama.

A chi serve e cosa otterrai

Questa guida e' pensata per sviluppatori, studenti e appassionati che sanno muoversi un minimo con Python e vogliono capire concretamente come si specializza un modello. Al termine avrai un modello affinato sui tuoi esempi (per esempio uno stile di risposta aziendale, un formato fisso, un dominio specifico) e un file pronto da eseguire in locale. Non serve possedere una GPU: usiamo quella gratuita di Colab.

Prerequisiti reali: un account Google (per Colab), un account Hugging Face gratuito (per scaricare i modelli), qualche decina di esempi di addestramento e un po' di pazienza. Nessun costo, a patto di accettare i limiti d'uso della GPU gratuita.

Cos'e' LoRA (e perche' rende tutto piu' leggero)

Il fine-tuning "classico" aggiorna tutti i miliardi di parametri di un modello: pesante, lento, esoso di memoria. LoRA (Low-Rank Adaptation) fa una cosa piu' furba: congela il modello originale e addestra solo un piccolo insieme di matrici aggiuntive, gli "adattatori". In pratica modifichi meno dell'1% dei parametri, ottenendo comunque un buon adattamento al tuo compito. Il risultato e' un addestramento che entra nella memoria di una GPU modesta e produce file di poche decine di megabyte, invece che di decine di gigabyte.

Uniamo LoRA alla quantizzazione a 4 bit (QLoRA): il modello base viene caricato in una forma compressa, riducendo ulteriormente la memoria richiesta. E' questa combinazione a rendere possibile addestrare un modello da 7-8 miliardi di parametri sui 15 GB di VRAM di una GPU T4 gratuita.

Quali strumenti useremo

  • Google Colab (gratuito): fornisce una GPU T4. Primo consiglio: e' la scelta migliore per iniziare, perche' non richiede installazioni sul tuo computer.
  • Unsloth: libreria che rende il fine-tuning con LoRA fino a 2 volte piu' veloce e con meno memoria. E' la nostra prima scelta perche' semplifica moltissimo il codice.
  • TRL di Hugging Face, con il suo SFTTrainer, che gestisce il ciclo di addestramento supervisionato.
  • Un modello base open, per esempio Llama 3.1 8B Instruct oppure un Qwen2.5 7B, entrambi disponibili in versione gia' quantizzata.

In alternativa a Colab, chi ha una GPU NVIDIA con almeno 8-12 GB di VRAM puo' eseguire tutto in locale; chi vuole piu' potenza puo' usare Colab Pro o servizi come Kaggle (che offre GPU gratuite con quote settimanali).

Con LoRA e la quantizzazione a 4 bit si addestra un modello da 8B sulla GPU gratuita di Colab.

Procedimento passo passo

1. Prepara Colab e installa Unsloth

Vai su colab.research.google.com, crea un nuovo notebook e imposta l'acceleratore GPU da Runtime > Change runtime type > T4 GPU. Poi installa Unsloth:

!pip install unsloth

2. Carica il modello base in 4 bit

from unsloth import FastLanguageModel
import torch

max_seq_length = 2048
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/Meta-Llama-3.1-8B-Instruct-bnb-4bit",
    max_seq_length = max_seq_length,
    load_in_4bit = True,
)

3. Aggiungi gli adattatori LoRA

model = FastLanguageModel.get_peft_model(
    model,
    r = 16,                 # "rango" degli adattatori: 8-32 va bene
    lora_alpha = 16,
    lora_dropout = 0,
    target_modules = ["q_proj","k_proj","v_proj","o_proj",
                      "gate_proj","up_proj","down_proj"],
    bias = "none",
    use_gradient_checkpointing = "unsloth",
)

4. Prepara il tuo dataset

Il formato piu' semplice e' una lista di coppie istruzione/risposta. Puoi caricare un file JSON tuo oppure un dataset da Hugging Face. Ecco come costruire al volo un piccolo set di esempio e formattarlo con il template della chat:

from datasets import Dataset

esempi = [
    {"istruzione": "Descrivi il prodotto in una frase di marketing.",
     "input": "Scarpe da corsa leggere",
     "output": "Corri piu' leggero: massima ammortizzazione, minimo peso."},
    {"istruzione": "Descrivi il prodotto in una frase di marketing.",
     "input": "Zaino impermeabile 25L",
     "output": "Affronta ogni acquazzone: 25 litri sempre asciutti."},
    # ... aggiungi almeno 50-100 esempi per risultati apprezzabili
]

def formatta(e):
    testo = tokenizer.apply_chat_template(
        [{"role":"user","content": e["istruzione"] + "\n" + e["input"]},
         {"role":"assistant","content": e["output"]}],
        tokenize=False)
    return {"text": testo}

dataset = Dataset.from_list(esempi).map(formatta)

Regola d'oro: la qualita' dei dati conta piu' della quantita'. Meglio 100 esempi puliti e coerenti che 1.000 esempi confusi.

5. Avvia l'addestramento

from trl import SFTTrainer, SFTConfig

trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = dataset,
    args = SFTConfig(
        per_device_train_batch_size = 2,
        gradient_accumulation_steps = 4,
        warmup_steps = 5,
        max_steps = 60,          # per una prova; usa num_train_epochs per dataset veri
        learning_rate = 2e-4,
        logging_steps = 1,
        optim = "adamw_8bit",
        output_dir = "outputs",
    ),
)
trainer.train()

Su una T4 un giro di prova da 60 passi richiede pochi minuti. Vedrai la loss scendere: e' il segnale che il modello sta imparando dai tuoi esempi.

6. Prova subito il modello affinato

FastLanguageModel.for_inference(model)
messaggi = [{"role":"user","content":"Descrivi il prodotto in una frase di marketing.\nBorraccia termica 1L"}]
inputs = tokenizer.apply_chat_template(messaggi, add_generation_prompt=True, return_tensors="pt").to("cuda")
print(tokenizer.decode(model.generate(inputs, max_new_tokens=64)[0], skip_special_tokens=True))

Ti aspetti una frase nello stile dei tuoi esempi, per esempio: "Acqua sempre alla temperatura giusta: 1 litro, caldo o freddo per ore."

7. Esporta per Ollama (uso in locale)

Per usare il modello fuori da Colab, esportalo nel formato GGUF, gia' quantizzato:

model.save_pretrained_gguf("modello-finetunato", tokenizer, quantization_method="q4_k_m")

Scarica il file .gguf, crea un Modelfile con la riga FROM ./modello-finetunato.Q4_K_M.gguf e importalo con ollama create mio-modello -f Modelfile. Da quel momento puoi interrogarlo in locale con ollama run mio-modello, senza connessione e senza costi per chiamata.

Errori comuni e come risolverli

  • "CUDA out of memory": riduci max_seq_length (es. 1024), abbassa per_device_train_batch_size a 1 o scegli un modello piu' piccolo.
  • Il modello "dimentica" tutto e ripete gli esempi: e' overfitting. Riduci il numero di passi/epoche, aumenta il dataset o abbassa il learning_rate.
  • La GPU non e' attiva: controlla di aver selezionato la runtime T4 e verifica con !nvidia-smi.
  • Errore di accesso al modello su Hugging Face: alcuni modelli richiedono di accettare la licenza sul sito e di effettuare il login con huggingface-cli login.

Varianti, alternative e quando NON farlo

Se il tuo obiettivo e' solo dare al modello dei documenti da cui attingere informazioni aggiornate, il fine-tuning non e' la strada giusta: meglio un sistema RAG (recupero delle informazioni dai tuoi file). Il fine-tuning conviene quando vuoi cambiare lo stile, il formato o il comportamento del modello in modo stabile. Per compiti semplici, spesso basta un buon prompt: prova prima quello, e passa al fine-tuning solo se il prompt non basta.

Per andare oltre, puoi sperimentare con dataset piu' grandi, provare modelli base diversi, aumentare il rango LoRA per compiti complessi o usare l'addestramento su piu' epoche con un set di validazione per monitorare la generalizzazione. La documentazione ufficiale di Unsloth e di TRL e' il punto di riferimento per approfondire ogni parametro.