Optimiser les performances de TensorFlow à l'aide du profileur

Ce guide explique comment utiliser les outils disponibles avec le profileur TensorFlow pour suivre les performances de vos modèles TensorFlow. Vous apprendrez à comprendre les performances de votre modèle sur l'hôte (CPU), le périphérique (GPU) ou sur une combinaison de l'hôte et du ou des périphériques.

Le profilage permet de comprendre la consommation de ressources matérielles (temps et mémoire) des différentes opérations TensorFlow (ops) dans votre modèle, de résoudre les goulots d'étranglement de performances et, en fin de compte, d'accélérer l'exécution du modèle.

Ce guide vous expliquera comment installer le profileur, les différents outils disponibles, les différents modes de collecte des données de performances par le profileur et quelques bonnes pratiques recommandées pour optimiser les performances du modèle.

Si vous souhaitez profiler les performances de votre modèle sur les Cloud TPU, reportez-vous au guide Cloud TPU .

Installer les prérequis du profileur et du GPU

Installez le plugin Profiler pour TensorBoard avec pip. Notez que le Profiler nécessite les dernières versions de TensorFlow et TensorBoard (>=2.2).

pip install -U tensorboard_plugin_profile

Pour profiler sur le GPU, vous devez :

  1. Répondez aux pilotes NVIDIA® GPU et aux exigences de CUDA® Toolkit répertoriées dans la section Configuration logicielle requise pour le support GPU TensorFlow .
  2. Assurez-vous que l' interface des outils de profilage NVIDIA® CUDA® (CUPTI) existe sur le chemin :

    /sbin/ldconfig -N -v $(sed 's/:/ /g' <<< $LD_LIBRARY_PATH) | \
    grep libcupti
    

Si vous n'avez pas CUPTI sur le chemin, ajoutez son répertoire d'installation à la variable d'environnement $LD_LIBRARY_PATH en exécutant :

export LD_LIBRARY_PATH=/usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH

Ensuite, exécutez à nouveau la commande ldconfig ci-dessus pour vérifier que la bibliothèque CUPTI est trouvée.

Résoudre les problèmes de privilège

Lorsque vous exécutez le profilage avec CUDA® Toolkit dans un environnement Docker ou sous Linux, vous pouvez rencontrer des problèmes liés à des privilèges CUPTI insuffisants ( CUPTI_ERROR_INSUFFICIENT_PRIVILEGES ). Accédez à NVIDIA Developer Docs pour en savoir plus sur la façon de résoudre ces problèmes sous Linux.

Pour résoudre les problèmes de privilège CUPTI dans un environnement Docker, exécutez

docker run option '--privileged=true'

Outils de profileur

Accédez au profileur à partir de l'onglet Profil dans TensorBoard, qui n'apparaît qu'après avoir capturé certaines données de modèle.

Le profileur propose une sélection d'outils pour aider à l'analyse des performances :

  • Page de présentation
  • Analyseur de pipeline d'entrée
  • Statistiques TensorFlow
  • Visionneuse de traces
  • Statistiques du noyau GPU
  • Outil de profil de mémoire
  • Visionneuse de modules

Page de présentation

La page de présentation fournit une vue de niveau supérieur des performances de votre modèle lors d'une exécution de profil. La page vous montre une page de présentation agrégée pour votre hôte et tous les appareils, et quelques recommandations pour améliorer les performances de formation de votre modèle. Vous pouvez également sélectionner des hôtes individuels dans la liste déroulante Hôte.

La page de présentation affiche les données comme suit :

image

  • Résumé des performances : affiche un résumé de haut niveau des performances de votre modèle. Le résumé des performances comporte deux parties :

    1. Répartition du temps de pas : décompose le temps de pas moyen en plusieurs catégories de temps passé :

      • Compilation : Temps passé à compiler les noyaux.
      • Entrée : temps passé à lire les données d'entrée.
      • Sortie : temps passé à lire les données de sortie.
      • Lancement du noyau : temps passé par l'hôte pour lancer les noyaux
      • Temps de calcul de l'hôte..
      • Temps de communication appareil à appareil.
      • Temps de calcul sur l'appareil.
      • Tous les autres, y compris les frais généraux Python.
    2. Précisions de calcul de l'appareil - Indique le pourcentage de temps de calcul de l'appareil qui utilise des calculs 16 et 32 ​​bits.

  • Graphique de temps de pas : affiche un graphique du temps de pas de l'appareil (en millisecondes) sur tous les pas échantillonnés. Chaque étape est divisée en plusieurs catégories (avec des couleurs différentes) où le temps est passé. La zone rouge correspond à la partie du temps d'étape pendant laquelle les périphériques étaient inactifs en attendant les données d'entrée de l'hôte. La zone verte indique combien de temps l'appareil a réellement fonctionné.

  • Top 10 des opérations TensorFlow sur l'appareil (par exemple, GPU) : affiche les opérations sur l'appareil qui ont duré le plus longtemps.

    Chaque ligne affiche le temps propre d'une opération (sous forme de pourcentage de temps pris par toutes les opérations), le temps cumulé, la catégorie et le nom.

  • Environnement d'exécution : affiche un résumé de haut niveau de l'environnement d'exécution du modèle, notamment :

    • Nombre d'hôtes utilisés.
    • Type de périphérique (GPU/TPU).
    • Nombre de cœurs de périphérique.
  • Recommandation pour l'étape suivante : signale lorsqu'un modèle est lié à l'entrée et recommande des outils que vous pouvez utiliser pour localiser et résoudre les goulots d'étranglement des performances du modèle.

