Délégués GPU pour LiteRT

L'utilisation de processeurs graphiques (GPU) pour exécuter vos modèles de machine learning (ML) peut améliorer considérablement les performances de votre modèle et l'expérience utilisateur de vos applications compatibles avec le ML. LiteRT permet d'utiliser des GPU et d'autres processeurs spécialisés grâce à un pilote matériel appelé délégués. L'activation de l'utilisation des GPU avec vos applications LiteRT ML peut offrir les avantages suivants :

  • Vitesse : les GPU sont conçus pour un débit élevé de charges de travail massivement parallèles. Cette conception les rend bien adaptés aux réseaux neuronaux profonds, qui se composent d'un grand nombre d'opérateurs, chacun travaillant sur des Tensors d'entrée pouvant être traités en parallèle, ce qui entraîne généralement une latence plus faible. Dans le meilleur des cas, l'exécution de votre modèle sur un GPU peut être suffisamment rapide pour permettre des applications en temps réel qui n'étaient pas possibles auparavant.
  • Efficacité énergétique : les GPU effectuent les calculs de ML de manière très efficace et optimisée, en consommant généralement moins d'énergie et en générant moins de chaleur que la même tâche exécutée sur des CPU.

Ce document présente la compatibilité des GPU dans LiteRT et quelques utilisations avancées des processeurs GPU. Pour obtenir des informations plus spécifiques sur l'implémentation de la prise en charge des GPU sur des plates-formes spécifiques, consultez les guides suivants :

Compatibilité avec les opérations de ML sur GPU

Certaines opérations de ML TensorFlow, ou ops, ne peuvent pas être accélérées par le délégué GPU LiteRT. Le délégué est compatible avec les opérations suivantes en précision à virgule flottante 16 bits et 32 bits :

  • ADD
  • AVERAGE_POOL_2D
  • CONCATENATION
  • CONV_2D
  • DEPTHWISE_CONV_2D v1-2
  • EXP
  • FULLY_CONNECTED
  • LOGICAL_AND
  • LOGISTIC
  • LSTM v2 (Basic LSTM only)
  • MAX_POOL_2D
  • MAXIMUM
  • MINIMUM
  • MUL
  • PAD
  • PRELU
  • RELU
  • RELU6
  • RESHAPE
  • RESIZE_BILINEAR v1-3
  • SOFTMAX
  • STRIDED_SLICE
  • SUB
  • TRANSPOSE_CONV

Par défaut, toutes les opérations ne sont compatibles qu'avec la version 1. L'activation de la prise en charge de la quantification permet d'utiliser les versions appropriées, par exemple ADD v2.

Résoudre les problèmes liés à la compatibilité avec les GPU

Si certaines opérations ne sont pas compatibles avec le délégué GPU, le framework n'exécutera qu'une partie du graphique sur le GPU et le reste sur le CPU. En raison du coût élevé de la synchronisation CPU/GPU, un mode d'exécution fractionné comme celui-ci entraîne souvent des performances plus lentes que lorsque l'ensemble du réseau est exécuté sur le CPU seul. Dans ce cas, l'application génère un avertissement, par exemple :

WARNING: op code #42 cannot be handled by this delegate.

Il n'y a pas de rappel pour les échecs de ce type, car il ne s'agit pas d'un véritable échec d'exécution. Lorsque vous testez l'exécution de votre modèle avec le délégué GPU, vous devez être attentif à ces avertissements. Un grand nombre de ces avertissements peut indiquer que votre modèle n'est pas le mieux adapté à l'accélération GPU et qu'il peut nécessiter une refactorisation.

Exemples de modèles

Les exemples de modèles suivants sont conçus pour tirer parti de l'accélération GPU avec LiteRT. Ils sont fournis à titre de référence et de test :

Optimiser pour les GPU

