Lab 03-01: Da Hugging Face a GGUF¶
Obiettivo¶
Prendere un modello pubblicato in safetensors, convertirlo in GGUF F16 con convert_hf_to_gguf.py, controllare
che il file sia completo (metadati, tokenizer, chat template), farlo girare con llama cli e quantizzarlo in
Q8_0 e Q4_K_M. Alla fine avrai fatto a mano ciò che ggml-org e bartowski fanno per ogni modello che esce.
Prerequisiti¶
llamainstallato (modulo 01);git,python3≥ 3.10 convenv.- ~3 GB di disco (sorgenti + torch CPU ≈ 2 GB, modello ≈ 0,5 GB) e connessione internet.
- Aver letto la sezione 4 del modulo.
Materiale¶
| Cosa | Dove | Dimensione |
|---|---|---|
HuggingFaceTB/SmolLM2-135M-Instruct (Apache 2.0, architettura Llama): config.json, model.safetensors, tokenizer.json |
Hugging Face | ~270 MB (BF16) |
| Sorgenti di llama.cpp (solo per gli script Python, non li compiliamo) | git clone |
~250 MB |
| Alternativa senza installare nulla: lo space GGUF-my-repo | browser | – |
Il modello è piccolo apposta: la conversione dura meno di un minuto e il F16 pesa ~270 MB. Il procedimento è identico per un 8 B (solo con 16 GB di F16 da scrivere su disco).
Procedura¶
-
Sorgenti e ambiente Python:
git clone --depth 1 https://github.com/ggml-org/llama.cpp ~/llama.cpp cd ~/llama.cpp python3 -m venv .venv && source .venv/bin/activate python3 -m pip install -r requirements.txt # torch CPU, transformers, gguf, safetensors… python3 convert_hf_to_gguf.py --help | head -40Atteso nell'help:
--outtype {f32,f16,bf16,q8_0,tq1_0,tq2_0,auto},--outfile,--remote,--mmproj,--vocab-only,--print-supported-models. Prova anchepython3 convert_hf_to_gguf.py --print-supported-models | grep -i llama. -
Scarica il modello (o usa
--remoteal punto 3):pip install -U "huggingface_hub[cli]" hf download HuggingFaceTB/SmolLM2-135M-Instruct --local-dir ~/modelli/smollm2-135m ls -la ~/modelli/smollm2-135mAtteso:
config.json,generation_config.json,model.safetensors(~270 MB),tokenizer.json,tokenizer_config.json(contienechat_template). Apriconfig.json:"architectures": ["LlamaForCausalLM"],hidden_size 576,num_hidden_layers 30,torch_dtype bfloat16. -
Converti in F16:
python3 convert_hf_to_gguf.py ~/modelli/smollm2-135m --outtype f16 --outfile ~/modelli/smollm2-135m-f16.ggufAtteso nel log: il riconoscimento dell'architettura (
Model architecture: LlamaForCausalLM), una riga per tensore (token_embd.weight, torch.bfloat16 --> F16, shape = {576, 49152},blk.0.attn_q.weight …), il tokenizer (gguf: … chat_template) e alla fineModel successfully exported to …. Dimensione ≈ 270 MB. Variante senza download:python3 convert_hf_to_gguf.py --remote HuggingFaceTB/SmolLM2-135M-Instruct --outtype f16 --outfile ~/modelli/smollm2-135m-f16.gguf. -
Ispeziona il GGUF con gli strumenti del lab 00-02:
Controlla:
general.architecture = llama,llama.block_count = 30,tokenizer.ggml.tokenscon 49 152 voci,tokenizer.chat_templatepresente,general.file_type = 1(F16), e nell'indice tensori tipiF16per i pesi eF32per le norm. -
Eseguilo:
Atteso: banner con
ftype : F16, una risposta sensata (è un modello da 135 M: sarà breve e in inglese o in un italiano approssimativo) e la riga dei tempi. Se il modello risponde a vanvera o non si ferma, il sospetto numero uno è il chat template:llama cli -v … | grep example_format. -
Quantizza in due tipi e confronta le dimensioni:
llama quantize ~/modelli/smollm2-135m-f16.gguf ~/modelli/smollm2-135m-Q8_0.gguf Q8_0 llama quantize ~/modelli/smollm2-135m-f16.gguf ~/modelli/smollm2-135m-Q4_K_M.gguf Q4_K_M ls -la ~/modelli/smollm2-135m-*.ggufAtteso:
Q8_0≈ 145 MB,Q4_K_M≈ 100 MB (l'embedding da 49 152 × 576 resta aq6_Ke il vocabolario pesa molto su un modello così piccolo: il rapporto non sarà 16:4). Le due righe finalimodel size/quant sizedanno i bpw. -
Perplexity dei tre file sullo stesso testo, per vedere quanto costa la quantizzazione su un modello minuscolo:
for q in f16 Q8_0 Q4_K_M; do llama perplexity -m ~/modelli/smollm2-135m-$q.gguf -f ../lab-03-02-scala-quantizzazioni/testo-it.txt -c 128 -t 4 2>&1 | grep Final; doneAtteso: F16 ≈ Q8_0 < Q4_K_M, con uno scarto in proporzione più grande di quello che vedrai sul modello da 0,8 B nel lab 03-02: più il modello è piccolo, più ogni bit conta.
Verifica del risultato¶
-
convert_hf_to_gguf.pyha prodotto un file congeneral.architecture = llamae il chat template. -
llama cli -mlo carica e risponde; il banner diceF16. - Hai tre file (F16, Q8_0, Q4_K_M) e sai spiegare perché il Q4_K_M non è un quarto del F16.
- Hai tre perplexity in ordine crescente.
Domande e varianti¶
- Perché la documentazione consiglia
--outtype auto(obf16) invece dif16per i modelli addestrati in BF16? (Pensa all'intervallo di valori rappresentabili.) - Cosa contiene
tokenizer_config.jsonche finisce nel GGUF? Egeneration_config.json, che invece non ci finisce? - Variante: converti un modello con
--vocab-onlye guarda quanto pesa: è un GGUF con solo il tokenizer. - Variante Docker:
docker run -v ~/modelli:/models ghcr.io/ggml-org/llama.cpp:full --all-in-one /models/ …come dadocs/docker.md, oppure entra nell'immagine (--entrypoint bash) e usapython3 /app/convert_hf_to_gguf.py. - Variante nel browser: carica lo stesso repo in GGUF-my-repo, scegli
Q4_K_M, e confronta il file prodotto con il tuo (gguf-dump: stessa build? stesso tipo per l'embedding?). - Per chi ha un modello multimodale (Gemma 3 4B, Qwen3.5-VL): aggiungi il passaggio
--mmproj --outtype q8_0e provallama cli -m … --mmproj … --image foto.jpg.
Consegna¶
lab-03-01.md con: le prime 20 righe del log di conversione; l'output di gguf-dump filtrato sulle chiavi
general.*, llama.block_count, tokenizer.chat_template (solo l'inizio); la risposta di llama cli; la tabella
file → dimensione → bpw → perplexity; le risposte alle prime due domande.