Analyse • IA, IA, mais qui es-tu donc ?
L’intelligence artificielle moderne, qu’est-ce que c’est ?
Ah, sacro-sainte IA ! Elle fait les choux gras des start-ups qui l’utilisent, dope les ventes des GPU et s’invite dans à peu près tous les débats comme une technologie de rupture, la dernière révolution en matière d’informatique. En bref, l’intelligence artificielle est the truc à intégrer de partout, jusqu’à prévoir une touche « Copilot » dédiée sur les « IA PC » approuvés par Microsoft afin de lancer l’assistant de la firme en une tape. Pourtant, derrière ce nom fourre-tout qu’est IA se trouvent de multiples concepts plus complexes et nuancés que nous allons parcourir ensemble dans ce dossier. Vous êtes prêts ? C’est parti !
Vous vous en serez sûrement doutés, mais cet article n’a pas pour but d’être exhaustif. Certains aspects du machine learning seront passés sous silence, qu’il s’agisse de type de réseaux, de méthode d’entraînement, de hardware ou encore du côté (vraiment) matheux… et il y a de fortes chances que de nouvelles découvertes en la matière rendent ce dossier obsolète en quelques années seulement. Ne vous étonnez donc pas de ne pas retrouver tel ou tel terme que vous avez entendu au travers d’un communiqué de presse ou une conférence : comme les anti-aliasing, il y en a bien trop pour tous pouvoir les couvrir, et beaucoup sont inutiles ou très peu répandus. Vous voilà prévenus !
Préambule : une intelligence, vous dites ?
Commençons par la face la plus visible du bazar : la dénomination grand public de la chose. Si le terme d’Intelligence Artificielle fait penser — remercions les œuvres de science-fiction pour cela — à des machines disposant de libre arbitre par un truchement informatique, il ne s’agit pas (encore ?) de cela. Ce concept se nomme Intelligence Artificielle Générale, et désigne un tel programme qui pourrait apprendre à faire « tout », pourvu qu’on le lui montre. Si le sujet passionne les philosophes et pose des questions éthiques, il ne s’agit pas des IA actuellement vendues comme révolutionnaires par diverses firmes (quant à leurs capacités réelles, direction le paragraphe suivant !), nous fermons donc cette parenthèse sans scrupule.
Du coup, que renferme ce terme d’IA ? Eh bien, du machine learning , traduit en français par « apprentissage automatisé », ou encore « apprentissage statistique » : il s’agit d’une catégorie de programme dans laquelle le programmeur ne spécifie pas directement la méthode de résolution d’une tâche (par exemple, « superpose deux images en faisant la moyenne des pixels »), mais un moyen de trouver des similitudes entres données (« voici 50 images et, pour chacune, une version upscalée, trouve l’opération qui a été faite »). Ainsi, un algorithme de machine learning se caractéristique par un besoin de données initiales, permettant de forger la base de connaissances du programme. De par le caractère critique de ces données (et des questions de propriété intellectuelle qui y sont liées), la transparence vis-à-vis des données sources a été l’un des points critiques de la réglementation européenne concernant l’IA. Cette dépendance aux données vaut aujourd’hui à l’IA le doux sobriquet de PISS, pour Plagiarized Information Synthesis System, ou « Système de synthèse d’informations plagiées » reflétant davantage le cœur du schmilblick : recracher les tendances présentes dans la majorité des données analysées… ce qui est loin d’être un calque parfait de l’intelligence humaine.
Si les réseaux de neurones se sont imposés comme la méthode canonique d’IA de ces dernières années, elle n’est pas la seule — et de loin. Les machines à support de vecteurs, k-voisins (kNN), k-means ou encore les arbres de décisions sont d’autres familles d’algorithmes entrant dans la catégorie du machine learning. Cependant, du fait d’une précision plus basse, d’une plus grande difficulté d’adaptation aux architectures matérielles actuelles et/ou d’une plus grande difficulté d’étalonnage, ces méthodes ont perdu en popularité au profit des réseaux de neurones, c’est pourquoi nous ne causerons plus que de ces derniers dans la suite de ce dossier. De même, il existe des méthodes d’apprentissage dites non-supervisées dans lesquelles l’apprentissage de l’algorithme se fait sans consigne initiale (c’est-à-dire sans mention explicite du résultat final) ; nous les passerons également sous silence pour des questions de lisibilité !
Du Perceptron à ChatGPT
Si vous êtes probablement très nombreux à connaître ChatGPT, vous êtes très probablement nettement moins à connaître le Perceptron. Si ses consonances pourraient rappeler une rubrique d’un site de hardware francophone bien connu, le terme n’a rien à y voir puisqu’il désigne le premier algorithme de machine learning proprement dit. Alors que l’explosion de l’IA aux yeux du grand public n’a que quelques années, ses premiers pas débutent au milieu du siècle dernier, principalement à partir de résultats… théoriques. Ce n’est qu’une fois le hardware suffisamment performant que la recherche s’est mue de fondamentale à appliquée, et a pu mener à la construction des réseaux profonds et, désormais, d’architectures plus complexes encore. Voyons cela !
Des débuts dans les années 60 !
Nous vous l’expliquions en introduction, l’IA moderne est un programme informatique, défini formellement comme une famille d’algorithme : une liste de course composée d’un nombre fini de choses à faire par une machine. Et, comme son nom l’indique, les réseaux de neurones sont inspirés du vivant en reprenant des idées des neurones présents dans les systèmes nerveux. Leur fonctionnement est évidemment trop complexe — et votre humble serviteur absolument ignorant de ce pan des sciences — pour être détaillé ici, mais ce que les informaticiens en ont extrait est la vision simplifiée suivante :
- un neurone prend ses informations de multiples neurones voisins (via ses dendrites)
- un neurone traite ces informations pour en faire la synthèse
- un neurone transmet ce résultat à un ou plusieurs voisins (via l’axone, relié aux dendrites d’autres neurones par des synapses)
Pour plus de détails, nous vous renvoyons au Laboratoire Parole dont ce schéma est issu
Dès les années 1940, cette structure est mimiquée par une machine (construite en 1957) que l’on nomme perceptron, et qui servait (déjà !) à des tâches de reconnaissance d’image — d’une définition de 20x20 sur la version Mark I, attention les yeux ! De nos jours, le perceptron désigne le programme implémenté par ses machines : un classificateur binaire, c’est-à-dire un programme répondant oui on non à une question, composés de neurones (artificiels).
En entrée de chaque neurone, des nombres x1, x2, ... . En sortie, « oui » ou « non ». Et en interne, la soupe secrète mathématique : on multiple chaque entrée par un nombre (nommé poids et usuellement noté w1, w2,…) et on somme tous ces résultats, en plus d’un biais b, tout ce beau monde étant possiblement positif comme négatif, voire nul. On obtient alors encore un nombre, que l’on convertit en réponse par une fonction d’activation qui est ici une simple comparaison : au-dessus de 0, c’est oui, en dessous, c’est non ! C'est ce que l'on nomme un booléen : une quantité qui ne peut prendre que deux valeurs : 0 (faux) ou 1 (vrai).
Pour les plus matheux, le neurone calcule θ[Σ (xi wi) + b)], soit theta (notre fonction d'activation) appliqué à la somme des xi multipliés par les poids wi et du biais b, une formule qui n’est pas sans rappeler un produit scalaire. Si vous n’avez rien suivi, pas de panique, ce sera la seule formule matheuse de ce dossier !
Multiplier deux nombres pour les rajouter à un troisième, cela porte le doux sobriquet de Multiplication-Accumulation, ou MAC, et parfois de FMA (Fused Multiply-Add) en fonction de la précision de la multiplication. Or, en ajoutant ensemble les sommes des multiplications entrée x poids, on obtient exactement.... des MAC. Ainsi, cette opération (une multiplication suivie d’une addition) est la première intégrée dans tous les bidules accélérateurs d’IA.
Le voilà, le neurone informatique !
Dans ce cas, vous noterez qu’un seul neurone est présent. La première évolution a ensuite été de le répliquer dans le sens de la largeur : plusieurs neurones opèrent en parallèle sur les mêmes xi d’entrée, formant une couche cachée. Puisque chacun de ces neurones possède ses poids et son biais, le produit scalaire précédent composé de MAC se transforme en un produit matrice-vecteur (le vecteur étant l’entrée, et la matrice constituée des poids d’une couche). Quelqu’un a parlé des Tensor Core, qui accélèrent justement les produits de matrices ? Pas si vite, matelots, nous ne sommes que dans les années 80 !
En sortie, un unique neurone condense les résultats issus de la couche cachée précédente :
On commence à se rapprocher des structures de réseaux profonds… patience, ça arrive !
Un potentiel énorme
Outre l’algorithme en tant que tel, ce sont ses propriétés qui ont fasciné, notamment sa capacité d’approximation universelle : avec un nombre suffisant de neurones dans cette couche cachée, à peu près n’importe quoi (tant que la sortie reste « sage » par rapport aux entrées, correspondant à la notion mathématique de contiguité) est approchable, et ce avec n’importe quel niveau de précision. C’est-à-dire qu’avec un nombre infini de neurones et des données d’apprentissage illimitées, le perceptron est tout-puissant. Pour autant, les années 1980 n’ont pas vu d’explosion du machine learning ! La faute incombant à la différence terrible entre théorie et pratique : si, en théorie, il est possible de faire faire tout et n’importe quoi à un perceptron à une couche cachée, cela ne dit pas la quantité de neurones, qui devient rapidement énorme, ni le temps à passer pour choisir les bons poids et biais. À une époque où l’informatique est loin des TFLOPS actuels, cette convergence restait inexploitable.
Or, dans les années 1990, une nouvelle manière d’assembler des perceptrons a le vent en poupe : en empilant les couches, plutôt les faisant grandir à l’infini. On parle alors de perceptron multicouche, ce qui a le double avantage d’être tout aussi expressif (i.e. puissant dans leur capacité à apprendre) tout en étant plus facilement entraînable, car moins gros : c’est la genèse des réseaux de neurones profonds (DNN) modernes. Dans le même temps, d’autres fonctions d’activations que le passant/pas passant voient le jour (citons le ReLU, qui a le bon avantage de transmettre un entier positif et non plus un booléen), ce qui simplifie davantage l’implémentation sur les machines d’époque tout en améliorant la précision.
Un perceptron à 3 couches cachées, 5 entrées et 4 sorties
Et l’inférence dans tout ça ?
Le perceptron multicouche peut être vu comme le modèle de base dont dérivent tous les réseaux actuels. Pour l’utiliser, c’est-à-dire pour réaliser la tâche apprise au préalable — on parle alors d’inférence — il faut traverser toutes les couches une à une. En partant de l’entrée encodée sous la forme d’un ensemble de nombres (qu’il s’agisse de texte, d’image, de sons, etc...), l’action des neurones consiste à multiplier et sommer tout ce beau monde ensemble, puis appliquer la fameuse fonction d’activation pour obtenir un nouvel ensemble de nombres (possiblement de plus grande ou plus petite taille que celui des données de départ), nommé résultat intermédiaire. L’opération se répète couche après couche, jusqu’à la dernière qui a pour caractéristique de comprendre autant de neurones que de nombres en sortie. À la fin, il ne reste plus que le résultat de la digestion des multiplications/activations successives de notre entrée, qui est interprétée différemment selon l’utilisation du réseau : son, image, probabilité de présence, etc. Ta-da ! Pour l’intelligence, par contre, difficile de la voir depuis cet angle...
En résumé, la genèse des réseaux de neurones remonte aux années 1960, puis 1980, sans pour autant que le matériel ne puisse apporter la puissance de calcul nécessaire pour révéler leur plein potentiel. Rendez-vous page suivante pour comprendre comment le perceptron a évolué pour donner les DNN actuels !
Et maintenant ?
En résumé, le perceptron est bien chouette, mais il faut atteindre un certain nombre de neurones pour réussir à obtenir des résultats convaincants, ce qui n’est pas chose facile lorsque les capacités de calcul sont limitées. Ainsi, c’est dans la fin des années 2000 et les années 2010 que le machine learning prend son envol, aidé par des architectures matérielles et des frameworks logiciels permettant enfin d’exploiter le potentiel des réseaux, tout en laissant libre cours à l’expérimentation de certains. Après avoir posé un cadre théorique, la recherche empirique de topologies (c’est-à-dire du moyen le meilleur d’agencer les couches [connexions, nombre de neurones]) a pu prendre son envol.
Il faut changer sa couche !
CNN : Viva la convoluzion
Empiler des couches type perceptron les unes sur les autres formes ce que l’on nomme des couches denses : tous les neurones d’une couche sont reliés à tous les neurones de la couche suivante. Si cela est pertinent dans des tâches où la place des données n’apporte pas d’information (par exemple pour croiser des statistiques entres elles de manière brutale), cela n’est pas le plus adapté pour les autres situations. Pour les applications de traitement d’image, typiquement, les pixels situés à l’autre bout du fichier ont peu de chance d’indiquer des informations pertinentes. En revanche, les pixels voisins, eux, permettent de détecter des formes, nuances et autres dégradés : bref, des éléments plus intéressants. Ainsi, un nouveau type de couche a rapidement pris le pas sur ces applications : les CNN, ou Convolutionnal Neural Networks. Leur fonctionnement est similaire au perceptron, si ce n’est que les neurones ne voient qu’une fenêtre de l’image d’entrée (par exemple un carré de 5x5 px ou 9x9 px). Notez qu’une image, sur ordinateur, possède une dimension de plus que sa longueur et sa largeur : les canaux, initialement les trois couleurs rouge, vert et bleu de l’image. Dans un CNN, ces canaux sont traités indifféremment par un CNN (connexions complètes) et sont convertis en un nombre plus important de fonctionnalités [features]). Un CNN typique va ainsi augmenter le nombre de fonctionnalités et diminuer la taille spatiale pour extraire des informations, avant de condenser le tout au moyen de couches complètes que l’on nomme tête du réseau.
La structure typique d’un CNN « simple ». Les premières couches de convolutions extraient les « features » en réduisant la taille spatiale de l’image au profit des fonctionnalités (épaisseur des images sur le schéma). Puis, le tout est converti en un vecteur, qui perd toute l’information spatiale. Enfin, la tête (couche de classification) permet d’obtenir en sortie la probabilité (pourcentage de chance d’après le réseau) que l’image contienne un cheval, un zèbre, un chien, etc. (Crédit : LinkedIn)
De ce fait, les couches convolutionnelles, proche de l’image, apprendront des motifs basiques (cercles, lignes, vagues,…), là où les couches intermédiaires combineront ces informations pour reconnaître des structures combinant plusieurs motifs (main, visage, corps,…). Enfin, la tête du réseau ne sert qu’à classifier ces informations, par exemple en reconnaissant un chat, un humain, un chien. Il est ainsi monnaie courante de remplacer la tête d’un réseau déjà entraîné par une nouvelle tête afin de réentrainer le bouzin pour une nouvelle tâche : un réseau reconnaissant un animal peut ainsi être utilisé pour classer des fleurs, et ce à moindre temps/coût (nous y reviendront dans deux pages). Au passage, ce phénomène de plasticité cérébrale n’est pas sans rappeler le même phénomène chez l’homme : comme quoi, ces réseaux artificiels ne sont pas si loin de leurs homologues biologiques.
RNN, LSTM : tout est une histoire de mémoire
De même, les réseaux de neurones sont également impliqués dans des tâches de traitement audio et vidéo, cas dans lequel les données possèdent une dimension temporelle. Dans le même esprit que les CNN, les couches denses sont moins efficaces et difficilement entraînables, car elles mettent au même niveau les mots situés au début et à la fin de la phrase, ce qui ne correspond pas aux grammaires des langues humaines, qui reposent sur le contexte. Dans ce cas, ce sont des réseaux dits récurrents (ou RNN) qui sont à l’œuvre. Ils possèdent un lien reliant chaque neurone à lui-même pour lui rappeler la valeur précédente donnée en sortie, d’où le qualificatif de « récurrents » ; lien que l’on peut également interpréter comme la présence d’une mémoire au sein du réseau, ou encore comme un réseau bien plus grand dont les neurones partagent leur poids/biais — si vous avez oublié de quoi il s’agit… direction la page précédente !
Un seul neurone récurrent (à gauche), avec sa liaison récurrente. De l’autre côté du signe « = », son interprétation déroulée : une succession de neurones partageant le même poids (woh) et biais (bo). En revanche, la sortie du neurone précédent (contrôlée par whh et bh) est connectée à l’entrée du neurone suivant, tout comme la nouvelle donnée d’entrée (contrôlée, elle, par whx).
Les LSTM (Long Short-Term Memory) poussent le concept plus loin encore en contrôlant la quantité de données issues de l’itération précédente en les stockant dans une cellule. Cette dernière est mise à jour et utilisée en fonction de valeurs prises par un signal d’entrée (input gate), un signal de sortie (exit gate) et une porte d’oublie (reset gate). Là encore, ce sont des coefficients similaires aux poids du perceptron qui régissent ces flots, avec l’avantage de permettre un entraînement de ces derniers plus aisé, pour ceux qui ont le courage de se plonger dans les mathématiques cachées dernières ces structures — à savoir, par nous ! —, car la cellule permet de conserver des informations au travers des couches, rendant les mécanismes de corrections des poids (voir page 6 pour plus de détails !) plus efficaces. Cependant, du fait du nombre considérable de coefficients à étalonner, les LSTM se font peu à peu délaisser au profit des GRU (Gated recurrent units), qui reprenne le principe en retirant le signal de sortie. Là encore, l’idée est de pouvoir en caser davantage pour la même puissance de calcul, et pouvoir entraîner le bouzin plus simplement.
Bon, clairement, là, on ne va pas trop rentrer dans les détails. Les flèches provenant du bas représentent les informations de la couche inférieure, et celle du haut, les informations qui vont vers couche supérieure. Côté gauche, ht-1 correspond à la sortie du neurone au pas de temps précédent, et à droite, la flèche envoie ht au pas de temps suivant à d’autres neurones.
Sans trop rentrer dans les détails, le RNN est très (trop) simpliste pour retenir efficacement de l’information. Le LSTM, lui, est bien plus complexe, mais paye le prix avec trois σ (traitement des signaux). Sur le GRU, seul deux σ sont nécessaires : la complexité est réduite !
Aparté : les réseaux creux
Toutes les couches abordées dans cette section ont un point commun : elles prennent en entrée toutes les données de la couche précédente, pour donner une sortie complète. En même temps, que faire d’autre ? Hé bien, il est possible de faire des réseaux dit creux (ou sparse en anglais) en prenant un réseau classique (dit dense) et en retirant une partie des neurones, typiquement ceux associés aux poids les plus faibles. Cette technique se base sur l’hypothèse du ticket gagnant qui suppose que dans tout réseau dense, il existe un sous-réseau creux de précision similaire (l’analogie avec le loto est claire : parmi tous les tickets, il en existe un gagnant). Pour passer d’un réseau dense à un réseau creux, il suffit de fixer à zéro les valeurs des poids/biais que l’on souhaite retirer, et, souvent, réentraîner le bousin en forçant ces zéros, histoire de récupérer un peu de la précision perdue.
À gauche, un réseau dense. À droite, la version creuse, retirant à la fois des neurones, mais aussi des poids (flèches).
L’intérêt de tout ça ? Économiser en puissance de calcul, pardi ! Si le même résultat peut être atteint avec moitié moins de neurones, alors il suffit de moitié moins de calcul : pratique pour l’embarqué, ou pour les fermes de GPU dont le but est d’effectuer en masse de l’inférence. Dans la pratique, les produits matriciels nécessaires à l’inférence passent ainsi de denses à creux, ce qui peut s’encoder de deux manières différentes : soit en représentant explicitement les zéros (i.e. en gardant le même encodage que pour une matrice dense, ce qui n’est pas très efficace en matière de stockage), soit en ne stockant que les nombres différents de zéros dans un format dit compressé, typiquement CSR ou CSC. Or, au passage, les jolis accès séquentiels deviennent aléatoires : au lieu d’avoir les poids et biais des neurones côte à côte, la sparsité engendre des « trous » imprévisibles dans les accès mémoires… ce qui n’est pas du plus simple pour le hardware !
Devinez quoi ? Cet inventaire est (très) loin d’être complet. Il existe de nombreux autres types de couches permettant par exemple de réduire la taille des données (pooling) ou de l’augmenter (transposed convolution), et les fonctions d’activations qui leur font suites sont, elles aussi, des objets d’étude à part entière !
C’en est (enfin) bon pour cette typologie des neurones, rendez-vous page suivante pour voir comment ces derniers sont rassemblés au sein de réseaux performants… et bien plus encore !
Un réseau oui, mais pas n’importe lequel !
Avoir des couches capables de traiter correctement les informations spatiales et temporelles, c’est bien ; encore faut-il avoir une architecture de réseau capable d’en tirer parti ! Au fur et à mesure des années, les réseaux ont évolué, gagné en taille et en précision : nous résumons ainsi ici une petite sélection totalement partiale de réseaux ayant, en leur temps, marqué un pas de plus vers l’IA moderne. En piste !
LeNet (1998)
Le perceptron multicouche, cela vous parle ? Espérons-le, puisque nous en avons causé une page plus tôt… et qu’il est exactement ce qui compose LeNet, à l’exception près d’utiliser de mixer convolutions, pooling (sélection) et couches denses classiques (et d’utiliser une fonction d’activation nommée tangente hyperbolique). Ce réseau a été développé par un certain Yann LeCun, français (cocorico !) de son état, prix Turing en 2018 (équivalent du Nobel, ce n’est pas de la gnognotte !) et actuellement Chief AI Scientist à Meta : un gars plutôt respectable ! En fait, le papier qui décrit son architecture (du réseau, pas du monsieur…) est si fondateur que Google lui recense 64 347 citations, un montant astronomique pour un article de recherche ! Pour en revenir au réseau, ce dernier prend en entrée une image de 28 px par 28 px en nuances de gris, et reconnaît les chiffres (0 - 9), permettant de détecter automatiquement les codes postaux avec un taux d’erreur de seulement 1 % (oui, en 1998 !). En fait, la sortie est composée de 10 nombres représentant la probabilité que l’image représente l’une des 10 classes apprises (une par chiffre), le montant le plus grand indiquant le chiffre le plus probable d’avoir été rédigé. Cela correspond à la sortie d’une tête de neurones dense de 3 couches, au-dessus de 2 couches de convolutions séparées par une couche de sélection (pooling) chacune.
Lenet, en image ! (Crédit : d2l.ai)
AlexNet (2012)
Plus grand, plus profond, plus puissant, telle aurait pu être la devise d’AlexNet. Reprenant les idées de LeNet, AlexNet bénéficie de la puissance de calcul accrue de son époque pour passer à des images de taille 224x224 et en couleur (donc 3 canaux RGB en entrée au lieu du noir seul), et sort une classification de l’image parmi 1000 classes. En fait, ce réseau fut créé pour participer à une compétition de machine learning populaire à l’époque reposant sur le jeu de données ImageNET (vainqueur de la version ILSVRC-2012) et reposait sur pour son entraînement sur… une implémentation CUDA ayant moulinée 5-6 jours sur deux GTX 580 3 GB, en partageant de manière astucieuse les couches pour bénéficier des 6 Gio de mémoire au complet. Au jeu des citations, le papier fondateur en accumule à l’heure actuelle 155 225, ce qui s’explique par une précision de 15,3 % sur les 5 premiers résultats (dans 84,7 % des images, le réseau sortait la réponse correcte parmi ses 5 suggestions les plus probables), ce qui mettait une tarte (10 points !) aux ténors de l’époque. Au niveau de sa structure, le réseau compte 5 couches de convolution et une tête composée de 3 couches denses (bien plus larges que LeNET, on parle de 4096 neurones ici, contre 120 maximum en 1998), avec la particularité d’utiliser une technique nommée dropout pour limiter la présence de connexions inutiles (faibles poids) au sein des couches denses, et une fonction d’activation ReLU.
L’achitecture d’AlexNet en image : un cube représente une sortie d’une couche, par exemple 55x55 éléments x 48 features pour la première sortie, immédiatement après l’image d’entrée 224x224 px sur 3 canaux. Notez les couches parallèles (sens de la hauteur), une optimisation scindant en deux les couches pour faire tourner le gros réseau sur deux GTX 580 3 GB en parallèle. (Crédit : medium.com)
VGG (2014)
Toujours dans les CNN, VGG représente leur aboutissement en allant toujours plus loin et plus profond. L’originalité a ici consisté à réduire au maximum la fenêtre de vue des neurones à un carré de 3 px par 3 px (contrairement au 5x5 ou 11x11 plus populaires auparavant), pour gagner en profondeur : pas moins de 16 ou 19 couches de convolutions sont présentes (selon la variante VGG16 ou VGG19), suivies d’une même tête de 3 couches denses. Tout le pataquès habituel se répète : le réseau gagne ILSVRC son année avec une précision de 92,7 % sur le jeu de donnée ImageNet, et son article introducteur tope les 122 825 citations ; sachant que le GPU retenu pour l’apprentissage fut une Titan Black (NVIDIA toujours, mais quelle surprise !), et que le processus a duré 2 à 3 semaines !
L’architecture de VGG16 (crédit : GeekForGeeks)
ResNet (2015)
Pour vous donner une idée de l’ampleur de ResNet, son papier est tout bonnement celui le plus cité du Machine Learning du XXIe siècle avec 217 234 citations à l’heure actuelle selon Google. La raison est compréhensible : les chercheurs se sont rendu compte qu’empiler les couches pour aller plus profond encore que VGG avait un effet contre-productif, contrairement à ce qui les mathématiques prétendent (plus le réseau est gros, plus il est expressif et donc plus il est performant). La raison provient de l’entraînement, que nous détaillons dans deux pages : à faire trop profond, le réseau devient trop complexe pour correctement apprendre, si bien que certaines couches (les premières du réseau) ne sont que très peu modifiées, et ainsi inaptes à leur tâche. Ainsi, le principe de ResNet a été d’utiliser des résidus, c’est à dire des connexions coupant plusieurs couches pour rapporter des informations potentiellement perdues lors des transformations vers les couches externes.
Au lieu d’utiliser une couche, ResNet utilise un bloc résiduel composé de 2 ou 3 couches et une liaison résiduelle (flèche courbe sur la droite), qui est sommée avec les sorties du bloc (Crédit image : By LunarLullaby—Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=131466161)
Cela a l’avantage de faciliter grandement l’entraînement, car les couches profondes sont alors mathématiquement rapprochées et donc plus facilement corrigibles du fait de ces liens. De ce fait, ResNet a pu exploser le nombre de couches en poussant le curseur jusqu’à 152 couches, portant sa précision à 4,49 % sur ImageNet et remportant ILSVRC 2015. Dans le même état d’esprit des connexions résiduelles, U-Net (2015) propose lui aussi des liaisons résiduelles entre couches, cette fois-ci via une architecture commençant par (classiquement) diminuer la taille spatiale des données, puis réaugmenter cette dernière. De ce fait, les liaisons résiduelles (qui ne peuvent être effectuées qu’entre données de même dimension) sont effectuées entre les premières et les dernières couches, puis entre celles du milieu-début et du milieu-fin et ainsi de suite, d’où de nom de réseau en « U » : la première barre descendante correspond à la réduction de taille, suivi par une-réaugmention ; les liaisons résiduelles étant alors des lignes horizontales au sein de ce même U.
Un extrait de ResNet : les blocs résiduels (ici représenté par « BN-ReLU-Conv ») reçoivent un mix des informations de la couche immédiatement précédente, mais également des toutes celles avant lui.
Vous l’avez sûrement constaté, l’IA ne s’est pas arrêtée en 2015, loin de là ! En revanche, c’est là que s’arrêtent les modèles compréhensibles sous l’angle du perceptron multicouche : au-delà, nous allons devoir nous contenter de descriptions plus haut niveau afin de (tenter) de résumer le schmilblick… C’est parti, dès la page suivante !
Les tendances du (presque) moment
Après ce (rapide) coup d’œil en arrière, cap sur les années 2020 et l’état de l’art des IA actuellement en utilisation. Entre les secrets industriels et la complexité de certaines méthodes, s’y retrouver n’est pas une chose aisée, ce pour quoi nous avons opté pour trois réseaux emblématiques de ces dernières années se basant chacun sur des technologies différentes : GauGAN, StableDiffusion et ChatGPT. Chacun méritant son analyse dédiée, notre objectif ici est de souligner leurs spécificités sans pour autant rentrer (trop) dans les détails : ne soyez donc pas étonnés de voir certains éléments passés sous silence, et n’hésitez pas à multiplier les sources si le sujet vous passionne. C’est parti !
Plus que jamais, cette section n’est pas explicite, tant les dernières avancées en matière d’IA ont permis un développement toujours plus effréné de la chose. Il est question ici de vous présenter certaines des méthodes récemment utilisées afin de vous donner un goût de l’étendue des possibles, mais ce contenu sera très probablement rapidement obsolète !
GAN comme dans GauGAN
Pour améliorer l’apprentissage, les réseaux de neurones génératifs adversariels (Generative Adversarial Networks, ou GANs) se basent sur un entraînement à deux réseaux. Le premier est un réseau génératif, c’est-à-dire qu’il produit la sortie désirée (typiquement un texte ou une image, à l’inverse des réseaux effectuant de la classification qui sortent une probabilité de détection), que l’on souhaite entraîner, et le second (nommé discriminateur) est un réseau beaucoup plus simple, usuellement constitué des quelques couches denses, dont le but est de deviner si le résultat a été créé par l’IA ou non. Lors de l’entraînement, les images du réseau et de vraies images sont soumises aléatoirement au discriminateur qui va, lui aussi, progresser, permettant ainsi d’améliorer la vraisemblance des créations en les forçant à ressembler aux images existantes.
GauGAN, développé en partenariat avec NVIDIA dans le but de promouvoir l’IA, est d’un d’entre eux, et permet de générer une image photoréaliste à partir d’un coloriage simple désignant la nature des zones concernées (eau, végétation, ciel, route, etc). En interne, nous ne sommes pas surpris de retrouver un mix de tout ce que nous avons vu précédemment : un assemblage de couche SPADE ResBLK qui rassemble une liaison résiduelle, deux convolutions et deux couches SPADE qui, sans trop sortir les maths, permettent de modifier les résultats partiels de manière conditionnelle (comprendre, en fonction du type de la zone concernée) pour améliorer la sortie. Au niveau du hardware, l’entraînement a été réalisé sur une station de travail DGX-1 équipée de 8 NVIDIA V100 (32 Gio de VRAM) : il faut bien faire tenir ce réseau mastoc en mémoire !
En dépit de son âge relativement récent, les GAN sont en (grand) déclin, si bien que cette architecture aurait également pu être abordée à la page précédente. Dans la pratique, les GANs sont désormais adjoints à d’autres modèles afin de rester compétitifs par rapport aux architectures concurrentes.
Le réseau générateur du GauGAN
Et quelques illustrations des sorties du bousin
StableDiffusion : en avant la génération d’images !
Si GauGAN avait marqué quelques esprits et fait causer de NVIDIA qui sponsorisait le projet, il ne représente pas la quintessence du réseau de neurones ; celle qui a participé à sa révélation auprès du grand public et a causé bon nombre de lois à son sujet. Par contre, ces qualificatifs peuvent totalement s’appliquer à StableDiffusion, un réseau de neurones permettant de générer des images sur la simple saisie d’un bref texte. Le réseau se base sur le concept de diffusion latente, et est composé de deux parties : un générateur chargé d’inventer des images à partir de ce fameux bruit, et une partie de traitement du texte chargée de guider le générateur vers la sortie souhaitée.
StableDiffusion n’est pas le seul projet s’étant attaqué à la génération d’images ! DALL-E, Midjourney et bien d’autres ont également abordé le problème sous des angles différents tout aussi passionnants… pour lequel un article dédié ne serait déjà pas suffisant pour expliquer toutes les subtilités.
Revenons à notre diffusion latente. Il s’agit un mécanisme cherchant à transformer des images en un bruit (noise), c’est-à-dire une distribution aléatoire des pixels. C’est ce concept qui donne le nom du réseau : similairement à deux colorants bleus et jaunes qui, dans un verre d’eau, vont se diffuser pour donner une coloration finale verte, la diffusion latente consiste entraîner un réseau de neurones conventionnel (U-Net, si vous souhaitez tout savoir) sur des images possédant initialement des différences marquées (un chat et un chien, par exemple), qui sont peu à peu corrompues en rajoutant une image aléatoire — notre bruit, quoi. In fine, ce réseau est alors capable de prédire à quel point l’image a été corrompue, i.e. sa différence avec une image normale.
Exemple de diffusion latente, modifié à partir des explications de cet article de stable-diffusion-art, dont nous vous encourageons la lecture !
Tout cela est fort intéressant, mais à quoi bon ? Hé bien, en inversant le procédé, il est possible de créer des images : il suffit de partir d’une image aléatoire, puis retirer plusieurs fois de suite les bruits prédits par notre réseau : la sortie va naturellement converger vers une image statistiquement proche de celles vues à l’entraînement. Mieux encore, en rajoutant une seconde entrée (au hasard, un nombre décrivant le contenu de l’image), il devient possible de contrôler la sortie du réseau selon votre bon vouloir : parfait, il ne reste plus qu’à guider cette génération pour la faire correspondre à l’entrée textuelle humaine.
Notez que pour StableDiffusion, les images sont en fait compressées en utilisant (encore) un réseau de neurones nommé Variational AutoEncoder (VAE) dont le but est d’extraire un maximum d’information des images pour les faire tenir dans un espace minimal, ce qui permet d’améliorer la vitesse de prédiction. Ainsi, toutes les transformations sont en fait effectuées sur images compressées ayant totalement perdu le sens d’une image (il n’est pas question d’un. zip mais d’une soupe IA !), qui sont par la suite décompressées par le réseau pour obtenir la sortie finale.
Et comment qu’on guide un réseau ? Par un autre réseau ! Cette fois-ci en se basant sur l’héritage des réseaux d’analyse de texte, la description de l’image est mâchée pour être réduite en valeurs nommées tokens, qui sont ensuite transformées en valeurs vectorielles par une transformation nommée word embedding. En français, cela signifie que la phrase analysée est retranscrites en nombre par une transformation qui traduit en des nombres similaire des expressions de sens similaires ; par exemple, « chat bleu » et « petit félin couleur ciel » se retrouvent traduits en des valeurs proches l’une de l’autre, tant bien même qu’aucun mot n’est commun. C’est cette sortie qui est par la suite donnée au modèle de prédiction de bruit, et qui fait ainsi le lien entre le mot « chat » (ou plutôt, sa traduction en nombre) et les images de chat !
En résumé, voilà le fonctionnement du bousin (Crédit : toujours stable-diffusion-art.com)
Ainsi, au final, les mots sont hachés par un premier réseau, leur sortie est elle-même combinée à une bouille de pixels compressée et donnée à manger à notre modèle basé sur de la diffusion latente. Ce dernier itère pour raffiner sa sortie un grand nombre de fois nommé sampling step. Il en sort une image, toujours compressée pour gagner en temps, que nous décompressons en utilisant le VAE pour obtenir l’image finale. Quelle complexité !
Un approfondissement vidéo francophone, si le sujet vous passionne !
GPT comme dans ChatGPT
Changeons radicalement de registre avec ChatGPT. Cette fois-ci, il n’est plus question de génération d’image, mais de génération de texte : selon l’entrée d’un humain, le programme doit répondre de manière pertinente, ce qui signifie combiner des tâches d’explications, de synthèse ou encore de discussion non dirigée. Ici, deux nouveautés : l’utilisation d’un réseau de type transformer (un bloc de base depuis repris dans bien d'autres applications du machine learning), et un pré-entraînement (pre-training) dont l’assemblage donne notre GPT, Generative Pre-trained Transformer.
Ici et comme dans StableDiffusion, tout commence par une de l’embedding transformant les mots en nombres. Il en suit une architecture composée de 12 répétitions du bloc transformer (pour GPT-1, 96 répétitions pour GPT-3), dont nous vous reportons la structure principale ici :
Le détail d’un transformer : encodeur à droite, décodeur à gauche (Source : Attention Is All You Need)
L'idée est de succéder aux LSTM et aux GRUs que nous avions abordés deux pages plus tôt en reposant sur la notion d'attention, c'est à dire en utilisant le contexte des mots pour modifier leur sens via la couche Multi-Head Attention (dont nous vous évitons le détail, à moins de vouloir faire une thèse en IA !). La couche Feed Forward est grosso modo l'analogue des couches denses version texte, et nous retrouvons des additions provenant des couches précédentes (nos résidus !) et la normalisation permettant de s'assurer que les résultats intermédiaires restent cohérents. Enfin, le transformer originel possède une particularité supplémentaire : il est composé de deux blocs : un encodeur et un décodeur, car il servait initialement à de la traduction. Le premier bloc comprenait l'anglais, le second le français, et ta-daaa, vois avez un traducteur. Pour ChatGPT, seul le décodeur est utilisé car il est question de génération.
Enfin, une des grandes améliorations de ChatGPT réside dans son mécanisme d'entraînement. Tout commence par le pré-entraînement (pre-training). qui consiste à étalonner le décodeur sur un corpus de texte de manière à lui faire prédire les mots suivants en partant de morceaux de phrases. Qualitativement, cela permet d'apprendre les basiques de la langue au réseau, le B-A.BA de ce qui lui est demandé. Il peut alors répondre, mais n'est vraiment pas malin et à tendance à répéter les mêmes choses en boucle, car la génération de texte consiste à compléter mot-à-mot sa propre sortie, et donc à rapidement se mordre la queue.
Ainsi, une seconde phase d'ajustement (fine-tuning) a lieu, qui est elle-même composée de deux étapes : SFT (Supervised Fine-Tuning), qui utilise un ensemble de dialogues annotés comme étant bons (par exemple une réponse à une question ou un compliment) ou mauvais (par exemple lorsque des sujets sensibles / illégaux sont abordés) pour raffiner les sorties du réseaux dans un nouveau round d'apprentissage. Parfois, ces dialogues sont eux-même des sorties d'autres réseaux... la boucle est bouclée ! La dernière phase d'ajustement vise à corriger cette déviance en faisant directement appel à l'humain, qui va (nous simplifions ici à l’extrême) passer en revue les sorties du réseau pour annoter les meilleurs des moins bonnes, et utiliser yet another réseau pour apprendre les sorties souhaitées. Finalement, c'est ce dernier réseau qui est utiliser pour corriger le modèle génératif, et pouf ! Voilà ChatGPT... aux secrets professionnels de la firme près, évidemment.
Les étapes complètes de l’entraînement du réseau : pré entraînement et ajustements (automatisé [SFT] et corrections par l’humain [RLHF])
Terminons par un peu plus de vulgarisation encore, tant le sujet mérite d'être creusé !
Avoir une architecture de réseau chiadée, c’est important pour que les bousins soient optimisés pour certaines tâches. Cependant, cela ne fait pas tout, puisqu’il faut lui donner à manger des informations préalablement à son utilisation : c’est l’apprentissage. Rendez-vous à la page suivante pour voir en quoi cela consiste !
L'entraînement : un casse-tête mathématique
Si vous vous êtes accrochés sur les pages précédentes (félicitations !), alors vous avez retenu qu’un réseau de neurones, c’est un empilement de couches qui se traduit par des produits matriciels et des MAC à gogo. Or, il y a bien un hic : ces calculs sont faits à partir des données d’entrée, mais aussi des poids/biais du réseau. Et ces derniers ne peuvent pas sortir du chapeau : il faut un moyen de les trouver et les fixer ! C’est ce que l’on nomme entraînement. Grâce à une technique (matheuse, comme toujours !) du nom de rétropropagation du gradient, il est possible de corriger petit à petit un réseau de neurones, à condition de posséder la sortie de référence d’une donnée d’entrée, et de bien choisir un paramètre nommé taux d’apprentissage (learning rate). Ce dernier quantifie à quel point le réseau est modifié pour coller à la nouvelle données. S’il est trop faible, l’entraînement va durer plus longtemps que nécessaire. Trop gros, il ne permettra pas de trouver le point optimal du réseau (comme si vous cherchiez une aiguille dans une botte de foin… en bougeant votre bras d’un mètre à chaque fois !).
Le principe
L’idée est logique : comparer la sortie du réseau actuelle avec celle désirée, et corriger en traversant les couches depuis celles de la sortie vers celles des entrées de manière à minimiser l’écart entre la prédiction et la sortie de référence. De ce fait, les couches externes sont plus facilement touchées, là où les couches internes ont besoin de plusieurs exemples pour arriver à quelque chose de satisfaisant, un phénomène connu sous le nom de disparition du gradient. Et par « plusieurs exemples », comprendre « plusieurs milliers, voir dizaines de milliers », d’autant plus que le réseau est profond… Cela signifie ainsi qu’un réseau performant a besoin d’énormément de données déjà annotées pour arriver à son mode de fonctionnement optimal, en plus d’une capacité de calcul tout aussi importante pour réaliser l’entraînement.
Le calcul de cette différence itérée de gradient et la taille des données d’entrées font de l’apprentissage une tâche bien plus gourmande que l’inférence (c’est-à-dire l’utilisation simple du réseau pour sa fonction, par exemple la reconnaissance d’image ou l’upscaling).
Certaines techniques ont cependant vu le jour pour combattre ce phénomène, telle l’intégration de LSTM (cf page 3) et l’utilisation de réseaux résiduels (voir ResNet, page 4) permettant, à l’entraînement, de rapprocher les couches les plus profondes pour combattre cette disparition du gradient, avec un succès tel que la majorité des réseaux actuels reprennent cette notion de résidus : des connexions directes entre couches non successives pour les rapprocher mathématiquement de la sortie. De même, les couches inférieures sont les plus proches des données, et sont donc plus propices à apprendre les caractéristiques propres à celles-ci, plutôt que les caractéristiques de la tâche demandée. Il est donc bénéfique de partir d’un réseau déjà entraîné sur une tâche (reconnaître des chats, par exemple) pour réaliser une autre tâche (reconnaître les chiens) en ne modifiant que les couches les plus externes ; un phénomène nommé Transfert Learning.
L’entraînement d’un réseau, c’est ça : une passe de prédiction (sens normal) pour obtenir l’état actuel du réseau, et une passe en sens inverse (rétropropagation) pour corriger les poids et biais des neurones en fonction d’une sortie de référence connue.
De plus, il est courant de réutiliser plusieurs fois les mêmes images lors de l’entraînement pour en extraire le maximum d’information, et être certains que toutes les couches ont bien subi l’influence de toutes les images : on nomme ainsi une epoch un passage complet de toutes les données pour l’apprentissage, sachant qu’un réseau peut s’entraîner en plusieurs dizaines voire centaines d’epoch. Dans le même esprit, il est également courant d’enrichir sa base de données en effectuant quelques transformations, par exemple en dupliquant des images pour leur apposer un effet « miroir ». Enfin, puisqu’une couche de neurones effectue un produit matrice-vecteur, il est plus commode d’en effectuer tout un tas en parallèle pour obtenir les produits matrice-matrice si chère aux Tensor Cores et autres NPU. Pour cela, les données sont regroupées en batch, dont la taille représente simplement le nombre de ces entrées données en parallèle au réseau. Plus ce nombre est grand, plus l’entraînement sera rapide (la magie du parallélisme), mais plus le besoin en mémoire (typiquement VRAM !) sera grand.
Le prétraitement des données d’entraînement est une étape cruciale dans la réalisation d’un réseau, tout autant que l’architecture de réseau lui-même. Un dicton classique en statistique est « garbage in, garbage out » : « Données pourries ? Sorties pourries ! ». Un grand classique consiste à normaliser les données : par exemple rendre les images de même taille.
Un autre exemple de l’importance des données est le DLSS. Dans ce cas, NVIDIA utilise un réseau pour réaliser de la super-résolution en temps réel : rajouter des pixels pour passer d’une image basse définition en meilleure définition. Dans sa version 1, le DLSS ne reposait que sur des données des images brutes : avec toute sa bonne volonté, il lui était difficile de proposer un rendu impeccable dans toutes les situations. Avec la version 2, l’intégration des vecteurs de mouvement dans les entrées a permis un rendu visuellement supérieur. Évidemment, l’architecture du réseau a aussi dû être modifiée, mais les contraintes de performances (le bousin doit être plus rapide que le rendu !) font que ces modifications n’ont pas pu être extrêmes.
Ainsi, pour pouvoir décider si un chat est sur une image, il vous faut donc une GTX 4080 et 10 000 images dont vous savez à l’avance si un chat y est présent ou non. Ces données ne se baladant pas dans la nature, il est courant pour certaines entreprises peu scrupuleuses de se servir allègrement sur le web, d’où les récentes avancées en matière de législation. D’autres entreprises se sont par ailleurs spécialisées dans la production de ces données, par exemple en payant à bas coût des humains dans des pays émergents pour effectuer ces tâches à la main.
Il convient également de noter que le réseau apprendra des données passées en entrée. Ainsi, il est courant d’observer des comportements biaisés de réseau de neurones, qui reflètent simplement les biais de la base d’entraînement. Par exemple, des chercheurs se sont aperçus qu’un réseau classifiant les animaux était en fait sensible à.... la présence de neige ou non sur l’image pour repérer une panthère des neiges. Malin, certainement, mais pas du tout ce que l’on souhaitait !
Et en pratique ?
Hé bien, en pratique, tout est bien plus corsé. Car on pourrait imaginer laisser le réseau apprendre parfaitement (les maths nous disent bien que le réseau peut apprendre n’importe quoi avec suffisamment de temps !), et une fois que le réseau sait effectuer sa tâche parfaitement notre base de donnée d’entraînement, le tour est joué.
Hé bien pas du tout ! Vous risquez dans ce cas de tomber dans le surapprentissage : le réseau a été trop entraîné et a fait du par-cœur : au lieu de la généralisation souhaitée (apprendre qu’un chat est un être mignon à quatre pattes), il a juste appris à reconnaître tous les chats utilisés lors de l’entraînement et plus rien d’autre. Pas glop ! À l’inverse, manquer de temps lors de l’entraînement crée un sous-apprentissage : les performances du réseau seront en dessous de leur plein potentiel. C’est probablement la meilleure explication aux contre-performances du DLSS lorsqu’il est intégré « à la va-vite » dans un jeu récent.
Exemple de sous-apprentissage, d’apprentissage correct et de surapprentissage. Le réseau apprend la courbe rouge, qui sert à séparer le « groupe des croix » du « groupe des ronds ». Tout à gauche, le réseau n’a pas assez appris, car il n’a pas « vu » la courbe. Au milieu, l’apprentissage est satisfaisant. À droite, le réseau a sur-appris et reflète des artefacts propres aux données (typiquement des erreurs dans le jeu de données d’entraînement).
Pour y remédier, une méthode nommée early stopping consiste à conserver une partie des données de l’entraînement comme un témoin du surapprentissage, et arrêter cet apprentissage lorsque les performances du réseau se dégradent. Mieux, pour éviter de « perdre » les informations de cet échantillon témoin, il est possible de remélanger les données et recommencer le procédé pour toujours plus de précision.
Une autre illustration du phénomène sur sur- et sous-apprentissage. Au fur et à mesure de l’entraînement (i.e. nombre d’epoch), le réseau s’améliore à la fois sur les données d’entraînement et celle de test (« précision globale »). Après un trop grand nombre d’epoch, le réseau comment à faire du par cœur, et sa précision en utilisation réelle diminue : pouf, le surapprentissage est là !
Du fait du surapprentissage, il est complètement sans intérêt d’évaluer un réseau sur des données déjà utilisées lors de l’entraînement. Il s’agit au mieux d’une négligence, et au pire d’une arnaque pure et simple : bien sûr que le réseau va performer correctement sur ces données, il les connaît ! Un réseau est donc systématiquement évalué sur sa précision à effectuer sa tâche sur des données qu’il n’a jamais vues. Cela explique notamment pourquoi le DLSS était au début implémenté différemment en fonction des jeux : la tâche est ô combien plus simple. Dans le même genre, une IA générative recrachant parfaitement un tableau ou une photo de son jeu de données d’apprentissage sera un autre car de surapprentissage évident.
(Presque) inspiré d’une histoire vraie. L’important n’est pas tant l’outil que son utilisation !
Bon à savoir !
Une mesure de la complexité d'un réseau (et, indirectement, de sa difficulté / capacité de calcul nécessaire à être entraîné) est le nombre de paramètres, c'est-à-dire le nombre de poids et de biais différents utilisés dans le réseau, qui doivent donc être étalonnés lors de l’entraînement... un nombre qui a explosé en même temps que le hardware : LeNet en compte 1516, là où StableDiffusion dans sa version 2.x en requiert... 860 millions !
Terminons cette page avec un peu plus de vulgarisation, si l’envie vous prend de creuser le sujet !
Apprendre, c’est bien, encore faut-il pouvoir le faire rapidement ! Rendez-vous à la page suivante pour comprendre (enfin !) les liens entre matériel et IA, en quoi les NPU intégrés sur Meteor Lake et les Ryzen with AI diffèrent des GPU et ce qui pourrait (ou pas !) arriver dans nos machines d’ici quelques années.
Et le hardware dans tout ça ?
Vous n’avez pas pu le manquer, l’IA façonne les dernières tendances des architectures matérielles, et ce depuis quelques années déjà. Si nous avons déjà abordé que le produit matriciel est la clef de voute du machine learning (entraînement comme inférence), cela ne se limite (heureusement) pas à cela. Mémoires, précisions et parallélisme sont également à prendre en compte et à calibrer correctement par rapport au rôle souhaité : de quoi expliquer pourquoi les marques nous inondent d’accélérateurs dans à peu près tout ce qui est programmable. Vous l’avez compris : c’est parti pour passer en revue le hardware, sauce ML !
Les GPU mènent la danse !
Difficile de cause IA sans causer GPU. Si les balbutiements des réseaux de neurones ont été effectués en laboratoire sur des CPU pour cause de facilité de programmation (l’idée originelle est de montrer que cela fonctionne, avant d’optimiser), l’idée de déporter les calculs sur carte graphique a été rapidement évoquée. Dès 2003, une équipe sud-coréenne avait déjà réussi à implémenter un perceptron multicouche sur… une ATi RADEON 9700 PRO (128 Mio de VRAM, 8 ROP et 325 MHz de fréquence… pas de doute, nous sommes bien dans les années 2000).
Pourtant, ce n’est pas ATi — racheté par AMD en 2006 — qui a mené la danse du machine learning sur GPU, mais NVIDIA. La raison est plus politique que technique : si ATi a été le premier à mettre en pratique le concept de GPGPU (effectuer du calcul scientifique sur les unités graphiques) avec leur API Close To Metal (CTM) en 2006, la concurrence des verts fût rude avec CUDA, lancé initialement en 2007, qui s’est imposé par la suite comme « la » référence pour programmer sur GPU dans la sphère professionnelle. Sentant l’IA arriver, le caméléon a été dégoter du personnel capable d’effectuer des optimisations ou de la recherche dans le secteur, et a pu ainsi asseoir sa domination par un cercle vertueux apportant renommée, ventes, argent puis cerveaux et performances.
Représentation logique d’une Radeon HD 5870. Tous les cœurs rouges sont des unités capables d’effectuer des multiplications-accumulation (MAC) en parallèle : le paradis des réseaux de neurones !
Néanmoins, ce serait mentir que de prétendre que NVIDIA a tordu le cou des réseaux pour les faire tenir sur GPU. Il y a plutôt eu un sacré coup de chance : naturellement, les réseaux de neurones demandent une quantité de calcul énorme, mais régulière (la même suite de calcul doit être effectuée pour toutes les couches : pas besoin d’attendre la valeur de données pour savoir quoi faire ensuite) et surtout parallèle. Un neurone se fiche pas mal des neurones adjacents de la même couche : il se contente de faire ses multiplications en partant de ses entrées. C’est parfait, car c’est exactement ce pour quoi les GPU ont été faits : des calculs de rastérisation dans lequel un pixel se fiche pas mal des valeurs des pixels adjacents au moment du rendu ! Difficile en fait d’imaginer une application aussi parfaitement adaptée, tout du moins à l’époque des empilements simples de perceptrons, et dans laquelle les architectures vectorielles (i.e. basée sur des calculs parallèles au niveau des cœurs GPU, à contrario des architectures scalaires, à plus grand nombre de cœurs, mais traitant une seule donnée à la fois) dominaient ! Visionnaires, les verts ont su prendre la vague au bon moment et développer/imposer leur écosystème basé sur CUDA au sein de l’industrie en coupant l’herbe sous le pied d’ATi/AMD — pourtant tout aussi capable de machine learning à l’époque —, une stratégie qui a encore des répercussions aujourd’hui sur la popularité des cartes du caméléon.
Depuis, les architectures ont évolué vers le scalaire, mais embarquent chez NVIDIA depuis 2017 (microarchitecture Volta) des unités dédiées nommées Tensor Cores permettant de déporter une partie des calculs matriciels, les plus lourds, des unités standards. Un choix révélant l’importance de l’IA parmi les clients du caméléon, car ces unités spécialisées perdent fatalement en flexibilité d’usage ; bien que certaines simulations numériques bénéficient elles aussi de produits matriciels accélérés.
Le Tensor Core : l’unité canonique de Machine Learning pour NVIDIA
Cependant, un écueil de taille reste : les multiplications de nombres, une fois mises en parallèle, cela reste gourmand, en temps comme en énergie. Il est alors venu une idée aux concepteurs de réseau : passer d’une représentation en nombre flottants 32-bit à une représentation sur 16-bit, voire en utilisant des nombres entiers ou en précision à virgule fixe. Sans trop rentrer dans les détails, cela correspond à changer la manière dont les 0 et 1 matériels représentent des nombres, ce qui troque leur précision et leur capacité à exprimer des grands nombres pour des calculs plus rapides et moins énergivores. Évidemment, tout cela ne se fait pas sans une dose de mathématique limitant l’impact sur les performances des réseaux et est principalement utilisé lors de l’inférence sur des appareils plus contraints en puissance, ce pourquoi nous y reviendrons dans la dernière section de cette page : gardez juste en tête que les compatibilités FP16, BF16, FP8 et INT16/INT8 servent principalement à l’inférence de masse sur GPU, en gardant les mêmes architectures de réseaux que celle vues précédemment.
Avec les dernières tendances de réseaux creux (c. f. notre page « Le multicouche et le modernisme »), NVIDIA a revu sa copie et propose depuis quelques années un Tensor Core adapté pour cet usage, c’est-à-dire capable de fonctionner sur des données possédant moins de 50 % de zéros en utilisant une représentation compressée, mais dédiée des matrices, reposant sur une contrainte de régularité des zéros. Si cela n’est pas aussi efficace qu’une architecture 100 % sur-mesure, difficile de faire mieux sans exploser les bandes passantes, où revoir complètement la puce pour verser dans les accès aléatoires à la VRAM, typiques des représentations compressées de matrices creuses… de quoi laisser penser qu’une séparation des architectures IA et GPU est possible, voire déjà à l’œuvre. En effet, avec la scission des générations grand public/pro (Turing vs Volta, Lovelace vs Hopper), les GPU pour datacenter intègrent déjà en avant-première les technologies d’IA et font usage de mémoires HBM plus rapides. Et, si jamais les pros veulent une architecture plus flexible, des dérivés des joueuses RTX leur sont également proposés : une carte pour chaque utilisation !
Version dense à gauche, creuse à droite : une sélection se fait au moyen d’un tableau des indices pour lesquels les valeurs sont non-nulles.
Du côté de la concurrence, les architectures présentent de légères variations, comme chez Intel ou les unités matricielles nommées XMX directement au sein de son bloc de base Xe-Core et accélérant les opérations en fonctionnant sur une largeur de donnée de 1024 bits. Chez AMD, la Mi300, basée sur CDNA3 (version pour serveurs de calcul des architectures graphiques d’AMD) offrent également un support de la sparsité, s’alignant ainsi sur le leader vert.
Et les CPU ?
Pour les CPU, l’histoire est plus complexe. Leur facilité de programmation les a mises sur la première ligne lorsque les réseaux de neurones étaient encore une utilisation de niche, mais les processeurs ont rapidement été délaissés au profit des GPU dès qu’il est question d’utilisation dédiée au machine learning. Cependant, tout n’est pas perdu pour eux, car certains centres de calcul ne servent pas uniquement à l’IA, et certaines configurations (typiquement des ordinateurs portables) ne peuvent pas utiliser de GPU dédié faute d’en avoir un ! Ainsi, les processeurs ont évolué en rajoutant des unités de calcul vectorielles (i.e. capable d’effectuer des additions/multiplication en parallèle), ce qui est parfait pour nos réseaux ! Notez tout de même que, tout comme les GPU, ces unités ne sont pas arrivées pour le machine learning, mais se sont révélées bien utiles une fois ce dernier grandement répandu.
Lier un back-end vectoriel avec une charge de travail d’IA, tel est le rôle DL Boost d’Intel.
Cependant, des extensions tel DL Boot (chez Intel, également nommé VNNI) sont apparues pour améliorer l’utilisabilité des unités vectorielles dans le cadre du machine learning. Dans le même genre, le déploiement progressif de l’AVX-512 (du vectoriel toujours plus parallèle) et l’ajout du support du BFloat16/Int8 sont autant d’adaptations des processeurs aux tâches de machine learning. Contrairement aux GPU, les CPU sont prévus pour faire tous les types de calcul « moyennement bien », il est donc peu probable que la tendance d’intégration d’instructions dédiées à l’IA s’amenuise ! En revanche, pour améliorer les performances dans des tâches d’inférences légères, il faut alors soit passer sur GPU, soit s’équiper d’un accélérateur dédié… Que nous abordons au paragraphe suivant !
Et… autre chose ?
Du matériel spécialisé
Impossible de passer à côté tant la chose est matraquée lors de toutes les annonces : les SoC mobiles, les CPU Meteor Lake, les CPU Ryzen with AI sont tous équipé d’un « coprocesseur d’IA », ou encore un « NPU » voir « DPU » pour Neural Processing Unit et Deep Leaning Processing Unit. Une puce magique étant tout simplement l’équivalent d’un GPU pour l’IA. Vient alors l’interrogation suivante : si un GPU est déjà « naturellement adapté pour l’IA », qu’est-ce donc que ces bidules-ci ?
Chez GraphCore, la sauce est claire : des unités indépendantes mises côte à côte avec une mémoire locale et un interconnecte qui poutre pour échanger les résultats partiels.
Hé bien, un GPU, cela peut encore faire du calcul graphique, du Ray Tracing ou de la simulation. Pour gagner sur le rapport performance/consommation (comprendre : consommer le moins possible d’énergie pour chauffer le moins possible, et prendre le moins d’espace sur une puce), cette flexibilité de programmation est jetée dans la corbeille pour ne garder que les primitives essentielles : les multiplication-accumulations et les produits matriciels. Ainsi, un accélérateur d’IA n’est rien de plus qu’une série d’unités de calcul matriciel reliées la plupart du temps dans un interconnect en mesh (on parle alors de réseau systolique) facilitant les échanges de résultats entre les accélérateurs adjacents, ce qui convient parfaitement aux couches d’un réseau appliquant leur transformation les unes après les autres. Selon la cible — entraînement de masse ou inférence légère dans un smartphone — les technologies mémoires et les tailles des puces seront grandement variables, mais le principe global restera similaire. Dans le domaine, vous trouverez ainsi du Gaudi (Intel), Google Tensor Processing Unit et GraphCore pour de l’entraînement haute performance en data center, et du Movidius ou du Qualcom Hexagon pour de l’inférence plus légère sur des produits embarqués.
AMD XDNA : une grille fixe d’accélérateurs matriciels, simple et efficace pour de l’accélération embarquée !
Une histoire d’encodage de nombres
Une des difficultés principales en lisant les spécifications d’un accélérateur est de décoder sa puissance réelle. En effet, les constructeurs y vont chacun de leur sauce pour afficher des nombres toujours plus grands sans que cela fasse parfois grand sens (coucou, NVIDIA et les TFLOPS en matrice creuse, c’est-à-dire en comptant les opérations du type 0 +0, qui ne sont pourtant jamais exécutées !). Une partie de cette confusion vient du format des données, qui correspond à la manière de représenter un nombre sur un ordinateur.
Deux familles de format existent : les entiers et les flottants. Les premiers correspondent directement à un nombre sans virgule en suivant une logique identique à notre système décimal, précédé par un bit (une valeur 0 ou 1) indiquant s’il s’agit d’un nombre positif ou négatif. Il a l’avantage d’être très facilement manipulable pour effectuer des opérations courantes (addition/multiplication), mais ne permet pas de représenter des nombres « très grand » comme plusieurs milliards de milliards, ou très petit comme des millionièmes de pourcent. Pour cela, on utilise des flottants, qui se composent de deux entiers (et du bit de signe) : l’un, nommé mantisse représente effectivement une valeur, mais l’autre un exposant qui permettra de multiplier la mantisse par un nombre grand, moyen ou petit et ainsi obtenir des nombres très grands ou très petits ! Pour voir donner un ordre d’idée, avec 32 bits, l’entier maximal est 2 147 483 647 et le plus petit (le plus proche de zéro différent de 0) est 1, alors que le plus grand flottant est environ 3,40 suivis de… 38 zéros (!) pour un nombre le plus petit proche de zéro de 1,40 précédés de… 45 zéros avant la virgule. Diantre ! La chose est d’autant plus complexe qu’il est possible de modifier le nombre total de bits, et, dans le cas des flottants, de changer la répartition exposant/mantisse afin de mieux coller aux besoins d’un réseau : ainsi est né le BFloat16 (Brain Float-16) et, récemment, les flottants 8-bits qui ne faisaient pas partie du standard initial. À cela s’ajoutent les précisions entières : Int32, Int16, Int8 et parfois même Int4.
Un bit de signe (s), et le reste se partage entre exposant (exp) et mantisse.
Avec des représentations aussi différentes, les performances sont fatalement, elles aussi, différentes. Le flottant a l’avantage d’être plus précis, mais gourmand en ressources, alors que les entiers sont bien plus économes, mais nécessitent une opération nommée quantization pour transformer un réseau flottant en entier, ainsi qu’une seconde étape d’apprentissage supplémentaire pour tenter de corriger en partie la perte de précision induite par le passage en entiers. C’est donc sans surprise que les meilleures performances se retrouvent en Int4 sparse.... mais bon courage pour les exploiter dans des tâches d’entraînement ! C’est ainsi que les coprocesseurs d’IA tels ceux intégrés dans le Snapdragon X mesurent leur performance en TOPS et non en TFLOPS : pour économiser de l’énergie, seules les opérations Int sont accélérées à pleine vitesse. Couplé à l’économie réalisée en partageant la DRAM avec la DDR du CPU, ces accélérateurs réalisent l’exploit d’offrir des performances satisfaisantes en inférence pour une fraction de la consommation des GPU : bien pratique pour des tâches continues comme du sous-titrage automatique ou des effets applicables sur une webcam.
Quoi qu’en prétende le marketing, un réseau FP32 et un réseau INT8 sont deux choses différentes, quand bien même l’un serait dérivé directement de l’autre. Leur précision de prédiction est différente, tout comme leurs besoins hardwares. Il est donc aussi pertinent de les comparer l’un avec l’autre que de comparer FXAA et MSAA : si l’un comme l’autre servent à la même chose, les résultats comme le coût en performances peuvent être radicalement différents !
Les FPGA
Cela tombe bien, il existe également toute une famille de puces très efficaces sur le calcul entier et reconfigurable, c’est-à-dire adaptable facilement en fonction du réseau souhaité : les FPGA. Bien que plus gourmands qu’un accélérateur dédié, les FPGA sont capables de rivaliser avec les GPU sur le plan du ratio performance/consommation dès lors que les opérations s’effectuent en INT et non en FP. Il n’est donc pas surprenant de voir AMD, concepteur de CPU et de GPU, racheter en 2020 Xilinx, un fabriquant de FPGA, et installer dans ses APU Ryzen une partie « IA » XDNA directement transposée des FPGA Versal de la firme gobée. À l’inverse, Intel ayant racheté Altera en 2015 (l’autre concurrent), a plus ou moins manqué le tournant IA, ce qui explique en partie sa récente prise d’indépendance. De ce fait, l’accélérateur intégré sur Meteor Lake provient de Movidius, une start-up dévorée par les bleus en 2016.
Pour autant, les FPGA restent les plus discrets dans la course à l’IA, d’une part par des outils de programmation encore jeunes (ou un langage imbitable type VHDL pour encoder la chose !) et une flexibilité d’utilisation ayant du mal à se justifier par rapport à un accélérateur dédié… surtout lorsque Intel, AMD, Qualcomm et NVIDIA offrent déjà des solutions clef en main pour l’embarquée. Mais, qui sait, le futur révélera peut-être ces unités programmables aux yeux du grand public, comme Apple avait essayé de le faire sur un de ces Mac Pro, qui embarquait une de ces puces pour améliorer les performances en encodage vidéo !
Une grille d’unités de calcul, des mémoires parallèles, des petits accélérateurs (DSP)… sur le papier, un FPGA a de sérieux arguments pour accélérer des réseaux de neurones ; et pourtant son utilisation reste rare face aux GPU et autres accélérateurs embarqués.
Avec toutes ces explications, vous voilà un peu plus armés pour déchiffrer la sauce marketing balançant des TOPS à tort et à travers ! Terminons ce dossier par un petit récapitulatif conclusif à la page suivante.
Conclusion et directions de recherche
Depuis ses débuts timides au milieu du siècle dernier jusqu’à l’explosion de ChatGPT, les réseaux de neurones sont passés du fantasme fou de chercheurs à une machinerie performante capable d’effectuer de nombreuses tâches allant de la reconnaissance d’image à la discussion — même si cette dernière montre vite ses limites face à un utilisateur un tant soit peu vigilant. Cette explosion s’est effectuée pas à pas, et doit énormément aux GPU et à NVIDIA qui, dans les années 2010, a grandement participé au développement et à l’accélération de sa prolifération.
L’évolution de la popularité du machine learning dans les recherches Google : la bulle n’a pas fini de monter !
Du côté pratique, l’IA a sans nul doute révolutionné les applications de l’informatique, particulièrement dans les domaines de la reconnaissance d’image, de la transcription et de la synthèse vocale, ou encore de la génération de texte/image. Pour autant, sa puissance demeure limitée et son utilisation cantonnée à des tâches spécifiques voire rébarbative pour un humain : à vous de juger l’« intelligence » de la chose. Pour autant, la popularité du Machine Learning est encore loin de s’estomper vu l’engouement de l’industrie à son sujet ! Nombreuses sont les start-ups à vouloir se tailler une part du gâteau IA… Reste à voir si ledit gâteau est suffisamment généreux pour tous ou si « The cake is a lie ». Bien que la croissance de l’IA a de beaux attributs la rapprochant d’une bulle, nombreux sont les domaines encore peu explorés, citons par exemple les questions de sécurité et de protections des réseaux (ou contre les réseaux). Et, dans cette ruée vers le machine learning, les concepteurs de matériel sont aux premières loges, le carnet de commandes de NVIDIA affichant un an de délai pour une H100 qui a été dévoilée en… mars 2022.
Face aux GPU, le pragmatisme voudrait que les architectures dédiées, par essence plus efficaces, dominent à terme le marché de la haute performance, cette prédiction est freinée par deux écueils. D’une part, le prix de ces accélérateurs est lié à leur popularité, car le coût du design et de la maintenance des logiciels qui les accompagne est astronomique — bienvenue dans le monde merveilleux du hardware ! Cela signifie que bon nombre d’entre eux restent tarifés trop haut par rapport aux moyens de certaines entreprises (surtout si la machine se destine également à d’autres tâches que le machine learning, ce qui est le cas notamment des supercalculateurs destinés à la recherche). De plus, qui dit spécialisé dit… spécialisé. À un type d’architecture de réseau ou, tout du moins, aux types d’architecture en vogue au moment du design ; ce qui peut être quelques années avant la sortie du matériel en question. Entre-temps, les besoins peuvent avoir changés, et la perspective de se retrouver avec du matériel inefficace dans quelques années a de quoi rebuter — d’autant plus que les progrès annuels en matière de lithographie se chargent déjà de rendre les machines obsolètes au fur et à mesure des années.
Les androïdes et leurs rêves de moutons électriques, ça n’est (heureusement) pas pour tout de suite !
Reste qu’un pan de la recherche contemporaine se concentre sur les réseaux creux (ou sparse), c’est-à-dire des réseaux dans lesquelles une partie des neurones de certaines couches est tout bonnement et simplement supprimée afin de réduire la quantité de calcul nécessaire sans pour autant impacter la précision du réseau. Dans ce domaine-là, les architectures actuelles montrent rapidement leurs limites, car les accès à la mémoire passent de linéaires (accès à tous les poids et biais d’une couche selon une liste connue et régulière) à un motif aléatoire (liste des poids différents de zéro irrégulier et plus ou moins imprévisible). Et, si vous suivez les performances des mémoires, si les débits sont toujours très bons en accès séquentiel, l’aléatoire peut plomber sérieusement les performances ! Si, sur ce point-là, NVIDIA fait — une fois encore — bonne figure avec une première intégration de la sparsité dans ses GPU à partir de la génération Ampere, cette dernière se base toujours sur un modèle séquentiel d’accès à la mémoire suivi par une sélection : un choix qui aura du mal à passer à l’échelle si la proportion de creux dans les réseaux venait à s’intensifier. Son concurrent, AMD n’est pour le moment pas mieux loti puisque seule la série Radeon Instinct MI300 intègre des unités adaptées aux matrices creuses pour lesquelles des restrictions similaires à celles de NVIDIA s’appliquent. Pour le moment, l’équation se résout en intégrant des accélérateurs basse consommation pour l’inférence immédiate, et en laissant de gros GPU s’occuper de l’entraînement et/ou d’inférence de masse en datacenters ; une spécialisation qui ne semble définitivement pas près de s’arrêter !