Quantizzazione post-allenamento

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

La quantizzazione post-allenamento è una tecnica di conversione che può ridurre le dimensioni del modello migliorando al tempo stesso la latenza della CPU e dell'acceleratore hardware, con un leggero degrado nell'accuratezza del modello. È possibile quantizzare un modello TensorFlow float già addestrato quando lo si converte in formato TensorFlow Lite utilizzando il convertitore TensorFlow Lite .

Metodi di ottimizzazione

Ci sono diverse opzioni di quantizzazione post-allenamento tra cui scegliere. Di seguito una tabella riassuntiva delle scelte e dei vantaggi che offrono:

Tecnica Benefici Hardware
Quantizzazione della gamma dinamica 4 volte più piccolo, 2x-3x accelerazione processore
Quantizzazione intera intera 4 volte più piccolo, 3 volte più velocità CPU, Edge TPU, microcontrollori
Quantizzazione Float16 2 volte più piccola, accelerazione GPU CPU, GPU

Il seguente albero decisionale può aiutare a determinare quale metodo di quantizzazione post-formazione è il migliore per il tuo caso d'uso:

opzioni di ottimizzazione post-allenamento

Quantizzazione della gamma dinamica

La quantizzazione dell'intervallo dinamico è un punto di partenza consigliato perché fornisce un utilizzo ridotto della memoria e un calcolo più rapido senza che sia necessario fornire un set di dati rappresentativo per la calibrazione. Questo tipo di quantizzazione quantizza staticamente solo i pesi da virgola mobile a intero al momento della conversione, fornendo 8 bit di precisione:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()

Per ridurre ulteriormente la latenza durante l'inferenza, gli operatori di "gamma dinamica" quantizzano dinamicamente le attivazioni in base al loro intervallo a 8 bit ed eseguono calcoli con pesi e attivazioni a 8 bit. Questa ottimizzazione fornisce latenze vicine alle inferenze a virgola fissa. Tuttavia, le uscite vengono ancora archiviate utilizzando la virgola mobile, quindi l'aumento della velocità delle operazioni in gamma dinamica è inferiore a un calcolo a virgola fissa completo.

Quantizzazione intera intera

È possibile ottenere ulteriori miglioramenti della latenza, riduzioni nell'utilizzo di picco della memoria e compatibilità con dispositivi hardware o acceleratori di soli interi assicurandosi che tutta la matematica del modello sia quantizzata con numeri interi.

Per la quantizzazione intera completa, è necessario calibrare o stimare l'intervallo, cioè (min, max) di tutti i tensori a virgola mobile nel modello. A differenza dei tensori costanti come pesi e bias, i tensori variabili come l'input del modello, le attivazioni (output degli strati intermedi) e l'output del modello non possono essere calibrati a meno che non si eseguano alcuni cicli di inferenza. Di conseguenza, il convertitore richiede un set di dati rappresentativo per calibrarli. Questo set di dati può essere un piccolo sottoinsieme (circa 100-500 campioni) dei dati di addestramento o convalida. Fare riferimento alla funzione representative_dataset() di seguito.

Dalla versione TensorFlow 2.7, è possibile specificare il set di dati rappresentativo tramite una firma come nell'esempio seguente:

def representative_dataset():
  for data in dataset:
    yield {
      "image": data.image,
      "bias": data.bias,
    }

Se sono presenti più firme nel modello TensorFlow specificato, è possibile specificare il set di dati multiplo specificando le chiavi di firma:

def representative_dataset():
  # Feed data set for the "encode" signature.
  for data in encode_signature_dataset:
    yield (
      "encode", {
        "image": data.image,
        "bias": data.bias,
      }
    )

  # Feed data set for the "decode" signature.
  for data in decode_signature_dataset:
    yield (
      "decode", {
        "image": data.image,
        "hint": data.hint,
      },
    )

È possibile generare il set di dati rappresentativo fornendo un elenco di tensori di input:

def representative_dataset():
  for data in tf.data.Dataset.from_tensor_slices((images)).batch(1).take(100):
    yield [tf.dtypes.cast(data, tf.float32)]

A partire dalla versione TensorFlow 2.7, si consiglia di utilizzare l'approccio basato sulla firma rispetto all'approccio basato sull'elenco dei tensori di input poiché l'ordine dei tensori di input può essere facilmente capovolto.

A scopo di test, puoi utilizzare un set di dati fittizio come segue:

def representative_dataset():
    for _ in range(100):
      data = np.random.rand(1, 244, 244, 3)
      yield [data.astype(np.float32)]
 

