Quantizzazione del tensore La storia non raccontata

La storia non raccontata della quantizzazione del tensore

Uno sguardo approfondito agli aspetti di implementazione della quantizzazione nei framework di apprendimento automatico

Scritto in collaborazione con Naresh Singh.

Indice

  • Introduzione
  • Cosa significano i termini scala e zero-point per la quantizzazione?
  • Tipi di schemi di quantizzazione
  • Esempi di scala di quantizzazione e zero-point
  • Quantizzazione e normalizzazione dell’attivazione
  • Conclusione
  • Riferimenti

Introduzione

Nel resto di questo articolo, cercheremo di rispondere alle seguenti domande con esempi concreti.

  1. Cosa significano i termini scala e zero-point per la quantizzazione?
  2. Quali sono i diversi tipi di schemi di quantizzazione?
  3. Come calcolare la scala e il zero-point per i diversi schemi di quantizzazione?
  4. Perché il zero-point è importante per la quantizzazione?
  5. Come le tecniche di normalizzazione beneficiano della quantizzazione?

Cosa significano i termini scala e zero-point per la quantizzazione?

Scala: Quando si quantizza un intervallo di numeri in virgola mobile, si rappresenta tipicamente un intervallo in virgola mobile [Fmin..Fmax] nell’intervallo quantizzato [Qmin..Qmax]. In questo caso, la scala è il rapporto tra l’intervallo in virgola mobile e l’intervallo quantizzato.

Vedremo un esempio su come calcolarla successivamente.

Zero-point: Il zero-point per la quantizzazione è la rappresentazione del numero in virgola mobile 0.0 nell’intervallo quantizzato. In particolare, il zero-point è un valore quantizzato e rappresenta il valore in virgola mobile 0.0 per tutti gli scopi pratici. Vedremo come viene calcolato con esempi successivamente, insieme al motivo per cui tale rappresentazione è di interesse pratico per noi.

Successivamente, diamo un’occhiata agli schemi di quantizzazione principali utilizzati nella pratica e familiarizziamo con le loro somiglianze e differenze.

Tipi di schemi di quantizzazione

Quando si considerano i tipi di quantizzazione disponibili per l’uso durante la compressione del modello, ci sono 2 tipi principali tra cui scegliere.

  1. Quantizzazione simmetrica: In questo caso, il zero-point è zero, cioè 0,0 dell’intervallo in virgola mobile è lo stesso di 0 nell’intervallo quantizzato. Tipicamente, questo è più efficiente da calcolare durante l’esecuzione, ma può comportare una minore precisione se l’intervallo in virgola mobile è distribuito in modo disuguale intorno al numero in virgola mobile 0.0.
  2. Quantizzazione affine (o asimmetrica): Questa è quella che ha un zero-point che ha un valore diverso da zero.

Ma prima di approfondire i dettagli, cerchiamo di definire cosa significa zero-point.

Esempi di scala di quantizzazione e zero-point

Iniziamo con un esempio molto semplice e costruiamolo passo dopo passo.

Esempio 1: Quantizzazione simmetrica uint8

Diciamo che vogliamo mappare l’intervallo di numeri in virgola mobile [0.0 .. 1000.0] nell’intervallo quantizzato [0 .. 255]. L’intervallo [0 .. 255] è l’insieme di valori che possono essere rappresentati da un intero non segnato a 8 bit.

Per effettuare questa trasformazione, vogliamo ridimensionare l’intervallo di numeri in virgola mobile in modo che sia vero quanto segue:

Numero in virgola mobile 0.0 = Numero quantizzato 0

Numero in virgola mobile 1000.0 = Numero quantizzato 255

Questo viene chiamato quantizzazione simmetrica perché il numero in virgola mobile 0.0 viene quantizzato a 0.

Quindi, definiamo una scala, che è uguale a

Dove,

In questo caso, la scala = 3.9215

Per convertire da un valore in virgola mobile a un valore quantizzato, possiamo semplicemente dividere il valore in virgola mobile per la scala. Ad esempio, il valore in virgola mobile 500.0 corrisponde al valore quantizzato

