Vai al contenuto
Illustrazione 3D di un utente al PC con blocchi di dati luminosi che rappresentano l'elaborazione locale.

Immagine generata con IA

Il fine-tuning di un modello linguistico parte da un modello già addestrato e lo adatta ai propri dati: uno stile di risposta, un argomento, un formato. Con LoRA e QLoRA il modello non si riaddestra da capo. I pesi originali restano congelati e si addestra solo un piccolo adattatore che vi si aggiunge, per cui basta una GPU di casa.

It doesn't fit. Train it anyway: an 8B model on a 4 GB cardIl video si carica da YouTube quando premi play

Questa guida usa Soup (pacchetto soup-cli), uno strumento open source con licenza Apache-2.0 che governa l’addestramento con un file YAML e pochi comandi. Si parte dall’installazione e si arriva a un file GGUF da usare in Ollama o in LM Studio. I comandi sono quelli della documentazione ufficiale, elencata in fondo.

Cosa serve

I requisiti vengono dalla sezione Requirements del README di Soup:

  • Python 3.10, 3.11 o 3.12. Sono le versioni provate dalla CI del progetto. Python 3.13 e successivi non sono ancora supportati, perché lo stack di PyTorch non è stato validato su quelle versioni.
  • Una GPU con CUDA, cioè NVIDIA, che è la scelta consigliata. Soup gira anche su Apple Silicon (MPS). La CPU è sperimentale e molto lenta e va bene solo per le prove. In quel caso la quantizzazione si disattiva da sola.
  • Almeno 8 GB di VRAM per addestrare con QLoRA un modello da 7 miliardi di parametri.
  • Un compilatore C++ e CMake, che servono più avanti per l’esportazione GGUF quantizzata.

La tabella ufficiale indica la taglia massima di modello per ogni quantità di VRAM, con QLoRA a 4 bit:

VRAMModello massimo (QLoRA 4 bit)Esempi
8 GB~7BLlama-3.1-8B, Mistral-7B
16 GB~14BPhi-4-14B, Qwen2.5-14B
24 GB~34BCodeLlama-34B, Yi-1.5-34B
48 GB~70BLlama-3.3-70B
80 GB e oltre70B+ (completo) o MoEMixtral-8x22B, DeepSeek-V3

