🧙 Maestro Yoda Cap. 20 · Training, fine-tuning, allineamento

Puntata 222

Puntata 222 — TEST: LoRA fine-tuning su Colab gratis

Livello: 🧙 Maestro Yoda · Capitolo 20 · Training, fine-tuning, allineamento

Hai letto 10 puntate di teoria. Adesso si trasforma in pratica: in 30 minuti, su una GPU gratis di Colab, fine-tuning di un Llama-3 piccolo. Niente più scuse.

screenshot di Google Colab con una cella di output che mostra una loss curve in discesa. Stato: “T4 GPU available”. In sovrimpressione: “27/100 epoche”. Tag: “$0 di spesa, 1 modello custom in uscita”

Cosa farai

Step concreti:

  1. Apri Colab (gratis, T4 GPU ~16 GB VRAM).
  2. Scegli un modello base piccolo (1B–3B).
  3. Scegli un dataset di SFT (o crealo).
  4. Lancia LoRA con unsloth (per la velocità) o trl (per la trasparenza).
  5. Verifica che il modello impari (curva di loss in discesa).
  6. Confronta risposte prima/dopo.
  7. Salva il LoRA adapter (~100 MB) e/o pushalo su HuggingFace Hub.

Tempo richiesto: 30-60 minuti dall’apertura del notebook all’output funzionante. Tutto gratis.


Prima del codice: scelte da fare

Modello base

Su T4 (16 GB VRAM), in QLoRA 4-bit:

  • Llama-3.2-1B-Instruct: facilissimo, 8 GB VRAM. Buono per imparare.
  • Llama-3.2-3B-Instruct: ok, ~12 GB. Più capacità, training un po’ più lento.
  • Phi-3.5-mini (3.8B): ottimo rapporto qualità/dimensioni.
  • Gemma-2-2B-it: leggero, buona quality.
  • Qwen-2.5-1.5B-Instruct: multilingua, ottimo per italiano.
  • Mistral-7B / Llama-3.1-8B: serve A100 o L4, scordatelo su T4 gratis.

Per il primo run consigliato: Llama-3.2-1B-Instruct. Veloce, fa vedere effetto del fine-tuning, basso rischio di OOM.

Dataset

Tre opzioni:

  1. Dataset pubblico (più veloce):

    • mlabonne/Alpaca-1k — 1000 esempi
    • tatsu-lab/alpaca — 52k esempi
    • databricks/databricks-dolly-15k — 15k esempi (CC-licensed)
    • HuggingFaceTB/smol-smoltalk — chat italiano-friendly
  2. Dataset specifico tuo dominio:

    • 50-500 esempi annotati a mano (json)
    • Format: [{"messages":[{"role":"user","content":"..."},{"role":"assistant","content":"..."}]}, ...]
  3. Generato sinteticamente (vedi puntata 220):

    • Usa GPT-4-mini o Claude Haiku via API.
    • 100-500 esempi a budget basso ($5-20).

Per primo test: HuggingFaceTB/smol-smoltalk (italiano-friendly, leggero).

Stack

Consigliato per Colab gratis: unsloth. 2-5× più veloce di transformers su GPU consumer, gestisce quantization automaticamente.

Alternativa più trasparente ma più lenta: transformers + trl + peft + bitsandbytes.


Il notebook completo

Cella 1 — Install

!pip install -U unsloth datasets transformers trl peft bitsandbytes accelerate

Cella 2 — Load model in 4-bit

from unsloth import FastLanguageModel
import torch

max_seq_length = 2048
dtype = None  # auto-detect
load_in_4bit = True

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/Llama-3.2-1B-Instruct-bnb-4bit",
    max_seq_length = max_seq_length,
    dtype = dtype,
    load_in_4bit = load_in_4bit,
)

# Setup LoRA
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 = 32,
    lora_dropout = 0,
    bias = "none",
    use_gradient_checkpointing = "unsloth",
    random_state = 42,
)

Cella 3 — Test pre-training

