Introduction
Imaginez que vous vouliez construire un système capable de reconnaître les visages. Problème : pour un ordinateur, une image n’est rien d’autre qu’une suite de nombres. Et il suffit d’un petit changement de lumière, d’angle ou de pose pour que deux photos d’un même visage deviennent méconnaissables pour la machine. Comparer les images pixel par pixel ? Inutile. Chercher des correspondances dans une base de données géante ? Trop lent, trop imprécis. Ce qu’il faut, c’est extraire l’essence d’un visage. Ce qui ne change pas, même quand tout le reste change. Et cette essence ne se trouve pas dans les pixels eux-mêmes, mais dans un espace caché, invisible à l’œil nu. Un espace latent.

Plusieurs images d’une même personne, prises dans des conditions différentes (lumière, pose, expression). Visuellement, il est évident qu’il s’agit du même individu — mais pixel par pixel, ces images sont très différentes. Ce contraste illustre pourquoi on ne peut pas comparer les images directement dans l’espace des pixels : il faut un espace latent qui capture les traits essentiels, au-delà des variations superficielles.
Dans cet espace latent, deux photos d’une même personne — même prises sous des éclairages différents, avec ou sans lunettes, en souriant ou pas — se retrouvent côte à côte. Parce qu’elles partagent ce qui compte vraiment : la distance entre les yeux, la courbe du nez, la forme du menton. Bref, les traits essentiels. Mais comment trouver cet espace ? Comment le construire sans tout indiquer à la main ? Sans étiqueter des millions d’images ? Sans dicter à la machine ce qu’elle doit observer ?

Différentes photos d’un même individu (à gauche) sont toutes projetées dans une même zone de l’espace latent, tandis que les images d’un autre individu (à droite) forment un groupe distinct. L’espace latent organise spontanément les données selon leur ressemblance visuelle, sans supervision.
C’est ici qu’intervient une idée brillante dans sa simplicité : l’auto-encodeur. Un auto-encodeur, c’est un réseau de neurones qui apprend à reconstruire ce qu’on lui donne. Mais pour y parvenir, il commence par compresser l’information dans un espace plus petit — un goulot d’étranglement, où il est forcé de ne garder que l’essentiel. Puis, il tente de reconstruire l’image originale à partir de cette version comprimée. Et s’il y parvient, c’est qu’il a compris quelque chose de profond : les variables latentes cachées dans les données. Le tout sans supervision, sans annotation, sans étiquette : on donne la même image à l’entrée et à la sortie, et la magie opère entre les deux.

Une image passe à travers une structure symétrique : l’encodeur compresse l’information (partie gauche de l’entonnoir), le vecteur latent (en rouge) contient l’essentiel, puis le décodeur reconstruit l’image (partie droite). Cette compression vise à forcer le réseau à apprendre ce qui est important, et à ignorer le bruit.
Cette idée ne date pas d’hier. Dans les années 90, on parlait déjà de “réseaux diabolo”. Mais avec l’explosion du deep learning dans les années 2010, les auto-encodeurs ont pris une toute autre ampleur. On les a empilés, raffinés, hybridés. Aujourd’hui, ils sont partout : dans les outils d’amélioration d’image, dans les systèmes de détection d’anomalies, et surtout… au cœur des modèles génératifs modernes.
Comprendre les auto-encodeurs, ce n’est donc pas juste explorer une vieille idée du deep learning. C’est poser une brique essentielle dans la compréhension des modèles d’aujourd’hui, et une étape incontournable pour qui veut, demain, construire son propre modèle Stable Diffusion.
Architecture des auto-encodeurs
Un auto-encodeur, c’est donc un réseau de neurones qui apprend à reproduire ce qu’on lui donne en entrée. Mais au lieu de faire une simple copie brute, il passe par une version compressée. Dans cet article, on va décortiquer leur fonctionnement et même faire quelques expériences. Commençons par notre image d’entrée, qu’on note $x$. C’est ce qu’on veut encoder, compresser, résumer.