Analyseur de pipeline d'entrée

Lorsqu'un programme TensorFlow lit les données d'un fichier, il commence en haut du graphique TensorFlow de manière pipeline. Le processus de lecture est divisé en plusieurs étapes de traitement de données connectées en série, où la sortie d'une étape est l'entrée de la suivante. Ce système de lecture des données s'appelle le pipeline d'entrée .

Un pipeline typique de lecture d'enregistrements à partir de fichiers comporte les étapes suivantes :

  1. Lecture de dossier.
  2. Prétraitement du fichier (optionnel).
  3. Transfert de fichiers de l'hôte vers l'appareil.

Un pipeline d'entrée inefficace peut ralentir considérablement votre application. Une application est considérée comme liée à l'entrée lorsqu'elle passe une partie importante du temps dans le pipeline d'entrée. Utilisez les informations obtenues à partir de l'analyseur de pipeline d'entrée pour comprendre où le pipeline d'entrée est inefficace.

L'analyseur de pipeline d'entrée vous indique immédiatement si votre programme est lié à l'entrée et vous guide à travers l'analyse côté périphérique et côté hôte pour déboguer les goulots d'étranglement des performances à n'importe quelle étape du pipeline d'entrée.

Consultez les conseils sur les performances du pipeline d'entrée pour connaître les meilleures pratiques recommandées pour optimiser vos pipelines d'entrée de données.

Tableau de bord du pipeline d'entrée

Pour ouvrir l'analyseur de pipeline d'entrée, sélectionnez Profil , puis sélectionnez input_pipeline_analyzer dans la liste déroulante Outils .

image

Le tableau de bord contient trois sections :

  1. Summary : Résume le pipeline d'entrée global avec des informations indiquant si votre application est liée à l'entrée et, si c'est le cas, dans quelle mesure.
  2. Analyse côté appareil : affiche les résultats détaillés de l'analyse côté appareil, y compris le temps d'étape de l'appareil et la plage de temps passé par l'appareil à attendre les données d'entrée sur les cœurs à chaque étape.
  3. Analyse côté hôte : affiche une analyse détaillée côté hôte, y compris une répartition du temps de traitement des entrées sur l'hôte.

Résumé du pipeline d'entrée

Le récapitulatif signale si votre programme est lié à l'entrée en présentant le pourcentage de temps passé par l'appareil à attendre l'entrée de l'hôte. Si vous utilisez un pipeline d'entrée standard qui a été instrumenté, l'outil signale où la majeure partie du temps de traitement des entrées est passée.

Analyse côté appareil

L'analyse côté appareil fournit des informations sur le temps passé sur l'appareil par rapport à l'hôte et sur le temps passé par l'appareil à attendre les données d'entrée de l'hôte.

  1. Temps de pas tracé par rapport au numéro de pas : affiche un graphique du temps de pas de l'appareil (en millisecondes) sur tous les pas échantillonnés. Chaque étape est divisée en plusieurs catégories (avec des couleurs différentes) où le temps est passé. La zone rouge correspond à la partie du temps d'étape pendant laquelle les périphériques étaient inactifs en attendant les données d'entrée de l'hôte. La zone verte indique combien de temps l'appareil a réellement fonctionné.
  2. Statistiques de temps de pas : indique la moyenne, l'écart type et la plage ([minimum, maximum]) du temps de pas de l'appareil.

Analyse côté hôte

L'analyse côté hôte rapporte une répartition du temps de traitement des entrées (le temps passé sur les opérations de l'API tf.data ) sur l'hôte en plusieurs catégories :

  • Lecture de données à partir de fichiers à la demande : temps passé à lire des données à partir de fichiers sans mise en cache, préchargement et entrelacement.
  • Lecture à l'avance des données des fichiers : temps passé à lire les fichiers, y compris la mise en cache, la prélecture et l'entrelacement.
  • Prétraitement des données : temps consacré aux opérations de prétraitement, telles que la décompression d'images.
  • Mise en file d'attente des données à transférer vers l'appareil : temps passé à mettre les données dans une file d'attente d'alimentation avant de les transférer vers l'appareil.

Développez Statistiques des opérations d'entrée pour inspecter les statistiques des opérations d'entrée individuelles et leurs catégories ventilées par temps d'exécution.

image

Un tableau de données source apparaîtra avec chaque entrée contenant les informations suivantes :

  1. Opération d'entrée : affiche le nom de l'opération TensorFlow de l'opération d'entrée.
  2. Count : affiche le nombre total d'instances d'exécution d'opérations pendant la période de profilage.
  3. Temps total (en ms) : affiche la somme cumulée du temps passé sur chacune de ces instances.
  4. % de temps total : affiche le temps total passé sur une opération sous forme de fraction du temps total passé dans le traitement des entrées.
  5. Temps propre total (en ms) : affiche la somme cumulée du temps propre passé sur chacune de ces instances. Le temps propre mesure ici le temps passé à l'intérieur du corps de la fonction, à l'exclusion du temps passé dans la fonction qu'il appelle.
  6. % de temps libre total . Affiche le temps libre total sous forme de fraction du temps total consacré au traitement des entrées.
  7. Catégorie . Affiche la catégorie de traitement de l'op d'entrée.

Statistiques TensorFlow