FastLanguageModel.for_inference(model)
prompt = "Spiegami brevemente cos'è la fotosintesi."
inputs = tokenizer.apply_chat_template(
    [{"role": "user", "content": prompt}],
    tokenize=True, return_tensors="pt", add_generation_prompt=True
).to("cuda")
out = model.generate(inputs, max_new_tokens=200, temperature=0.7)
print("PRE-TRAINING:", tokenizer.decode(out[0], skip_special_tokens=True))

Cella 4 — Load dataset

from datasets import load_dataset
dataset = load_dataset("HuggingFaceTB/smol-smoltalk", split="train[:2000]")
# Filtra solo dialoghi multi-turn brevi
dataset = dataset.filter(lambda x: len(x["messages"]) <= 6)
print(f"Esempi: {len(dataset)}")
print(dataset[0])

Cella 5 — Format con chat template

def formatting_func(example):
    text = tokenizer.apply_chat_template(
        example["messages"], tokenize=False, add_generation_prompt=False
    )
    return text

# Per SFTTrainer di trl/unsloth basta passare il dataset + formatting_func

Cella 6 — Training

from trl import SFTTrainer, SFTConfig

trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = dataset,
    formatting_func = formatting_func,
    args = SFTConfig(
        per_device_train_batch_size = 2,
        gradient_accumulation_steps = 4,
        warmup_steps = 5,
        num_train_epochs = 1,  # alza a 2-3 se vuoi più qualità
        learning_rate = 2e-4,
        fp16 = not torch.cuda.is_bf16_supported(),
        bf16 = torch.cuda.is_bf16_supported(),
        logging_steps = 10,
        output_dir = "outputs",
        max_seq_length = max_seq_length,
        optim = "adamw_8bit",
        weight_decay = 0.01,
        lr_scheduler_type = "linear",
        seed = 42,
        save_strategy = "no",
        report_to = "none",
    ),
)

trainer.train()

Cella 7 — Test post-training

FastLanguageModel.for_inference(model)
inputs = tokenizer.apply_chat_template(
    [{"role": "user", "content": prompt}],
    tokenize=True, return_tensors="pt", add_generation_prompt=True
).to("cuda")
out = model.generate(inputs, max_new_tokens=200, temperature=0.7)
print("POST-TRAINING:", tokenizer.decode(out[0], skip_special_tokens=True))

Cella 8 — Save (opzionale)

# Salva solo l'adapter LoRA (~100 MB)
model.save_pretrained("llama-1b-mio-lora")

# Oppure mergialo nel base e salva tutto (~3 GB in 4-bit, ~5 GB in 16-bit)
model.save_pretrained_merged("llama-1b-mio-merged", tokenizer, save_method="merged_16bit")

# O pusha su HuggingFace Hub
# model.push_to_hub("tuo-username/llama-1b-mio-lora", tokenizer)

Cosa aspettarti

Tempi su T4 (Colab free)

  • Loading model: 1-2 min
  • Loading dataset: 30 sec
  • Training 1 epoca su 2000 esempi: 20-40 min
  • Generation pre/post: ~10 sec/each

Loss curve

Dovresti vedere la loss scendere da ~1.5-2.0 iniziale a ~1.0-1.5 finale. Se non scende, controlla:

  • Dataset format giusto?
  • Learning rate troppo alto?
  • Hai congelato il base model?

Cambiamenti osservabili

Dopo 1 epoca su un dataset chat-style:

  • Tone più “chat assistant”.
  • Format leggermente diverso.
  • Refusal pattern modificati (se dataset li include).
  • Knowledge non cambia in modo evidente (è già nel base).

Non aspettarti miracoli con 1 epoca su 2000 esempi e un 1B model. Aspettati percepibile shift di style.


Errori comuni

OOM (Out Of Memory)

  • Riduci per_device_train_batch_size a 1.
  • Riduci max_seq_length a 1024 o 512.
  • Usa modello più piccolo (1B invece di 3B).
  • Verifica load_in_4bit=True.

Loss non scende

  • Learning rate basso? Prova 5e-4 o 1e-3.
  • Dataset format sbagliato? Stampa dataset[0] e verifica.
  • Target modules sbagliati? Per Llama-3 usa quelli sopra.