In questo semplice esempio, lo 0.0 dell’intervallo in virgola mobile corrisponde esattamente allo 0 nell’intervallo quantizzato. Questo viene chiamato quantizzazione simmetrica. Vediamo cosa succede quando questo non è il caso.

Esempio-2: Quantizzazione affine uint8

Supponiamo di voler mappare l’intervallo in virgola mobile [-20.0 .. 1000.0] nell’intervallo quantizzato [0 .. 255].

In questo caso, abbiamo un fattore di scala diverso poiché il nostro xmin è diverso.

Vediamo cosa rappresenta il numero in virgola mobile 0.0 nell’intervallo quantizzato se applichiamo il fattore di scala a 0.0

Bene, questo non sembra proprio corretto poiché, secondo il diagramma sopra, ci saremmo aspettati che il valore in virgola mobile -20.0 corrispondesse al valore quantizzato 0.

Qui entra in gioco il concetto di zero-point. Lo zero-point agisce come un bias per spostare il valore in virgola mobile scalato e corrisponde al valore nell’intervallo quantizzato che rappresenta il valore in virgola mobile 0.0. Nel nostro caso, lo zero point è il negativo della rappresentazione in virgola mobile scalata di -20.0, che è -(-5) = 5. Lo zero point è sempre il negativo della rappresentazione del valore in virgola mobile minimo poiché il minimo sarà sempre negativo o zero. Scopriremo di più su questo argomento nella sezione che spiega l’esempio 4.

Ogni volta che quantizziamo un valore, aggiungiamo sempre lo zero point a questo valore scalato per ottenere il valore quantizzato effettivo nell’intervallo di quantizzazione valido. Nel caso in cui vogliamo quantizzare il valore -20.0, lo calcoliamo come il valore scalato di -20.0 più lo zero point, che è -5 + 5 = 0. Quindi, quantized(-20.0, scala=4, zp=5) = 0.

Esempio-3: Quantizzazione affine int8

Cosa succede se il nostro intervallo quantizzato è un intero con segno a 8 bit invece di un intero a 8 bit senza segno? Beh, l’intervallo è ora [-128 .. 127].

In questo caso, -20.0 nell’intervallo float corrisponde a -128 nell’intervallo quantizzato, e 1000.0 nell’intervallo float corrisponde a 127 nell’intervallo quantizzato.

Il modo in cui calcoliamo lo zero point è che lo calcoliamo come se l’intervallo quantizzato fosse [0 .. 255] e poi lo spostiamo di -128, quindi lo zero point nel nuovo intervallo è

Pertanto, il punto di riferimento per il nuovo intervallo è -123.

Fino ad ora, abbiamo esaminato esempi in cui l’intervallo dei punti in virgola mobile include il valore 0.0. Nei prossimi esempi, vedremo cosa succede quando l’intervallo dei punti in virgola mobile non include il valore 0.0.

L’importanza di 0.0

Perché è importante che il valore in virgola mobile 0.0 sia rappresentato nell’intervallo dei punti in virgola mobile?

Quando si utilizza una convoluzione con padding, ci aspettiamo che i pixel di bordo siano riempiti utilizzando il valore 0.0 nel caso più comune. Pertanto, è importante che 0.0 sia rappresentato nell’intervallo dei punti in virgola mobile. Allo stesso modo, se il valore X verrà utilizzato per il padding nella tua rete, devi assicurarti che il valore X sia rappresentato nell’intervallo dei punti in virgola mobile e che la quantizzazione ne sia consapevole.

Esempio-4: La storia non raccontata – intervallo dei punti in virgola mobile distorto

Ora, vediamo cosa succede se 0.0 non fa parte dell’intervallo dei punti in virgola mobile.

In questo esempio, stiamo cercando di quantizzare l’intervallo dei punti in virgola mobile [40.0 .. 1000.0] nell’intervallo quantizzato [0 .. 255].

Dato che non possiamo rappresentare il valore 0.0 nell’intervallo dei punti in virgola mobile, dobbiamo estendere il limite inferiore dell’intervallo a 0.0.