Représentation schématique d’un auto-encodeur entièrement connecté : à gauche, les neurones d’entrée (en violet) reçoivent l’image ; au centre, la couche cachée représente l’espace latent (en orange) ; à droite, la sortie (en vert) tente de reconstruire l’image d’origine. Le « goulot d’étranglement » force le réseau à comprimer l’information.
Le réseau est divisé en deux parties : un encodeur, qui transforme l’image en une représentation plus compacte — un vecteur latent, ou code — et un décodeur, qui tente de reconstruire l’image à partir de cette version comprimée. La reconstruction, on la note $\hat{x}$. L’objectif est simple : faire en sorte que $\hat{x}$ ressemble le plus possible à $x$. Mais comme le réseau est obligé de passer par un goulot d’étranglement — une couche centrale très étroite — il est contraint de ne garder que l’essentiel.
Décomposons un peu. L’encodeur est un réseau de neurones auquel on apprend à transformer une image, comme un chiffre manuscrit, en un vecteur beaucoup plus petit, qu’on note $z$. Ce vecteur, c’est la représentation latente. Un résumé. Au lieu de garder tous les pixels, on garde juste ce qui permet de reconnaître le chiffre. La taille de ce vecteur est un choix qu’on fait à la construction du réseau.

Une image $x$ est transformée par l’encodeur $E(x)$ en un vecteur latent $z$, ici de dimension 3. Cette représentation condensée est la forme minimale d’information conservée pour que le réseau puisse ensuite reconstruire l’image.
Si l’espace latent est de dimension 3, alors à partir d’une image, l’encodeur va produire un vecteur de 3 nombres. Imaginez : comprimer une image de 784 pixels (comme dans MNIST) en seulement 3 nombres.
Formellement, on note l’encodeur $E$, et il transforme $x$ en $z$ :
$$z = E(x; \theta)$$
où $\theta$ désigne les poids de l’encodeur, appris pendant l’entraînement.
Puis vient le décodeur.

Un vecteur latent $z$, ici explicité comme une liste de trois valeurs, est directement injecté dans le décodeur. Ce dernier produit une image $\hat{x}$ à partir de ces coordonnées dans l’espace latent. Cela illustre le principe de génération : produire une image sans passer par une donnée réelle en entrée.
Lui, il fait l’inverse : il prend le vecteur $z$ et tente de reconstruire une image $\hat{x}$ aussi proche que possible de l’originale :
$$\hat{x} = D(z; \phi)$$
où $\phi$ représente les poids du décodeur.

Le fonctionnement de l’auto-encodeur se résume ainsi : encoder l’entrée en $z$, puis utiliser ce vecteur pour reconstruire l’image d’origine. L’apprentissage repose entièrement sur cette boucle : $\hat{z}=D(E(x))$.
En combinant les deux, on obtient la définition complète de l’auto-encodeur :
$$\hat{x} = D(E(x; \theta); \phi)$$
Le passage par l’espace latent $z$ agit comme un goulot d’étranglement. Ce passage obligé dans un espace de plus faible dimension, sans supervision externe, force le réseau à apprendre ce qui est essentiel. Ce n’est pas nous qui disons à la machine ce qu’elle doit regarder : elle le découvre par elle-même, en cherchant simplement à reconstruire le mieux possible ce qu’elle a vu.
Mais comment entraîne-t-on un tel système ? Comme toujours en deep learning, on utilise la descente du gradient, mais pour ça, il nous faut une fonction de perte.
Fonction de perte : MSE
Et ici, la fonction de perte est très simple : on mesure l’écart entre l’image d’origine $x$ et l’image reconstruite $\hat{x}$, grâce à l’erreur quadratique moyenne, ou MSE (Mean Squared Error) :
$$\mathcal{L}(x, \hat{x}) = ||x – \hat{x}||^2 = ||x – D(E(x))||^2$$
La MSE, ou moyenne des erreurs quadratiques se fait sur toutes les dimensions de $x$ :
$$\text{MSE}(x, \hat{x}) = \frac{1}{d} \sum_{i=1}^d (x_i – \hat{x}_i)^2$$
On compare donc chaque pixel de l’image originale à celui de l’image reconstruite, on calcule la différence, on l’élève au carré, puis on fait la moyenne sur tous les pixels. Le résultat est une erreur globale.