Intero con float fallback (usando l'input/output float predefinito)

Per quantizzare completamente un modello intero, ma utilizzare gli operatori float quando non hanno un'implementazione intera (per garantire che la conversione avvenga senza intoppi), attenersi alla seguente procedura:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
tflite_quant_model = converter.convert()

Solo intero

La creazione di modelli di soli interi è un caso d'uso comune per TensorFlow Lite per microcontrollori e TPU Coral Edge .

Inoltre, per garantire la compatibilità con dispositivi di soli interi (come microcontroller a 8 bit) e acceleratori (come Coral Edge TPU), puoi applicare la quantizzazione intera completa per tutte le operazioni, inclusi input e output, utilizzando i seguenti passaggi:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_quant_model = converter.convert()

Quantizzazione Float16

È possibile ridurre le dimensioni di un modello in virgola mobile quantizzando i pesi in float16, lo standard IEEE per i numeri in virgola mobile a 16 bit. Per abilitare la quantizzazione float16 dei pesi, attenersi alla seguente procedura:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_quant_model = converter.convert()

I vantaggi della quantizzazione float16 sono i seguenti:

  • Riduce le dimensioni del modello fino alla metà (poiché tutti i pesi diventano la metà della loro dimensione originale).
  • Provoca una perdita minima di precisione.
  • Supporta alcuni delegati (ad esempio il delegato GPU) che possono operare direttamente sui dati float16, risultando in un'esecuzione più veloce rispetto ai calcoli float32.

Gli svantaggi della quantizzazione float16 sono i seguenti:

  • Non riduce la latenza tanto quanto una quantizzazione in matematica a virgola fissa.
  • Per impostazione predefinita, un modello quantizzato float16 "dequantizzerà" i valori dei pesi in float32 quando viene eseguito sulla CPU. (Si noti che il delegato GPU non eseguirà questa dequantizzazione, poiché può operare su dati float16.)

Solo intero: attivazioni a 16 bit con pesi a 8 bit (sperimentale)

Questo è uno schema di quantizzazione sperimentale. È simile allo schema "solo intero", ma le attivazioni sono quantizzate in base al loro intervallo a 16 bit, i pesi sono quantizzati in numeri interi a 8 bit e la distorsione è quantizzata in numeri interi a 64 bit. Questa viene ulteriormente definita quantizzazione 16x8.

Il vantaggio principale di questa quantizzazione è che può migliorare significativamente la precisione, ma solo leggermente aumentare le dimensioni del modello.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8]
tflite_quant_model = converter.convert()

Se la quantizzazione 16x8 non è supportata per alcuni operatori nel modello, è ancora possibile quantizzare il modello, ma gli operatori non supportati sono mantenuti in float. La seguente opzione dovrebbe essere aggiunta a target_spec per consentire ciò.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8,
tf.lite.OpsSet.TFLITE_BUILTINS]
tflite_quant_model = converter.convert()

Esempi di casi d'uso in cui i miglioramenti dell'accuratezza forniti da questo schema di quantizzazione includono: * super risoluzione, * elaborazione del segnale audio come cancellazione del rumore e beamforming, * riduzione del rumore dell'immagine, * ricostruzione HDR da una singola immagine.

Lo svantaggio di questa quantizzazione è:

  • Attualmente l'inferenza è notevolmente più lenta dell'intero intero a 8 bit a causa della mancanza di un'implementazione ottimizzata del kernel.
  • Attualmente è incompatibile con i delegati TFLite con accelerazione hardware esistenti.

Un tutorial per questa modalità di quantizzazione può essere trovato qui .

Precisione del modello

Poiché i pesi vengono quantizzati dopo l'allenamento, potrebbe esserci una perdita di precisione, in particolare per le reti più piccole. Modelli pre-addestrati completamente quantizzati sono forniti per reti specifiche su TensorFlow Hub . È importante verificare l'accuratezza del modello quantizzato per verificare che qualsiasi degrado nell'accuratezza rientri nei limiti accettabili. Sono disponibili strumenti per valutare l' accuratezza del modello TensorFlow Lite .

In alternativa, se il calo di precisione è troppo elevato, prendere in considerazione l'utilizzo di un training consapevole della quantizzazione . Tuttavia, ciò richiede modifiche durante l'addestramento del modello per aggiungere falsi nodi di quantizzazione, mentre le tecniche di quantizzazione post-addestramento in questa pagina utilizzano un modello pre-addestrato esistente.

Rappresentazione per tensori quantizzati

La quantizzazione a 8 bit approssima i valori in virgola mobile utilizzando la formula seguente.

\[real\_value = (int8\_value - zero\_point) \times scale\]

La rappresentazione si compone di due parti principali:

  • Pesi per asse (aka per canale) o per tensore rappresentati da int8 valori di complemento a due nell'intervallo [-127, 127] con punto zero uguale a 0.

  • Attivazioni/ingressi per tensore rappresentati da int8 valori di complemento a due nell'intervallo [-128, 127], con un punto zero nell'intervallo [-128, 127].

Per una visione dettagliata del nostro schema di quantizzazione, vedere le nostre specifiche di quantizzazione . I fornitori di hardware che desiderano collegarsi all'interfaccia delegata di TensorFlow Lite sono incoraggiati a implementare lo schema di quantizzazione ivi descritto.