Aide à protéger la Grande barrière de corail avec tensorflow sur Kaggle Rejoignez Défi

Quantification post-entraînement

La quantification post-entraînement est une technique de conversion qui peut réduire la taille du modèle tout en améliorant la latence du processeur et de l'accélérateur matériel, avec peu de dégradation de la précision du modèle. Vous pouvez quantifie un modèle tensorflow flottant déjà formé lorsque vous le convertir en tensorflow le format Lite à l' aide du tensorflow Lite Converter .

Méthodes d'optimisation

Vous avez le choix entre plusieurs options de quantification post-entraînement. Voici un tableau récapitulatif des choix et des avantages qu'ils procurent :

Technique Avantages Matériel
Quantification de la plage dynamique 4x plus petit, 2x-3x accélération CPU
Quantification entière entière 4x plus petit, 3x+ accélération CPU, Edge TPU, Microcontrôleurs
Quantification Float16 2x plus petit, accélération GPU CPU, GPU

L'arbre de décision suivant peut aider à déterminer quelle méthode de quantification post-formation est la meilleure pour votre cas d'utilisation :

options d'optimisation post-formation

Quantification de la plage dynamique

La forme la plus simple de quantification post-entraînement quantifie statiquement uniquement les poids de la virgule flottante à l'entier, qui a une précision de 8 bits :

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

Lors de l'inférence, les poids sont convertis de 8 bits de précision en virgule flottante et calculés à l'aide de noyaux à virgule flottante. Cette conversion est effectuée une fois et mise en cache pour réduire la latence.

Pour améliorer encore la latence, les opérateurs de « plage dynamique » quantifient dynamiquement les activations en fonction de leur plage jusqu'à 8 bits et effectuent des calculs avec des poids et des activations de 8 bits. Cette optimisation fournit des latences proches de l'inférence à virgule fixe complète. Cependant, les sorties sont toujours stockées en virgule flottante, de sorte que l'accélération avec les opérations de plage dynamique est inférieure à un calcul complet à virgule fixe.

Quantification entière entière

Vous pouvez obtenir d'autres améliorations de la latence, des réductions de l'utilisation maximale de la mémoire et une compatibilité avec les périphériques matériels ou les accélérateurs entiers uniquement en vous assurant que tous les calculs du modèle sont quantifiés par des entiers.

Pour une quantification entière, vous devez calibrer ou estimer la plage, c'est-à-dire (min, max) de tous les tenseurs à virgule flottante du modèle. Contrairement aux tenseurs constants tels que les poids et les biais, les tenseurs variables tels que l'entrée du modèle, les activations (sorties des couches intermédiaires) et la sortie du modèle ne peuvent être calibrés que si nous exécutons quelques cycles d'inférence. Par conséquent, le convertisseur a besoin d'un ensemble de données représentatif pour les calibrer. Cet ensemble de données peut être un petit sous-ensemble (environ 100 à 500 échantillons) des données d'apprentissage ou de validation. Reportez - vous à la representative_dataset() fonction ci - dessous.

Depuis la version 2.7 tensorflow, vous pouvez spécifier l'ensemble de données représentant par une signature comme l'exemple suivant:

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

S'il existe plusieurs signatures dans le modèle TensorFlow donné, vous pouvez spécifier le jeu de données multiple en spécifiant les clés de signature :

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,
      },
    )

Vous pouvez générer l'ensemble de données représentatif en fournissant une liste de tenseurs d'entrée :

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

Depuis la version TensorFlow 2.7, nous vous recommandons d'utiliser l'approche basée sur la signature plutôt que l'approche basée sur la liste des tenseurs d'entrée, car l'ordre des tenseurs d'entrée peut être facilement inversé.

À des fins de test, vous pouvez utiliser un jeu de données factice comme suit :

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