Il progetto parla anche di un modello da 8B addestrato su una GPU da portatile con 4 GB. Quel risultato richiede il layer streaming, una funzione opzionale ancora in BETA. Le misure sono state fatte sulla v0.72.2, prima della correzione arrivata con la v0.73.0, e il README dice che vanno ripetute (issue #361). Questa guida segue il percorso standard e il layer streaming non lo usa.

Creare un ambiente virtuale e installare Soup

Per l’installazione più pulita il README indica pipx o uv tool, che danno a Soup un ambiente tutto suo. pip è la strada per chi lavora già dentro un ambiente virtuale, un notebook Colab o un’immagine Docker. Qui si usa pip, quindi l’ambiente va creato prima.

Su Debian 12, Ubuntu 23.04 e versioni successive pip non può scrivere nel Python di sistema (regola PEP 668) e si ferma con l’errore externally-managed-environment. Il README lo risolve con python3 -m venv .venv && source .venv/bin/activate, da lanciare nella cartella di lavoro su Linux o macOS. La prima parte crea l’ambiente nella sottocartella .venv, la seconda lo attiva. L’attivazione dura quanto il terminale aperto: in ogni nuova sessione va rifatta dalla stessa cartella. Il python3 usato deve essere una delle versioni supportate.

Con l’ambiente attivo si installa Soup. Il pacchetto base soup-cli contiene solo la CLI leggera, con gli strumenti per la configurazione e i dati. Per addestrare serve l’extra [train], che aggiunge PyTorch, transformers, peft, trl e le altre librerie.

pip install "soup-cli[train]"

Le virgolette devono essere doppie, perché è l’unica forma che funziona in tutte le shell (bash, zsh, PowerShell e cmd.exe). Se l’installazione si chiude senza errori, nel terminale c’è il comando soup.

Controllare GPU e dipendenze con soup doctor

Prima di scaricare un modello conviene controllare che Soup veda la GPU e che le librerie siano a posto. soup doctor riunisce in un solo report la GPU, le risorse del sistema, le dipendenze e la versione di Soup.

soup doctor

Se usate una GPU NVIDIA, verificate che Soup possa utilizzarla tramite CUDA. Su Apple Silicon il dispositivo previsto è MPS; la CPU è supportata per prove sperimentali, molto lente e senza quantizzazione. Il Command reference spiega che il comando esce con codice 1 se manca una dipendenza essenziale o se un pacchetto installato supera la versione massima prevista. In quel caso l’ambiente va sistemato prima di proseguire.

Creare la configurazione dal template chat

Soup legge tutto da un file soup.yaml: modello di partenza, dataset, parametri di addestramento e cartella di output. Il template chat è il punto di partenza per un modello conversazionale.

Schermata della Web UI di Soup con l'editor YAML per configurare i parametri del fine-tuning LoRA.
L’interfaccia Web UI di Soup per la creazione della configurazione di fine-tuning via YAML. — Immagine: GitHub
soup init --template chat

Alla fine nella cartella di lavoro c’è il file soup.yaml. Nell’esempio completo del README le voci principali sono queste:

  • base: il modello di partenza su Hugging Face, per esempio meta-llama/Llama-3.1-8B-Instruct. Per usarlo dovete prima richiedere e ottenere l’accesso dalla sua pagina, accettando la Llama 3.1 Community License e le condizioni d’uso, quindi autenticarvi nel terminale con hf auth login usando lo stesso account. La licenza Apache-2.0 di Soup non si estende al modello;
  • task: sft: l’addestramento supervisionato;
  • training: epoche, learning rate, batch_size: auto, i parametri di lora (r e alpha) e quantization: 4bit, cioè QLoRA;
  • output: ./output: la cartella dove finisce l’adattatore.

Dalla v0.75 Soup rifiuta le chiavi che non riconosce. Basta un errore di battitura come quantizaton e la configurazione non si carica. Il messaggio d’errore indica il nome del campo che probabilmente si voleva scrivere.

Preparare il dataset

Soup riconosce da solo diversi formati di dati, tra cui Alpaca, ShareGPT e ChatML, in file JSONL, JSON, CSV, Parquet o TXT. Nella maggior parte dei casi basta indicare il file. Per un modello chat la cosa più semplice è un file JSONL, con un esempio per riga, in formato alpaca o sharegpt.

Il README non riporta i campi di ogni formato. Lo schema, con un esempio svolto per ciascun formato, si trova nella pagina docs/data.md del repository di Soup, a cui il README rimanda. In alternativa il Command reference elenca soup data demo alpaca_demo --output ./d.jsonl, che copia nella cartella corrente un file JSONL d’esempio in formato alpaca. Aprite quel file e usatelo come modello per le vostre righe, con la stessa struttura.

Salvate i vostri esempi in un file JSONL, per esempio ./data/train.jsonl come nella configurazione ufficiale. Poi controllatene il formato con soup data validate <percorso>, anche questo nel Command reference: al posto di <percorso> va il percorso del vostro file, cioè ./data/train.jsonl se avete seguito l’esempio. Il formato viene riconosciuto in automatico.

Nel soup.yaml il dataset si configura nel blocco data:

  • train: il percorso del file con i vostri dati, per esempio ./data/train.jsonl (non il file d’esempio ./d.jsonl, a meno che non vogliate fare solo una prova);
  • format: il formato, per esempio alpaca;
  • val_split: facoltativo, è la quota di esempi tenuta da parte per la validazione. Nell’esempio ufficiale vale 0.1, cioè il 10%.

Avviare il fine-tuning LoRA/QLoRA

Con configurazione e dataset pronti si avvia l’addestramento. Quantizzazione a 4 bit, adattatore LoRA e dimensione del batch li gestisce Soup sulla base del file di configurazione.

soup train --config soup.yaml

Quando l’addestramento finisce, l’adattatore sta nella cartella indicata da output, cioè ./output con la configurazione d’esempio. Dalla v0.75 Soup registra e mostra anche la loss di validazione, che prima veniva calcolata e poi scartata.

Provare il modello in chat

Prima di esportare il modello conviene provarlo. soup chat apre nel terminale una chat interattiva con il modello appena addestrato.

soup chat --model ./output

Fate domande simili a quelle del dataset e confrontate le risposte con quelle che vi aspettate. La documentazione non dice come si chiude la sessione.

Unire l’adattatore al modello di base

L’unione (merge) produce un modello unico e indipendente, con i pesi LoRA fusi in quelli del modello di base. Soup ricava il modello di base dal file adapter_config.json dell’adattatore. Il passaggio è facoltativo: come spiega la pagina Serving & export, quando si esporta in GGUF un adattatore il merge avviene da sé.

soup merge --adapter ./output --output ./merged

Il modello unito finisce nella cartella ./merged.

Compilare llama.cpp ed esportare in GGUF

GGUF è il formato di file che usano Ollama, llama.cpp e LM Studio. Per la conversione Soup si appoggia a llama.cpp. Al primo utilizzo lo clona da internet (tag b5270) nella cartella ~/.soup/llama.cpp della vostra home, ma non lo compila. La conversione in f16 o f32 funziona con il solo script Python. Le versioni quantizzate richiedono invece il programma llama-quantize, da compilare una volta.

Se la cartella ~/.soup/llama.cpp non esiste ancora, lanciate prima dalla cartella del progetto l’esportazione non quantizzata soup export --model ./output --format gguf --quant f16, che compare nella pagina Serving & export. Non ha bisogno di llama-quantize e al primo utilizzo fa scaricare llama.cpp. Il risultato è un GGUF in f16, non quantizzato.

A quel punto si compila. Su Linux e macOS bastano un compilatore C++ e CMake. Su Windows il progetto ha validato Visual Studio 2022 Build Tools con il carico di lavoro «Desktop development with C++» e CMake 3.14 o successivo. La compilazione indicata è solo per CPU: le build CUDA di llama.cpp il progetto non le ha provate.

cd ~/.soup/llama.cpp
cmake -B build -DGGML_NATIVE=OFF -DLLAMA_CURL=OFF -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release --target llama-quantize -j 4

Soup cerca il programma compilato in build/bin/llama-quantize, in build/bin/Release/llama-quantize.exe con MSVC e nel PATH. Non installate con pip il file requirements.txt di llama.cpp: fissa torch a una versione solo CPU, che sostituisce quella CUDA e rompe l’addestramento. Le poche dipendenze dello script di conversione le installa già Soup.

Tornate nella cartella del progetto, quella con soup.yaml e ./output, ed esportate in versione quantizzata. L’esempio ufficiale usa q4_k_m. Le altre quantizzazioni supportate sono q4_0, q5_k_m, q8_0, f16 e f32.

soup export --model ./output --format gguf --quant q4_k_m

Il risultato è un file con estensione .gguf. La pagina non dice esattamente dove viene salvato, ma nei suoi esempi il file quantizzato compare come ./output/model.q4_k_m.gguf. Prendete nota del percorso completo, perché serve nei prossimi passaggi.

Caricare il GGUF in Ollama

Secondo la documentazione di Ollama sull’importazione, per un GGUF composto da un solo file si crea un file di testo chiamato Modelfile che contiene questa riga:

FROM /path/to/file.gguf

Al posto di /path/to/file.gguf scrivete il percorso completo del file .gguf prodotto da soup export. Ollama non quantizza i GGUF durante l’importazione, ma il vostro è già stato quantizzato in q4_k_m.

Dalla cartella che contiene il Modelfile si crea il modello. my-model è un nome d’esempio, quello con cui il modello comparirà in Ollama. Potete sceglierne un altro, purché sia lo stesso in ollama create e in ollama run.

ollama create my-model

Poi lo si avvia per provarlo:

ollama run my-model

Usare il GGUF in LM Studio e LM Studio Bionic

In LM Studio basta mettere il file nella struttura di cartelle che l’app si aspetta. Dentro ~/.lmstudio/models/ si crea una cartella per l’editore, dentro questa una per il modello, e lì si mette il file GGUF. Per esempio ~/.lmstudio/models/io/mio-modello/mio-modello-q4_k_m.gguf, con nomi di cartella scelti da voi.

Menu a tendina di LM Studio per selezionare ed eseguire il modello GGUF desiderato.
Selezione del modello di intelligenza artificiale all’interno dell’interfaccia di LM Studio. — Immagine: LM Studio

Per LM Studio Bionic la documentazione dice di scegliere nel selettore dei modelli un modello Local, che deve essere già scaricato e adatto all’hardware disponibile. Non descrive però una procedura per importare un GGUF creato in proprio, quindi qui non ne riportiamo una.

Se qualcosa va storto

  • error: externally-managed-environment durante pip install: è la regola PEP 668 di Debian 12, Ubuntu 23.04 e successive. Create e attivate l’ambiente virtuale come nella sezione sull’installazione e ripetete pip install, oppure usate pipx o uv tool.
  • pip rifiuta il nome del pacchetto: probabilmente avete usato le virgolette singole ('soup-cli[train]'). Servono quelle doppie.
  • Crash nativi di PyTorch prima ancora che Soup parta: controllate la versione di Python. Con la 3.13 e successive pip sceglieva pacchetti di PyTorch non testati, che andavano in crash. Usate la 3.10, la 3.11 o la 3.12.
  • La configurazione non si carica: dalla v0.75 una chiave sconosciuta o scritta male blocca il caricamento, con codice di uscita 1. Correggete o eliminate la chiave segnalata.

Restano fuori da questa guida Docker, l’addestramento su più GPU, DeepSpeed, il backend MLX, i metodi DPO, GRPO e RLHF, la Web UI, autopilot e la pubblicazione su Hugging Face: sono tutti descritti nella documentazione del progetto. Per disinstallare Soup non c’è un comando documentato.

Documentazione di riferimento

Pagine consultate il 6 ottobre 2026. La guida si basa sulla documentazione della v0.75.

Leggi anche