L’image d’entrée $x$ est compressée par l’encodeur $E(x)$ en un vecteur latent $z$, puis reconstruite par le décodeur $D(z)$ en $\hat{x}$. L’objectif est de minimiser la perte $L(x,\hat{x})=∥x−\hat{x}∥$, appelée erreur quadratique moyenne (MSE), pour rendre la reconstruction la plus fidèle possible.
Plus cette erreur est petite, plus la reconstruction est fidèle. L’auto-encodeur est donc entraîné à minimiser cette erreur, en ajustant les poids des deux réseaux — encodeur et décodeur — à chaque itération.
Le modèle est mis à jour par descente de gradient, comme dans n’importe quel réseau de neurones.
Entraînement
Voyons maintenant ce qui se passe quand on entraîne un auto-encodeur en pratique. Pour ça, on utilise l’un des jeux de données les plus classiques du machine learning : MNIST, une collection de chiffres manuscrits en noir et blanc, chacun centré dans une image de 28 × 28 pixels. Pendant l’entraînement, on observe deux courbes clés : la MSE d’entraînement, et celle de validation. Ces courbes indiquent dans quelle mesure le modèle parvient à reconstruire correctement les images, respectivement sur les données vues et non vues pendant l’apprentissage.

Comparaison entre les chiffres originaux (haut) et les reconstructions finales (bas) après plusieurs milliers d’itérations. Les formes sont nettes, bien différenciées, et montrent que l’auto-encodeur a appris à encoder l’essentiel de chaque chiffre tout en supprimant les détails superflus.
Au tout début de l’entraînement, la perte est très élevée. Et c’est parfaitement normal. L’auto-encodeur est une coquille vide. Il ne sait pas encore quoi faire des images qu’on lui donne. Il n’a aucune idée de comment les compresser, ni comment les reconstruire. C’est comme demander à un élève de reproduire une œuvre d’art sans lui expliquer ce qu’il regarde.
Mais au fil des itérations, la courbe descend. Le modèle commence à apprendre. Il repère des structures, des régularités dans les chiffres. Il découvre comment encoder l’essentiel d’une image dans un vecteur court… puis comment décoder ce vecteur pour retrouver l’image d’origine. L’erreur diminue : preuve qu’il commence à s’améliorer.

Courbe de la perte MSE (Mean Squared Error) en fonction du nombre d’itérations. Elle chute rapidement au début, signe que le modèle progresse vite, puis se stabilise progressivement à mesure que l’apprentissage atteint ses limites.
Mais une courbe ne dit pas tout. Pour vraiment comprendre ce qu’il apprend, rien ne vaut une observation directe des reconstructions, à différents moments de l’entraînement.
Sur la ligne du haut, on affiche des images originales du dataset MNIST. En dessous, les reconstructions générées par le modèle à partir de son espace latent. Avant même la première itération, les reconstructions ne ressemblent à rien. Des amas de pixels flous, un brouillard aléatoire, sans forme ni signification.

Les images originales (ligne du haut) sont comparées aux reconstructions produites par l’auto-encodeur à l’itération 0 (ligne du bas). Sans entraînement, le modèle produit un bruit sans signification. Il ne sait encore rien extraire ni reconstruire.
Mais très vite, quelque chose apparaît. Après quelques dizaines d’itérations, on devine des motifs flous. Des formes encore hésitantes, mais qui commencent à esquisser des chiffres. C’est comme si le modèle dessinait à tâtons. Vers la centième itération, les chiffres deviennent reconnaissables, même s’ils ont tous un petit air de famille : beaucoup de formes se confondent, comme si le modèle hésitait. Est-ce un 9 ? Un 4 ? Un 1 ?

Dès les premières itérations, des motifs commencent à apparaître dans les reconstructions. Les chiffres sont encore flous et déformés, mais on devine des structures émergentes. L’auto-encodeur commence à apprendre à représenter les formes de base.
Vers 200 itérations, des chiffres nets émergent. Les 0, 1, 2, 7 sont facilement identifiables. Mais certaines ambiguïtés persistent. Le 4 et le 9, en particulier, sont encore mal séparés. Les reconstructions ont parfois la boucle du 9 là où on attendrait une ligne droite.

