Boîte à outils de survie GPU pour l’ère de l’IA
(journal.hexmos.com)- Le développement IA ne peut plus se contenter d’une exécution séquentielle de type CPU ; il faut comprendre le modèle de traitement massivement parallèle des GPU pour bien maîtriser les performances d’entraînement et d’inférence
- Un CPU dispose généralement de 2 à 16 cœurs en usage grand public, et excelle dans les tâches monothread et à branchements conditionnels, tandis qu’un GPU, avec ses milliers de petits cœurs, est mieux adapté aux calculs matriciels, au traitement d’images et au deep learning
- AWS propose des environnements d’exécution GPU comme P3/P4, P5/Inf1, G4 et Amazon SageMaker ; p3.2xlarge coûte 3,06 $/h, p5.48xlarge 98,32 $/h, et g4dn.xlarge environ 0,526 $/h
- CUDA de NVIDIA permet aux développeurs de gérer directement le flux d’exécution parallèle, de l’allocation mémoire GPU à la copie de données, au lancement de kernels et à la compilation
- Les exemples d’addition de tableaux, de génération de Mandelbrot et de CNN de classification chat/chien montrent comment découper une boucle séquentielle en threads GPU ; Mandelbrot passe ainsi de 4,07 s sur CPU à 0,0046 s sur GPU
Pourquoi les connaissances CPU ne suffisent plus
- Beaucoup de développeurs ont appris à programmer et à résoudre des problèmes selon une approche centrée sur le CPU, mais le CPU repose fondamentalement sur une architecture séquentielle
- Le CPU traditionnel exécute les instructions de façon linéaire et optimise un petit nombre de cœurs puissants pour les performances en thread unique
- Lorsqu’il faut traiter plusieurs tâches en même temps, le coût du traitement séquentiel augmente, car chaque tâche doit être exécutée à son tour
- Le multithreading peut améliorer les performances, mais la philosophie de conception du CPU reste globalement proche de l’exécution séquentielle
Modèles d’IA et traitement parallèle
- Les architectures IA modernes comme les Transformer exploitent le traitement parallèle pour améliorer les performances d’entraînement
- Les RNN fonctionnent de manière séquentielle, mais les Transformer comme GPT peuvent traiter plusieurs mots en même temps, ce qui améliore l’efficacité d’apprentissage et les capacités du modèle
- L’apprentissage parallèle permet d’entraîner des modèles plus grands, et des modèles plus grands peuvent produire de meilleurs résultats
- Le parallélisme ne s’applique pas seulement au traitement du langage naturel, mais aussi à la reconnaissance d’images
- AlexNet en est un exemple : différentes parties d’une image sont traitées en parallèle pour identifier des motifs
- En raison de sa conception axée sur les performances monothread, le CPU a du mal à répartir et exécuter efficacement les calculs massivement parallèles nécessaires aux modèles d’IA complexes
Comment les GPU réduisent les goulets d’étranglement
- Le GPU est conçu avec un grand nombre de petits cœurs spécialisés plutôt qu’avec quelques gros cœurs puissants comme le CPU
- Le parallélisme du GPU s’exprime particulièrement bien dans les charges de travail où le même type d’opération doit être répété à grande échelle, comme le rendu graphique ou les calculs mathématiques complexes
- Les frameworks de deep learning comme TensorFlow sont optimisés pour exploiter la puissance des GPU et accélérer l’entraînement comme l’inférence
- L’entraînement des réseaux de neurones implique beaucoup de calculs matriciels, et le GPU excelle dans leur parallélisation grâce à son grand nombre de cœurs
Différences de rôle entre CPU et GPU
-
CPU
- Le CPU est conçu avant tout pour le traitement séquentiel, et convient bien aux tâches qui exécutent un flux d’instructions linéaire
- Il est adapté au calcul généraliste, aux tâches système et aux algorithmes complexes avec branchements conditionnels
- Un CPU grand public compte généralement un nombre relativement limité de cœurs, souvent de 2 à 16
- Chaque cœur peut traiter indépendamment son propre jeu d’instructions
-
GPU
- Le GPU est conçu selon une architecture parallèle, efficace pour traiter simultanément un grand nombre de sous-tâches
- Il est particulièrement adapté au rendu graphique, aux calculs mathématiques complexes et à l’exécution d’algorithmes parallélisables
- Il découpe les tâches en unités parallèles plus petites pour exécuter plusieurs opérations en même temps
- Les cœurs GPU se comptent souvent par milliers et sont organisés en streaming multiprocessors (SMs) ou structures similaires
- Il convient aux traitements manipulant simultanément de grands volumes de données, comme l’image, la vidéo, le deep learning ou les simulations scientifiques
Environnements GPU disponibles sur AWS
- AWS propose plusieurs instances GPU pour des charges de travail comme le machine learning
-
Instances GPU généralistes
-
Instances optimisées pour l’inférence
- L’inférence consiste à injecter des données en temps réel dans un modèle IA entraîné afin d’obtenir une prédiction ou de résoudre une tâche
- P5 et Inf1 sont conçues pour l’inférence machine learning, où la faible latence et le coût sont essentiels
- p5.48xlarge coûte 98,32 $/h et fournit 8 GPU NVIDIA H100 avec 80 Go chacun, soit 640 Go de mémoire vidéo au total
-
Instances optimisées pour le graphisme
- Les G4 instances sont conçues pour gérer des tâches intensives en graphisme
- Les développeurs de jeux vidéo peuvent utiliser des instances G4 pour rendre des graphismes 3D de jeu
- g4dn.xlarge coûte 0,526 $/h et utilise un GPU NVIDIA T4 avec 16 Go de mémoire
-
Service managé de machine learning
- Amazon SageMaker est un service managé de machine learning qui donne accès à des instances GPU comme P3, P4 et P5
- SageMaker convient aux organisations qui veulent se lancer dans le machine learning sans gérer elles-mêmes l’infrastructure sous-jacente
- Une page distincte est disponible pour les tarifs Amazon SageMaker
Premiers pas avec NVIDIA CUDA
- CUDA est une plateforme de calcul parallèle et un modèle de programmation développés par NVIDIA, qui permettent d’accélérer l’exécution des applications grâce aux GPU
- L’exemple montre le flux de développement CUDA : allocation de mémoire GPU, copie des données, exécution du kernel et récupération du résultat
-
Installation
- Téléchargez le base installer et le driver installer depuis CUDA
- Ajoutez les variables d’environnement suivantes dans le
.bashrcdu dossier personnelexport PATH="/usr/local/cuda-12.3/bin:$PATH"export LD_LIBRARY_PATH="/usr/local/cuda-12.3/lib64:$LD_LIBRARY_PATH"
- Exécutez les commandes suivantes
sudo apt-get install cuda-toolkitsudo apt-get install nvidia-gds
- Redémarrez le système pour appliquer les changements
-
Commandes de vérification utiles
lspci | grep VGA: identifie et liste les GPU du systèmenvidia-smi: fournit des informations détaillées sur l’utilisation, la température et la mémoire des GPU NVIDIAsudo lshw -C display: affiche des informations sur les contrôleurs d’affichage, dont les cartes graphiquesinxi -G: montre les informations du sous-système graphique, y compris GPU et affichagesudo hwinfo --gfxcard: permet d’obtenir des informations détaillées sur les cartes graphiques du système
Paralléliser une addition de tableaux avec CUDA
- L’addition de tableaux est un bon problème pour expliquer la parallélisation sur GPU
- Les tableaux d’exemple sont
A = [1,2,3,4,5,6],B = [7,8,9,10,11,12], et le résultat estC = [8,10,12,14,16,18] - L’approche CPU parcourt les éléments du tableau un par un pour effectuer l’addition
- À mesure que le volume de données augmente, le temps d’exécution séquentiel s’allonge, tandis que le GPU peut effectuer simultanément des opérations comme
1+7,2+8,3+9 - L’exemple CUDA utilise un fichier de kernel
.cu__global__désigne une fonction kernel appelée sur le GPUvectorAddreçoit trois pointeurs entiersa,betcet effectue l’addition vectoriellethreadIdx.xrécupère l’indice du thread courant- Chaque thread stocke la somme de l’élément correspondant dans
c[i]
- La fonction
mainsuit l’ordre allocation mémoire GPU, copie des données, exécution du kernel, copie du résultatcudaMallocallouecudaA,cudaBetcudaCdans la mémoire GPUcudaMemcpycopieaetbde l’hôte vers le GPUvectorAdd <<<1, sizeof(a) / sizeof(a[0])>>>lance le kernel- Le vecteur résultat
cudaCest recopié du GPU vers l’hôte
- La compilation et l’exécution se font avec la commande
nvcc - Le code complet est fourni
Utiliser le GPU pour générer des images en Python
- La génération de l’ensemble de Mandelbrot consiste à produire des motifs visuels complexes à partir du comportement de nombres dans une équation donnée, et c’est une tâche gourmande en ressources
- Dans l’exemple Python sur CPU, chaque pixel est parcouru pour calculer la valeur de Mandelbrot, et la génération d’une image 1024×1536 prend 4,07 secondes
- La version accélérée par GPU utilise la Numba library
- Le décorateur
@jiteffectue une compilation Just-In-Time qui transforme le code Python en code machine cuda.jitsert à créermandel_gpu, avecdevice=Truepour indiquer une exécution sur GPUmandel_kernels’exécute sur le GPU CUDA et répartit la génération de Mandelbrot entre les threads GPU
- Le décorateur
create_fractal_gpugère l’allocation mémoire GPU, la configuration des threads et blocs, l’exécution du kernel, la synchronisation et la copie du résultatthreadsperblock = (16, 16)est utilisécuda.synchronize()attend la fin du travail GPUd_image.copy_to_host(image)copie le résultat vers le CPU
- Le temps d’exécution sur GPU est de 0,0046 seconde, bien plus rapide que le code basé sur CPU
- Le code complet est fourni
Entraîner un réseau de neurones de classification chat/chien avec un GPU
- Un exemple de réseau de neurones distinguant chats et chiens est utilisé pour montrer comment les GPU servent en IA
- Les prérequis sont CUDA et TensorFlow
- TensorFlow peut être installé avec
pip install tensorflow[and-cuda] - Le jeu de données utilisé est Kaggle Dogs vs. Cats
- Après téléchargement, les images de chats et de chiens sont organisées dans des sous-dossiers distincts du dossier d’entraînement
- TensorFlow peut être installé avec
- Le modèle utilise un réseau de neurones convolutif (CNN)
- pandas et numpy servent à la manipulation des données
Sequentialpermet d’empiler linéairement les couches du réseauConvolution2D,MaxPooling2D,Dense,Flattensont les couches qui composent le CNNImageDataGeneratorest utilisé pour l’augmentation de données en temps réel pendant l’entraînement
- Les données d’entraînement sont chargées avec
ImageDataGenerator- On applique
rescale=1./255,shear_range=0.2,zoom_range=0.2,horizontal_flip=True - Les images d’entrée sont configurées en taille
(64, 64), batch size 32, mode de classification binaire
- On applique
- L’architecture du CNN est composée de couches de convolution, max pooling, flatten, Dense et d’une sortie sigmoid
- Le modèle est compilé avec l’optimiseur
adam, la lossbinary_crossentropyet la métriqueaccuracy - L’entraînement s’exécute avec
epochs=25,validation_steps=2000, puis le modèle est enregistré dans un fichier.h5viaclassifier.save('trained_model.h5') - Le code d’inférence charge
trained_model.h5, convertit l’image en(64, 64), puis affichedogsi la prédiction est supérieure ou égale à 0.5, sinoncat - Le code complet est fourni
L’étendue des usages du GPU
- À l’ère de l’IA, il devient difficile d’ignorer les capacités des GPU, et les développeurs doivent mieux comprendre leur potentiel
- En passant d’algorithmes séquentiels à des algorithmes parallélisés, le GPU devient un outil d’accélération des calculs complexes
- Les capacités de traitement parallèle du GPU sont particulièrement utiles pour les grands jeux de données et les architectures de réseaux de neurones complexes en IA et en machine learning
- Les GPU sont utilisés au-delà du machine learning traditionnel, notamment dans la recherche scientifique, la simulation et les traitements intensifs en données
- Leur capacité de traitement parallèle est mise à profit pour résoudre des problèmes dans des domaines variés comme la découverte de médicaments, la modélisation climatique ou les simulations financières
1 commentaires
Avis sur Hacker News
Le code de cet article est faux. Aucun kernel CUDA n’est appelé : https://github.com/RijulTP/GPUToolkit/blob/f17fec12e008d0d37...
90 % du temps passé à « calculer » l’ensemble de Mandelbrot avec le code compilé en JIT ne sert pas au calcul réel, mais à la compilation de la fonction
Si vous voulez vraiment apprendre CUDA, implémenter une multiplication de matrices est un bon exercice ; des tutoriels utiles sont https://cnugteren.github.io/tutorial/pages/page1.html et https://siboehm.com/articles/22/CUDA-MMM
Elle prend deux vecteurs X et Y en virgule flottante 32 bits, ainsi qu’un scalaire A, multiplie chaque X[i] par A puis l’ajoute à Y[i] : https://developer.nvidia.com/blog/six-ways-saxpy/
L’article affirme que « tous les développeurs devraient savoir » cela, mais en réalité il traite plutôt de la manière dont les GPU sont utilisés en IA. La plupart des développeurs ne sont pas des développeurs IA, et n’interagissent pas directement avec l’IA ni n’utilisent directement des GPU
En plus, il aborde à peine la 3D, qui est pourtant la raison fondamentale de l’existence des GPU
Avec des notions de base, on comprend aussi mieux les discours « IA » vendus aux managers
L’attitude consistant à dire que « les domaines adjacents ne servent à rien » est quelque chose que j’ai souvent vu à l’école. Côté administration système, mes camarades disaient qu’ils n’avaient pas besoin de connaître la programmation, mais il fallait faire du scripting ; dans une école de développement logiciel, on disait qu’il n’était pas nécessaire de connaître les réseaux, mais quelques années plus tard DevOps apparaissait partout dans les offres d’emploi
Si l’article fait environ 1 500 mots, même en le lisant comme un support d’étude cela prend une douzaine de minutes, et passer deux heures à exécuter les exemples de code n’est pas un gros investissement. Bien sûr, cela suppose que l’article soit une bonne introduction
curl. Je suis toujours développeur embedded, mais depuis j’ai beaucoup appris en backend, frontend et infrastructure, et il me semble très probable qu’une situation similaire se produise autour de l’IA dans l’ensemble du secteur au cours des prochaines annéesJe pense que la raison pour laquelle Python domine en IA est que la relation Python-C ressemble à la relation CPU-GPU
Les GPU sont très performants mais difficiles à programmer directement ; les gens les utilisent donc via des appels à des API de haut niveau comme PyTorch
C est aussi performant mais difficile à programmer ; on utilise donc Python comme couche d’abstraction au-dessus de C
Il n’est pas évident que les gens doivent comprendre les GPU aussi en profondeur. C’est encore plus vrai s’ils ne sont pas plongés dans l’entraînement ou l’exploitation de modèles d’IA ; et si la loi de Moore prend fin et que le multithreading devient le principal moyen d’augmenter les performances, il y a de fortes chances que de nouveaux langages adaptés au paradigme de la programmation parallèle apparaissent. Mojo ressemble à un point de départ
L’idée serait que, dès de simples calculs répétitifs, chaque instruction exploite intelligemment en arrière-plan tous les cœurs CPU en parallèle, et que les tâches qui le permettent soient transférées au GPU
Je me demande si ce genre de tentative a déjà existé, ou même si c’est possible au départ
Dire que « lorsqu’un CPU rencontre plusieurs tâches, il alloue ses ressources pour traiter chaque tâche une par une » est beaucoup trop simpliste. J’en viendrais presque à souhaiter que les CPU soient encore aussi simples
Il est légitime que l’article se concentre sur le modèle de programmation, mais du point de vue des performances, dire qu’un « CPU exécute les instructions séquentiellement » est fondamentalement faux. Les pipelines exécutent des instructions en parallèle, il y a aussi le SIMD, et plusieurs cœurs peuvent travailler ensemble sur le même problème
La grande différence est que le CPU consacre beaucoup de silicium et d’énergie à la gestion du flux de contrôle pour exécuter efficacement un seul thread, tandis que le GPU consacre ces ressources à davantage d’unités de calcul et exécute un très grand nombre de threads pour masquer les latences liées au flux de contrôle et à la mémoire
Dire que les CPU sont adaptés au code sériel et les GPU au code parallèle est vrai dans une certaine mesure, mais c’est une approximation assez grossière. À budget énergétique comparable, de l’ordre de quelques centaines de watts, un CPU compte environ 100 « cœurs » qui exécutent des tâches indépendantes une par une, hyperthreading compris, et masquent la latence mémoire grâce à la prédiction de branchement et au pipelining
Un GPU compte environ 100 « unités de calcul », et chaque unité entrelace l’exécution d’environ 80 tâches indépendantes, masquant la latence mémoire en exécutant l’instruction suivante d’une autre tâche
La terminologie est assez déroutante, et il est très probable qu’un CPU dispose d’unités vectorielles de 256 bits de large, tandis qu’un GPU en ait de 2048 bits, mais avec un peu de recul les deux architectures paraissent assez similaires
On pourrait appeler ça Xeon Chi
La plupart des langages de programmation étant conçus pour un traitement séquentiel façon CPU, alors qu’Erlang/Elixir ont été conçus pour le parallélisme façon GPU, je me demande si Nx / Axon va décoller : https://github.com/elixir-nx/
Il faudrait un guide d’achat. J’aimerais savoir quel est le minimum à dépenser et quels sont les meilleurs choix par tranche de budget. Le problème, c’est que ces informations changent de temps en temps, et je ne sais pas s’il existe une ressource maintenue à jour en continu
https://colab.google/
https://www.kaggle.com/docs/notebooks
https://www.paperspace.com/gradient/free-gpu
On est revenus aux articles putaclic du genre « ce que tout développeur devrait savoir » ?
J’aime les textes qui abordent la complexité de front, et comme je connais un peu à la fois les méthodes quantitatives et les détails qualitatifs de domaines comme le matériel informatique, j’apprécie les articles qui expliquent vraiment les détails d’un sujet
Par exemple, que tous les programmeurs doivent ou non connaître « What every programmer should know about memory » est une autre question, mais un bon programmeur devrait avoir une idée de la façon dont un ordinateur fonctionne réellement. Le point essentiel à retenir de cet article, la localité, apparaît souvent naturellement dans du bon code rapide, facile à suivre et bien adapté au problème
Bon article, mais les instances AWS P5, avec les P4d et P4de, sont clairement orientées entraînement plutôt qu’inférence. Les types d’instances plus adaptés à l’inférence sont G4dn et G5, qui utilisent respectivement des GPU T4 et A10G
Je débute presque totalement en programmation GPU, mais j’ai trouvé cet article intéressant. C’est impressionnant de voir à quel point les choses ont progressé, au point qu’on puisse entraîner aussi facilement un simple réseau neuronal « chien ou chat »