Entier avec repli flottant (en utilisant l'entrée/sortie flottante par défaut)

Afin de quantifier entièrement un modèle, mais d'utiliser des opérateurs flottants lorsqu'ils n'ont pas d'implémentation d'entier (pour garantir que la conversion se déroule en douceur), procédez comme suit :

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

Entier uniquement

Création de modèles uniquement des nombres entiers est un cas d'utilisation commune pour tensorflow Lite pour Microcontrôleurs et Coral bord PUT .

De plus, pour assurer la compatibilité avec les appareils uniquement entiers (tels que les microcontrôleurs 8 bits) et les accélérateurs (tels que le Coral Edge TPU), vous pouvez appliquer la quantification entière pour toutes les opérations, y compris l'entrée et la sortie, en procédant comme suit :

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

Quantification Float16

Vous pouvez réduire la taille d'un modèle à virgule flottante en quantifiant les poids à float16, la norme IEEE pour les nombres à virgule flottante 16 bits. Pour activer la quantification float16 des poids, procédez comme suit :

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

Les avantages de la quantification float16 sont les suivants :

  • Il réduit la taille du modèle jusqu'à la moitié (puisque tous les poids deviennent la moitié de leur taille d'origine).
  • Cela entraîne une perte minimale de précision.
  • Il prend en charge certains délégués (par exemple, le délégué GPU) qui peuvent fonctionner directement sur les données float16, ce qui entraîne une exécution plus rapide que les calculs float32.

Les inconvénients de la quantification float16 sont les suivants :

  • Il ne réduit pas autant la latence qu'une quantification en mathématiques à virgule fixe.
  • Par défaut, un modèle quantifié float16 "déquantifiera" les valeurs de poids en float32 lorsqu'il sera exécuté sur le CPU. (Notez que le délégué GPU n'effectuera pas cette déquantification, car il peut fonctionner sur des données float16.)

Entier uniquement : activations de 16 bits avec des poids de 8 bits (expérimental)

Il s'agit d'un schéma de quantification expérimental. Il est similaire au schéma « entier uniquement », mais les activations sont quantifiées en fonction de leur plage de 16 bits, les poids sont quantifiés en entier de 8 bits et le biais est quantifié en entier de 64 bits. C'est ce qu'on appelle en outre la quantification 16x8.

Le principal avantage de cette quantification est qu'elle peut améliorer considérablement la précision, mais n'augmenter que légèrement la taille du modèle.

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

Si la quantification 16x8 n'est pas prise en charge pour certains opérateurs dans le modèle, le modèle peut toujours être quantifié, mais les opérateurs non pris en charge sont conservés en flottant. L'option suivante doit être ajoutée à target_spec pour permettre cela.

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

Exemples de cas d'utilisation où les améliorations de précision fournies par ce schéma de quantification incluent : * la super-résolution, * le traitement du signal audio tel que la suppression du bruit et la formation de faisceau, * le débruitage d'image, * la reconstruction HDR à partir d'une seule image.

L'inconvénient de cette quantification est :

  • Actuellement, l'inférence est sensiblement plus lente que l'entier complet de 8 bits en raison du manque d'implémentation optimisée du noyau.
  • Actuellement, il est incompatible avec les délégués TFLite à accélération matérielle existants.

Un tutoriel pour ce mode de quantification se trouve ici .

Précision du modèle

Étant donné que les poids sont quantifiés après l'entraînement, il pourrait y avoir une perte de précision, en particulier pour les petits réseaux. Modèles entièrement pré-formés quantifiées sont prévus pour les réseaux spécifiques dans le référentiel de modèles Lite tensorflow . Il est important de vérifier la précision du modèle quantifié pour vérifier que toute dégradation de la précision se situe dans des limites acceptables. Il existe des outils pour évaluer la précision des modèles tensorflow Lite .

Par ailleurs, si la perte de précision est trop élevée, envisagez d' utiliser la formation au courant de quantification . Cependant, cela nécessite des modifications lors de l'entraînement du modèle pour ajouter de faux nœuds de quantification, alors que les techniques de quantification post-entraînement sur cette page utilisent un modèle pré-entraîné existant.

Représentation pour les tenseurs quantifiés

La quantification sur 8 bits se rapproche des valeurs à virgule flottante à l'aide de la formule suivante.

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

La représentation comporte deux parties principales :

  • Poids par axe (alias par canal) ou par tenseur représentés par des valeurs de complément à deux int8 dans la plage [-127, 127] avec un point zéro égal à 0.

  • Activations/entrées par tenseur représentées par des valeurs de complément à deux int8 dans la plage [-128, 127], avec un point zéro dans la plage [-128, 127].

Pour une vue détaillée de notre système de quantification, s'il vous plaît voir notre spécification de quantification . Les fournisseurs de matériel qui souhaitent se connecter à l'interface déléguée de TensorFlow Lite sont encouragés à mettre en œuvre le schéma de quantification qui y est décrit.