Comparaison entre les chiffres originaux (haut) et les reconstructions finales (bas) après plusieurs milliers d’itérations. Les formes sont nettes, bien différenciées, et montrent que l’auto-encodeur a appris à encoder l’essentiel de chaque chiffre tout en supprimant les détails superflus.
Et puis, avec le temps — plusieurs centaines, voire milliers d’itérations — un petit miracle se produit. Le haut du 4 devient plus net, sa barre horizontale s’efface, tandis que le 9 garde fièrement sa boucle. Le modèle a fini par comprendre la différence entre ces deux chiffres.

Après un entraînement prolongé, les reconstructions deviennent très précises. Le modèle a appris à reproduire fidèlement la structure des chiffres manuscrits. C’est à ce stade qu’on commence à voir des détails subtils apparaître, comme la différenciation nette entre les 4 et les 9.
Il les a séparés dans son espace latent, sans jamais qu’on lui ait dit quoi que ce soit.
Dimension latente
Justement, parlons de cet espace latent. Ce fameux espace invisible, situé entre l’encodeur et le décodeur, dans lequel notre image est compressée sous forme de vecteur. Mais avant de l’explorer, une question simple se pose : quelle taille doit-il faire ?
Si l’espace latent est trop petit, le modèle manque littéralement de place pour encoder toute l’information. Il est obligé de jeter des détails, parfois cruciaux. À l’inverse, s’il est trop grand, il n’a plus besoin de faire d’effort : il peut simplement recopier les données sans chercher à en extraire l’essentiel. C’est un peu comme mémoriser un texte mot à mot, au lieu de comprendre son sens.

Avec un espace latent réduit à seulement 2 dimensions, les reconstructions sont fortement limitées. Les formes sont grossières, floues, et certains chiffres sont mal différenciés. Le modèle n’a pas assez de capacité pour encoder toutes les variations utiles.
Prenons un exemple. Avec un espace latent de seulement 2 dimensions, les reconstructions sont très limitées. On reconnaît vaguement certains chiffres — les 0, les 1, parfois les 9 — mais les autres sont déformés. Les 2 et les 7 se ressemblent, les 4 sont presque méconnaissables.

Avec 4 dimensions, la qualité des reconstructions s’améliore nettement. Les chiffres sont plus lisibles, mais certaines confusions persistent — notamment entre les 4, les 9 et les 1. Le modèle dispose de plus de place pour encoder, mais reste limité.
En passant à 4 dimensions, le réseau gagne en expressivité. Le 2 et le 7 sont mieux différenciés, les formes sont plus claires. Mais le 4 reste flou, mal formé, comme si le modèle hésitait encore.

À 7 dimensions, l’auto-encodeur parvient à reconstruire fidèlement tous les chiffres. La forme, les proportions et les détails sont bien restitués. C’est un bon compromis entre compression et expressivité : l’espace latent est assez grand pour encoder l’essentiel sans surapprentissage.
C’est seulement à partir de 7 dimensions que tout devient net. Tous les chiffres sont bien reconstruits, leurs formes sont fidèles, même si certains détails fins peuvent encore être un peu lissés. Ce qui est remarquable, c’est qu’on arrive à résumer une image de 784 pixels (28 × 28) avec seulement 7 nombres… et à la reconstruire correctement. Voilà la force d’un bon espace latent : compresser l’essentiel, éliminer le superflu, et garder ce qui compte vraiment.
Exploration de l’espace latent
Prenons maintenant notre espace latent de 7 dimensions. C’est une belle compression… mais c’est aussi très difficile à visualiser. Sept dimensions, notre cerveau ne sait pas les imaginer. Alors pour explorer cet espace invisible, on utilise un outil bien connu en machine learning : le T-SNE. Cet algorithme permet de projeter un espace de grande dimension dans un plan 2D, tout en essayant de préserver autant que possible les relations de proximité entre les points.
Prenons un exemple. On choisit un chiffre — disons un 7 — qu’on fait passer dans notre auto-encodeur. On récupère les 7 valeurs de sa représentation latente, et on les projette avec T-SNE. On recommence avec d’autres 7, et on observe que ces points s’agglomèrent dans une même région du plan. Normal : ces images se ressemblent, et le réseau les encode de manière similaire. Refaisons l’opération avec des 9, et là aussi, on obtient un regroupement clair, dans une autre zone.