Possiamo vedere che una parte dell’intervallo quantizzato viene sprecata. Per determinare quanto, calcoliamo il valore quantizzato a cui si abbina il valore in virgola mobile 40.0.

Quindi, stiamo sprecando l’intervallo [0 .. 9] nell’intervallo quantizzato, che corrisponde a circa il 3,92% dell’intervallo. Questo potrebbe influire significativamente sull’accuratezza del modello dopo la quantizzazione.

Questo distorto è necessario se vogliamo assicurarci che il valore 0.0 nell’intervallo dei punti in virgola mobile possa essere rappresentato nell’intervallo quantizzato.

Un’altra ragione per includere il valore 0.0 nell’intervallo dei punti in virgola mobile è che è molto utile confrontare efficientemente un valore quantizzato per verificare se è 0.0 nell’intervallo dei punti in virgola mobile. Pensiamo agli operatori come ReLU, che riducono tutti i valori al di sotto di 0.0 nell’intervallo dei punti in virgola mobile a 0.0.

È importante per noi essere in grado di rappresentare il punto zero utilizzando lo stesso tipo di dato (int8 con segno o senza segno) dei valori quantizzati. Questo ci consente di effettuare questi confronti in modo rapido ed efficiente.

Successivamente, vediamo come la normalizzazione dell’attivazione aiuta nella quantizzazione del modello. Ci concentreremo specificamente su come la standardizzazione dei valori di attivazione ci consente di utilizzare efficacemente l’intero intervallo quantizzato.

Quantizzazione e Normalizzazione dell’Attivazione

La normalizzazione batch/layer modifica il tensore di attivazione in modo da avere media zero e varianza unitaria per canale o per layer.

Supponiamo di avere un tensore di input con un intervallo dei punti in virgola mobile di [2000.0 .. 4000.0]. Ecco come apparirebbe l’intervallo quantizzato.

Osserviamo che metà dell’intervallo quantizzato [-127 .. -1] non viene utilizzato. Questo è problematico poiché stiamo quantizzando l’intero intervallo dei punti in virgola mobile utilizzando solo 7 dei 8 bit disponibili. Ciò comporterà inevitabilmente un errore di quantizzazione più elevato e una ridotta precisione del modello. Per risolvere questo problema, applichiamo la normalizzazione di layer al tensore di attivazione.

Dopo l’applicazione della normalizzazione dei livelli ad un tensore di attivazione, il tensore di attivazione avrà un intervallo di numeri in virgola mobile compreso tra -2.0 e 2.0. Ciò può essere rappresentato nell’intervallo di numeri interi con segno int8 [-128 .. 127]. Per garantire la simmetria della distribuzione, limitiamo l’intervallo quantizzato a [-127 .. 127].

Pertanto, la normalizzazione evita buchi o parti inutilizzate nell’intervallo quantizzato.

Conclusioni

Abbiamo visto cosa sono la quantizzazione affine (asimmetrica) e la quantizzazione simmetrica e come differiscono. Abbiamo anche imparato cosa significano scala e punto zero e come calcolarli per entrambi questi tipi di schemi di quantizzazione.

In seguito, abbiamo visto la necessità di includere il float 0.0 nell’intervallo in virgola mobile e il motivo e il modo in cui ciò viene fatto nella pratica. Ciò comporta uno svantaggio, ovvero uno spazio sprecato nell’intervallo quantizzato.

Infine, abbiamo visto come la normalizzazione aiuta la quantizzazione portando le attivazioni in un intervallo fisso e evitando lo spreco di spazio nell’intervallo quantizzato. In effetti, la normalizzazione basata sulla media 0 può aiutare a convertire la quantizzazione affine in quantizzazione simmetrica e questo può velocizzare le operazioni durante l’inferenza.

Tutte le immagini in questo post sono state create dall’autore/i.

Riferimenti

  1. Efficient Deep Learning Book, Capitolo 2: Introduzione alle tecniche di compressione
  2. Hugging Face: Quantizzazione
  3. TensorRT: Quantizzazione
  4. Neural Network Distiller: Quantizzazione
  5. Lei Mao: Quantizzazione
  6. Quantizzazione di numeri in virgola mobile