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.
Cosa farai
Step concreti:
- Apri Colab (gratis, T4 GPU ~16 GB VRAM).
- Scegli un modello base piccolo (1B–3B).
- Scegli un dataset di SFT (o crealo).
- Lancia LoRA con
unsloth(per la velocità) otrl(per la trasparenza). - Verifica che il modello impari (curva di loss in discesa).
- Confronta risposte prima/dopo.
- 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:
-
Dataset pubblico (più veloce):
mlabonne/Alpaca-1k— 1000 esempitatsu-lab/alpaca— 52k esempidatabricks/databricks-dolly-15k— 15k esempi (CC-licensed)HuggingFaceTB/smol-smoltalk— chat italiano-friendly
-
Dataset specifico tuo dominio:
- 50-500 esempi annotati a mano (json)
- Format:
[{"messages":[{"role":"user","content":"..."},{"role":"assistant","content":"..."}]}, ...]
-
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_sizea 1. - Riduci
max_seq_lengtha 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 ditrlpuro.optim="adamw_8bit"riduce memoria e velocizza.gradient_checkpointingsalva 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:
- Dataset tuo: crea 100-500 esempi in italiano del tuo dominio. Ripeti.
- DPO: dopo SFT, aggiungi un layer di DPO con
DPOTrainersu un dataset di preferenze (~500 coppie). - Multiple LoRA: addestra 3 LoRA su 3 personalità diverse. Caricale a runtime e cambia “modello virtuale” senza ricaricare il base.
- 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. - 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
- Il dataset è tutto. Cambiare LR del 10% sposta poco; cambiare dataset cambia drasticamente il modello.
- Il LoRA non aggiunge conoscenza. Cambia comportamento. Non aspettarti che il tuo Llama-1B sappia tutta la storia romana dopo SFT su Wikipedia.
- La loss “non scende abbastanza” è normale. SFT non è pretraining. La loss finale di 1.0-1.5 può essere il tuo stato di plateau.
- L’overfitting succede. 5 epoche su 100 esempi → modello che ripete pappagallo. Tieniti su 1-3 epoche e dataset più grande possibile.
- Le specifiche di
target_modulescontano. Soloq,vvs 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.cppper inferenza efficiente su CPU/M-series.
TEST hands-on (ribadito)
- Apri Colab. Imposta GPU T4 (gratis).
- Esegui le 8 celle qui sopra. Documenta:
- Tempo totale
- Loss iniziale e finale
- Confronto pre/post training su 3 prompt diversi
- Modifica un iperparametro (es.
rda 16 a 64). Rilancia. Confronta. - Salva il LoRA. Ricarica in una nuova session. Verifica che le risposte siano le stesse.
- 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).