Les techniques suivantes peuvent vous aider à améliorer les performances lors de l'exécution de modèles sur du matériel GPU à l'aide du délégué GPU LiteRT :

  • Opérations de remodelage : certaines opérations rapides sur un processeur peuvent avoir un coût élevé pour le GPU sur les appareils mobiles. Les opérations de remodelage sont particulièrement coûteuses à exécuter, y compris BATCH_TO_SPACE, SPACE_TO_BATCH, SPACE_TO_DEPTH, etc. Vous devez examiner attentivement l'utilisation des opérations de remodelage et tenir compte du fait qu'elles peuvent avoir été appliquées uniquement pour explorer les données ou pour les premières itérations de votre modèle. Les supprimer peut améliorer considérablement les performances.

  • Canaux de données d'image : sur le GPU, les données Tensor sont divisées en quatre canaux. Par conséquent, un calcul sur un Tensor de forme [B,H,W,5] est à peu près le même sur un Tensor de forme [B,H,W,8], mais nettement moins bon que sur un Tensor de forme [B,H,W,4]. Si le matériel de caméra que vous utilisez est compatible avec les frames d'image au format RGBA, l'utilisation de cette entrée à quatre canaux est beaucoup plus rapide, car elle évite une copie de mémoire du format RVB à trois canaux vers le format RVBX à quatre canaux.

  • Modèles optimisés pour le mobile : pour des performances optimales, vous devez envisager de réentraîner votre classificateur avec une architecture réseau optimisée pour le mobile. L'optimisation pour l'inférence sur l'appareil peut réduire considérablement la latence et la consommation d'énergie en tirant parti des fonctionnalités matérielles mobiles.

Compatibilité avancée avec les GPU

Vous pouvez utiliser des techniques avancées supplémentaires avec le traitement par GPU pour améliorer encore les performances de vos modèles, y compris la quantification et la sérialisation. Les sections suivantes décrivent ces techniques plus en détail.

Utiliser des modèles quantifiés

Cette section explique comment le délégué GPU accélère les modèles quantifiés sur 8 bits, y compris les suivants :

Pour optimiser les performances, utilisez des modèles qui comportent à la fois des Tensors d'entrée et de sortie à virgule flottante.

Fonctionnement

Étant donné que le backend GPU n'est compatible qu'avec l'exécution à virgule flottante, nous exécutons les modèles quantifiés en leur donnant une "vue à virgule flottante" du modèle d'origine. En règle générale, vous devez suivre les étapes ci-dessous :

  • Les Tensors constants (tels que les pondérations/biais) sont déquantifiés une seule fois dans la mémoire du GPU. Cette opération a lieu lorsque le délégué est activé pour LiteRT.

  • Les entrées et sorties du programme GPU, si elles sont quantifiées sur 8 bits, sont respectivement déquantifiées et quantifiées pour chaque inférence. Cette opération est effectuée sur le processeur à l'aide des noyaux optimisés de LiteRT.

  • Les simulateurs de quantification sont insérés entre les opérations pour imiter le comportement quantifié. Cette approche est nécessaire pour les modèles dans lesquels les opérations s'attendent à ce que les activations suivent les limites apprises lors de la quantification.

Pour savoir comment activer cette fonctionnalité avec le délégué GPU, consultez les ressources suivantes :

Réduire le temps d'initialisation avec la sérialisation

La fonctionnalité de délégué GPU vous permet de charger le code du noyau précompilé et les données du modèle sérialisées et enregistrées sur le disque lors des exécutions précédentes. Cette approche évite la recompilation et peut réduire le temps de démarrage jusqu'à 90 %. Cette amélioration est obtenue en échangeant de l'espace disque contre un gain de temps. Vous pouvez activer cette fonctionnalité avec quelques options de configuration, comme indiqué dans les exemples de code suivants :

C++

    TfLiteGpuDelegateOptionsV2 options = TfLiteGpuDelegateOptionsV2Default();
    options.experimental_flags |= TFLITE_GPU_EXPERIMENTAL_FLAGS_ENABLE_SERIALIZATION;
    options.serialization_dir = kTmpDir;
    options.model_token = kModelToken;

    auto* delegate = TfLiteGpuDelegateV2Create(options);
    if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;
      

Java

    GpuDelegate delegate = new GpuDelegate(
      new GpuDelegate.Options().setSerializationParams(
        /* serializationDir= */ serializationDir,
        /* modelToken= */ modelToken));

    Interpreter.Options options = (new Interpreter.Options()).addDelegate(delegate);
      

Lorsque vous utilisez la fonctionnalité de sérialisation, assurez-vous que votre code respecte les règles d'implémentation suivantes :

  • Stockez les données de sérialisation dans un répertoire qui n'est pas accessible aux autres applications. Sur les appareils Android, utilisez getCodeCacheDir(), qui pointe vers un emplacement privé pour l'application actuelle.
  • Le jeton de modèle doit être unique à l'appareil pour le modèle spécifique. Vous pouvez calculer un jeton de modèle en générant une empreinte digitale à partir des données du modèle à l'aide de bibliothèques telles que farmhash::Fingerprint64.