Après un long entraînement, la projection t-SNE révèle des clusters bien définis. Chaque chiffre se retrouve dans une région distincte, preuve que l’auto-encodeur a appris à encoder les chiffres selon leur structure visuelle, sans supervision.
Ce qu’on voit, c’est que sans jamais lui avoir demandé de classer, notre auto-encodeur a appris à organiser les images similaires dans des zones proches de son espace latent. Il n’a pas besoin de labels. Il a simplement découvert, en essayant de reconstruire au mieux, que certains chiffres partageaient les mêmes structures internes.

Avant tout entraînement, la projection 2D des vecteurs latents via t-SNE montre un nuage totalement désorganisé. Les points correspondant aux différents chiffres sont mélangés : le réseau n’a encore appris aucune structure.
Encore plus fascinant : si on observe cette projection à différents moments de l’entraînement, on voit l’espace se structurer progressivement. Au début, tout est flou, mélangé. Mais au fil des itérations, les groupes se forment. Les chiffres similaires s’attirent, les différents s’éloignent. L’espace latent s’organise de lui-même, à mesure que le réseau apprend.

Après seulement 200 itérations, une structure commence à émerger. Les chiffres similaires commencent à se regrouper, mais les frontières restent floues. Le modèle commence à capturer une organisation globale dans l’espace latent.
Alors bien sûr, ce n’est pas parfait. Il y a parfois des 6 un peu trop proches des 0, ou des 4 qui flirtent avec des 1. C’est le reflet de deux choses : d’une part, la nature ambigüe de certains chiffres manuscrits dans MNIST, et d’autre part, les limites du T-SNE lui-même — car réduire 7 dimensions à 2, c’est forcément perdre des informations.
Pour y voir plus clair, on peut projeter cet espace latent en 3D. Et là, en explorant les points dans un outil comme Blender, la structure devient encore plus évidente. On voit apparaître des nuages distincts, bien séparés, correspondant à chaque chiffre. On peut littéralement naviguer dans l’espace latent. Observer comment les chiffres s’organisent, comment le modèle a appris — tout seul — une forme de cartographie sémantique de son univers interne.
Et ce n’est que le début. Car une fois qu’on a ce type de structure, on peut aller beaucoup plus loin.
Interpolation et génération dans l’espace latent
Faisons une nouvelle expérience. On prend deux images, par exemple un 4 et un 2, et on les encode avec notre auto-encodeur. On obtient deux vecteurs dans l’espace latent. Ensuite, on effectue une interpolation entre ces deux points. C’est-à-dire qu’on trace un chemin entre leurs représentations latentes, et qu’on génère des points intermédiaires le long de ce trajet.

On encode deux images (un 4 et un 2) dans l’espace latent, puis on interpole entre leurs vecteurs. À chaque point intermédiaire, on demande au décodeur de générer une image. Le résultat illustre comment l’auto-encodeur organise les chiffres selon des continuités sémantiques dans l’espace latent.
Chaque point interpolé est ensuite passé dans le décodeur… et là, surprise : au milieu du chemin, un 9 apparaît.
Pourquoi un 9 ? Ce n’est pas un bug, mais une conséquence géométrique. Dans l’espace latent, il est probable que le 9 soit proche, à la fois, du 4 et du 2. Cet espace n’a pas été conçu pour représenter les classes comme dans un classifieur, mais pour organiser les données selon leurs similarités structurelles.

En interpolant entre la représentation latente d’un 4 et celle d’un 2, on observe une transition continue de formes. De manière surprenante, un 9 émerge au milieu. Ce phénomène illustre la géométrie implicite de l’espace latent : le modèle n’invente pas un chiffre hybride, mais génère ce qui est le plus probable à cet endroit — ici, une forme ressemblant fortement à un 9.
Et ici, manifestement, certains 9 ressemblent beaucoup à des mélanges de 4 et de 2. Le modèle n’a jamais été entraîné à générer des transitions continues — il n’a vu que des images fixes — mais comme l’espace est structuré autour des exemples du dataset, il peut parfois produire des images crédibles à partir de points intermédiaires.
Et ça pose une autre question : peut-on générer une image de toutes pièces ? Tirer une image « au hasard » ? La réponse intuitive serait : il suffit de choisir un point aléatoire dans l’espace des pixels. Mais ça ne marche pas. L’espace des images possibles est immense, et la grande majorité de ces combinaisons donnent… du bruit. Imaginez choisir chaque pixel au hasard : vous avez plus de chances de tomber sur de la neige télévisuelle que sur un chat ou un chiffre.
Heureusement, notre auto-encodeur a appris à projeter toutes les images du dataset dans un espace latent compact, structuré, et dense. En théorie, chaque point dans cet espace correspond à une image plausible. C’est là toute l’idée derrière l’IA générative : échantillonner un vecteur dans cet espace, puis le passer au décodeur… pour produire une image.

