Grazie per esserti sintonizzato su Google I/O. Visualizza tutte le sessioni su richiesta Guarda su richiesta

Quantizzazione post-allenamento

La quantizzazione post-training è una tecnica di conversione che può ridurre le dimensioni del modello migliorando al contempo la latenza dell'acceleratore hardware e della CPU, con una riduzione minima dell'accuratezza del modello. Puoi quantizzare un modello TensorFlow float già addestrato quando lo converti nel formato TensorFlow Lite utilizzando TensorFlow Lite Converter .

Metodi di ottimizzazione

È possibile scegliere tra diverse opzioni di quantizzazione post-addestramento. Ecco una tabella riassuntiva delle scelte e dei vantaggi che offrono:

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

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

opzioni di ottimizzazione post-formazione

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ù veloce senza che tu debba fornire un set di dati rappresentativo per la calibrazione. Questo tipo di quantizzazione, quantizza staticamente solo i pesi da virgola mobile a numero intero al momento della conversione, che fornisce 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 "intervallo dinamico" 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 a inferenze a virgola fissa. Tuttavia, gli output vengono ancora memorizzati utilizzando la virgola mobile, quindi la maggiore velocità delle operazioni in intervallo dinamico è inferiore a un calcolo completo in virgola fissa.

Quantizzazione intera intera

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

Per la quantizzazione intera completa, è necessario calibrare o stimare l'intervallo, ovvero (min, max) di tutti i tensori in virgola mobile nel modello. A differenza dei tensori costanti come pesi e distorsioni, 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 eseguiamo 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, puoi 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 è presente più di una firma nel modello TensorFlow specificato, è possibile specificare il set di dati multipli specificando le chiavi della 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 2.7 di TensorFlow, consigliamo di utilizzare l'approccio basato sulla firma rispetto all'approccio basato sull'elenco di tensori di input perché l'ordinamento del tensore 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)]
 

Numero intero con fallback float (utilizzando l'input/output float predefinito)

Per quantizzare un modello completamente intero, ma utilizzare gli operatori float quando non hanno un'implementazione intera (per garantire che la conversione avvenga senza problemi), utilizzare 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
tflite_quant_model = converter.convert()

Solo intero

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

Inoltre, per garantire la compatibilità con i dispositivi solo interi (come i microcontrollori a 8 bit) e gli acceleratori (come Coral Edge TPU), è possibile applicare la quantizzazione intera intera 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 a virgola mobile quantizzando i pesi a float16, lo standard IEEE per i numeri a virgola mobile a 16 bit. Per abilitare la quantizzazione float16 dei pesi, utilizzare i seguenti passaggi:

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ù rapida rispetto ai calcoli float32.

Gli svantaggi della quantizzazione float16 sono i seguenti:

  • Non riduce la latenza tanto quanto una quantizzazione alla matematica a virgola fissa.
  • Per impostazione predefinita, un modello quantizzato float16 "dequantizza" 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 numeri interi: 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 un numero intero a 8 bit e il bias è quantizzato in un numero intero a 64 bit. Questo è indicato come quantizzazione 16x8 ulteriormente.

Il vantaggio principale di questa quantizzazione è che può migliorare significativamente la precisione, ma aumentare solo leggermente 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, il modello può comunque essere quantizzato, ma gli operatori non supportati vengono mantenuti in float. La seguente opzione dovrebbe essere aggiunta a target_spec per consentirlo.

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 della precisione 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 del numero intero a 8 bit a causa della mancanza di un'implementazione del kernel ottimizzata.
  • Attualmente non è compatibile 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 verificarsi una perdita di precisione, in particolare per le reti più piccole. Su TensorFlow Hub vengono forniti modelli pre-addestrati completamente quantizzati per reti specifiche. È importante controllare l'accuratezza del modello quantizzato per verificare che qualsiasi degradazione dell'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 dell'addestramento 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 usano un modello pre-addestrato esistente.

Rappresentazione per tensori quantizzati

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

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

La rappresentazione ha due parti principali:

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

  • Attivazioni/input 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, consulta 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 qui descritto.