
-
Combinez l’apprentissage automatique et les graphismes de Metal 4
Découvrez comment combiner l'apprentissage automatique dans vos applications graphiques à l'aide de Metal 4. Nous présenterons la ressource tensorielle et l'encodeur ML pour exécuter des modèles sur la chronologie de carte graphique parallèlement à votre travail de rendu et de calcul. Découvrez comment Shader ML vous permet d'intégrer des réseaux neuronaux directement dans vos shaders pour obtenir des effets avancés et des gains de performances. Nous montrerons également de nouveaux outils de débogage pour les charges de travail Metal 4 ML en action à l'aide d'un exemple d'app.
Chapitres
- 0:00 - Introduction
- 2:52 - Découvrez les tenseurs
- 6:21 - Encodage des réseaux ML
- 12:51 - Intégrer ML dans votre shader
- 20:26 - Déboguer vos charges de travail ML
Ressources
Vidéos connexes
WWDC25
- Allez plus loin avec les jeux Metal 4
- Découvrez Metal 4
- Explorez les jeux Metal 4
- Nouveautés du rendu Metal pour les apps immersives
WWDC24
-
Rechercher dans cette vidéo…
Bonjour. Je m’appelle Preston Provins et je suis ingénieur au sein de l’équipe Metal Framework d’Apple. Mon collègue Scott me rejoindra plus tard. Je vais présenter les nouveautés de Metal qui associent apprentissage automatique et jeux, tandis que Scott vous parlera des ajouts aux outils GPU visant à améliorer le débogage pour l’apprentissage automatique dans Metal 4. J’ai hâte de vous montrer comment combiner l’apprentissage automatique et les graphismes avec Metal 4 dans cette séance. Pour découvrir toutes les fonctionnalités de Metal 4, regardez la conférence sur les bases de Metal 4 qui dévoile les dernières nouveautés. L’apprentissage automatique transforme les jeux et les graphismes grâce à des techniques comme la mise à l’échelle, la compression des ressources, la fusion d’animations et le Neural Shading. Ces techniques repoussent les frontières de la créativité et de l’immersion. Elles simulent des phénomènes complexes, améliorent la fidélité visuelle et facilitent l’exploration de nouveaux styles et effets. Core ML est idéal pour un large éventail de tâches d’apprentissage automatique comme la segmentation, la classification, l’IA générative et bien plus encore. Il facilite la création de modèles d’apprentissage automatique. Si votre application d’apprentissage automatique nécessite une intégration étroite avec la chronologie GPU, Metal 4 est l’outil qu’il vous faut. Dans une image classique, un jeu peut effectuer un vertex skinning dans une passe de calcul, rastériser la scène dans une passe de rendu et appliquer un anticrénelage dans une autre passe de calcul. L’anticrénelage est généralement réalisé avec des techniques de traitement d’image comme l’anticrénelage temporel. Les techniques de pointe remplacent ces méthodes traditionnelles par un réseau d’apprentissage automatique. Ce réseau met à l’échelle l’image, ce qui permet d’effectuer le reste du rendu à une résolution inférieure, pour des performances optimales.
L’exécution de petits réseaux neuronaux dans un shader devient de plus en plus courante. Là où un shader de fragment traditionnel échantillonnerait les textures des matériaux, les techniques innovantes utilisent de petits réseaux neuronaux pour décompresser les textures à la volée et atteindre des taux de compression plus élevés. Cette technique de rendu neuronal compresse les ensembles de matériaux à 50 % de l’empreinte de compression par blocs. Cette séance présente les MTLTensors, la nouvelle ressource de Metal 4 pour les workflows d’apprentissage automatique. Nous explorerons MTL4MachineLearningCommandEncoder, qui exécute des réseaux entiers sur la chronologie GPU aux côtés de vos autres appels de dessin et de répartition. Nous présenterons Shader ML, qui permet d’intégrer des opérations d’apprentissage automatique dans vos propres shaders. Enfin, nous montrerons comment intégrer l’apprentissage automatique dans votre application avec le débogueur Metal. Vous connaissez déjà MTLBuffers et MTLTextures. Cette année, Metal 4 introduit MTLTensor, une nouvelle ressource qui permet d’appliquer l’apprentissage automatique aux données plus facilement que jamais. MTLTensor est un type de données fondamental en apprentissage automatique, utilisé dans les contextes de calcul, de graphisme et d’apprentissage automatique. Les charges de travail d’apprentissage automatique utiliseront largement les tenseurs. MTL4MachineLearningCommandEncoder utilise les MTLTensors pour représenter les entrées et les sorties, tandis que Shader ML les exploite pour représenter les poids ainsi que les entrées et sorties.
Les MTLTensors sont des conteneurs multidimensionnels de données, décrits par un rang et un nombre de dimensions par rang. Ils peuvent s’étendre bien au-delà de deux dimensions, offrant la flexibilité requise pour décrire toute disposition de données dans des cas pratiques d’apprentissage automatique. Les MTLTextures, par exemple, sont limitées à quatre canaux et ont des limites strictes pour leurs étendues en fonction du format de texture. En apprentissage automatique, il est courant d’utiliser des données avec plus de deux dimensions, comme les opérations de convolution. Utiliser une représentation plate des données comme MTLBuffer nécessiterait des schémas d’indexation complexes pour les données à plusieurs dimensions. L’indexation de données multidimensionnelles dans un MTLTensor est bien plus simple, car le stride et la dimension de chaque rang sont intégrés à l’objet MTLTensor et automatiquement utilisés dans les calculs d’indexation. Voyons maintenant comment créer un MTLTensor. Son rang décrit le nombre d’axes qu’il possède. Ce MTLTensor a un rang de deux. Il contient des lignes de colonnes de données. Les étendues de la dimension décrivent le nombre de points de données le long de cet axe. La propriété dataType définit le format des données encapsulées dans le MTLTensor. Les propriétés usage indiquent comment le MTLTensor sera utilisé : MTLTensorUsageMachineLearning pour MTL4MachineLearningCommandEncoder, MTLTensorUsageCompute ou MTLTensorUsageRender pour une utilisation dans vos programmes de shader. Il est également possible de combiner des utilisations, comme avec la propriété usage des textures. Ce sont des propriétés importantes de MTLTensor qui doivent être renseignées dans un objet MTLTensorDescriptor. Créons maintenant un MTLTensor dans le code. Une fois les propriétés du descripteur remplies, créez un MTLTensor en appelant newTensorWithDescriptor:offset:error: sur un objet MTLDevice. Les MTLTensors peuvent être créés à partir d’un objet MTLDevice ou MTLBuffer, mais ceux créés à partir d’un MTLDevice offrent les meilleures performances. Tout comme les MTLTextures peuvent être swizzled, créer un MTLTensor à partir d’un objet MTLDevice génère une disposition opaque optimisée pour la lecture et l’écriture. Concentrons-nous sur la création de MTLTensors à partir d’un MTLBuffer préexistant. Contrairement aux MTLTensors créés à partir d’un MTLDevice, ceux issus d’un MTLBuffer ne sont pas supposés être compactés, vous devez donc spécifier ses strides. Le stride le plus interne doit toujours être égal à un. Le deuxième stride indique le nombre d’éléments sautés lorsque l’index de ligne est incrémenté.
Il est possible que le MTLBuffer source contienne du remplissage, comme des colonnes inutilisées à la fin de la ligne. Vous devez en tenir compte pour que le MTLTensor encapsule les éléments appropriés. Pour créer un MTLTensor à partir d’un tampon sous-jacent, définissez les propriétés dataType et usage comme pour un tenseur alloué à un appareil. Ensuite, remplissez la propriété strides du MTLTensorDescriptor afin que le MTLTensor résultant encapsule correctement le contenu du MTLBuffer. Enfin, utilisez newTensorWithDescriptor:offset:error: sur le MTLBuffer source. Maintenant que nous savons allouer et créer des MTLTensors, explorons le nouvel encodeur d’apprentissage automatique pour ajouter le travail ML à la chronologie GPU. Metal 4 permet d’ajouter facilement des commandes de calcul et de rendu à la chronologie GPU, respectivement avec MTL4ComputeCommandEncoder et MTL4RenderCommandEncoder. Cette année, nous allons encore plus loin dans l’unification en ajoutant le travail ML à la chronologie GPU. MTL4MachineLearningCommandEncoder permet d’exécuter des modèles complets en parallèle avec les autres commandes Metal sur le GPU, tout en assurant leur synchronisation et leur intégration fluide avec les autres MTLCommands. Ce nouvel encodeur dédié à l’encodage des commandes d’apprentissage automatique possède une interface similaire à celle des encodeurs de calcul et de rendu. Les primitives de synchronisation Metal 4 fonctionnent également avec les commandes d’apprentissage automatique, comme pour le calcul et le rendu. La synchronisation permet de contrôler l’orchestration du travail et de faciliter la parallélisation pour maintenir des performances élevées. Le workflow de création de MTL4MachineLearningCommandEncoder peut être séparé en deux parties : hors ligne et exécution. La partie hors ligne du workflow a lieu avant le lancement de l’application, tandis que la partie exécution se produit pendant la durée de vie de l’application, par exemple au milieu d’une image. Commençons par la partie hors ligne du workflow, la création d’un MTLPackage. Un MTLPackage est un conteneur regroupant une ou plusieurs fonctions, chacune représentant un réseau ML, que vous pouvez utiliser dans Metal pour exécuter le travail d’apprentissage automatique. Ce format est optimisé pour le chargement et l’exécution avec Metal. Pour créer un MTLPackage, vous devez avoir un package CoreML. Ici, nous utilisons le convertisseur CoreML pour convertir un réseau ML depuis son framework ML d’origine, tel que PyTorch ou Tensorflow, en un package CoreML. Voici un exemple d’exportation d’un modèle PyTorch à l’aide de la bibliothèque CoreML Tools en Python. Il suffit d’importer les outils et d’exécuter convert sur le modèle pour générer une exportation. Enregistrez cette exportation en tant que package ML. Un point important à signaler ici : tous les packages CoreML ne sont pas des programmes ML et seuls les programmes ML sont pris en charge. Si le package CoreML a été exporté vers une ancienne version de l’OS, lisez cet article sur l’exportation de fichiers de modèles CoreML en tant que package ML. Une fois le package CoreML créé, il suffit d’exécuter la ligne de commande metal-package-builder sur le modèle enregistré pour générer un MTLPackage. Cela convertit le package CoreML dans un format qui peut être chargé efficacement à l’exécution. Voilà pour la création d’un MTLPackage. La partie hors ligne du workflow est terminée, le reste concerne l’exécution. Pour compiler le réseau, ouvrez le MTLPackage en tant que MTLLibrary. Créez un descripteur de fonction à l’aide du nom de la fonction qui représente le réseau dans le package. Dans ce cas, la fonction principale. Pour compiler le réseau, créez un MTL4MachineLearningPipelineState. Pour cela, utilisez un MTL4MachineLearningPipelineStateDescriptor avec le descripteur de fonction. Si le réseau a des entrées dynamiques, spécifiez la taille de chaque entrée dans le MTL4MachineLearningPipelineStateDescriptor. Compilez le réseau pour l’appareil spécifique en créant le MTL4MachineLearningPipelineState avec le MTL4MachineLearningPipelineStateDescriptor.
C’est ainsi qu’un objet MTL4MachineLearningPipelineState est créé. L’étape suivante consiste à créer le MTL4MachineLearningCommandEncoder et à encoder le travail. Explorons plus en détail l’utilisation de l’objet MTL4MachineLearningCommandEncoder pour répartir le travail sur la chronologie GPU. Créez l’objet MTL4MachineLearningCommandEncoder, comme vous le feriez pour un encodeur de calcul ou de rendu. Définissez l’objet MTL4MachineLearningPipelineState créé, puis associez les entrées et les sorties utilisées. Enfin, répartissez le travail à l’aide de la méthode dispatchNetworkWithIntermediatesHeap.
L’encodeur ML utilise un heap pour stocker les données intermédiaires entre les opérations. Plutôt que de créer et libérer des tampons, il permet la réutilisation des ressources pour différentes répartitions. Pour créer ce MTLHeap, créez un MTLHeapDescriptor et définissez la propriété type sur MTLHeapTypePlacement. Vous pouvez obtenir la taille minimale du heap pour le réseau en interrogeant l’intermediateHeapSize du pipeline. Définissez la propriété size du heap pour qu’elle soit supérieure ou égale à cette valeur. Après avoir encodé vos répartitions réseau, terminez l’encodage et soumettez vos commandes pour les exécuter sur la chronologie GPU.
Comme mentionné, les primitives de synchronisation Metal 4 fonctionnent aussi avec les commandes d’apprentissage automatique, tout comme avec le calcul et le rendu. Le travail qui ne dépend pas de la sortie d’apprentissage automatique peut être exécuté en parallèle s’il est correctement synchronisé.
Seul le travail consommant la sortie réseau doit attendre la fin du travail d’apprentissage automatique planifié.
Pour synchroniser les répartitions MTL4MachineLearningCommandEncoder, vous pouvez utiliser les primitives de synchronisation Metal 4 standard comme MTLBarriers et MTLFences. Le nouveau MTLStageMachineLearning permet d’identifier les charges de travail ML dans les barrières. Par exemple, pour faire attendre votre rendu sur les sorties produites par un réseau, vous pouvez placer une barrière entre l’étape de rendu appropriée et l’étape d’apprentissage automatique. Examinons MTL4MachineLearningCommandEncoder en action : dans cet exemple, MTL4MachineLearningCommandEncoder est utilisé pour répartir un réseau entièrement convolutionnel afin de prédire les valeurs d’occlusion par pixel. Cette évaluation nécessite une synchronisation précise. Le tampon de profondeur et les normales en espace de vue sont renseignés avant le lancement de la charge de travail ML. Pendant que le réseau traite les données, le moteur de rendu répartit d’autres tâches de rendu en parallèle et attend les résultats neuronaux avant de composer l’image finale. MTL4MachineLearningCommandEncoder ne se limite pas au traitement d’images complètes pour les jeux. Vous pouvez l’utiliser pour tout réseau respectant un budget de calcul en temps réel, en exploitant les primitives de synchronisation Metal 4 pour une intégration optimale. C’est ainsi que MTL4MachineLearningCommandEncoder de Metal 4 facilite l’exécution de lourdes charges de travail ML sur la chronologie GPU. En résumé : L’apprentissage automatique relie le calcul et le rendu dans Metal 4 grâce à MTL4MachineLearningCommandEncoder. MTL4MachineLearningCommandEncoder permet à des réseaux entiers de s’exécuter sur la chronologie GPU. Les ressources peuvent être partagées avec d’autres commandes GPU, et l’ensemble robuste de primitives de synchronisation Metal 4 garantit des capacités ML haute performance. Metal 4 introduit également Shader ML pour intégrer des opérations d’apprentissage automatique plus petites dans vos kernels et shaders existants. Les jeux de pointe adoptent l’apprentissage automatique pour remplacer les algorithmes de rendu traditionnels. Les techniques basées sur l’apprentissage automatique offrent des solutions pour l’éclairage global, l’ombrage des matériaux, la compression géométrique, la compression des matériaux et bien plus encore. Ces techniques permettent d’améliorer les performances ou de réduire l’empreinte mémoire. Prenons la compression neuronale des matériaux, une technique qui permet jusqu’à 50 % de compression par rapport aux formats de compression par blocs. Avec les matériaux traditionnels, vous échantillonnez les textures des matériaux, telles que les cartes d’albédo et de normales. Puis, vous utilisez les valeurs échantillonnées pour l’ombrage. Avec la compression neuronale des matériaux, vous échantillonnez les données de texture latentes, effectuez une inférence avec les valeurs échantillonnées et utilisez la sortie du réseau pour l’ombrage.
Diviser chaque étape dans son propre pipeline est inefficace, car chaque étape doit synchroniser les tenseurs avec la mémoire de l’appareil, exécuter les opérations, puis synchroniser les sorties pour les opérations suivantes.
Pour obtenir les meilleures performances, votre app doit combiner ces étapes en une seule répartition de shader. Avec Shader ML, Metal vous permet d’exécuter votre réseau ML directement dans votre shader de fragment, sans passer par la mémoire de l’appareil entre les étapes. Vous pouvez initialiser les tenseurs d’entrée, exécuter votre réseau et ombrer uniquement les pixels nécessaires à chaque image. Cela optimise l’empreinte mémoire d’exécution ainsi que l’espace disque de votre jeu. Examinons plus en détail l’évaluation neuronale des matériaux. L’initialisation des MTLTensors d’entrée peut être divisée en deux parties : le chargement des poids du réseau et la création de la caractéristique d’entrée MTLTensor. La caractéristique d’entrée MTLTensor est créée en échantillonnant les textures liées avec une coordonnée UV pour le fragment.
Lors de l’inférence, la caractéristique d’entrée MTLTensor est transformée par des matrices de poids apprises pour extraire des caractéristiques, calculer des activations et propager des informations via les couches. Cette évaluation est répétée pour plusieurs couches et le résultat est un matériau décompressé. Pour finir, les matériaux décompressés sont utilisés pour les calculs d’ombrage du fragment.
Voyons comment initialiser nos MTLTensors d’entrée avec Shader ML. Tout d’abord, déclarons un shader de fragment qui utilisera Shader ML et passera les poids du réseau. Commencez par inclure le nouvel en-tête metal_tensor. Nous allons utiliser le type MTLTensor pour accéder aux poids du réseau. Les MTLTensors sont liés au shader à l’aide de slots de liaison de tampon. Il est également possible de passer des MTLTensors en utilisant des tampons d’arguments. Le type MTLTensor est modélisé. Le premier argument de modèle est le dataType du MTLTensor. Comme ces MTLTensors ont été créés dans la mémoire de l’appareil, nous utilisons le qualificateur d’espace d’adressage de l’appareil. Le deuxième argument représente les dimensions du MTLTensor et le type à utiliser pour l’indexation dans le MTLTensor. Ici, nous utilisons dextents pour définir un tenseur de rang deux avec des étendues dynamiques. Notre fragment shader est prêt. Implémentons l’algorithme de compression neuronale des matériaux. Les poids du réseau étant déjà passés, nous pouvons créer le MTLTensor d’entrée en échantillonnant quatre textures latentes. MTLTensor n’est pas seulement une ressource pouvant être liée : Vous pouvez également créer un MTLTensor en ligne directement dans vos shaders ! Créez un MTLTensor encapsulant les valeurs échantillonnées et utilisez-le pour l’évaluation du réseau. Les MTLTensors en ligne sont supposés être compactés, il n’est donc pas nécessaire de passer des strides lors de la création. L’initialisation des MTLTensors d’entrée est terminée et nous sommes prêts à inférer des valeurs du réseau neuronal. L’évaluation transforme les entrées à l’aide de paramètres appris qui sont ensuite activés. Les activations sont passées aux couches suivantes, et celles de la couche finale forment le matériau décompressé.
Cette année, Metal introduit Metal Performance Primitives pour rendre les opérations MTLTensor accessibles dans le langage de shading. Cette bibliothèque est un ensemble d’API haute performance qui assure de puissantes solutions portables sur MTLTensors. Elle fournit des opérations de multiplication de matrices et de convolution. La multiplication de matrices est au cœur de l’évaluation des réseaux neuronaux. Nous utiliserons l’implémentation matmul2d fournie par Metal Performance Primitives pour implémenter une routine d’évaluation de réseau portable et performante. Pour commencer, incluez le nouvel en-tête MetalPerformancePrimitives dans votre shader Metal. Les paramètres de votre multiplication de matrices sont configurés à l’aide de l’objet matmul2d_descriptor. Le premier ensemble de paramètres du modèle spécifie la taille du problème de la multiplication de matrices. L’ensemble suivant contrôle si les entrées de la multiplication de matrices doivent être transposées lors de l’exécution de l’opération. Le dernier paramètre du modèle contrôle vos exigences de précision.
En plus du descripteur, l’opération matmul2d doit être spécialisée avec le nombre de threads qui participeront à l’opération. Comme nous sommes dans un shader de fragment, nous utiliserons execution_thread pour indiquer que la multiplication complète se fera par ce thread. Ensuite, exécutez la multiplication de matrices avec cette configuration.
Enfin, activez chaque élément du résultat de la multiplication de matrices à l’aide de la fonction d’activation ReLU. Ce processus est répété pour la deuxième couche afin d’évaluer notre réseau directement dans le shader. Une fois l’évaluation terminée, les matériaux décompressés peuvent être utilisés pour l’ombrage. Le MTLTensor de sortie contient des informations de canal qui peuvent ensuite servir comme n’importe quelle valeur échantillonnée à partir d’une texture. Voici une démonstration en temps réel de la compression neuronale de matériaux comparée aux matériaux traditionnels. Il n’y a aucune perte de qualité perceptible lors de l’utilisation des matériaux neuronaux, surtout s’ils sont ombrés. Voici la couleur de base isolée. Il est très difficile de remarquer des différences entre les matériaux neuronaux et les matériaux traditionnels, et pourtant les premiers utilisent moitié moins de mémoire et d’espace disque.
Les opérations MTLTensor ne sont pas exclusives aux shaders de fragment. Elles peuvent être utilisées dans toutes les fonctions et étapes de shader. Si un simdgroup ou un threadgroup entier effectue les mêmes opérations sur les mêmes données, vous pouvez tirer parti du matériel en choisissant un groupe d’exécution plus important. Mais si vos opérations MTLTensor divergent par rapport aux données ou présentent un flux de contrôle non uniforme sur le site d’appel des opérations MTLTensor, vous devez utiliser un groupe d’exécution à thread unique. D’autres schémas d’exécution supposent qu’il n’y a pas de divergence et que le flux de contrôle est uniforme pour le groupe d’exécution. En résumé, vous pouvez désormais effectuer des opérations ML comme la multiplication de matrices et la convolution dans vos shaders. Shader ML facilite l’exécution de plusieurs opérations ML dans un seul shader. Cela permet d’utiliser le cache, nécessite moins de répartitions et réduit la bande passante mémoire, en particulier avec les réseaux plus petits. Shader ML vous offre le contrôle précis indispensable pour créer des opérations personnalisées. Il n’a jamais été aussi facile d’implémenter des techniques ML de pointe dans vos apps Metal. Voilà comment vous pouvez utiliser Shader ML pour intégrer des réseaux neuronaux dans votre programme de shader. Je vais céder la parole à mon collègue Scott qui va vous montrer comment les nouveaux outils de Metal 4 facilitent le débogage des charges de travail ML. Bonjour à tous, je m’appelle Scott Moyers et je suis ingénieur logiciel au sein de l’équipe GPU Tools. Preston vous a montré une application qui utilise l’apprentissage automatique pour calculer l’occlusion ambiante. Cette app encode un réseau d’apprentissage automatique directement dans son pipeline de rendu Metal. Pendant le développement, j’ai rencontré un problème où la sortie présentait de graves artefacts. Je vais activer uniquement la passe d’occlusion ambiante pour illustrer mon problème. Il devrait y avoir des ombres dans les coins des objets, mais au lieu de cela, il y a beaucoup de bruit et la structure de la scène est à peine visible. Je vais vous montrer comment j’ai utilisé les nouveaux outils pour résoudre ce problème. Tout d’abord, capturons une trace GPU de l’app dans Xcode. Pour ce faire, je clique sur l’icône Metal en bas de l’écran, puis sur le bouton de capture.
Une fois la capture terminée, l’image capturée apparaît dans le résumé. Le navigateur de débogage à gauche fournit la liste des commandes utilisées par l’application pour créer l’image. Par exemple, le tampon de commandes hors écran contient de nombreux encodeurs, y compris la passe G-buffer. Le tampon de commandes suivant contient mon MTL4MachineLearningCommandEncoder. Metal 4 m’a offert un contrôle précis de la synchronisation, et même si j’ai configuré avec soin les barrières et les évènements entre les passes dépendantes, je me suis demandé si un problème de synchronisation pouvait expliquer ces difficultés. Pour vérifier cela, je me suis tourné vers Dependency Viewer, un outil qui fournit une vue d’ensemble de la structure de votre application Metal. Je vais cliquer sur l’icône Dépendances en haut à gauche.
Cette interface affiche toutes les commandes de l’application et toutes les primitives de synchronisation comme les barrières et les évènements. Le fait de zoomer sur un encodeur de commandes révèle encore plus de détails. On voit la fin de mon premier tampon de commandes.
La commande dessous copie les normales dans un MTLTensor. Ensuite, il y a une barrière suivie d’un MTL4MachineLearningCommandEncoder. Je vais dézoomer pour examiner la structure globale. Ma nouvelle passe d’occlusion ambiante se trouve dans le tampon de commandes à droite. Avant cette passe l’application fonctionnait bien, ce qui laisse supposer que les dépendances dans le tampon de commandes supérieur et inférieur sont correctes. J’examine le nouveau tampon de commandes contenant le MTL4MachineLearningCommandEncoder.
Avant que le tampon de commandes puisse démarrer, un signal d’évènement partagé est attendu. À la fin du tampon de commandes, un signal débloque le suivant. Il ne peut donc pas y avoir d’autres commandes exécutées en parallèle avec ce tampon. Le tampon de commande comporte des barrières entre chaque encodeur, garantissant que chaque commande s’exécute l’une après l’autre. J’étais convaincu à ce stade qu’il n’y avait aucun problème de synchronisation, du moins dans cette image. Cette cause étant éliminée, j’ai décidé de vérifier directement MTL4MachineLearningCommandEncoder. En cliquant sur l’appel de répartition du réseau d’occlusion ambiante, j’accède à ses ressources liées. Sur la droite, l’éditeur auxiliaire affiche le MTLTensor de sortie. Comme il a les mêmes artefacts que l’application en cours d’exécution, il est évident que le résultat est incorrect. Je vais double-cliquer sur le MTLTensor d’entrée pour l’afficher à côté de la sortie. L’entrée correspond à ce que j’attendais pour les normales en espace de vue : les objets orientés différemment ont des intensités de composantes différentes. Le problème doit donc venir de mon réseau d’apprentissage automatique. Revenons à notre vue des ressources liées et cette fois, je double-clique sur Network pour l’ouvrir dans le nouveau débogueur de réseau ML. Cet outil est essentiel pour comprendre ce qui se passe à l’intérieur du modèle.
Ce graphique représente la structure de mon réseau d’occlusion ambiante. Je l’ai écrit dans PyTorch, et dans les phases de génération de ma cible, comme l’a suggéré Preston, je l’exporte comme package CoreML, puis je le convertis en MTLPackage. Les cases représentent les opérations, tandis que les connexions montrent le flux de données dans le modèle, de gauche à droite. Je voulais savoir quelle opération était responsable de l’apparition des artefacts. Je savais que la sortie finale était incorrecte contrairement à l’entrée, alors j’ai décidé de diviser le graphique en deux. Choisissons une opération à peu près au milieu. Sélectionner une opération affiche sa description à droite, ainsi que ses attributs, ses entrées et ses sorties. De plus, je peux inspecter les données intermédiaires MTLTensor produites par toutes les opérations. Je vais cliquer sur l’aperçu pour l’ouvrir dans le visualiseur MTLTensor. Je vois que les artefacts sont déjà présents ici, je vais donc vérifier une opération antérieure.
Cette opération contient également des artefacts dans la sortie. Examinons son entrée.
Ce MTLTensor semble mettre en évidence les bords de la scène, ce qui est normal puisque l’entrée du réseau correspond aux bords extraits de notre tampon de profondeur. Cette partie du réseau doit donc présenter une anomalie.
Cette zone assemblée peut être développée en cliquant sur les flèches en haut à gauche de l’opération.
D’après l’ordre et les types de ces opérations, je reconnais ma fonction SignedSmoothstep. Elle prend d’abord la valeur absolue de l’entrée. Ensuite, elle limite la valeur entre 0 et 1. Mais elle élève ensuite le résultat à sa propre puissance, ce qui me semble incorrect, car je ne me souviens pas avoir vu une opération de puissance dans la fonction SignedSmoothstep. Allons voir ce qui se passe dans le code Python. Je vais arrêter la séance de débogage et revenir au code source.
Le modèle que j’exécute se trouve dans la classe LightUNet. Je vais accéder à sa fonction de propagation avant pour vérifier qu’il fait ce que j’attends.
La première opération personnalisée exécutée est SignedSmoothstep, qui est la zone assemblée visualisée dans le débogueur de réseau ML. Je vais passer directement à sa fonction de propagation avant.
Il doit s’agir d’une opération smoothstep simple dans laquelle je conserve le signe de l’entrée. Cette ligne contient le bug : j’ai tapé trop d’astérisques, transformant mon opérateur de multiplication en opérateur de puissance. Supprimons cet astérisque en trop et relançons l’exécution.
Et voilà une implémentation fonctionnelle de l’occlusion ambiante neuronale à l’aide du MTL4MachineLearningCommandEncoder intégré à Metal 4.
Dans cette démo, je vous ai montré comment le débogueur Metal permet de déboguer une application d’apprentissage automatique Metal 4. Le visualiseur de dépendances m’a aidé à valider la synchronisation. Puis, j’ai inspecté les entrées et sorties du réseau à l’aide du visualiseur MTLTensor, ce qui a confirmé que le problème venait bien du réseau. Enfin, j’ai utilisé le débogueur de réseau ML pour parcourir les opérations sur le réseau et identifier le problème.
Ces outils font partie d’une suite plus large disponible pour le débogage et l’optimisation des apps Metal. Récapitulons ce que nous avons vu aujourd’hui. Metal 4 introduit MTLTensor, une nouvelle ressource multidimensionnelle conçue pour les données d’apprentissage automatique. Les MTLTensors offrent une grande flexibilité pour les dispositions de données complexes multidimensionnelles et simplifient l’indexation grâce à des informations intégrées sur les dimensions et les strides. Les nouvelles fonctionnalités de Metal 4 permettent de combiner les charges de travail d’apprentissage automatique dans vos pipelines Metal. MTL4MachineLearningCommandEncoder permet à des réseaux ML entiers de s’exécuter directement sur la chronologie GPU. Cela garantit une intégration et une synchronisation transparentes avec votre travail de calcul et de rendu. Pour les réseaux plus petits, Shader ML et la bibliothèque Metal Performance Primitives permettent d’intégrer des opérations de machine learning directement dans vos shaders. Enfin, le débogueur Metal vous offre une visibilité incroyable sur ce qui se passe dans votre application Metal 4. Le nouveau débogueur de réseau ML facilite la compréhension de votre réseau et son exécution sur l’appareil. Ce type d’informations est essentiel pour garantir l’exactitude et optimiser les performances. Essayez par vous-même MTL4MachineLearningCommandEncoder et Shader ML de Metal 4 en installant la dernière version de l’OS et de Xcode. Pour en savoir plus sur la façon dont les outils de développement Metal peuvent vous aider, visitez le site web des développeurs Apple. Et pour tirer le meilleur parti de votre application Metal 4, regardez les autres conférences sur Metal 4. Nous avons hâte de voir ce que vous allez créer avec ces nouvelles fonctionnalités. Merci !
-
-
8:13 - Exporting a Core ML package with PyTorch
import coremltools as ct # define model in PyTorch # export model to an mlpackage model_from_export = ct.convert( custom_traced_model, inputs=[...], outputs=[...], convert_to='mlprogram', minimum_deployment_target=ct.target.macOS16, ) model_from_export.save('model.mlpackage')
-
9:10 - Identifying a network in a Metal package
library = [device newLibraryWithURL:@"myNetwork.mtlpackage"]; functionDescriptor = [MTL4LibraryFunctionDescriptor new] functionDescriptor.name = @"main"; functionDescriptor.library = library;
-
9:21 - Creating a pipeline state
descriptor = [MTL4MachineLearningPipelineDescriptor new]; descriptor.machineLearningFunctionDescriptor = functionDescriptor; [descriptor setInputDimensions:dimensions atBufferIndex:1]; pipeline = [compiler newMachineLearningPipelineStateWithDescriptor:descriptor error:&error];
-
9:58 - Dispatching a network
commands = [device newCommandBuffer]; [commands beginCommandBufferWithAllocator:cmdAllocator]; [commands useResidencySet:residencySet]; /* Create intermediate heap */ /* Configure argument table */ encoder = [commands machineLearningCommandEncoder]; [encoder setPipelineState:pipeline]; [encoder setArgumentTable:argTable]; [encoder dispatchNetworkWithIntermediatesHeap:heap];
-
10:30 - Creating a heap for intermediate storage
heapDescriptor = [MTLHeapDescriptor new]; heapDescriptor.type = MTLHeapTypePlacement; heapDescriptor.size = pipeline.intermediatesHeapSize; heap = [device newHeapWithDescriptor:heapDescriptor];
-
10:46 - Submitting commands to the GPU timeline
commands = [device newCommandBuffer]; [commands beginCommandBufferWithAllocator:cmdAllocator]; [commands useResidencySet:residencySet]; /* Create intermediate heap */ /* Configure argument table */ encoder = [commands machineLearningCommandEncoder]; [encoder setPipelineState:pipeline]; [encoder setArgumentTable:argTable]; [encoder dispatchNetworkWithIntermediatesHeap:heap]; [commands endCommandBuffer]; [queue commit:&commands count:1];
-
11:18 - Synchronization
[encoder barrierAfterStages:MTLStageMachineLearning beforeQueueStages:MTLStageVertex visibilityOptions:MTL4VisibilityOptionDevice];
-
15:17 - Declaring a fragment shader with tensor inputs
// Metal Shading Language 4 #include <metal_tensor> using namespace metal; [[fragment]] float4 shade_frag(tensor<device half, dextents<int, 2>> layer0Weights [[ buffer(0) ]], tensor<device half, dextents<int, 2>> layer1Weights [[ buffer(1) ]], /* other bindings */) { // Creating input tensor half inputs[INPUT_WIDTH] = { /* four latent texture samples + UV data */ }; auto inputTensor = tensor(inputs, extents<int, INPUT_WIDTH, 1>()); ... }
-
17:12 - Operating on tensors in shaders
// Metal Shading Language 4 #include <MetalPerformancePrimitives/MetalPerformancePrimitives.h> using namespace mpp; constexpr tensor_ops::matmul2d_descriptor desc( /* M, N, K */ 1, HIDDEN_WIDTH, INPUT_WIDTH, /* left transpose */ false, /* right transpose */ true, /* reduced precision */ true); tensor_ops::matmul2d<desc, execution_thread> op; op.run(inputTensor, layerN, intermediateN); for (auto intermediateIndex = 0; intermediateIndex < intermediateN(0); ++intermediateIndex) { intermediateN[intermediateIndex, 0] = max(0.0f, intermediateN[intermediateIndex, 0]); }
-
18:38 - Render using network evaluation
half3 baseColor = half3(outputTensor[0,0], outputTensor[1,0], outputTensor[2,0]); half3 tangentSpaceNormal = half3(outputTensor[3,0], outputTensor[4,0], outputTensor[5,0]); half3 worldSpaceNormal = worldSpaceTBN * tangentSpaceNormal; return baseColor * saturate(dot(worldSpaceNormal, worldSpaceLightDir));
-
-
- 0:00 - Introduction
Nous sommes ravis de présenter Metal 4, qui améliore l’intégration de l’apprentissage automatique dans les jeux et les graphismes. Metal 4 permet d’utiliser des techniques telles que l’upscaling, la compression des ressources et le mélange d’animations à l’aide de réseaux d’apprentissage automatique, ce qui améliore les performances et la fidélité visuelle. Les principales fonctionnalités comprennent MTLTensors pour les workflows d’apprentissage automatique, MTL4MachineLearningCommandEncoder pour l’exécution de réseaux sur la timeline GPU, Shader ML pour l’intégration d’opérations d’apprentissage automatique dans les shaders, ainsi que des outils de débogage améliorés. CoreML est idéal pour créer des modèles d’apprentissage automatique Vous pouvez également intégrer facilement l’apprentissage automatique dans vos apps à l’aide du débogueur Metal.
- 2:52 - Découvrez les tenseurs
Metal 4 présente MTLTensor, une nouvelle ressource spécialement conçue pour les charges de travail liées à l’apprentissage automatique. Les MTLTensors sont des conteneurs de données multidimensionnels qui permettent de représenter efficacement les structures de données complexes couramment utilisées dans l’apprentissage automatique, telles que celles requises pour les opérations de convolution. Ils simplifient l’indexation des données multidimensionnelles par rapport aux représentations plates telles que les MTLBuffers. Un MTLTensor est défini par son rang (nombre d’axes), ses étendues (nombre de points de données le long de chaque axe), son type de données et ses propriétés d’utilisation. Ces propriétés sont spécifiées dans un objet MTLTensorDescriptor. Vous pouvez créer des MTLTensors à partir d’un objet MTLDevice, qui offre des performances optimisées avec une disposition opaque, ou à partir d’un objet MTLBuffer existant, où vous devez spécifier les pas pour tenir compte du remplissage potentiel.
- 6:21 - Encodage des réseaux ML
La dernière version de Metal introduit également le MTL4MachineLearningCommandEncoder, qui permet d’intégrer de manière transparente le travail d’apprentissage automatique dans la timeline du GPU, parallèlement aux commandes de calcul et de rendu. Ce nouvel encodeur permet d’exécuter des modèles d’apprentissage automatique complets sur le GPU, en synchronisation avec d’autres commandes Metal à l’aide de primitives de synchronisation standard telles que les barrières et les clôtures. Le flux de travail comprend deux parties principales : hors ligne et en cours d’exécution. Hors ligne, le système convertit un package CoreML en un MTLPackage optimisé à l’aide de l’outil de ligne de commande metal-package-builder. Lors de l’exécution, le système compile MTLPackage en MTL4MachineLearningPipelineState et crée le MTL4MachineLearningCommandEncoder configuré avec l’état du pipeline, les entrées et les sorties, puis distribue les commandes encodées au GPU. L’encodeur utilise un MTLHeap pour stocker les données intermédiaires, ce qui optimise l’utilisation des ressources. Cela permet l’exécution parallèle de tâches non dépendantes, améliorant ainsi les performances. Les capacités de synchronisation de Metal 4 garantissent que les tâches utilisant les résultats d’un modèle d’apprentissage automatique attendent que celui-ci ait terminé son exécution, ce qui le rend adapté à de nombreuses apps en temps réel, et pas seulement aux jeux.
- 12:51 - Intégrer ML dans votre shader
Metal 4 introduit Shader ML, qui permet aux développeurs d’intégrer des opérations d’apprentissage automatique directement dans les shaders. Cela améliore les performances et réduit l’empreinte mémoire en éliminant la nécessité de synchroniser les tenseurs entre la mémoire de l’appareil et les shaders. La compression de textures de matériaux à l’aide de réseaux neuronaux, une technique spécifique de l’apprentissage automatique, en est un exemple. Cette technique compresse les textures des matériaux jusqu’à 50 % par rapport aux formats traditionnels compressés par blocs. Avec Shader ML, l’ensemble du processus d’évaluation des matériaux neuronaux, de l’initialisation des tenseurs d’entrée à l’inférence et à l’ombrage, peut être regroupé en un seul appel de shader. Metal Performance Primitives est intégré à Shader ML, fournissant des API hautes performances telles que la multiplication matricielle et la convolution. Cela vous permet de mettre en œuvre efficacement des routines d’évaluation de réseaux neuronaux dans des shaders de fragments, ce qui se traduit par des apps en temps réel sans perte de qualité perceptible, mais avec une réduction significative de l’utilisation de la mémoire et de l’espace disque.
- 20:26 - Déboguer vos charges de travail ML
Dans l’exemple fourni, vous pouvez utiliser les nouveaux outils GPU de Xcode pour déboguer une charge de travail d’apprentissage automatique qui provoque des artefacts visuels marqués dans le calcul de l’occlusion ambiante d’une app. Vous pouvez capturer la trace GPU et utiliser le visualiseur de dépendances pour inspecter la synchronisation des tampons de commandes, ce qui permet d’écarter tout problème. Vous examinerez ensuite les tenseurs d’entrée et de sortie du MTL4MachineLearningCommandEncoder afin de confirmer que le problème provient bien du réseau d’apprentissage automatique en soi. Ensuite, vous pouvez ouvrir le réseau dans le nouveau débogueur ML Network Debugger, un outil visuel qui représente la structure du modèle (écrit en PyTorch et converti en CoreML et MTLPackage), afin d’identifier l’opération spécifique responsable de l’introduction des artefacts. En examinant le graphique, on constate que les artefacts sont présents à la fois dans la sortie et dans l’entrée d’une opération précédente, ce qui indique un problème au sein du réseau. La fonction SignedSmoothstep est identifiée comme étant la source du problème. Après avoir examiné le code Python, un bug a été découvert : un astérisque en trop qui faisait interpréter au système une opération de multiplication comme une opération de puissance. La correction de cette erreur résout le problème, et l’implémentation de l’occlusion ambiante neuronale utilisant le MTL4MachineLearningCommandEncoder » de Metal 4 est ainsi déboguée avec succès.