Che i pesi stiano in VRAM non significa che il sistema funzioni
L'errore più comune nella scelta di un modello locale è confrontare la dimensione del file quantizzato con la memoria della GPU e dare la decisione per risolta. Quel calcolo copre solo, in modo approssimativo, i pesi. Durante l'inferenza intervengono inoltre la cache delle chiavi e dei valori —cache KV—, le attivazioni e i buffer temporanei, lo spazio riservato dal runtime, il contesto di ogni richiesta e, in un servizio, le richieste simultanee. Una configurazione può caricare il modello, rispondere a una domanda breve e comunque fallire quando riceve un documento lungo o più richieste contemporaneamente.
La conseguenza pratica è importante: «sta» deve significare che completa il carico massimo previsto con un margine misurato, non che avvia una sessione isolata. Se il margine scompare, il risultato può essere un errore di memoria, una riduzione automatica del contesto, il trasferimento di parte del lavoro alla RAM di sistema o una latenza molto irregolare. Quale di questi comportamenti si verifichi dipende dal runtime e dalla sua configurazione; non deve essere supposto senza verificarlo.
La quantizzazione è una delle diverse leve. Ridurre i bit dei pesi di solito libera memoria e può consentire l'uso di un modello più grande, ma non elimina da sola il costo crescente della cache KV quando si amplia il contesto o la concorrenza. Può anche modificare la qualità, le prestazioni o i percorsi di esecuzione disponibili a seconda del formato e del backend. Per questo non esiste un'equivalenza universale tra «4 bit», «6 bit» e «8 bit».
Questa guida parte da un carico di lavoro specifico: quale lunghezza di input deve essere accettata, quanti token vengono generati, quante richieste coesisteranno, quale latenza è utile e quali errori sarebbero inaccettabili. Se si sta ancora scegliendo la famiglia di modelli, è consigliabile rivedere prima la guida sui modelli locali e, se il dubbio è tra modelli base diversi, usare la sezione di confronto prima di attribuire alla quantizzazione differenze che derivano dal modello.
Regola di decisione
Non adottare una quantizzazione perché il modello si carica. Adottarla solo se mantiene il contesto, la concorrenza e la qualità obiettivo sotto un carico ripetibile, con memoria libera sufficiente per assorbire la variazione del runtime.
Cosa significa quantizzare e quali parti di un modello si quantizzano
Quantizzare significa rappresentare valori reali con un piccolo insieme discreto di numeri per usare meno bit e operare su di essi più velocemente, a scapito dell'introduzione di un errore di arrotondamento [1]. Nella quantizzazione lineare a 8 bit, la documentazione di ONNX Runtime la esprime come valore_fp32 = scala × (valore_quantizzato − punto_zero): la scala è un numero reale positivo e il punto zero è il valore quantizzato che rappresenta esattamente lo zero. Nella variante simmetrica la scala viene calcolata con il valore assoluto massimo; in quella asimmetrica, con l'intervallo tra minimo e massimo [3]. Il lavoro di Wu et al. di NVIDIA esamina come scegliere quei parametri e propone un flusso a 8 bit che ha mantenuto la precisione a meno dell'1% rispetto al riferimento in virgola mobile in tutte le reti studiate [2].
In un LLM non si quantizza una sola cosa, ma tre, e ciascuna si comporta in modo diverso. I pesi sono fissi, possono essere convertiti una volta e occupano la maggior parte della memoria al caricamento. Le attivazioni cambiano con ogni input e sono più difficili da quantizzare perché contengono valori atipici molto grandi. La cache KV cresce con il contesto e la concorrenza. La notazione WxAy riassume la combinazione: W4A16 significa pesi a 4 bit con attivazioni a 16 bit, e W8A8 significa pesi e attivazioni a 8 bit; vLLM, ad esempio, documenta FP8 W8A8, INT8 W8A8 e INT4 W4A16 come schemi distinti [27].
Quantizzare solo i pesi riduce la memoria e, poiché la generazione token per token è limitata dalla lettura della memoria e non dal calcolo, può anche ridurre la latenza. GPTQ e AWQ lo documentano: il primo attribuisce le sue accelerazioni a un minore movimento di dati, non all'operare con interi [5], e il secondo segnala che, in FP16, la generazione ha un'intensità aritmetica di circa 1 operazione per byte letto [6]. Quantizzare anche le attivazioni consente di usare unità di calcolo a interi o FP8, ma obbliga a gestire i valori atipici: in SmoothQuant, alcune attivazioni sono circa 100 volte maggiori della maggior parte dei valori [7], e in LLM.int8() annullare le poche dimensioni atipiche peggiora la perplejità tra il 600% e il 1000% [4].
La granularità conta tanto quanto il numero di bit. Un singolo fattore di scala per tensore è economico, ma un solo valore estremo rovina la risoluzione di tutti gli altri. Per questo i formati pratici usano una scala per canale o per gruppo di pesi, ad esempio ogni 128 [5][6]. Dettmers e Zettlemoyer hanno scoperto che ridurre la dimensione del blocco, ovvero dividere i parametri in blocchi che vengono quantizzati separatamente, è uno dei pochi miglioramenti chiari nell'equilibrio tra bit e precisione [9]. La conseguenza è che «4 bit» non sono mai esattamente 4 bit: Q4_K memorizza 4,5 bit per peso e Q6_K, 6,5625 [22], e il Q4_K_M misurato da llama.cpp in Llama 3.1 8B arriva a 4,8944 bit per peso [21].
Le voci di memoria da separare
Una stima utile inizia scomponendo la memoria in voci che possano essere osservate separatamente. La prima sono i pesi del modello. La loro dimensione dipende dal numero di parametri, dalla rappresentazione quantizzata e dai metadati propri del formato, come scale, blocchi o strutture ausiliarie. Pertanto, dividere semplicemente i parametri per otto, sei o quattro offre un'indicazione, ma non sostituisce la dimensione reale informata dal formato e dal runtime.
La seconda voce è la cache KV. In un decoder autoregressivo, il sistema conserva chiavi e valori dei token già elaborati per non ricalcolarli a ogni token generato. La documentazione di Transformers descrive tensori di cache con dimensioni di batch, teste, lunghezza della sequenza e dimensione della testa. Ci sono chiavi e valori, e questo archiviazione si ripete per layer. A parità di architettura, aumentare il contesto, il batch o la concorrenza aumenta la memoria necessaria.
La terza voce riunisce attivazioni e buffer temporanei. La loro dimensione dipende dal backend, dalla precisione di calcolo, dai kernel, dal prefill di input lunghi, dalla lunghezza di generazione e da come vengano raggruppate le richieste. Non è consigliabile sostituirla con una costante universale. Il quarto componente è la memoria non attribuita direttamente al modello, come contesto di esecuzione, librerie, allocatori e frammentazione. Il quinto è un margine operativo esplicito: memoria che non viene assegnata deliberatamente per tollerare picchi, differenze tra misurazioni e carico reale.
In un server, batch e concorrenza meritano una precisione aggiuntiva. Un batch può essere il numero di sequenze elaborate insieme in un passo, mentre la concorrenza è il numero di richieste vive. Secondo lo scheduler, entrambi i numeri possono essere correlati ma non sono intercambiabili. Per stimare la cache deve essere conteggiata la somma dei token vivi delle sequenze che coesistono, non solo la lunghezza massima di una richiesta.
Voci da registrare prima di decidere
| Voce | Cosa la determina | Come verificarla |
|---|---|---|
| Pesi | Modello base, formato e quantizzazione | Memoria dopo il caricamento del modello o report del runtime |
| Cache KV | Layer, teste KV, dimensione della testa, token vivi, dtype | Capacità o uso della cache e lunghezza effettiva servita |
| Attivazioni e temporanei | Prefill, generazione, batch, kernel e backend | Picco di memoria durante il carico rappresentativo |
| Memoria del runtime | Librerie, allocatore, contesto del dispositivo e frammentazione | Memoria prima e dopo l'avvio del processo |
| Margine | Variabilità e carico massimo previsto | Memoria libera minima osservata in test ripetuti |
Stimare pesi e cache KV prima di scaricare o distribuire
La stima non pretende di prevedere ogni byte: pretende di scartare configurazioni non fattibili e definire quali test meritino di essere eseguiti. Per i pesi, usare la dimensione informata per l'artefatto specifico che si intende caricare, non un numero generico per la famiglia del modello. Se si conosce solo il numero di parametri, trattare il risultato come un minimo teorico incompleto. I formati di quantizzazione memorizzano informazioni aggiuntive e alcuni runtime convertono o duplicano strutture durante il caricamento.
Per un'architettura tipo Llama, un'approssimazione concettuale della cache KV per sequenza è: layer moltiplicati per due, moltiplicati per teste di chiave-valore, moltiplicati per lunghezza della sequenza, moltiplicati per dimensione della testa e per byte di ogni elemento di cache. Il fattore due rappresenta K e V. Per più sequenze simultanee, si sommano i token residenti di ciascuna. Se il runtime usa una cache statica, può riservare capacità fino a un massimo anche quando l'uso istantaneo è inferiore; se usa una cache dinamica, l'uso può crescere con la richiesta. Entrambe le strategie richiedono misurazione.
È cruciale impiegare il numero di teste di chiave-valore, non sempre il numero totale di teste di attenzione. Nell'attenzione multi-query o grouped-query, più teste di query condividono proiezioni KV. Questo può ridurre in modo sostanziale la cache rispetto a un'architettura con una proiezione KV per testa di attenzione. La configurazione esatta del modello deve fornire layer, teste KV e dimensione della testa; non devono essere dedotte da un nome commerciale.
I byte per elemento della cache devono anche essere verificati. Quantizzare i pesi non implica automaticamente quantizzare la cache KV. Alcuni ambienti consentono di scegliere un tipo di dati di cache quantizzato, altri usano di default una precisione diversa, e altri applicano offloading. Queste opzioni cambiano il budget di memoria e possono influenzare le prestazioni o il comportamento numerico. Annotare la configurazione reale del runtime, non solo i bit che appaiono nel nome del file.
Esempio concettuale di calcolo della cache
Si supponga un modello con 32 layer, 8 teste KV, dimensione della testa 128 e cache di 2 byte per elemento. Per una sequenza di 8.192 token: 32 × 2 × 8 × 8.192 × 128 × 2 byte, circa 1 GiB di cache KV. Due sequenze simultanee con quella lunghezza raddoppierebbero approssimativamente quella voce. È una stima della cache, non di tutta la VRAM: mancano pesi, temporanei, memoria del runtime e margine. Se la cache viene salvata con un altro tipo di dati o preassegnata, il risultato effettivo cambia.
Quantizzare anche la cache KV: quando conviene
La formula precedente dà la cache KV con 2 byte per elemento. Per vedere l'ordine di grandezza con architetture reali, la tabella seguente applica la stessa formula ai parametri pubblicati nei file di configurazione di ciascun modello [30]. La differenza tra Llama 2 7B (32 teste KV) e Llama 3 8B (8 teste KV) è un fattore 4 per token, che corrisponde all'attenzione per gruppi di query (GQA): più teste di query condividono ogni testa di chiave e valore [20].
Cache KV per sequenza in 16 bit, calcolata con la formula di questo articolo
| Modello | Layer / teste KV / dimensione | KV per token | Contesto | Cache per sequenza |
|---|---|---|---|---|
| Llama 2 7B | 32 / 32 / 128 | 512 KiB | 4.096 token | 2 GiB |
| Llama 3 8B | 32 / 8 / 128 | 128 KiB | 8.192 token | 1 GiB |
| Llama 3.1 8B | 32 / 8 / 128 | 128 KiB | 131.072 token | 16 GiB |
| Llama 3.1 70B | 80 / 8 / 128 | 320 KiB | 131.072 token | 40 GiB |
Un dettaglio della tabella è importante per il dimensionamento: con 131.072 token di contesto, la cache di una singola sequenza di Llama 3.1 8B occupa più dei pesi dello stesso modello in Q4_K_M (4,58 GiB), in modo che abbassare i bit dei pesi non risolve il contesto lungo. In Llama 3.1 70B, 40 GiB di cache per sequenza equivalgono a poco più della metà di una GPU da 80 GB.
Quantizzare la cache attacca quel termine. KIVI propone una quantizzazione asimmetrica a 2 bit senza fine-tuning: le chiavi vengono quantizzate per canale e i valori per token, perché le loro distribuzioni sono diverse. I suoi autori informano di un uso massimo della memoria 2,6 volte inferiore, inclusi i pesi, batch fino a 4 volte maggiori e un rendimento tra 2,35 e 3,47 volte in carichi reali [17]. KVQuant combina quantizzazione per canale delle chiavi, quantizzazione prima della posizione rotatoria e valori atipici isolati per vettore; con 3 bit informa di una degradazione della perplejità inferiore a 0,1 in Wikitext-2 e C4, e di come LLaMA-7B possa servire un contesto di 1 milione di token su una singola A100 da 80 GB [18]. Sono risultati degli autori, con i loro propri kernel e modelli: servono per sapere cosa è possibile, non per promettere ciò che otterrà nel suo runtime.
Gli strumenti lo espongono già. Transformers include una cache quantizzata con i backend hqq (da 1 a 8 bit) e quanto (2 e 4 bit), e avverte che può peggiorare la latenza quando il contesto è corto e la GPU ha memoria in eccesso [25]. TensorRT-LLM consente una cache KV in FP8 e, con quantizzazione preliminare, in NVFP4 [28], e vLLM elenca la cache KV quantizzata tra le sue opzioni [27]. Se il suo problema è la frammentazione e lo spreco della cache, e non la dimensione di ogni elemento, l'approccio è diverso: PagedAttention gestisce la cache KV in blocchi per sfruttare meglio la memoria in servizio [19]. In ogni caso, la regola dell'articolo si mantiene: verificare separatamente se la quantizzazione influisce sui pesi, sulla cache KV o su entrambi, e misurare la qualità con contesto lungo.
Metodi di quantizzazione: dall'arrotondamento al più vicino a GPTQ, AWQ e GGUF
Ci sono due famiglie. La quantizzazione post-addestramento (PTQ) converte un modello già addestrato, con poco o nessun riaddestramento e, a volte, con un piccolo set di calibrazione. La quantizzazione consapevole dell'addestramento (QAT) riaddestra o distilla il modello simulando la quantizzazione. ONNX Runtime raccomanda di ricorrere a QAT quando nessuna PTQ raggiunge la precisione desiderata [3]. Per LLM, LLM-QAT osserva che i metodi PTQ funzionano bene fino a 8 bit e si rompono con meno, e propone una distillazione senza dati con generazioni del modello stesso [31]. Per chi esegue modelli di terze parti in locale, la decisione pratica è quasi sempre scegliere tra artefatti già quantizzati con PTQ.
La linea di base è l'arrotondamento al valore più vicino (RTN). Gli altri metodi cercano di ridurre l'errore che RTN commette nei pochi pesi o attivazioni che contano di più. La tabella riassume i più citati e quale dato verificabile fornisce la loro fonte.
Metodi di quantizzazione di LLM e cosa documenta ogni fonte
| Metodo | Cosa quantizza | Idea centrale | Dato della fonte |
|---|---|---|---|
| LLM.int8() [4] | Pesi e attivazioni a 8 bit | Moltiplicazione con scale per vettore e decomposizione mista: le dimensioni atipiche (circa lo 0,1%) vengono mantenute a 16 bit | Gli atipici appaiono in modo sistematico da 6.700 milioni di parametri; la memoria scende vicino al 50% |
| GPTQ [5] | Solo pesi, 3 o 4 bit | Quantizzazione layer per layer con informazioni di secondo ordine, senza riaddestramento | Quantizza modelli da 175.000 milioni di parametri in circa 4 ore di GPU; calibra con 128 segmenti di 2.048 token di C4 |
| AWQ [6] | Solo pesi, 4 bit | Protegge tra lo 0,1% e l'1% dei canali in uscita, scelti per la magnitudine delle attivazioni, mediante scalatura per canale, senza backpropagation | Usa un set di calibrazione molto più piccolo di GPTQ nel suo confronto: 16 contro 192 sequenze |
| SmoothQuant [7] | Pesi e attivazioni a 8 bit (W8A8) | Trasferisce la difficoltà delle attivazioni ai pesi con un fattore per canale; α = 0,5 è il valore abituale | Fino a 1,56 volte più veloce e circa la metà della memoria |
| QLoRA e NF4 [8] | Pesi a 4 bit per fine-tuning | Tipo NormalFloat a 4 bit, doppia quantizzazione e ottimizzatori paginati | Fine-tuning di un modello da 65.000 milioni di parametri su una singola GPU da 48 GB |
| k-quants di GGUF [21][22] | Pesi, per superblocchi | Superblocchi formati da blocchi di 16 o 32 pesi, ciascuno con la sua scala (da 4 a 8 bit a seconda del tipo) | Q4_K occupa 4,5 bit per peso e Q6_K, 6,5625 [22] |
| FP8 e formati MX [13][14] | Pesi e attivazioni | Virgola mobile a 8 bit (E4M3 e E5M2) e formati con un fattore di scala per blocco | FP8 propone un formato di scambio a 8 bit per addestrare e inferire; MX combina scala per blocco con tipi di elemento stretti |
| AQLM e BitNet b1.58 [15][16] | Pesi a 1–3 bit | AQLM: quantizzazione additiva con più libri di codici; BitNet b1.58: pesi ternari {−1, 0, 1} addestrati così fin dall'inizio | BitNet b1.58 non è una conversione di un modello già addestrato, ma una ricetta di addestramento |
Tre lezioni si ripetono in questi lavori. La prima è che i pesi sono la parte facile e le attivazioni atipiche, quella difficile: LLM.int8() e SmoothQuant partono da quel stesso problema e lo risolvono in modi opposti, isolando gli atipici o distribuendo la loro magnitudine con i pesi [4][7]. La seconda è che proteggere una frazione minuscola di canali basta per recuperare gran parte della qualità a 4 bit [6]. La terza è che il miglioramento della precisione costa spesso complessità: meno bit si usano, più il metodo ha bisogno di calibrazione, kernel specializzati o entrambi, e per questo il formato deve essere compatibile con il suo motore, come dettagliato più avanti.
Cosa cambia passando da 8 a 6 o 4 bit
Come regola generale, ridurre la precisione dei pesi riduce la loro impronta di memoria rispetto a una rappresentazione di maggiore precisione dello stesso modello. Questo può rendere fattibile una GPU più piccola, lasciare più budget per il contesto o consentire più richieste concorrenti. Tuttavia, la riduzione osservata non deve seguire una proporzione esatta da otto a sei a quattro. L'imballaggio, le scale per gruppo, il formato del file, le conversioni interne e i buffer del backend cambiano il risultato.
La qualità non dipende nemmeno solo dal numero di bit. Contano l'algoritmo di quantizzazione, la dimensione del gruppo, quali tensori ricevono un trattamento speciale, il modello base e il task. Una quantizzazione a 4 bit di un metodo può conservare bene un task e un'altra a 6 bit di un altro metodo può non farlo; il senso inverso è anche possibile. Per questo motivo, i bit servono per formulare ipotesi di test, non per certificare la precisione.
La velocità richiede la stessa cautela. Meno memoria può ridurre i trasferimenti e migliorare la fattibilità su un dispositivo limitato, ma un formato può mancare di kernel efficienti in un backend specifico o richiedere conversioni. Aumentare il contesto può spostare il collo di bottiglia verso la gestione della cache e il prefill. Le prestazioni devono essere misurate con due metriche separate: tempo fino al primo token per input rappresentativi e tasso di generazione successivo. Un singolo numero di token al secondo nasconde differenze rilevanti.
Come punto di partenza, provare 8 bit quando la qualità è critica e il budget lo consente; 6 bit quando è necessario recuperare una parte significativa della memoria senza saltare direttamente all'opzione più aggressiva; e 4 bit quando la VRAM è la restrizione dominante o quando i test dimostrano che non c'è perdita inaccettabile. Queste sono priorità di prova, non raccomandazioni universali.
Quale risparmio e quale perdita sono stati misurati
Le cifre seguenti sono misurazioni pubblicate, non stime di questo articolo. Iniziano dalla dimensione: la tabella riproduce la documentazione di llama.cpp per Llama 3.1 8B. La colonna di generazione proviene dallo stesso README, che non indica l'hardware di quei test, in modo che serve solo per confrontare le righe tra loro.
Llama 3.1 8B in GGUF: dimensione e velocità misurate da llama.cpp [21]
| Formato | Bit per peso | Dimensione | Rispetto a F16 | Generazione (token/s) |
|---|---|---|---|---|
| IQ1_S | 2,0042 | 1,87 GiB | 12,5 % | 79,73 |
| Q2_K | 3,1593 | 2,95 GiB | 19,7 % | 79,85 |
| Q3_K_M | 3,9960 | 3,74 GiB | 25,0 % | 71,68 |
| Q4_K_M | 4,8944 | 4,58 GiB | 30,6 % | 71,93 |
| Q5_K_M | 5,7036 | 5,33 GiB | 35,6 % | 67,23 |
| Q6_K | 6,5633 | 6,14 GiB | 41,0 % | 58,67 |
| Q8_0 | 8,5008 | 7,95 GiB | 53,1 % | 50,93 |
| F16 | 16,0005 | 14,96 GiB | 100 % | 29,17 |
Due osservazioni. La prima è che Q8_0 occupa poco più della metà di F16 e Q4_K_M, meno di un terzo: il risparmio di memoria segue i bit per peso. La seconda è che la velocità non segue i bit: IQ1_S e Q2_K generano quasi alla stessa velocità (79,73 e 79,85 token/s) nonostante Q2_K usi 1,2 bit in più per peso, e Q3_K_M genera a una velocità quasi identica a Q4_K_M (71,68 e 71,93). Tuttavia, tutti i quantizzati superano chiaramente F16, che genera 29,17 token/s in quello stesso test. Lo studio di Jin et al. lo riassume così: la quantizzazione risparmia memoria, ma può anche rallentare l'inferenza, e sfruttarla richiede lavoro di ingegneria e supporto hardware [12].
Un secondo insieme di misurazioni, fatte con Transformers su un modello di circa 15.500 milioni di parametri, mostra la memoria massima reale rispetto alla regola di due byte per parametro.
Memoria massima di GPU di bigcode/octocoder in Transformers [26]
| Precisione | Picco di memoria | Nota |
|---|---|---|
| bfloat16 | 29,03 GiB | Vicino a 2 byte per parametro |
| 8 bit (LLM.int8()) | 15,22 GiB | La guida lo vede fattibile su GPU consumer come la 4090; leggermente più lento |
| 4 bit (NF4) | 9,54 GiB | Cambio lieve nell'output di esempio; leggermente più lento di 8 bit |
La guida di Hugging Face che contiene quella tabella espone una regola utile: i pesi di un modello da X miliardi di parametri occupano circa 2 × X GB in bfloat16 e circa 4 × X GB in float32 [26]. Per aritmetica, con 8 bit il fattore scende a 1 × X e con 4 bit a 0,5 × X, prima di aggiungere scale, cache KV e memoria del runtime.
Ora, la perdita di qualità. La tabella riunisce quattro studi con metriche diverse, per cui le loro righe non sono confrontabili tra loro: ogni riga confronta il metodo con il suo riferimento a 16 bit.
Perdita di qualità pubblicata dagli autori di ciascun metodo
| Studio | Modello e metrica | Riferimento 16 bit | Risultato quantizzato |
|---|---|---|---|
| GPTQ [5] | OPT-175B, perplejità in WikiText2 | 8,34 | 4 bit: RTN 10,54 e GPTQ 8,37. 3 bit: GPTQ 8,68 e RTN 7,3 × 10³ |
| AWQ [6] | Llama-2-7B, perplejità in WikiText | 5,47 | INT4 g128: RTN 5,73, GPTQ 5,69, AWQ 5,60. INT3 g128: RTN 6,66, GPTQ 6,43, AWQ 6,24 |
| SmoothQuant [7] | OPT-175B, precisione media in task | 66,9 % | W8A8 ingenuo 35,5 %; LLM.int8() 66,7 %; SmoothQuant 66,8 % |
| QLoRA [8] | MMLU medio dopo fine-tuning | BF16: 53,0 | NF4 con doppia quantizzazione: 53,1; FP4: 52,2 |
Gli studi comparativi più recenti guardano modelli e task più vicini all'uso reale. Kurtic et al., con oltre 500.000 valutazioni su tutta la famiglia Llama 3.1, concludono che FP8 (W8A8) è praticamente senza perdita a qualsiasi dimensione, che INT8 W8A8 ben calibrato perde tra l'1% e il 3% di precisione e che INT4 di soli pesi è più competitivo del previsto, al livello degli 8 bit [10]. Lee et al., con modelli da 1.000 milioni a 405.000 milioni di parametri e 13 set di dati, osservano che i modelli piccoli possono subire cali severi a 4 bit, che quelli da 70.000 milioni si mantengono stabili, che AWQ tende a superare GPTQ nella quantizzazione di soli pesi e che i modelli quantizzati tendono a fallire di più nel seguire istruzioni e nel rilevare allucinazioni [11]. Dettmers e Zettlemoyer, con oltre 35.000 esperimenti, concludono che 4 bit è quasi universalmente la precisione ottimale per i bit totali del modello e la sua precisione zero-shot: un modello da 30.000 milioni di parametri a 8 bit e un altro da 60.000 milioni a 4 bit occupano lo stesso spazio, ma possono performare in modo molto diverso, in modo che contano sia la dimensione del modello sia i bit [9].
Queste cifre non devono essere applicate così com'è. Quelle di GPTQ, AWQ e SmoothQuant provengono da modelli del 2022 e 2023; la perplejità in WikiText non misura il seguire istruzioni né l'assenza di allucinazioni; e le differenze tra metodi possono cambiare con ogni famiglia di modelli. La loro utilità è fissare un ordine di grandezza —8 bit quasi senza perdita, 4 bit accettabili in modelli grandi, 3 bit o meno solo con metodi specifici— che il suo stesso test deve confermare.
Albero di decisioni riassuntivo
| Situazione osservata | Prima azione | Cosa non deve essere supposto |
|---|---|---|
| I pesi non stanno con margine | Provare precisione inferiore o un modello più piccolo | Che abbassare i bit risolverà il costo del contesto |
| I pesi stanno, ma fallisce con input lunghi | Ridurre il contesto obiettivo, rivedere la cache KV o usare più VRAM | Che la dimensione del file predice la capacità del contesto |
| Fallisce con più richieste | Dimensionare per token vivi concorrenti e batch reale | Che un test di una singola sessione rappresenta il servizio |
| La qualità cala in task critici | Aumentare la precisione, cambiare metodo o usare un modello più piccolo con più bit | Che più parametri compensano qualsiasi perdita |
| La latenza è instabile | Misurare prefill, generazione, offloading e memoria libera | Che la media di token al secondo basta |
Procedura di decisione: dalla restrizione al candidato fattibile
Definire prima il contratto operativo. Scrivere la lunghezza massima di input che deve realmente essere supportata, una riserva di token di output, il numero massimo di richieste vive, l'obiettivo di latenza e i task critici. Distinguere un massimo eccezionale da un obiettivo abituale. Se un'applicazione elabora documenti lunghi, misurare solo messaggi brevi non rappresenta il suo rischio di memoria né la sua qualità utile.
Poi raccogliere i parametri dell'architettura e del runtime. Per il modello, registrare layer, teste KV, dimensione della testa e il formato dei pesi. Per il runtime, registrare il tipo di dati della cache, se riserva cache statica o dinamica, se consente offloading, il limite di memoria della GPU e qualsiasi regolazione di batch o token in volo. Negli strumenti di servizio, il budget di cache può essere configurato esplicitamente o derivato da una frazione della memoria disponibile; entrambi i casi devono essere presenti nell'esperimento.
Calcolare una banda, non un'unica cifra: pesi osservati o stimati, cache KV per il carico obiettivo, una riserva per temporanei e un margine. Se il totale supera la VRAM disponibile prima di applicare il margine, scartare la combinazione. Se entra di poco, classificarla come candidata a rischio e provarla sotto carico massimo. Se entra con margine, non darla per approvata fino a validare qualità e latenza.
Scegliere almeno tre candidati che rispondano a ipotesi diverse: il modello desiderato a 8 bit, a 6 bit e a 4 bit; o, quando un candidato non ha senso, un modello più piccolo con una precisione superiore. Mantenere costanti modello base, revisione, prompt, contesto massimo, limite di output, seed quando compatibile, parametri di decodifica, hardware e versione del runtime. Cambiare più variabili alla volta impedisce di attribuire una differenza alla quantizzazione.
Processo riproducibile in sette passi
-
Definire contesto di input, output riservato, concorrenza e latenza obiettivo.
-
Registrare architettura, artefatto di pesi e configurazione della cache.
-
Stimare pesi, cache KV e margine per il massimo di token vivi.
-
Scartare candidati che non entrano prima del margine o che richiedono presupposti non verificati.
-
Eseguire candidati confrontabili con parametri identici.
-
Misurare memoria, tempo al primo token, generazione, errori e qualità dell'output.
-
Conservare la configurazione solo se supera la soglia di qualità e mantiene margine sotto carico massimo.
Prova minima per rilevare una perdita che conti
Una prova utile non deve essere enorme, ma deve essere rappresentativa. Costruire un piccolo insieme di casi che includa il lavoro che motiva il deployment: estrazione strutturata, classificazione, sintesi di documenti, assistenza al codice o risposte con vincoli, a seconda dei casi. Includere input di lunghezza abituale e alcuni vicini al limite operativo. Gli input lunghi sono necessari perché possono rivelare sia errori di memoria sia perdite nel seguire istruzioni o nel recupero di dettagli.
Per ogni caso, definire cosa verrà validato prima di eseguire il modello. Alcuni task ammettono confronto esatto: uno schema JSON valido, etichette consentite, campi obbligatori, una query che deve contenere valori specifici o test automatizzati per codice. Altri richiedono revisione umana con una rubrica: fedeltà al documento, copertura, assenza di invenzioni, conformità al formato e utilità. Non confondere fluidità con correttezza.
Fissare una soglia esplicita. Ad esempio, una configurazione può essere scartata se non rispetta più casi critici del candidato di riferimento, se peggiora la validità strutturale oltre un limite deciso dal team o se produce nuovi errori su dati sensibili. La soglia appartiene al rischio dell'applicazione; non può essere inferita dai bit. In un task di bozza creativa può essere accettata più variazione che nell'estrazione di dati per un processo successivo.
Ripetere i test. Con decodifica stocastica, più esecuzioni aiutano a separare la variazione di generazione dalla degradazione sistematica. Con decodifica deterministica, le ripetizioni sono ancora utili per osservare la stabilità delle prestazioni e gli errori di memoria. Reportare i risultati per tipo di task e lunghezza di input, non solo una media globale. Una quantizzazione che sembra equivalente nella media può concentrare i suoi fallimenti nei documenti più lunghi o nel task di maggiore impatto.
Confronto equo
Non confrontare una quantizzazione con contesto di 4.000 token contro un'altra con 16.000, né una con offloading e un'altra senza, se si vuole attribuire differenze ai pesi. Se si cambia una condizione per necessità di memoria, riportarla come una configurazione operativa diversa.
Come misurare la perdita: perplejità, divergenza KL e task propri
La perplejità misura la sorpresa media del modello di fronte a un testo di riferimento; si calcola con il modello originale e con quello quantizzato sullo stesso testo, e i lavori citati la riportano su WikiText e C4 [5][6][18]. La documentazione di llama.cpp raccomanda la perplejità e la divergenza KL tra le distribuzioni del modello originale e di quello quantizzato come metriche di precisione del quantizzato [21]. Jin et al. osservano che la perplejità serve come indicatore sostitutivo nella maggior parte dei benchmark che hanno provato [12], ma Lee et al. mostrano che il seguire istruzioni e il rilevamento di allucinazioni ne risentono più di quanto suggeriscano le metriche di testo piano [11]. Per questo la perplejità serve per scartare candidati e la prova minima con casi propri rimane la decisione finale.
La calibrazione è un'altra variabile. GPTQ calibra con 128 segmenti casuali di 2.048 token di C4 [5] e AWQ, con 16 sequenze [6]; llama.cpp offre una matrice di importanza (imatrix) per guidare la quantizzazione [21]. Non verificato: le fonti consultate non misurano come influisce calibrare con testo principalmente in inglese sulla qualità in spagnolo o altre lingue; verificarlo con il suo corpus.
Un avvertimento pratico di llama.cpp: ri-quantizzare un modello che era già quantizzato, con l'opzione --allow-requantize, produce una perdita di qualità grave [21]. Partire sempre dai pesi a 16 bit e annotare quale artefatto specifico è stato scaricato, nel registro minimo di esecuzione.
Tre profili operativi e le loro priorità
Su un portatile con GPU limitata, la priorità è di solito evitare una configurazione che dipenda continuamente dalla RAM di sistema o dall'offloading per un'esperienza interattiva. Iniziare con un contesto realistico, una singola richiesta e un modello o quantizzazione che lasci margine. Se 4 bit è l'unico modo per caricare il modello, confrontare anche un modello più piccolo a 6 o 8 bit. Il secondo può essere più stabile e più utile nel task specifico, anche se ha meno parametri.
Su una workstation con una GPU, c'è più margine per scegliere tra qualità, contesto e velocità, ma il limite è ancora condiviso da pesi, cache e temporanei. È un ambiente appropriato per confrontare 4, 6 e 8 bit sotto lo stesso corpus e per decidere se riservare VRAM per contesti lunghi. Se si prevede di alternare tra sessioni brevi e analisi documentale, misurare entrambi i profili: il risultato di una conversazione breve non dimensiona il secondo.
Su un server con concorrenza moderata, l'unità di pianificazione non è più il file del modello, ma la capacità totale di token vivi. Il gestore della cache e lo scheduler delle richieste sono parte della decisione. Una configurazione che funziona per una singola sessione può esaurire la memoria quando coincidono prefill lunghi. Definire limiti di ammissione, lunghezza massima, riserva di output e concorrenza; poi provare raffiche e miscele di richieste brevi e lunghe. Le metriche di coda e i percentili di latenza sono più informative del miglior risultato isolato.
In ciascuno dei tre profili, l'offloading è un'opzione che deve essere dichiarata, non una soluzione invisibile. Può ampliare la capacità apparente trasferendo parte dei dati, ma può anche alterare la latenza e dipendere dal collegamento tra CPU e GPU. Decidere con misurazioni se quel compromesso è accettabile per il caso d'uso.
Compatibilità: il formato deve adattarsi al motore e alla GPU
Scegliere 4 bit non è una decisione sul modello, ma sulla triade modello, formato e motore. GGUF è un formato binario che salva i tensori e un insieme standardizzato di metadati, progettato per llama.cpp e strumenti derivati come LM Studio, GPT4All e Ollama [22]. Gli altri formati vivono in altri motori, con requisiti hardware propri. La tabella raccoglie ciò che documentano le loro pagine ufficiali consultate il 17 ottobre 2026; questo campo cambia rapidamente e conviene rivedere la versione vigente.
Cosa documenta ogni motore sulla quantizzazione
| Motore | Formati documentati | Condizioni segnalate |
|---|---|---|
| llama.cpp e GGUF [21][22] | Quantizzatori da circa 2 a 8 bit per peso nelle tabelle di Llama 3.1 8B, e tipi F16 e BF16 | Quantizzare da pesi a 16 bit; evitare la ri-quantizzazione |
| Transformers con bitsandbytes [23][24] | 8 bit (LLM.int8()) e 4 bit (NF4 e FP4); la panoramica elenca inoltre AWQ, GPTQ, GGUF, AQLM, HQQ e torchao | LLM.int8() richiede GPU NVIDIA Turing o successiva; NF4 e FP4, Pascal o successiva |
| vLLM [27] | AWQ, GPTQ, FP8 W8A8, INT8 W8A8, INT4 W4A16, bitsandbytes, GGUF e cache KV quantizzata | AWQ non figura come compatibile con Volta; FP8 W8A8 con llm-compressor figura in Ada, Hopper e GPU AMD |
| TensorRT-LLM [28] | FP4, FP8 (per tensore, per blocchi e per righe), cache KV in FP8 e NVFP4, e AWQ e GPTQ in W4A16 e W4A8 | Il backend predefinito ammette FP4 e FP8 in Blackwell e Hopper |
| ONNX Runtime [3] | 8 bit dinamica e statica, e 4 bit per blocchi in MatMul con RTN, GPTQ o HQQ | Su GPU richiede Tensor Core a interi a 8 bit, come T4 o A100 |
| PyTorch [29] | API torch.ao.quantization in migrazione a torchao | La documentazione chiede di migrare a torchao |
Due conseguenze per la decisione. La prima: su hardware senza le istruzioni adeguate, quantizzare può peggiorare le prestazioni per il costo di quantizzare e de-quantizzare, come avverte ONNX Runtime [3]. La seconda: il miglior formato dipende dal pattern di utilizzo. Kurtic et al. trovano che W4A16 è il più redditizio con richieste sincrone e che W8A8 domina con elaborazione continua asincrona per batch [10]. È la stessa distinzione che questo articolo fa tra il portatile di una singola sessione e il server con concorrenza.
Registro minimo di un'esecuzione
Annotare: identificatore e revisione del modello base; artefatto e metodo di quantizzazione; versione e backend del runtime; GPU e memoria disponibile; tipo di cache KV; limite di contesto; riserva di output; batch, concorrenza e limite di token in volo; offloading; parametri di decodifica; corpus e rubrica; memoria libera minima; tempo al primo token; tasso di generazione; errori e risultato di qualità. Senza questo registro, un confronto successivo difficilmente sarà riproducibile.
Segnali per scartare una configurazione e checklist di adozione
Scartare una configurazione quando produce errori di memoria intermittenti, anche se una dimostrazione breve funziona. L'intermittenza di solito indica che la memoria disponibile dipende dalla forma delle richieste, dal picco di prefill, dalla frammentazione o da altri carichi del processo. È anche motivo di scarto se il runtime riduce silenziosamente il contesto effettivo, se il sistema è stabile solo con un carico inferiore a quello previsto o se il margine osservato scompare in test ripetuti.
La degradazione della qualità deve essere analizzata per pattern. Errori concentrati in estrazione, calcoli, campi obbligatori, seguire istruzioni o documenti estesi hanno più peso di cambiamenti stilistici se quei task sono critici. Un output apparentemente ragionevole ma con valori inventati non deve essere approvato solo per un punteggio medio. Rivedere anche la validità del formato quando l'output alimenta software successivo.
Prima di fissare un'opzione predefinita, verificare privacy e operazione. Eseguire inferenza sul dispositivo non garantisce di per sé che nessun dato ne esca: download di modelli, telemetria, registrazione di prompt, aggiornamento di dipendenze e strumenti di osservabilità sono aspetti separati. Quali dati vengono conservati e quali comunicazioni effettua ogni componente devono essere verificati nella configurazione e nell'ambiente di rete scelto.
Il risultato finale non deve essere «la quantizzazione più grande possibile». Può essere 6 bit per un profilo interattivo, 4 bit per un profilo di analisi con grande contesto, o 8 bit per un task dove la perdita rilevata non sia accettabile. Mantenere le alternative insieme al loro registro di test. Se cambiano il runtime, l'hardware, il formato dei pesi o il carico di lavoro, rimisurare: la conclusione precedente smette di essere una garanzia.
Checklist prima di adottare una quantizzazione
-
I pesi, la cache KV, i temporanei e il margine sono stati misurati o giustificati separatamente.
-
Il contesto, l'output riservato e la concorrenza riflettono il carico massimo previsto.
-
È stato verificato se la quantizzazione influisce sui pesi, sulla cache KV o su entrambi.
-
Le configurazioni confrontate usano lo stesso modello base e condizioni equivalenti.
-
Il corpus contiene task critici e input lunghi rappresentativi.
-
Esiste una soglia di perdita accettabile definita prima di rivedere i risultati.
-
La memoria libera minima, gli errori e i percentili di latenza sono stati registrati.
-
La configurazione di offloading, telemetria, download e registri è stata rivista.
-
La decisione può essere riprodotta a partire dal registro tecnico completo.
Limiti dell'evidenza e dati non verificati
Le cifre di GPTQ, AWQ, SmoothQuant e LLM.int8() sono state misurate su OPT, BLOOM e Llama 2; non si estendono ai modelli attuali. Kurtic et al. e Lee et al. usano Llama 3.x e sono più vicini, ma non coprono tutti i modelli né le lingue.
I miglioramenti di velocità di KIVI, KVQuant e AWQ (TinyChat) provengono da kernel e GPU dei loro autori. Nel README di llama.cpp non è indicato l'hardware delle misurazioni di velocità né la precisione della colonna di dimensione originale, anche se le loro cifre (32,1 GB per 8B) coincidono con circa 4 byte per parametro; questa lettura è un'inferenza nostra e non figura nella fonte.
I parametri di architettura della tabella di cache KV provengono da repliche pubbliche dei file config.json su Hugging Face (NousResearch e unsloth), perché i repository ufficiali di Meta richiedono autenticazione. La dimensione della testa di Llama 3.1 70B (128) si deriva dividendo la dimensione nascosta per il numero di teste di attenzione.
La tabella di compatibilità riflette la documentazione consultata il 17 ottobre 2026, inclusa la versione in sviluppo di vLLM. Non verificato: l'effetto della lingua di calibrazione, il comportamento di ogni quantizzatore con architetture di miscela di esperti e le prestazioni sulla sua GPU specifica; queste tre cose richiedono la sua stessa misurazione.
Riferimenti
-
[1] Gholami, A. et al. (2021). A Survey of Quantization Methods for Efficient Neural Network Inference. arXiv:2103.13630. https://arxiv.org/abs/2103.13630
-
[2] Wu, H. et al. (2020). Integer Quantization for Deep Learning Inference: Principles and Empirical Evaluation. arXiv:2004.09602. https://arxiv.org/abs/2004.09602
-
[3] ONNX Runtime. Quantize ONNX models. https://onnxruntime.ai/docs/performance/model-optimizations/quantization.html
-
[4] Dettmers, T., Lewis, M., Belkada, Y., Zettlemoyer, L. (2022). LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale. arXiv:2208.07339. https://arxiv.org/abs/2208.07339
-
[5] Frantar, E., Ashkboos, S., Hoefler, T., Alistarh, D. (2022). GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers. arXiv:2210.17323. https://arxiv.org/abs/2210.17323
-
[6] Lin, J. et al. (2023). AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration. arXiv:2306.00978. https://arxiv.org/abs/2306.00978
-
[7] Xiao, G. et al. (2022). SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models. arXiv:2211.10438. https://arxiv.org/abs/2211.10438
-
[8] Dettmers, T., Pagnoni, A., Holtzman, A., Zettlemoyer, L. (2023). QLoRA: Efficient Finetuning of Quantized LLMs. arXiv:2305.14314. https://arxiv.org/abs/2305.14314
-
[9] Dettmers, T., Zettlemoyer, L. (2022). The case for 4-bit precision: k-bit Inference Scaling Laws. arXiv:2212.09720. https://arxiv.org/abs/2212.09720
-
[10] Kurtic, E. et al. (2024). «Give Me BF16 or Give Me Death»? Accuracy-Performance Trade-Offs in LLM Quantization. arXiv:2411.02355. https://arxiv.org/abs/2411.02355
-
[11] Lee, J. et al. (2024). Exploring the Trade-Offs: Quantization Methods, Task Difficulty, and Model Size in Large Language Models From Edge to Giant. arXiv:2409.11055. https://arxiv.org/abs/2409.11055
-
[12] Jin, R. et al. (2024). A Comprehensive Evaluation of Quantization Strategies for Large Language Models. arXiv:2402.16775. https://arxiv.org/abs/2402.16775
-
[13] Micikevicius, P. et al. (2022). FP8 Formats for Deep Learning. arXiv:2209.05433. https://arxiv.org/abs/2209.05433
-
[14] Rouhani, B. D. et al. (2023). Microscaling Data Formats for Deep Learning. arXiv:2310.10537. https://arxiv.org/abs/2310.10537
-
[15] Ma, S. et al. (2024). The Era of 1-bit LLMs: All Large Language Models are in 1.58 Bits. arXiv:2402.17764. https://arxiv.org/abs/2402.17764
-
[16] Egiazarian, V. et al. (2024). Extreme Compression of Large Language Models via Additive Quantization. arXiv:2401.06118. https://arxiv.org/abs/2401.06118
-
[17] Liu, Z. et al. (2024). KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache. arXiv:2402.02750. https://arxiv.org/abs/2402.02750
-
[18] Hooper, C. et al. (2024). KVQuant: Towards 10 Million Context Length LLM Inference with KV Cache Quantization. arXiv:2401.18079. https://arxiv.org/abs/2401.18079
-
[19] Kwon, W. et al. (2023). Efficient Memory Management for Large Language Model Serving with PagedAttention. arXiv:2309.06180. https://arxiv.org/abs/2309.06180
-
[20] Ainslie, J. et al. (2023). GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. arXiv:2305.13245. https://arxiv.org/abs/2305.13245
-
[21] ggml-org. llama.cpp, tools/quantize/README.md. https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md
-
[22] Hugging Face. GGUF (documentazione dell’Hub). https://huggingface.co/docs/hub/gguf
-
[23] Hugging Face. Quantization (documentazione di Transformers). https://huggingface.co/docs/transformers/main/en/quantization/overview
-
[24] Hugging Face. bitsandbytes (documentazione di Transformers). https://huggingface.co/docs/transformers/main/en/quantization/bitsandbytes
-
[25] Hugging Face. KV cache strategies (documentazione di Transformers). https://huggingface.co/docs/transformers/en/kv_cache
-
[26] Hugging Face. Optimizing LLMs for speed and memory. https://huggingface.co/docs/transformers/main/en/llm_tutorial_optimization
-
[27] vLLM. Quantization. https://docs.vllm.ai/en/latest/features/quantization/
-
[28] NVIDIA. TensorRT LLM: Quantization. https://nvidia.github.io/TensorRT-LLM/features/quantization.html
-
[29] PyTorch. Quantization (documentazione 2.14). https://docs.pytorch.org/docs/2.14/quantization.html
-
[30] Hugging Face. File config.json di Llama 2 7B, Llama 3 8B, Llama 3.1 8B e 70B (repliche NousResearch e unsloth). https://huggingface.co/unsloth/Meta-Llama-3.1-8B/blob/main/config.json
-
[31] Liu, Z. et al. (2023). LLM-QAT: Data-Free Quantization Aware Training for Large Language Models. arXiv:2305.17888. https://arxiv.org/abs/2305.17888
Continua a esplorare
Fonti consultate
Correzioni e trasparenza
Se trovi un dato errato o non aggiornato, inviaci la pagina e la fonte da verificare.
Proponi una correzione