Abstract. Dire che una rete è «INT8» non descrive come i suoi numeri siano rappresentati. La scelta della scala può cancellare un intero canale con pesi piccoli, anche quando il tipo intero dispone ancora di molti valori inutilizzati per quel canale. Confrontiamo quantizzazione simmetrica per tensore e per canale attraverso un livello lineare controllato, una derivazione dell'errore d'uscita e un esperimento riproducibile. Mostriamo anche un controesempio: un errore minore sui pesi non garantisce un errore minore per ogni ingresso. Chiudiamo distinguendo compressione, memoria di lavoro e prestazioni realmente misurate su embedded.
Servono algebra lineare elementare e nozioni di probabilità. Tutti i numeri sperimentali seguenti provengono da una simulazione in NumPy: non abbiamo eseguito inferenza su una scheda, misurato consumi o addestrato una rete. I pesi hanno unità convenzionali; le metriche d'errore sono riferite alle variabili del nostro modello didattico.
1. Otto bit non definiscono una risoluzione
Un codice intero assume valori discreti; per interpretarlo come numero reale serve una mappa. Nella rappresentazione affine, r̂ = s(q−z), s è una scala positiva e z lo zero-point intero. Qui scegliamo pesi simmetrici con z = 0 e codici da −127 a 127. Il valore −128 non viene usato. Questa convenzione è coerente con i pesi INT8 della specifica LiteRT, che distingue inoltre la granularità per tensore da quella per asse. Le attivazioni della specifica possono invece avere zero-point diverso da zero; non confondiamo i due casi.
Q = 2^(b−1) − 1 s = a / Q q(w) = clip(round(w/s), −Q, Q) w_hat = s q(w)
a è il massimo valore assoluto rappresentabile, b il numero di bit. Arrotondiamo al più vicino; nei pareggi il codice usa la convenzione di NumPy verso l'intero pari. Se |w| ≤ a, l'errore assoluto non supera s/2. Se |w| > a interviene la saturazione: l'errore può essere molto maggiore e il limite precedente non vale. Scegliere a significa quindi scambiare risoluzione nella regione centrale e copertura delle code.
Con b = 8 e a = 10, s vale circa 0,07874. Un peso di 0,01 diventa zero: non perché otto bit siano intrinsecamente insufficienti, ma perché quella scala dedica tutti i livelli a un intervallo molto più grande. Con a = 0,01, lo stesso peso è rappresentato esattamente dal codice 127. L'effetto della scala va studiato prima di attribuire genericamente una perdita di qualità alla «bassa precisione».
2. Un livello lineare con quattro scale naturali
Definiamo un livello y = Wx, con otto ingressi e quattro uscite, senza bias. Ogni riga è un canale d'uscita. Costruiamo un vettore base comune e moltiplichiamolo per quattro ampiezze:
u = [−1, −0.51, −0.11, 0.07, 0.23, 0.49, 0.81, 1] α = [0.01, 0.1, 1, 10] W[c, j] = α[c] u[j] W ∈ R^(4×8), x ∈ R^8, y ∈ R^4
La struttura è intenzionalmente controllata: i canali hanno la stessa forma e differiscono soltanto per ampiezza. Non è un modello estratto da un prodotto e non pretende di rappresentare la distribuzione di tutte le reti reali. Serve a isolare una causa precisa di errore. In una rete addestrata, forme, outlier e sensibilità dei canali possono essere molto diversi.
Per tensore usiamo un'unica scala s = max|W|/127 = 10/127. Per canale usiamo sc = maxj|W[c,j]|/127 = αc/127. Quantizziamo e ricostruiamo i pesi, lasciando gli ingressi in virgola mobile. Questa scelta separa l'effetto dei pesi da quello delle attivazioni: il nostro esperimento non è ancora una pipeline d'inferenza interamente intera.
3. Derivare l'errore d'uscita
Indichiamo con E = Ŵ−W la matrice dell'errore. Per un ingresso x l'errore d'uscita è Ex. Se x ha media nulla e covarianza Σ, possiamo calcolare la media del suo quadrato senza simulazione:
δy = Ex E[||δy||²] = E[xᵀEᵀEx] = tr(EΣEᵀ) Σ = I ⇒ E[||δy||²] = ||E||F² E[(δy_c)²] = ||E[c,:]||² se Σ = I
L'identità usa la linearità della traccia e E[xxᵀ] = Σ per ingressi a media nulla. Con media μ diversa da zero occorre aggiungere ||Eμ||². Questa precisazione conta per attivazioni rettificate o variabili con offset: applicare automaticamente la formula isotropa può dare una previsione sbagliata.
Nel nostro esperimento generiamo 100.000 vettori gaussiani standard indipendenti con seed 20260922 e confrontiamo la media empirica degli errori con la formula esatta. Il campionamento è una verifica numerica; la tabella usa il valore analitico, così non confondiamo il rumore Monte Carlo con differenze fra metodi. La formula non prevede accuratezza di classificazione o qualità di una rete non lineare: descrive soltanto il livello dichiarato.
4. Il risultato: un canale può scomparire
Definiamo l'errore relativo di un canale come ||E[c,:]||₂/||W[c,:]||₂. Non è una percentuale di predizioni errate. Un valore pari a uno, nel primo canale per tensore, significa qui che tutti i suoi pesi sono stati arrotondati a zero.
| Ampiezza α | Errore relativo INT8 per tensore | Errore relativo INT8 per canale | MSE d'uscita per tensore | MSE d'uscita per canale |
|---|---|---|---|---|
| 0,01 | 100% | 0,1859% | 3,2262×10⁻⁴ | 1,1148×10⁻⁹ |
| 0,1 | 31,7980% | 0,1859% | 3,2621×10⁻³ | 1,1148×10⁻⁷ |
| 1 | 3,7089% | 0,1859% | 4,4380×10⁻³ | 1,1148×10⁻⁵ |
| 10 | 0,1859% | 0,1859% | 1,1148×10⁻³ | 1,1148×10⁻³ |
Il canale più ampio usa la stessa scala in entrambi i metodi e produce lo stesso risultato. Negli altri, la scala per canale preserva la risoluzione relativa. L'uguaglianza degli errori relativi non è una legge universale: deriva dal fatto che tutte le righe sono copie scalate dello stesso vettore. Questo controllo costruito ci permette di riconoscere il meccanismo senza attribuirgli una generalità che non possiede.
Abbiamo ripetuto il calcolo con b = 4, Q = 7 e intervallo simmetrico −7…7. Con scala per canale l'errore relativo sale al 7,6444%; con scala per tensore si annullano i due canali più piccoli. È una simulazione di codici a quattro bit, non una verifica di supporto INT4 in un runtime o di impacchettamento in memoria. La scelta tra quattro e otto bit richiede una valutazione del compito oltre a questi errori locali.

