Model Quantization
La quantizzazione del modello archivia pesi e attivazioni con una precisione inferiore, per esempio INT8, per ridurre le dimensioni dei modelli e velocizzare l'inferenza. Scopri come funziona e le differenze tra PTQ e QAT.
La quantizzazione del modello converte i pesi di una rete neurale e spesso anche le sue attivazioni da numeri in virgola mobile a 32 bit (FP32) a formati a precisione ridotta, come i float a 16 bit (FP16), gli interi a 8 bit (INT8) o gli interi a 4 bit. Ogni valore richiede quindi meno bit per essere memorizzato e trasferito; inoltre, l'aritmetica intera è più rapida e consuma meno energia sulla maggior parte dei processori. Un modello INT8 è un quarto delle dimensioni dell'originale FP32, consentendo ai modelli di rilevamento di oggetti di funzionare in tempo reale su telefoni, fotocamere e altri dispositivi di AI edge, e ai modelli linguistici di grandi dimensioni (LLMs) di entrare in una singola GPU.
Come funziona la quantizzazione#
La quantizzazione mappa un intervallo di numeri reali su un insieme ridotto di livelli interi. Nel comune schema affine, ogni valore float x viene memorizzato come intero q = round(x / scale) + zero_point e recuperato come x ≈ (q - zero_point) × scale. La scala determina l'ampiezza del passo tra i livelli, mentre il punto zero garantisce che il valore float 0 corrisponda a un intero esatto. INT8 ha 256 livelli, quindi è necessario scegliere con attenzione l'intervallo: un intervallo troppo ampio spreca livelli per valori che non si verificano mai, mentre uno troppo ristretto taglia i valori anomali. I pesi sono noti in anticipo, perciò i loro intervalli si ricavano direttamente dal modello addestrato e spesso si calcolano separatamente per ogni canale di output. Le attivazioni dipendono dall'input, quindi i loro intervalli vengono misurati durante la calibrazione, facendo passare nel modello un piccolo insieme di immagini o campioni di testo rappresentativi.
import numpy as np
rng = np.random.default_rng(0)
weights = rng.normal(0, 0.05, size=1000).astype(np.float32)
# Affine INT8 quantization maps the observed float range onto 256 integer levels
scale = (weights.max() - weights.min()) / 255
zero_point = np.round(-128 - weights.min() / scale)
q = np.clip(np.round(weights / scale + zero_point), -128, 127).astype(np.int8)
# Dequantize to measure the rounding error the model has to tolerate
restored = (q.astype(np.float32) - zero_point) * scale
print(f"Size: {weights.nbytes} -> {q.nbytes} bytes")
print(f"Max error: {np.abs(weights - restored).max():.5f} (half a step: {scale / 2:.5f})")Stampa Size: 4000 -> 1000 bytes e un errore massimo di 0.00068, pari alla metà di un passo di quantizzazione. I pesi si riducono di quattro volte e nessun valore si sposta di oltre mezzo passo rispetto al valore originale.
Quantizzazione post-training e addestramento consapevole della quantizzazione#
- Quantizzazione post-training (PTQ): converte un modello già addestrato, usando un piccolo set di calibrazione per impostare gli intervalli delle attivazioni. Richiede pochi minuti e, oltre a quei campioni, non ha bisogno di dati di addestramento. La quantizzazione dinamica è una variante di PTQ che calcola gli intervalli delle attivazioni al volo durante l'inferenza; la quantizzazione dei soli pesi mantiene le attivazioni in virgola mobile.
- Addestramento consapevole della quantizzazione (QAT): simula la quantizzazione durante l'addestramento o il fine-tuning arrotondando i valori nel passaggio forward, così i pesi imparano a tollerare l'arrotondamento. Recupera gran parte della precisione persa con PTQ, al costo di un addestramento aggiuntivo.
Di solito i team iniziano con PTQ e passano a un altro metodo solo quando la perdita di precisione è eccessiva:
Gli LLM aggiungono metodi specifici. Tecniche a 4 bit per i soli pesi, come GPTQ e AWQ, comprimono miliardi di parametri mantenendo le attivazioni a 16 bit; il formato GGUF archivia modelli quantizzati per l'inferenza locale, mentre le GPU recenti eseguono calcoli nativi in formati a precisione ridotta in virgola mobile, come FP8.
Quantizzazione e tecniche correlate#
- Precisione mista: tecnica di addestramento che esegue la maggior parte delle operazioni in FP16 o BF16, mantenendo una copia master dei pesi in FP32, per accelerare l'addestramento. La quantizzazione è destinata all'inferenza e si spinge oltre, spesso fino agli interi.
- Pruning del modello: rimuove pesi o interi canali per ridurre il numero di operazioni e viene spesso combinato con la quantizzazione, che rende meno costosa ogni operazione rimanente.
- Distillazione della conoscenza: addestra un modello studente piccolo a imitare un modello insegnante grande. In seguito, spesso il modello studente viene quantizzato per la distribuzione.
La quantizzazione nella pratica#
L'accelerazione dipende dal supporto hardware: un modello INT8 è più veloce solo sui processori dotati di kernel INT8, come le GPU NVIDIA con TensorRT, le CPU Intel con OpenVINO e le unità di elaborazione neurale (NPUs) dei telefoni. Ultralytics YOLO26 applica PTQ durante l'esportazione con quantize=8, calibrando il modello su immagini del dataset data; invece, model.train(quantize=8) esegue il fine-tuning di un checkpoint con QAT. Questo esempio esporta un modello INT8 per LiteRT, il runtime di Google per Android e i dispositivi embedded:
from ultralytics import YOLO
# Carica un modello nano YOLO26 preaddestrato
model = YOLO("yolo26n.pt")
# Quantizzazione post-training INT8; le immagini di coco8.yaml calibrano gli intervalli delle attivazioni
model.export(format="litert", quantize=8, data="coco8.yaml")Misurata su COCO con TensorRT, la PTQ INT8 riduce la mAP50-95 di YOLO26n solo da 0.403 a 0.393, ma incide maggiormente su YOLO26s, portandola da 0.479 a 0.441; la QAT riporta YOLO26s a 0.471.