Training troppo lento

  • unsloth è ~2× più veloce di trl puro.
  • optim="adamw_8bit" riduce memoria e velocizza.
  • gradient_checkpointing salva memoria ma rallenta — se hai VRAM disabilitalo.

Output ripetitivo / loop

  • Modello sotto-trainato (>=2 epoche).
  • Repetition penalty in generation: repetition_penalty=1.1.
  • Temperatura troppo bassa.

Step successivi

Dopo aver fatto il primo LoRA con successo, prova:

  1. Dataset tuo: crea 100-500 esempi in italiano del tuo dominio. Ripeti.
  2. DPO: dopo SFT, aggiungi un layer di DPO con DPOTrainer su un dataset di preferenze (~500 coppie).
  3. Multiple LoRA: addestra 3 LoRA su 3 personalità diverse. Caricale a runtime e cambia “modello virtuale” senza ricaricare il base.
  4. Quantization post-training: il tuo modello merged può essere quantizzato a GGUF Q4_K_M per girare su CPU/M1 con llama.cpp. 30 secondi di setup.
  5. Pubblica: pusha su HuggingFace Hub. Aggiungi un model card con dataset, hyperparameters, intended use. È un portfolio piece.

Le 5 lezioni che capirai solo dopo aver fatto questo TEST

  1. Il dataset è tutto. Cambiare LR del 10% sposta poco; cambiare dataset cambia drasticamente il modello.
  2. Il LoRA non aggiunge conoscenza. Cambia comportamento. Non aspettarti che il tuo Llama-1B sappia tutta la storia romana dopo SFT su Wikipedia.
  3. La loss “non scende abbastanza” è normale. SFT non è pretraining. La loss finale di 1.0-1.5 può essere il tuo stato di plateau.
  4. L’overfitting succede. 5 epoche su 100 esempi → modello che ripete pappagallo. Tieniti su 1-3 epoche e dataset più grande possibile.
  5. Le specifiche di target_modules contano. Solo q,v vs tutti i layer LoRA-fy = differenza grossa.

Diploma di SFT

Se hai completato questo TEST:

  • Hai fatto fine-tuning di un LLM open.
  • Hai capito cosa cambia e cosa no.
  • Hai ~$0 di costo e un adapter LoRA tuo.
  • Sei pronto a sperimentare LoRA su task reali.

Il prossimo capitolo (puntate 223+) entra in HuggingFace Hub, open source, modelli quantizzati e l’ecosistema open completo.


Glossario lampo

  • Colab T4 — GPU gratis offerta da Google Colab, ~16 GB VRAM, lenta ma sufficiente per LoRA.
  • OOM — Out Of Memory, errore quando il modello + dati superano la VRAM disponibile.
  • Gradient checkpointing — tecnica che salva memoria ricomputando attivazioni durante il backward (più lento, meno VRAM).
  • Merge — fondere LoRA nel base model per produrre un modello “monolitico”.
  • GGUF — formato di file quantizzato usato da llama.cpp per inferenza efficiente su CPU/M-series.

TEST hands-on (ribadito)

  1. Apri Colab. Imposta GPU T4 (gratis).
  2. Esegui le 8 celle qui sopra. Documenta:
    • Tempo totale
    • Loss iniziale e finale
    • Confronto pre/post training su 3 prompt diversi
  3. Modifica un iperparametro (es. r da 16 a 64). Rilancia. Confronta.
  4. Salva il LoRA. Ricarica in una nuova session. Verifica che le risposte siano le stesse.
  5. Bonus: ripeti con un dataset tuo di 100 esempi. Vedi quanto cambia.

Take-away

Il fine-tuning di un LLM è passato in 4 anni da “$1M e 8 H100” a “$0 e una GPU gratuita di Colab”. È la più drastica democratizzazione tecnica degli ultimi anni. Significa che chiunque legga questa puntata ora può, letteralmente in un pomeriggio, addestrare un modello AI personalizzato. La barriera non è più tecnica. È la cura del dataset.


➡️ Prossima puntata: HuggingFace — il “GitHub dell’AI” (apre Capitolo 21).