L'outil TensorFlow Stats affiche les performances de chaque opération TensorFlow exécutée sur l'hôte ou l'appareil au cours d'une session de profilage.

image

L'outil affiche les informations sur les performances dans deux volets :

  • Le volet supérieur affiche jusqu'à quatre graphiques à secteurs :

    1. La distribution du temps d'auto-exécution de chaque opération sur l'hôte.
    2. La distribution du temps d'auto-exécution de chaque type d'opération sur l'hôte.
    3. La distribution du temps d'auto-exécution de chaque opération sur l'appareil.
    4. La distribution du temps d'auto-exécution de chaque type d'opération sur l'appareil.
  • Le volet inférieur affiche un tableau qui présente des données sur les opérations TensorFlow avec une ligne pour chaque opération et une colonne pour chaque type de données (triez les colonnes en cliquant sur l'en-tête de la colonne). Cliquez sur le bouton Exporter au format CSV sur le côté droit du volet supérieur pour exporter les données de ce tableau sous forme de fichier CSV.

    Notez que:

    • Si des ops ont des ops enfants :

      • Le temps total "accumulé" d'une opération inclut le temps passé à l'intérieur de l'opération enfant.
      • Le temps "auto" total d'une op n'inclut pas le temps passé à l'intérieur de l'op enfant.
    • Si une opération s'exécute sur l'hôte :

      • Le pourcentage du temps libre total sur l'appareil encouru par l'op on sera de 0.
      • Le pourcentage cumulé du temps libre total sur l'appareil jusqu'à cette opération incluse sera de 0.
    • Si une opération s'exécute sur l'appareil :

      • Le pourcentage du temps libre total sur l'hôte encouru par cette opération sera de 0.
      • Le pourcentage cumulé du temps libre total sur l'hôte jusqu'à cette opération incluse sera de 0.

Vous pouvez choisir d'inclure ou d'exclure le temps d'inactivité dans les graphiques à secteurs et le tableau.

Visionneuse de traces

Le visualiseur de traces affiche une chronologie qui indique :

  • Durées des opérations exécutées par votre modèle TensorFlow
  • Quelle partie du système (hôte ou périphérique) a exécuté une opération. En règle générale, l'hôte exécute les opérations d'entrée, prétraite les données de formation et les transfère à l'appareil, tandis que l'appareil exécute la formation réelle du modèle.

La visionneuse de traces vous permet d'identifier les problèmes de performances dans votre modèle, puis de prendre des mesures pour les résoudre. Par exemple, à un niveau élevé, vous pouvez identifier si la formation des entrées ou des modèles prend la majorité du temps. En descendant, vous pouvez identifier les opérations qui prennent le plus de temps à s'exécuter. Notez que la visionneuse de trace est limitée à 1 million d'événements par appareil.

Interface de visualisation des traces

Lorsque vous ouvrez la visionneuse de traces, elle apparaît et affiche votre exécution la plus récente :

image

Cet écran contient les principaux éléments suivants :

  1. Volet Chronologie : affiche les opérations que l'appareil et l'hôte ont exécutées au fil du temps.
  2. Volet Détails : affiche des informations supplémentaires pour les opérations sélectionnées dans le volet Chronologie.

Le volet Chronologie contient les éléments suivants :

  1. Barre supérieure : Contient diverses commandes auxiliaires.
  2. Axe de temps : affiche le temps par rapport au début de la trace.
  3. Étiquettes de section et de piste : chaque section contient plusieurs pistes et comporte un triangle sur la gauche sur lequel vous pouvez cliquer pour développer et réduire la section. Il y a une section pour chaque élément de traitement dans le système.
  4. Sélecteur d'outils : contient divers outils permettant d'interagir avec la visionneuse de traces, tels que le zoom, le panoramique, la sélection et la synchronisation. Utilisez l'outil Chronométrage pour marquer un intervalle de temps.
  5. Événements : ils indiquent le temps pendant lequel une opération a été exécutée ou la durée des méta-événements, tels que les étapes d'entraînement.
Tronçons et pistes

Le visualiseur de traces contient les sections suivantes :

  • Une section pour chaque nœud de périphérique , étiquetée avec le numéro de la puce de périphérique et le nœud de périphérique dans la puce (par exemple, /device:GPU:0 (pid 0) ). Chaque section de nœud de périphérique contient les pistes suivantes :
    • Étape : affiche la durée des étapes d'entraînement en cours d'exécution sur l'appareil
    • TensorFlow Ops : affiche les opérations exécutées sur l'appareil
    • XLA Ops : affiche les opérations XLA (ops) exécutées sur l'appareil si XLA est le compilateur utilisé (chaque opération TensorFlow est traduite en une ou plusieurs opérations XLA. Le compilateur XLA traduit les opérations XLA en code qui s'exécute sur l'appareil).
  • Une section pour les threads s'exécutant sur le processeur de la machine hôte, intitulée "Host Threads" . La section contient une piste pour chaque thread CPU. Notez que vous pouvez ignorer les informations affichées à côté des étiquettes de section.
Événements

Les événements de la chronologie sont affichés dans différentes couleurs ; les couleurs elles-mêmes n'ont pas de signification spécifique.

La visionneuse de traces peut également afficher des traces d'appels de fonctions Python dans votre programme TensorFlow. Si vous utilisez l'API tf.profiler.experimental.start , vous pouvez activer le traçage Python en utilisant le tuple nommé ProfilerOptions lors du démarrage du profilage. Sinon, si vous utilisez le mode d'échantillonnage pour le profilage, vous pouvez sélectionner le niveau de traçage à l'aide des options de la liste déroulante dans la boîte de dialogue Capturer le profil .

image

Statistiques du noyau GPU

Cet outil affiche les statistiques de performances et l'opération d'origine pour chaque noyau accéléré par GPU.

image

L'outil affiche les informations dans deux volets :

  • Le volet supérieur affiche un graphique à secteurs qui montre les noyaux CUDA qui ont le temps total écoulé le plus élevé.

  • Le volet inférieur affiche un tableau avec les données suivantes pour chaque paire noyau-op unique :

    • Un classement par ordre décroissant de la durée totale écoulée du GPU, regroupé par paire noyau-opération.
    • Le nom du noyau lancé.
    • Le nombre de registres GPU utilisés par le noyau.
    • La taille totale de la mémoire partagée (partagée statique + dynamique) utilisée en octets.
    • La dimension de bloc exprimée sous blockDim.x, blockDim.y, blockDim.z .
    • Les dimensions de la grille exprimées sous gridDim.x, gridDim.y, gridDim.z .
    • Si l'op est éligible pour utiliser Tensor Cores .
    • Si le noyau contient des instructions Tensor Core.
    • Le nom de l'op qui a lancé ce noyau.
    • Le nombre d'occurrences de cette paire kernel-op.
    • Le temps GPU total écoulé en microsecondes.
    • Le temps GPU écoulé moyen en microsecondes.
    • Le temps GPU minimum écoulé en microsecondes.
    • Le temps GPU écoulé maximal en microsecondes.

Outil de profil de mémoire

L'outil de profil de mémoire surveille l'utilisation de la mémoire de votre appareil pendant l'intervalle de profilage. Vous pouvez utiliser cet outil pour :

  • Déboguez les problèmes de mémoire insuffisante (OOM) en identifiant l'utilisation maximale de la mémoire et l'allocation de mémoire correspondante aux opérations TensorFlow. Vous pouvez également déboguer les problèmes de MOO qui peuvent survenir lorsque vous exécutez l'inférence multi-locataire .
  • Déboguer les problèmes de fragmentation de la mémoire.

L'outil de profil de mémoire affiche les données dans trois sections :

  1. Résumé du profil de mémoire
  2. Graphique chronologique de la mémoire
  3. Tableau de répartition de la mémoire

Résumé du profil de mémoire

Cette section affiche un résumé de haut niveau du profil de mémoire de votre programme TensorFlow, comme illustré ci-dessous :

Le récapitulatif du profil de mémoire comporte six champs :

  1. ID mémoire : liste déroulante qui répertorie tous les systèmes de mémoire de l'appareil disponibles. Sélectionnez le système de mémoire que vous souhaitez afficher dans la liste déroulante.
  2. #Allocation : nombre d'allocations de mémoire effectuées pendant l'intervalle de profilage.
  3. #Deallocation : le nombre de désallocations de mémoire dans l'intervalle de profilage
  4. Capacité de mémoire : La capacité totale (en Gio) du système de mémoire que vous sélectionnez.
  5. Utilisation maximale du tas : utilisation maximale de la mémoire (en Gio) depuis le début de l'exécution du modèle.
  6. Utilisation maximale de la mémoire : utilisation maximale de la mémoire (en Gio) dans l'intervalle de profilage. Ce champ contient les sous-champs suivants :
    1. Horodatage : horodatage du moment où le pic d'utilisation de la mémoire s'est produit sur le graphique chronologique.
    2. Stack Reservation : Quantité de mémoire réservée sur la pile (en Gio).
    3. Heap Allocation : Quantité de mémoire allouée sur le tas (en Gio).
    4. Mémoire libre : Quantité de mémoire libre (en Gio). La capacité de mémoire est la somme totale de la réservation de pile, de l'allocation de tas et de la mémoire libre.
    5. Fragmentation : Le pourcentage de fragmentation (plus bas c'est mieux). Il est calculé comme un pourcentage de (1 - Size of the largest chunk of free memory / Total free memory) .

Graphique chronologique de la mémoire

Cette section affiche un graphique de l'utilisation de la mémoire (en Gio) et du pourcentage de fragmentation par rapport au temps (en ms).

image

L'axe X représente la chronologie (en ms) de l'intervalle de profilage. L'axe Y à gauche représente l'utilisation de la mémoire (en Gio) et l'axe Y à droite représente le pourcentage de fragmentation. À chaque instant sur l'axe des X, la mémoire totale est divisée en trois catégories : pile (en rouge), tas (en orange) et libre (en vert). Survolez un horodatage spécifique pour afficher les détails des événements d'allocation/désallocation de mémoire à ce stade, comme ci-dessous :

image

La fenêtre contextuelle affiche les informations suivantes :

  • timestamp(ms) : emplacement de l'événement sélectionné sur la chronologie.
  • event : Le type d'événement (allocation ou désallocation).
  • required_size(GiBs) : La quantité de mémoire demandée. Ce sera un nombre négatif pour les événements de désallocation.
  • allocation_size(GiBs) : La quantité réelle de mémoire allouée. Ce sera un nombre négatif pour les événements de désallocation.
  • tf_op : l'op TensorFlow qui demande l'allocation/la désallocation.
  • step_id : l'étape d'entraînement au cours de laquelle cet événement s'est produit.
  • region_type : le type d'entité de données auquel cette mémoire allouée est destinée. Les valeurs possibles sont temp pour les temporaires, output pour les activations et les gradients, et persist / dynamic pour les poids et les constantes.
  • data_type : Le type d'élément tenseur (par exemple, uint8 pour un entier non signé de 8 bits).
  • tensor_shape : La forme du tenseur alloué/désalloué.
  • memory_in_use(GiBs) : la mémoire totale utilisée à ce moment précis.

Tableau de répartition de la mémoire

Ce tableau affiche les allocations de mémoire actives au point d'utilisation maximale de la mémoire dans l'intervalle de profilage.

image

Il existe une ligne pour chaque opération TensorFlow et chaque ligne comporte les colonnes suivantes :

  • Nom de l'op : le nom de l'op TensorFlow.
  • Allocation Size (GiBs) : quantité totale de mémoire allouée à cette opération.
  • Taille demandée (Gio) : La quantité totale de mémoire demandée pour cette opération.
  • Occurrences : Le nombre d'attributions pour cette op.
  • Type de région : le type d'entité de données auquel cette mémoire allouée est destinée. Les valeurs possibles sont temp pour les temporaires, output pour les activations et les gradients, et persist / dynamic pour les poids et les constantes.
  • Type de données : Le type d'élément tenseur.
  • Shape : La forme des tenseurs alloués.

Visionneuse de modules

L'outil Pod Viewer affiche la répartition d'une étape de formation pour tous les travailleurs.

image

  • Le volet supérieur a un curseur pour sélectionner le numéro d'étape.
  • Le volet inférieur affiche un histogramme empilé. Il s'agit d'une vue de haut niveau des catégories de temps d'étape décomposées placées les unes sur les autres. Chaque colonne empilée représente un travailleur unique.
  • Lorsque vous survolez une colonne empilée, la carte sur le côté gauche affiche plus de détails sur la répartition des étapes.

analyse des goulots d'étranglement tf.data

L'outil d'analyse des goulots d'étranglement tf.data détecte automatiquement les goulots d'étranglement dans les pipelines d'entrée tf.data de votre programme et fournit des recommandations sur la manière de les résoudre. Il fonctionne avec n'importe quel programme utilisant tf.data quelle que soit la plate-forme (CPU/GPU/TPU). Son analyse et ses recommandations s'appuient sur ce guide .

Il détecte un goulot d'étranglement en suivant ces étapes :

  1. Trouvez l'hôte le plus lié à l'entrée.
  2. Trouvez l'exécution la plus lente d'un pipeline d'entrée tf.data .
  3. Reconstruisez le graphique du pipeline d'entrée à partir de la trace du profileur.
  4. Trouvez le chemin critique dans le graphique du pipeline d'entrée.
  5. Identifiez la transformation la plus lente sur le chemin critique comme un goulot d'étranglement.

L'interface utilisateur est divisée en trois sections : Résumé de l'analyse des performances , Résumé de tous les pipelines d'entrée et Graphique du pipeline d'entrée .

Résumé de l'analyse des performances

image

Cette section fournit le résumé de l'analyse. Il signale les pipelines d'entrée tf.data lents détectés dans le profil. Cette section montre également l'hôte le plus lié à l'entrée et son pipeline d'entrée le plus lent avec la latence maximale. Plus important encore, il identifie quelle partie du pipeline d'entrée est le goulot d'étranglement et comment y remédier. Les informations sur les goulots d'étranglement sont fournies avec le type d'itérateur et son nom long.

Comment lire le nom long de l'itérateur tf.data

Un nom long est au format Iterator::<Dataset_1>::...::<Dataset_n> . Dans le nom long, <Dataset_n> correspond au type d'itérateur et les autres jeux de données dans le nom long représentent les transformations en aval.

Par exemple, considérons l'ensemble de données de pipeline d'entrée suivant :

dataset = tf.data.Dataset.range(10).map(lambda x: x).repeat(2).batch(5)

Les noms longs des itérateurs de l'ensemble de données ci-dessus seront :

Type d'itérateur Nom long
Intervalle Itérateur::Batch::Repeat::Map::Range
Carte Itérateur::Batch::Repeat::Map
Répéter Itérateur ::Batch ::Répéter
Lot Itérateur ::Lot

Résumé de tous les pipelines d'entrée

image

Cette section fournit le résumé de tous les pipelines d'entrée sur tous les hôtes. En règle générale, il existe un pipeline d'entrée. Lors de l'utilisation de la stratégie de distribution, il existe un pipeline d'entrée hôte exécutant le code tf.data du programme et plusieurs pipelines d'entrée de périphérique récupérant les données du pipeline d'entrée hôte et les transférant aux périphériques.

Pour chaque pipeline d'entrée, il affiche les statistiques de son temps d'exécution. Un appel est considéré comme lent s'il dure plus de 50 μs.

Graphique de pipeline d'entrée

image

Cette section affiche le graphique du pipeline d'entrée avec les informations de temps d'exécution. Vous pouvez utiliser "Host" et "Input Pipeline" pour choisir l'hôte et le pipeline d'entrée à afficher. Les exécutions du pipeline d'entrée sont triées par heure d'exécution dans l'ordre décroissant que vous pouvez choisir à l'aide de la liste déroulante Rang .

image

Les nœuds du chemin critique ont des contours en gras. Le nœud goulot d'étranglement, qui est le nœud avec le plus long temps propre sur le chemin critique, a un contour rouge. Les autres nœuds non critiques ont des contours en pointillés gris.

Dans chaque nœud, Start Time indique l'heure de début de l'exécution. Le même nœud peut être exécuté plusieurs fois, par exemple, s'il y a une Batch dans le pipeline d'entrée. S'il est exécuté plusieurs fois, il s'agit de l'heure de début de la première exécution.

La durée totale est le temps de mur de l'exécution. S'il est exécuté plusieurs fois, c'est la somme des temps de mur de toutes les exécutions.

Le temps propre est le temps total sans le temps superposé avec ses nœuds enfants immédiats.

"# Calls" est le nombre de fois que le pipeline d'entrée est exécuté.

Collecter des données de performances

Le profileur TensorFlow collecte les activités de l'hôte et les traces GPU de votre modèle TensorFlow. Vous pouvez configurer le profileur pour collecter des données de performances via le mode programmatique ou le mode d'échantillonnage.

API de profilage

Vous pouvez utiliser les API suivantes pour effectuer le profilage.

  • Mode programmatique utilisant le TensorBoard Keras Callback ( tf.keras.callbacks.TensorBoard )

    # Profile from batches 10 to 15
    tb_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir,
                                                 profile_batch='10, 15')
    
    # Train the model and use the TensorBoard Keras callback to collect
    # performance profiling data
    model.fit(train_data,
              steps_per_epoch=20,
              epochs=5,
              callbacks=[tb_callback])
    
  • Mode programmatique utilisant l'API de fonction tf.profiler

    tf.profiler.experimental.start('logdir')
    # Train the model here
    tf.profiler.experimental.stop()
    
  • Mode programmatique utilisant le gestionnaire de contexte

    with tf.profiler.experimental.Profile('logdir'):
        # Train the model here
        pass
    

  • Mode d'échantillonnage : effectuez un profilage à la demande à l'aide de tf.profiler.experimental.server.start pour démarrer un serveur gRPC avec l'exécution de votre modèle TensorFlow. Après avoir démarré le serveur gRPC et exécuté votre modèle, vous pouvez capturer un profil via le bouton Capturer le profil dans le plug-in de profil TensorBoard. Utilisez le script de la section Installer le profileur ci-dessus pour lancer une instance TensorBoard si elle n'est pas déjà en cours d'exécution.

    Par exemple,

    # Start a profiler server before your model runs.
    tf.profiler.experimental.server.start(6009)
    # (Model code goes here).
    #  Send a request to the profiler server to collect a trace of your model.
    tf.profiler.experimental.client.trace('grpc://localhost:6009',
                                          'gs://your_tb_logdir', 2000)
    

    Exemple de profilage de plusieurs travailleurs :

    # E.g. your worker IP addresses are 10.0.0.2, 10.0.0.3, 10.0.0.4, and you
    # would like to profile for a duration of 2 seconds.
    tf.profiler.experimental.client.trace(
        'grpc://10.0.0.2:8466,grpc://10.0.0.3:8466,grpc://10.0.0.4:8466',
        'gs://your_tb_logdir',
        2000)
    

Utilisez la boîte de dialogue Capturer le profil pour spécifier :

  • Liste délimitée par des virgules d'URL de service de profil ou de noms de TPU.
  • Une durée de profilage.
  • Le niveau de traçage des appels de périphérique, d'hôte et de fonction Python.
  • Combien de fois vous voulez que le profileur réessaye de capturer des profils en cas d'échec au début.

Profilage de boucles d'entraînement personnalisées

Pour profiler des boucles d'entraînement personnalisées dans votre code TensorFlow, instrumentez la boucle d'entraînement avec l'API tf.profiler.experimental.Trace afin de marquer les limites des étapes pour le profileur.

L'argument de name est utilisé comme préfixe pour les noms d'étape, l'argument de mot clé step_num est ajouté dans les noms d'étape et l'argument de mot clé _r fait que cet événement de trace est traité comme un événement d'étape par le profileur.

Par exemple,

for step in range(NUM_STEPS):
    with tf.profiler.experimental.Trace('train', step_num=step, _r=1):
        train_data = next(dataset)
        train_step(train_data)

Cela activera l'analyse des performances basée sur les étapes du profileur et entraînera l'affichage des événements d'étape dans la visionneuse de trace.

Assurez-vous d'inclure l'itérateur d'ensemble de données dans le contexte tf.profiler.experimental.Trace pour une analyse précise du pipeline d'entrée.

L'extrait de code ci-dessous est un anti-modèle :

for step, train_data in enumerate(dataset):
    with tf.profiler.experimental.Trace('train', step_num=step, _r=1):
        train_step(train_data)

Profilage des cas d'utilisation

Le profileur couvre un certain nombre de cas d'utilisation selon quatre axes différents. Certaines des combinaisons sont actuellement prises en charge et d'autres seront ajoutées à l'avenir. Certains des cas d'utilisation sont :

  • Profilage local ou à distance : il s'agit de deux méthodes courantes de configuration de votre environnement de profilage. Dans le profilage local, l'API de profilage est appelée sur la même machine que votre modèle exécute, par exemple, un poste de travail local avec des GPU. Dans le profilage à distance, l'API de profilage est appelée sur une machine différente de celle sur laquelle votre modèle s'exécute, par exemple, sur un Cloud TPU.
  • Profilage de plusieurs travailleurs : vous pouvez profiler plusieurs machines lorsque vous utilisez les fonctionnalités de formation distribuée de TensorFlow.
  • Plate- forme matérielle : Profil CPU, GPU et TPU.

Le tableau ci-dessous fournit un aperçu rapide des cas d'utilisation pris en charge par TensorFlow mentionnés ci-dessus :

API de profilage Local Télécommande Plusieurs travailleurs Plates-formes matérielles
Rappel TensorBoard Keras Prise en charge Non supporté Non supporté CPU, GPU
API de démarrage/arrêt tf.profiler.experimental Prise en charge Non supporté Non supporté CPU, GPU
API tf.profiler.experimental client.trace Prise en charge Prise en charge Prise en charge Processeur, GPU, TPU
API du gestionnaire de contexte Prise en charge Non supporté Non supporté CPU, GPU

Meilleures pratiques pour des performances de modèle optimales

Utilisez les recommandations suivantes selon le cas pour vos modèles TensorFlow afin d'obtenir des performances optimales.

En général, effectuez toutes les transformations sur l'appareil et assurez-vous d'utiliser la dernière version compatible des bibliothèques telles que cuDNN et Intel MKL pour votre plate-forme.

Optimiser le pipeline de données d'entrée

Utilisez les données de [#input_pipeline_analyzer] pour optimiser votre pipeline d'entrée de données. Un pipeline d'entrée de données efficace peut considérablement améliorer la vitesse d'exécution de votre modèle en réduisant le temps d'inactivité de l'appareil. Essayez d'intégrer les meilleures pratiques détaillées dans le guide Meilleures performances avec l'API tf.data et ci-dessous pour rendre votre pipeline d'entrée de données plus efficace.

  • En général, la parallélisation de toutes les opérations qui n'ont pas besoin d'être exécutées de manière séquentielle peut optimiser considérablement le pipeline d'entrée de données.

  • Dans de nombreux cas, il est utile de modifier l'ordre de certains appels ou d'ajuster les arguments de manière à ce qu'ils fonctionnent le mieux pour votre modèle. Lors de l'optimisation du pipeline de données d'entrée, comparez uniquement le chargeur de données sans les étapes de formation et de rétropropagation pour quantifier l'effet des optimisations de manière indépendante.

  • Essayez d'exécuter votre modèle avec des données synthétiques pour vérifier si le pipeline d'entrée est un goulot d'étranglement des performances.

  • Utilisez tf.data.Dataset.shard pour la formation multi-GPU. Assurez-vous de partitionner très tôt dans la boucle d'entrée pour éviter les réductions de débit. Lorsque vous travaillez avec TFRecords, assurez-vous de partitionner la liste des TFRecords et non le contenu des TFRecords.

  • Parallélisez plusieurs opérations en définissant dynamiquement la valeur de num_parallel_calls à l'aide de tf.data.AUTOTUNE .

  • Envisagez de limiter l'utilisation de tf.data.Dataset.from_generator car il est plus lent par rapport aux opérations TensorFlow pures.

  • Envisagez de limiter l'utilisation de tf.py_function car elle ne peut pas être sérialisée et n'est pas prise en charge pour s'exécuter dans TensorFlow distribué.

  • Utilisez tf.data.Options pour contrôler les optimisations statiques du pipeline d'entrée.

Lisez également le guide d'analyse des performances tf.data pour plus de conseils sur l'optimisation de votre pipeline d'entrée.

Optimiser l'augmentation des données

Lorsque vous travaillez avec des données d'image, optimisez l'augmentation de vos données en effectuant une conversion vers différents types de données après avoir appliqué des transformations spatiales, telles que le retournement, le recadrage, la rotation, etc.

Utiliser NVIDIA® DALI

Dans certains cas, par exemple lorsque vous avez un système avec un rapport GPU/CPU élevé, toutes les optimisations ci-dessus peuvent ne pas être suffisantes pour éliminer les goulots d'étranglement dans le chargeur de données dus aux limitations des cycles CPU.

Si vous utilisez des GPU NVIDIA® pour des applications de vision par ordinateur et d'apprentissage en profondeur audio, envisagez d'utiliser la bibliothèque de chargement de données ( DALI ) pour accélérer le pipeline de données.

Consultez la documentation NVIDIA® DALI : Opérations pour obtenir une liste des opérations DALI prises en charge.

Utiliser le threading et l'exécution parallèle

Exécutez des opérations sur plusieurs threads CPU avec l'API tf.config.threading pour les exécuter plus rapidement.

TensorFlow définit automatiquement le nombre de threads de parallélisme par défaut. Le pool de threads disponible pour l'exécution des opérations TensorFlow dépend du nombre de threads CPU disponibles.

Contrôlez l'accélération parallèle maximale pour une seule opération en utilisant tf.config.threading.set_intra_op_parallelism_threads . Notez que si vous exécutez plusieurs opérations en parallèle, elles partageront toutes le pool de threads disponible.

Si vous avez des ops non bloquants indépendants (ops sans chemin dirigé entre eux sur le graphique), utilisez tf.config.threading.set_inter_op_parallelism_threads pour les exécuter simultanément en utilisant le pool de threads disponible.

Divers

Lorsque vous travaillez avec des modèles plus petits sur des GPU NVIDIA®, vous pouvez définir tf.compat.v1.ConfigProto.force_gpu_compatible=True pour forcer tous les tenseurs CPU à être alloués avec la mémoire épinglée CUDA afin d'améliorer considérablement les performances du modèle. Cependant, soyez prudent lorsque vous utilisez cette option pour des modèles inconnus/très volumineux, car cela pourrait avoir un impact négatif sur les performances de l'hôte (CPU).

Améliorer les performances de l'appareil

Suivez les bonnes pratiques détaillées ici et dans le guide d'optimisation des performances GPU pour optimiser les performances du modèle TensorFlow sur l'appareil.

Si vous utilisez des GPU NVIDIA, enregistrez l'utilisation du GPU et de la mémoire dans un fichier CSV en exécutant :

nvidia-smi
--query-gpu=utilization.gpu,utilization.memory,memory.total,
memory.free,memory.used --format=csv

Configurer la disposition des données

Lorsque vous travaillez avec des données contenant des informations sur les canaux (comme des images), optimisez le format de mise en page des données pour privilégier les canaux en dernier (NHWC plutôt que NCHW).

Les formats de données de dernier canal améliorent l'utilisation de Tensor Core et offrent des améliorations significatives des performances, en particulier dans les modèles convolutifs lorsqu'ils sont couplés à AMP. Les dispositions de données NCHW peuvent toujours être exploitées par Tensor Cores, mais introduisent une surcharge supplémentaire en raison des opérations de transposition automatique.

Vous pouvez optimiser la disposition des données pour préférer les dispositions NHWC en définissant data_format="channels_last" pour des couches telles que tf.keras.layers.Conv2D , tf.keras.layers.Conv3D et tf.keras.layers.RandomRotation .

Utilisez tf.keras.backend.set_image_data_format pour définir le format de disposition des données par défaut pour l'API backend Keras.

Maximisez le cache L2

When working with NVIDIA® GPUs, execute the code snippet below before the training loop to max out the L2 fetch granularity to 128 bytes.

import ctypes

_libcudart = ctypes.CDLL('libcudart.so')
# Set device limit on the current device
# cudaLimitMaxL2FetchGranularity = 0x05
pValue = ctypes.cast((ctypes.c_int*1)(), ctypes.POINTER(ctypes.c_int))
_libcudart.cudaDeviceSetLimit(ctypes.c_int(0x05), ctypes.c_int(128))
_libcudart.cudaDeviceGetLimit(pValue, ctypes.c_int(0x05))
assert pValue.contents.value == 128

Configure GPU thread usage

The GPU thread mode decides how GPU threads are used.

Set the thread mode to gpu_private to make sure that preprocessing does not steal all the GPU threads. This will reduce the kernel launch delay during training. You can also set the number of threads per GPU. Set these values using environment variables.

import os

os.environ['TF_GPU_THREAD_MODE']='gpu_private'
os.environ['TF_GPU_THREAD_COUNT']='1'

Configure GPU memory options

In general, increase the batch size and scale the model to better utilize GPUs and get higher throughput. Note that increasing the batch size will change the model's accuracy so the model needs to be scaled by tuning hyperparameters like the learning rate to meet the target accuracy.

Also, use tf.config.experimental.set_memory_growth to allow GPU memory to grow to prevent all the available memory from being fully allocated to ops that require only a fraction of the memory. This allows other processes which consume GPU memory to run on the same device.

To learn more, check out the Limiting GPU memory growth guidance in the GPU guide to learn more.

Miscellaneous

  • Increase the training mini-batch size (number of training samples used per device in one iteration of the training loop) to the maximum amount that fits without an out of memory (OOM) error on the GPU. Increasing the batch size impacts the model's accuracy—so make sure you scale the model by tuning hyperparameters to meet the target accuracy.

  • Disable reporting OOM errors during tensor allocation in production code. Set report_tensor_allocations_upon_oom=False in tf.compat.v1.RunOptions .

  • For models with convolution layers, remove bias addition if using batch normalization. Batch normalization shifts values by their mean and this removes the need to have a constant bias term.

  • Use TF Stats to find out how efficiently on-device ops run.

  • Use tf.function to perform computations and optionally, enable the jit_compile=True flag ( tf.function(jit_compile=True ). To learn more, go to Use XLA tf.function .

  • Minimize host Python operations between steps and reduce callbacks. Calculate metrics every few steps instead of at every step.

  • Keep the device compute units busy.

  • Send data to multiple devices in parallel.

  • Consider using 16-bit numerical representations , such as fp16 —the half-precision floating point format specified by IEEE—or the Brain floating-point bfloat16 format.

Ressources additionnelles

Known limitations

Profiling multiple GPUs on TensorFlow 2.2 and TensorFlow 2.3

TensorFlow 2.2 and 2.3 support multiple GPU profiling for single host systems only; multiple GPU profiling for multi-host systems is not supported. To profile multi-worker GPU configurations, each worker has to be profiled independently. From TensorFlow 2.4 multiple workers can be profiled using the tf.profiler.experimental.client.trace API.

CUDA® Toolkit 10.2 or later is required to profile multiple GPUs. As TensorFlow 2.2 and 2.3 support CUDA® Toolkit versions only up to 10.1, you need to create symbolic links to libcudart.so.10.1 and libcupti.so.10.1 :

sudo ln -s /usr/local/cuda/lib64/libcudart.so.10.2 /usr/local/cuda/lib64/libcudart.so.10.1
sudo ln -s /usr/local/cuda/extras/CUPTI/lib64/libcupti.so.10.2 /usr/local/cuda/extras/CUPTI/lib64/libcupti.so.10.1