Il metodo QLoRA consente di “congelare” una copia a 4‑bit del modello di base, addestrare piccole matrici a bassa rango (adapter) e poi fondere il risultato in un modello completo pronto per l’esportazione GGUF. In pratica è possibile perfezionare un LLM con 27 Miliardi di parametri su una GPU da 24 GB in meno di due ore, ottenendo un modello che restituisce JSON rigoroso per la classificazione di ticket di supporto.
Perché scegliere il fine‑tuning rispetto a prompting o RAG
Molti team ricorrono al fine‑tuning troppo presto. Spesso un system prompt ben costruito e qualche esempio risolvono la maggior parte dei problemi di formattazione, mentre la generazione aumentata da retrieval (RAG) è ideale per fornire fatti aggiornati o privati. Il fine‑tuning è quindi giustificato in quattro scenari specifici:
- Formato di output rigido (es. JSON) che il modello base non rispetta neanche con prompt accurati.
- Compiti di classificazione o estrazione molto specifici con centinaia di esempi etichettati, dove si desidera un modello piccolo ed economico.
- Stile o tono aziendale che non può essere mantenuto tramite prompt su larga scala.
- Riduzione di latenza e costi: un modello sintonizzato richiede prompt più brevi, poiché il comportamento è incorporato nei pesi.
Al contrario, per insegnare al modello nuovi fatti è preferibile usare retrieval, poiché gli adapter non assorbono conoscenza in modo affidabile. Se occorrono sia comportamento che conoscenza, si può combinare fine‑tuning per il formato e retrieval per i fatti.
Regole di decisione rapida
Un metodo pratico consiste nel provare prima il prompting, valutare il tasso di fallimento su 100 input realistici e procedere così:
- Se il tasso di fallimento è inferiore a qualche percento, fermarsi.
- Se i fallimenti riguardano formato o comportamento, raccogliere esempi e avviare il fine‑tuning.
- Se i fallimenti sono legati a conoscenza mancante, implementare retrieval.
Il log dei fallimenti sarà poi riutilizzato dallo script di valutazione per confrontare il modello sintonizzato con quello di base.
Il progetto: modello di ticket‑triage QLoRA
Il modello legge un singolo messaggio cliente e restituisce un oggetto JSON con quattro chiavi: category, priority, needshuman e summary. Le categorie sono fisse (billing, bug, accountaccess, feature_request, security) e la priorità può essere low, medium, high o urgent.
Per garantire riproducibilità, si utilizza un dataset sintetico generato dallo script del passo 3, composto da 800 righe di training e 100 di test. Le etichette seguono regole deterministicamente definite:
- I ticket di sicurezza sono sempre urgent.
- I problemi di account hanno priorità high di base.
- Un crash incrementa la priorità di un livello per i bug.
- Una frase finale che indica blocco dell'utente aggiunge un ulteriore livello di priorità.
Grazie a queste regole è possibile distinguere se il modello ha realmente appreso la logica o ha semplicemente memorizzato frasi.
Struttura delle cartelle del progetto
Il layout previsto è il seguente (tutti i comandi vanno eseguiti dalla radice del progetto):
ticket-triage/
.venv/
requirements.txt
Modelfile
run_all.sh
data/
train.jsonl (prompt + completion, 800 righe)
test.jsonl (prompt + label, 100 righe)
scripts/
preflight.py
make_dataset.py
train.py
merge.py
eval.py
out/
adapter/ (pesi LoRA)
merged/ (modello bf16 completo)
triage-f16.gguf
triage-q4km.gguf
Questa organizzazione facilita il flusso continuo dalla generazione dei dati alla valutazione finale.
Metriche di successo prima di iniziare
Stabilire obiettivi chiari è fondamentale per evitare di razionalizzare risultati deludenti. Le tre metriche monitorate sui 100 test sono:
- Percentuale di risposte che possono essere parse come JSON valido con tutte e quattro le chiavi.
- Accuratezza della categoria.
- Accuratezza della priorità.
Il modello di base dovrebbe già performare bene nella classificazione delle categorie ma fallire nella formattazione rigorosa; un modello sintonizzato dovrebbe quasi raggiungere il 100 % di JSON valido e migliorare significativamente la priorità.
Pianificazione del tempo
Le stime di tempo per il flusso da 90 minuti sono:
- Preparazione ambiente e dati: ~15 minuti.
- Training (passo più lungo, dipendente dalla GPU): ~45‑60 minuti.
- Merge e export: 15‑20 minuti.
- Import in Ollama + valutazione: ~10 minuti.
Con una GPU da 24 GB il training potrebbe avvicinarsi al limite superiore di queste stime.
Prerequisiti e versioni esatte (ottobre 2026)
Per evitare incompatibilità, le seguenti versioni sono raccomandate:
| Componente | Versione consigliata | Motivo |
|---|---|---|
| OS | Ubuntu Linux o WSL 2 con driver NVIDIA aggiornati | bitsandbytes richiede kernel CUDA a 4‑bit |
| Python | 3.11 o superiore | Compatibile con le ultime release di Hugging Face |
| PyTorch | Build CUDA corrispondente al driver | Una wheel CPU‑only disattiva la GPU |
| Transformers | 5.17.0 o superiore | Target di compatibilità dichiarato da PEFT |
| PEFT | v0.21.2 | Supporta encoder‑decoder con Transformers 5.18.0+ |
| TRL | v1.16.0 | Fornisce SFTTrainer e head fuso da v1.15 |
| Ollama | v0.40.11 | Gestisce import GGUF localmente |
| llama.cpp | Ultima build master | Include convertitore e quantizzatore GGUF |
È cruciale fissare queste versioni in requirements.txt una volta che la pipeline funziona, per poi aggiornare deliberatamente in futuro.
Passi operativi: dalla preflight al training
1. Controllo preliminare (preflight.py)
Lo script verifica spazio disco (≥150 GB), presenza della GPU, compatibilità driver e versioni di Python/PyTorch. Eventuali errori interrompono il flusso prima di consumare risorse.
2. Generazione dataset (make_dataset.py)
Vengono creati 800 esempi di training e 100 di test usando template con aperture casuali, numeri di riferimento e nomi di piani. Le etichette sono calcolate con le regole sopra citate, garantendo che ogni combinazione di categoria e priorità sia rappresentata.
3. Configurazione dell’ambiente
Creare un virtual environment (python -m venv .venv), attivarlo e installare le dipendenze da requirements.txt. Assicurarsi che bitsandbytes rilevi la GPU e che torch.cuda.is_available() ritorni True.
4. Training con QLoRA (train.py)
Il training utilizza TRL.SFTTrainer con i seguenti parametri chiave:
- model_id:
Qwen3.8-27B(checkpoint open‑weight). - bits: 4 (quantizzazione a 4‑bit).
- lorar: 64, loraalpha: 16, lora_dropout: 0.05.
- batch_size: 4 (adeguato alla memoria di 24 GB).
- max_steps: 300 (circa 30 min su RTX 4090, più su GPU più piccola).
Durante il training vengono salvati i pesi LoRA in out/adapter/.
5. Fusione dei pesi (merge.py)
Lo script carica il modello base quantizzato a 4‑bit, applica gli adapter LoRA e salva una copia bf16 completa in out/merged/. Questa operazione richiede circa 10‑15 minuti e consuma la RAM GPU.
6. Esportazione GGUF (llama.cpp)
Utilizzando llama.cpp, si converte il modello bf16 in due formati GGUF:
triage-f16.gguf(precisione 16‑bit).triage-q4km.gguf(quantizzazione 4‑bit per inferenza rapida).
Il processo di quantizzazione è necessario per l’import in Ollama.
7. Import in Ollama
Con ollama push si carica il file GGUF nella libreria locale, creando un modello chiamato ticket-triage. Ollama gestisce la cache e permette di servire il modello su CPU o GPU a seconda delle risorse disponibili.
8. Valutazione (eval.py)
Lo script invia i 100 esempi di test al modello tramite Ollama, raccoglie le risposte JSON e calcola le tre metriche di successo. I risultati vengono stampati in console e salvati in out/eval_report.json per confronti futuri.
Interpretazione dei risultati
Un modello soddisfacente dovrebbe presentare:
- ≥ 98 % di risposte parsabili come JSON.
- Accuratezza categoria ≥ 95 %.
- Accuratezza priorità ≥ 90 % (sopra la baseline del modello base).
Se le metriche sono inferiori, si può iterare aggiungendo più esempi al dataset o aumentando lora_r a 128. In alternativa, verificare che le regole di generazione dei dati non siano troppo sbilanciate.
Conclusioni e prospettive future
QLoRA dimostra che il fine‑tuning di modelli di grandi dimensioni è ora alla portata di team con risorse limitate. La combinazione di quantizzazione a 4‑bit, adapter leggeri e tool aggiornati (TRL, PEFT, Ollama) permette di creare pipeline end‑to‑end in meno di due ore, mantenendo la proprietà dei dati e la flessibilità di distribuzione interna.
Per progetti più ambiziosi, si può estendere la stessa metodologia a casi d’uso come classificazione di email, estrazione di entità o generazione di risposte strutturate per chatbot. L’unica limitazione rimane la memoria della GPU: modelli più grandi richiederanno hardware più capace o tecniche di off‑loading.