5. Il controesempio: meno errore sui pesi non basta sempre
La media isotropa assegna uguale importanza a tutte le direzioni d'ingresso. Un'applicazione può visitarne soprattutto alcune. Prendiamo la prima riga degli errori, et per tensore ed ec per canale, e costruiamo un ingresso ortogonale a et ma non a ec:
z = ec − et (ecᵀet)/(etᵀet) x = z / ||z|| etᵀx = 0, mentre ecᵀx può essere diverso da 0
Lo script costruisce proprio questo ingresso. L'errore del primo canale per tensore è circa −3,25×10⁻¹⁹, numericamente zero; quello per canale è circa 3,32×10⁻⁵. Il metodo con errore molto minore sui pesi perde questo confronto puntuale. Non è una raccomandazione a usare la scala peggiore: è la dimostrazione che l'ordinamento secondo una norma non implica un ordinamento per ogni possibile ingresso.
Per una distribuzione nota, il criterio tr(EΣEᵀ) pesa gli errori nelle direzioni effettivamente visitate. Per un compito reale, bisogna inoltre misurare la metrica finale: un piccolo cambiamento vicino a una soglia decisionale può essere importante, mentre un errore maggiore lontano dalla soglia può non modificare la decisione. La calibrazione e il test devono quindi riflettere gli ingressi d'uso, senza riutilizzare il test per scegliere continuamente le scale.
6. Clipping: una griglia più fine può peggiorare il risultato
Usiamo ora quattro valori di attivazione didattici: −0,2, 0,2, 0,8 e 1. Manteniamo una griglia simmetrica a otto bit e confrontiamo tre soglie a. Con a = 0,25 il passo è circa 0,00197, ma due valori saturano: il MSE è 0,21625031. Con a = 0,5 il passo è circa 0,00394 e il MSE scende a 0,08500031. Con a = 1 il passo è più grossolano, circa 0,00787, ma nessun valore satura e il MSE è circa 0,00000744.
Una risoluzione più fine dentro l'intervallo non garantisce un errore totale più piccolo. D'altra parte, un singolo outlier enorme potrebbe imporre una griglia troppo grossolana al resto dei dati. La soglia ottimale dipende dalla distribuzione e dalla perdita scelta: min-max, percentili e minimizzazione dell'errore non sono criteri equivalenti. Il nostro piccolo insieme illustra il fenomeno, non seleziona una procedura universale di calibrazione.
7. Dal calcolo simulato al kernel intero
Il lavoro fondativo di Jacob e colleghi, arXiv:1712.05877v1 del 2017, sviluppa la rappresentazione affine, la gestione degli zero-point e l'inferenza con aritmetica intera; le sezioni 2–4 includono metodo e confronti sperimentali su reti e CPU specifiche. Non trasferiamo quei risultati prestazionali al nostro esempio né a una scheda contemporanea senza misura. Inoltre il lavoro storico usa convenzioni che non vanno confuse automaticamente con la specifica INT8 attuale.
Nel caso semplificato con pesi simmetrici, un'uscita accumula prodotti tra codici dei pesi e ingressi centrati sul proprio zero-point. Il fattore reale è sx·sw,c; un'eventuale uscita quantizzata richiede poi una riscalatura verso sy. Bias, arrotondamento, saturazione e fusione delle operazioni fanno parte dell'implementazione. Ricostruire pesi float e moltiplicarli, come facciamo qui per isolare l'errore, non esercita questi dettagli del kernel.
Anche l'accumulatore deve essere dimensionato: la larghezza degli operandi non determina da sola quella della somma di molti prodotti. Per un confronto bit a bit occorre fissare arrotondamenti intermedi, saturazioni e ordine delle operazioni. Prima di parlare di accelerazione è necessario verificare che gli operatori quantizzati siano effettivamente eseguiti dal backend previsto, senza conversioni o fallback che cambino il percorso.
8. Memoria: i pesi non sono tutta la piattaforma
Il nostro livello contiene 32 pesi. In float32 occuperebbero 128 byte; in INT8 32 byte. Se memorizziamo scale float32, aggiungiamo 4 byte per tensore oppure 16 per i quattro canali. Il totale di soli pesi e scale diventa quindi 36 o 48 byte: rispetto ai 128 byte iniziali, riduzioni di circa 3,56× e 2,67×, non esattamente 4×. Sono conteggi teorici dichiarati, non dimensioni di un file LiteRT; escludono bias, metadati, allineamento e codice.
Su un microcontrollore i pesi possono risiedere in flash, mentre attivazioni, buffer temporanei e stato del runtime richiedono RAM. La documentazione TFLite Micro sulla memoria distingue sezioni non persistenti, temporanee e persistenti dell'arena e descrive il riuso dei buffer. Un modello piccolo su disco non dimostra che il picco di RAM entri nella scheda. L'analisi deve considerare quali tensori sono contemporaneamente vivi.
Analogamente, meno byte non equivalgono automaticamente a minore latenza o energia. Acquisizione del sensore, preprocessing, trasferimenti, istruzioni disponibili e frequenza possono dominare il tempo totale. Potenza in watt ed energia per inferenza in joule rispondono a domande diverse; senza un protocollo sul dispositivo non riportiamo nessun numero di prestazione.
9. Riproduzione e protocollo da portare sul dispositivo
import numpy as np
u = np.array([-1., -.51, -.11, .07, .23, .49, .81, 1.])
W = np.array([.01, .1, 1., 10.])[:, None] * u
for per_channel in [False, True]:
peak = np.max(np.abs(W), axis=1, keepdims=True) if per_channel else np.max(np.abs(W))
scale = peak / 127
Wq = np.clip(np.rint(W / scale), -127, 127) * scale
E = Wq - W
print(np.linalg.norm(E, axis=1) / np.linalg.norm(W, axis=1))
print(np.sum(E**2, axis=1))
Scarica esperimento, grafici e istruzioni; il file JSON dei risultati conserva scale, codici, errori analitici, confronto Monte Carlo e controesempio. Lo script controlla il limite s/2 nei casi senza clipping e la concordanza fra MSE analitico ed empirico entro il 3%. Quest'ultima tolleranza è una verifica del calcolo campionato, non una garanzia statistica generale.
Un passo successivo, qui proposto e non eseguito, consiste nell'esportare un modello reale e confrontare float, INT8 per tensore e INT8 per canale sugli stessi esempi separati dalla calibrazione. Registrare scheda e revisione, runtime, compilatore, operatori, clock, batch, forma degli ingressi, thread e condizioni termiche. Misurare qualità del compito, picco RAM, latenza mediana e di coda ed energia per inferenza. Conservare inoltre esempi nei quali la decisione cambia: spiegano il costo della compressione meglio di una sola media.
10. La scelta progettuale e il percorso EL-AI
In un'applicazione ipotetica di manutenzione predittiva, un canale numericamente piccolo potrebbe contribuire a distinguere un difetto raro. Cancellarlo perché un altro canale determina la scala globale sarebbe un rischio da verificare con dati pertinenti, non un danno che questo esperimento dimostri già. Il collegamento tra calcolo locale e decisione industriale passa attraverso una valutazione del sistema completo.
EL-AI ha esteso il proprio programma di approfondimento alle piattaforme embedded e alla robotica. Questo articolo sviluppa una competenza utile a tale direzione; non descrive una scheda proprietaria, un prodotto TinyML disponibile o misure effettuate dall'azienda. Le quattro lingue conservano il medesimo esperimento e le sue limitazioni.
Conclusione. La granularità della scala decide quali differenze sopravvivono alla rappresentazione intera. Nel caso costruito, una scala per canale preserva segnali cancellati dalla scala globale, ma non assicura superiorità su ogni ingresso né prestazioni migliori su qualunque hardware. Il percorso corretto collega errore di rappresentazione, distribuzione degli ingressi, metrica del compito e misure sul dispositivo.
Fonti e trasparenza
Fonti primarie collegate nel testo: specifica LiteRT INT8, aggiornata il 28 maggio 2026; Jacob e colleghi, preprint arXiv:1712.05877v1, 15 dicembre 2017, sezioni 2–4; documentazione TFLite Micro, gestione della memoria. Consultate il 22 settembre 2026. Esempio, controesempio e figure sono elaborazioni didattiche riproducibili, non risultati di una rete addestrata o di una ricerca sottoposta a peer review.
Testo e traduzioni realizzati con assistenza AI; nessuna revisione umana attestata. Copertina illustrativa generata con AI, non fotografia di una scheda o installazione EL-AI.