Des vecteurs latents tirés au hasard sont décodés en images. Le résultat est chaotique : formes floues, déformées ou incompréhensibles. L’auto-encodeur n’a pas appris à organiser son espace latent de manière continue ni cohérente : entre les points d’entraînement, il n’a aucune garantie de produire des images valides.
Alors testons. On prend des points au hasard dans l’espace latent, et on les passe dans le décodeur. Et là, le résultat est… décevant. Les formes sont étranges, parfois vaguement reconnaissables, mais souvent déformées, déchirées, ou totalement incompréhensibles. Pourquoi ? Parce que notre auto-encodeur n’a pas été entraîné à générer, mais à reconstruire. Il s’est contenté d’apprendre à reproduire les exemples qu’on lui a donnés. Et il le fait très bien. Mais rien ne garantit que l’espace latent est continu, ou bien rempli. Il n’y a aucune régularisation.
Imaginez que chaque image d’entraînement corresponde à un point très précis dans l’espace latent. Le réseau apprend rapidement à mémoriser ces points. Mais il ne sait pas forcément quoi faire entre eux, ni en dehors. Résultat : quand on échantillonne au hasard, on tombe souvent à côté. Même dans l’interpolation entre le 4 et le 2, si un 9 apparaît, c’est sans doute parce que certains 9 du dataset ressemblent à des points situés sur ce chemin. L’espace n’est pas homogène. Le modèle a appris une forme de surapprentissage compressé : il sait très bien reconstruire ce qu’il a vu… mais pas inventer ce qu’il n’a jamais rencontré.
Conclusion
Alors, comment faire mieux ? Comment passer d’un modèle qui reconstruit ce qu’il connaît à un modèle capable de générer de nouvelles images, crédibles, variées, et contrôlables ? C’est là qu’entrent en scène d’autres variantes d’auto-encodeurs, plus sophistiquées, pensées spécifiquement pour la génération. Parmi elles, les Variational Auto-Encoders, ou VAE, occupent une place centrale. Leur principe ? Forcer l’espace latent à suivre une distribution bien définie, souvent une gaussienne standard. Grâce à cette contrainte, on peut échantillonner n’importe où dans l’espace latent et obtenir, avec une forte probabilité, une image réaliste à la sortie. Ce mécanisme est au cœur de nombreux modèles génératifs modernes, y compris Stable Diffusion, qui utilise une version avancée de VAE pour encoder et manipuler les représentations visuelles de manière contrôlée.
Mais les VAE ne sont pas seuls. Il existe aussi les denoising auto-encoders, qui apprennent à restaurer une image à partir d’une version bruitée — une idée clé dans les modèles de diffusion. Les sparse auto-encoders, eux, introduisent une contrainte d’économie dans l’espace latent, forçant le modèle à n’activer qu’un petit nombre de neurones. Les VQ-VAE, quant à eux, utilisent un code discret, empruntant des idées au traitement du langage et aux modèles de compression. Et puis il y a les auto-encodeurs masqués, inspirés des Transformers, qui prédisent certaines parties d’une image à partir du reste — comme on le fait avec du texte dans BERT.
Toutes ces variantes ont un objectif commun : trouver une bonne représentation des données. Un code interne, latent, qui capture le sens, l’essence… et permet de reconstruire à partir de là.
Et ça, on en reparlera. Car cet article n’est que la première étape. Dans deuxième partie, on passera de la théorie à la pratique. On écrira notre propre auto-encodeur en Python, on l’entraînera, on observera ses résultats… et on ouvrira la porte à des modèles de plus en plus puissants.







1 Commentaire