Quantizzazione post-allenamento

La quantizzazione post-addestramento è una tecnica di conversione che può ridurre le dimensioni del modello migliorando anche la latenza della CPU e dell'acceleratore hardware, con un degrado minimo dell'accuratezza del modello. È possibile quantizzare un modello tensorflow galleggiante già addestrato quando si converte in formato tensorflow Lite utilizzando il tensorflow Lite Converter .

Metodi di ottimizzazione

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

Tecnica Benefici Hardware
Quantizzazione della gamma dinamica 4x più piccolo, 2x-3x accelerazione 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-allenamento

Quantizzazione della gamma dinamica

La forma più semplice di quantizzazione post-addestramento quantizza staticamente solo i pesi da virgola mobile a intero, che ha una precisione di 8 bit:

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()

All'inferenza, i pesi vengono convertiti da 8 bit di precisione in virgola mobile e calcolati utilizzando kernel in virgola mobile. Questa conversione viene eseguita una volta e memorizzata nella cache per ridurre la latenza.

Per migliorare ulteriormente la latenza, 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 all'inferenza a virgola fissa. Tuttavia, gli output vengono ancora archiviati utilizzando la virgola mobile in modo che l'accelerazione con le operazioni a intervallo dinamico sia inferiore a un calcolo a virgola fissa completo.

Quantizzazione intera intera

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

Per la quantizzazione completa di interi, è necessario calibrare o stimare l'intervallo, ovvero (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. Consultare la representative_dataset() funzione qui sotto.

Da tensorflow 2.7 versione, è possibile specificare il set di dati rappresentativo attraverso una firma come il seguente esempio:

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

È 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)]

Dalla versione TensorFlow 2.7, si consiglia di utilizzare l'approccio basato sulla firma rispetto all'approccio basato sull'elenco dei tensori di input perché l'ordinamento del tensore di input può essere facilmente capovolto.

A scopo di test, è possibile 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 fallback float (usando input/output float predefinito)

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

Creazione intero solo i modelli è un caso d'uso comune per tensorflow Lite per microcontrollori e Coral bordo TPU .

Inoltre, per garantire la compatibilità con dispositivi solo interi (come i microcontrollori a 8 bit) e acceleratori (come Coral Edge TPU), è possibile 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 su float16, lo standard IEEE per i numeri in virgola mobile a 16 bit. Per abilitare la quantizzazione dei pesi float16, 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).
  • Causa 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 punto fisso.
  • Per impostazione predefinita, un modello quantizzato float16 "dequantizza" i valori dei pesi a 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 un intero a 8 bit e il bias è quantizzato in un intero a 64 bit. Questo è indicato come quantizzazione 16x8 ulteriormente.

Il vantaggio principale di questa quantizzazione è che può migliorare significativamente l'accuratezza, 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ò ancora essere quantizzato, ma gli operatori non supportati vengono mantenuti in virgola mobile. 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 della precisione forniti da questo schema di quantizzazione includono: * super risoluzione, * elaborazione del segnale audio come cancellazione del rumore e formazione del fascio, * 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 del kernel ottimizzata.
  • 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 verificarsi una perdita di precisione, in particolare per le reti più piccole. Modelli completamente quantizzate Pre-addestrati sono previste per le reti specifiche nel repository di modelli tensorflow Lite . È importante controllare l'accuratezza del modello quantizzato per verificare che qualsiasi degrado dell'accuratezza rientri nei limiti accettabili. Ci sono strumenti per valutare tensorflow Lite precisione del modello .

In alternativa, se il calo precisione è troppo alto, è possibile utilizzare la quantizzazione di formazione a conoscenza . Tuttavia, ciò richiede modifiche durante l'addestramento del modello per aggiungere nodi di quantizzazione falsi, 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 ha due parti principali:

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

  • Attivazioni/ingressi per tensore rappresentati da valori int8 in 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 la nostra spec quantizzazione . I fornitori di hardware che desiderano collegarsi all'interfaccia delegata di TensorFlow Lite sono incoraggiati a implementare lo schema di quantizzazione ivi descritto.