Autoencodeurs variationnels (VAE) : de nouvelles images à partir d’un espace latent continu
Un modèle génératif à variable latente, un VAE écrit de zéro avec l’astuce de reparamétrisation, un espace latent ordonné, la génération de chiffres à partir du bruit face à un autoencodeur ordinaire, l’effet de β et l’effondrement, l’interpolation et les usages des VAE.
Série Bases de l’IA Partie 44 sur 52
- Carte de l’intelligence artificielle : qu’est-ce qui est quoi et comment tout s’emboîte
- Brève histoire de l’intelligence artificielle : de Turing aux modèles de langage
- Données, modèle, entraînement, inférence : les notions de base sur un seul exemple
- Lire la notation mathématique : symboles, indices, sommes et quantificateurs
- Ensembles et relations : les briques dont sont faites les définitions en IA
- Fonctions et applications : pourquoi tout modèle d’IA est une fonction
- Logique et démonstrations : les règles selon lesquelles on raisonne en IA
- Graphes et arbres : la structure sur laquelle reposent la recherche et les réseaux de neurones
- Algèbre linéaire pour l’apprentissage automatique : vecteurs, matrices et pourquoi l’IA multiplie surtout
- Dérivées, gradient et règle de dérivation en chaîne : comment un modèle sait dans quelle direction apprendre
- Descente de gradient : comment un modèle apprend
- Qu’est-ce que la probabilité : du lancer de dé aux axiomes de Kolmogorov
- Probabilité conditionnelle et théorème de Bayes : comment réviser une estimation quand on apprend du nouveau
- Variable aléatoire et fonction de répartition : une seule fonction qui dit tout de la loi
- Fonction de densité et fonction de masse : pourquoi une variable continue a une probabilité nulle en chaque point
- Espérance, variance et covariance : où se situe une loi, quelle est sa largeur et ce qui varie avec quoi
- Panorama des lois de probabilité : Bernoulli, binomiale, Poisson, uniforme, normale et exponentielle
- Loi des grands nombres et théorème central limite : pourquoi les moyennes sont si prévisibles
- Statistique et estimation de paramètres : maximum de vraisemblance, MAP et intervalles de confiance
- Théorie de l’information : entropie, entropie croisée et divergence KL — d’où viennent les fonctions de perte
- Recherche dans l’espace d’états : BFS, DFS, UCS et A* en animations
- Jeux et adversaires : minimax, élagage alpha-bêta et Monte Carlo tree search
- Satisfaction de contraintes : backtracking, propagation de contraintes et AC-3 sur un sudoku et une carte des régions tchèques
- Logique et systèmes à base de connaissances : chaînage avant et arrière, résolution et systèmes experts
- Planification : STRIPS, monde des blocs, heuristiques par relaxation et planification dans les agents IA
- Recherche locale et recuit simulé : un voyageur de commerce à travers les chefs-lieux de région tchèques
- Algorithmes génétiques : population, croisement, mutation et sélection sur un sac à dos de randonnée
- Stratégies d’évolution et programmation génétique : de la règle du cinquième à la loi de Kepler
- Optimisation par colonies de fourmis : comment une piste de phéromone trouve un chemin court
- Optimisation par essaim de particules : inertie, expérience propre et expérience de l’essaim
- Neuroévolution : faire évoluer les poids et la forme des réseaux de neurones, et l’algorithme NEAT
- Ensembles flous et fonctions d’appartenance : comment écrire « un peu chaud »
- Logique floue et probabilité : pourquoi « un peu chaud » ne veut pas dire « probablement chaud »
- Logique floue : t-normes, implications et règles SI–ALORS
- Commande floue : Mamdani et Sugeno, du ventilateur au pendule inversé sur un chariot
- Clustering flou : l’algorithme fuzzy c-means et les points entre les clusters
- Systèmes neuro-flous (ANFIS) : des règles floues qui apprennent à partir des données
- Chaînes de Markov : matrice de transition, distribution stationnaire et PageRank
- Modèles de Markov cachés : l’algorithme de Viterbi et le casino malhonnête
- Réseaux bayésiens : pourquoi l’herbe est mouillée et ce que cela implique pour l’arroseur
- Le filtre de Kalman : comment estimer, à partir de mesures imprécises, où se trouve une voiture et à quelle vitesse elle roule
- Types d’apprentissage automatique : supervisé, non supervisé, semi-supervisé et plus
- Prompts, contexte et RAG : comment donner au modèle les bonnes informations
- Autoencodeurs variationnels (VAE) : de nouvelles images à partir d’un espace latent continu
- Réseaux antagonistes génératifs (GAN) : le faussaire contre le détective
- Modèles de diffusion : comment une image naît du bruit pur
- Processus de décision markoviens : états, actions, récompenses et équations de Bellman
- Q-learning et DQN : apprendre la valeur des actions, du tableau au réseau de neurones
- Policy gradient et PPO : apprendre directement la politique
- Comment fonctionne un agent IA : un modèle de langage en boucle avec des outils
- Programmer avec des agents IA : consigne, tests et vérification
- Évaluation et sécurité des systèmes d’IA : comment mesurer et comment surveiller
Programme complet de la série 52 sur 83 sujets publiés
- Pourquoi un autoencodeur ordinaire génère mal.
- Comment fonctionne un VAE : une loi au lieu d’un point, la pénalité KL et l’astuce de reparamétrisation.
- Ce que coûte β, pourquoi les images d’un VAE sont floues et où l’on utilise les VAE aujourd’hui.
Utile à connaître avant : Théorie de l’information : entropie, entropie croisée et divergence KL — d’où viennent les fonctions de perte
L’autoencodeur de la partie sur les représentations sait comprimer une image en quelques nombres puis la reconstituer à partir d’eux. Une question vient naturellement : si le décodeur sait faire d’un code un chiffre, ne pourrait-on pas lui donner un code que nous avons inventé et obtenir un nouveau chiffre que personne n’a dessiné ? Avec un autoencodeur ordinaire, cela ne marche presque pas. L’autoencodeur variationnel est une variante qui permet exactement cela — et c’est le premier des modèles génératifs auxquels nous allons nous consacrer dans cette partie de la série.
Elle fait suite aux parties Autoencodeurs et Théorie de l’information (divergence KL).
1. De la compression à la génération
Définition — modèle génératif
Un modèle génératif apprend à partir des données x1, …, xn une loi p(x) dont on peut échantillonner — produire de nouveaux x qui ressemblent aux données sans en être des copies. Les modèles à variable latente le font en deux étapes : ils tirent un code z d’une loi simple p(z) (par exemple normale), et le décodeur en calcule x.
Un classifieur répond à « qu’est-ce que c’est ». Un modèle génératif sait répondre à « montre-moi quelque chose qui pourrait être dans les données ». La recette : lancez les dés pour obtenir quelques nombres, donnez-les à un peintre (le décodeur), et il peindra d’après eux une image.
p(x) = ∫ p(x | z) · p(z) dz
- p(z)
- loi du code (loi a priori) — nous la choisissons nous-mêmes et simple, ici N(0, I), car c’est d’elle que nous devrons tirer lors de la génération
- p(x | z)
- le décodeur — quelle est la probabilité de l’image x quand on reçoit le code z ; ici un réseau qui renvoie pour chaque pixel la probabilité qu’il soit allumé
- ∫ … dz
- somme sur tous les codes possibles — l’image a pu naître de n’importe quel code ; mais une intégrale sur tout l’espace latent, nous ne savons pas la calculer, et c’est pourquoi le VAE la remplace par une estimation (voir plus bas)
Le piège de l’autoencodeur ordinaire : nous ne savons pas d’où tirer le code. L’encodeur place les chiffres dans l’espace latent là où bon lui semble. Dans notre réseau à code bidimensionnel, les codes des chiffres de test se trouvent autour du point (-3,9; -0,9) avec des écarts types de 4,3 et 6,0, et entre les groupes il y a des trous où le décodeur n’a jamais rien vu.
2. L’autoencodeur variationnel
Définition — autoencodeur variationnel (VAE)
Un VAE (Diederik Kingma et Max Welling, 2013) a un encodeur qui, pour une image x, ne renvoie pas un seul point mais une loi normale q(z | x) = N(μ(x), diag σ²(x)). Il apprend en minimisant la perte
L = −Ez ~ q[log p(x | z)] + β · KL(q(z | x) ‖ N(0, I)),
où le premier terme est l’erreur de reconstruction (ici l’entropie croisée binaire des pixels) et le second une pénalité pour l’écart entre la loi du code et la loi normale centrée réduite. Pour une loi normale, KL = ½ ∑j (μj² + σj² − log σj² − 1). Avec β = 1, −L est une borne inférieure de la log-vraisemblance des données (ELBO).
L’encodeur n’a pas le droit de dire « ce sept habite exactement ici », seulement « quelque part dans ce petit nuage ». Le décodeur doit alors savoir dessiner un sept depuis n’importe quel endroit du nuage. Et la pénalité KL pousse tous les nuages vers l’origine et ne les laisse pas devenir trop petits — ils se chevauchent donc, remplissent l’espace, et il ne reste pas de trous entre eux.
L(x) = −Ez ~ q(z | x)[ log p(x | z) ] + β · KL( q(z | x) ‖ N(0, I) )
- −Ez ~ q[log p(x | z)]
- erreur de reconstruction — à quel point le décodeur dessine mal x à partir d’un code tiré dans le nuage. L’espérance sur tout le nuage (pas seulement son centre) oblige le décodeur à dessiner correctement dans tout le voisinage du code ; le signe moins, parce que nous voulons le logarithme de la probabilité le plus grand possible et la perte la plus petite possible
- β
- poids de la pénalité — règle le compromis entre précision et ordre dans l’espace ; β = 1 donne exactement l’ELBO, les autres valeurs sont essayées dans la section sur β
- KL(q(z | x) ‖ N(0, I))
- distance entre le nuage et la loi dont nous tirerons — sans elle, l’encodeur réduirait les nuages à des points et les disperserait loin les uns des autres (la reconstruction serait meilleure), et nous retrouverions un autoencodeur ordinaire
KL = ½ ∑j=1k ( μj² + σj² − log σj² − 1 )
- KL
- la pénalité de la formule précédente, calculée exactement pour une loi normale ; les coordonnées du code sont indépendantes, d’où la somme sur j
- μj²
- décalage du centre du nuage par rapport à l’origine — il croît quadratiquement, les codes éloignés coûtent donc cher et l’encodeur les ramène vers zéro, là où nous tirerons
- σj² − log σj²
- largeur du nuage — la fonction s − log s atteint son minimum 1 exactement en s = 1 ; elle pénalise un nuage trop large, mais surtout un nuage trop étroit (−log σ² → ∞ pour σ → 0), pour que le nuage ne devienne pas un point
Théorème — l’ELBO est une borne inférieure de la log-vraisemblance
Pour toute loi q(z | x), on a
log p(x) = Eq[log p(x | z)] − KL(q(z | x) ‖ p(z)) + KL(q(z | x) ‖ p(z | x)) ≥ Eq[log p(x | z)] − KL(q(z | x) ‖ p(z)).
Démonstration. log p(x) ne dépend pas de z, il est donc égal à sa propre espérance sous q. D’après le théorème de Bayes, p(x) = p(x | z) p(z) / p(z | x) ; on multiplie la fraction par q(z | x) / q(z | x) :
log p(x) = Eq[log p(x | z)] + Eq[log (p(z) / q(z | x))] + Eq[log (q(z | x) / p(z | x))].
Le deuxième terme vaut −KL(q ‖ p(z)), le troisième KL(q ‖ p(z | x)). La divergence KL est toujours positive ou nulle (inégalité de Gibbs de la partie sur la théorie de l’information), donc en omettant le troisième terme on obtient l’inégalité. ∎
Le membre de droite est l’ELBO (evidence lower bound, borne inférieure de l’évidence), et avec β = 1 c’est exactement −L. En minimisant L, nous relevons donc une borne inférieure de log p(x) sans devoir calculer l’intégrale. L’égalité a lieu quand q(z | x) coïncide avec la véritable loi a posteriori p(z | x).
Définition — entropie croisée binaire des pixels
Pour chacun des 64 pixels, le décodeur renvoie un nombre yj ∈ (0, 1) qu’il interprète comme la probabilité que le pixel soit allumé. Pour une image d’intensités xj ∈ [0, 1], on a
−log p(x | z) = −∑j=164 [ xj log yj + (1 − xj) log(1 − yj) ].
Exemple pour un pixel : il est allumé (x = 1) et le décodeur dit 0,9 → −ln 0,9 ≈ 0,105 ; il dit 0,5 → ln 2 ≈ 0,693 ; il est éteint (x = 0) et le décodeur dit 0,9 → −ln 0,1 ≈ 2,303. C’est l’erreur commise avec assurance qui coûte le plus. Comme la sortie est une sigmoïde, la dérivée par rapport à l’entrée de la sigmoïde vaut simplement yj − xj — exactement comme pour la régression logistique.
Définition — astuce de reparamétrisation (reparameterization trick)
Le tirage z ~ N(μ, σ²) ne se dérive pas par rapport à μ et σ. On l’écrit donc sous la forme z = μ + σ · ε, où ε ~ N(0, 1) est un bruit indépendant des paramètres. Alors ∂z/∂μ = 1 et ∂z/∂σ = ε, et le gradient traverse la rétropropagation jusque dans l’encodeur.
On « sort le hasard » : au lieu de « tire un point dans le nuage », on dit « tire un bruit générique, puis décale-le et étire-le selon l’encodeur ». Décaler et étirer, ce sont des calculs ordinaires, et ceux-là se dérivent.
zj = μj(x) + σj(x) · εj, σj = exp(½ · log σj²), εj ~ N(0, 1)
- μj(x)
- centre du nuage, sortie de l’encodeur — ∂z/∂μ = 1, le gradient du décodeur passe dans l’encodeur sans changement
- σj(x)
- largeur du nuage — l’encodeur ne renvoie pas σ mais log σ², qui peut être n’importe quel nombre réel ; l’exponentielle en fait toujours une largeur positive, si bien que le réseau n’a aucune contrainte à respecter. ∂z/∂σ = ε
- εj ~ N(0, 1)
- tout le hasard — il ne dépend d’aucun paramètre, il n’y a donc rien à dériver à travers lui ; lors de la génération, on omet l’encodeur et on tire z directement de N(0, I)
Exemple — un chiffre de test (0) dans le VAE entraîné avec k = 2
encodeur : μ = (0,285; 1,587), log σ² = (−2,695; −1,931) → σ = (0,260; 0,381)
KL1 = ½ (0,081 + 0,068 + 2,695 − 1) ≈ 0,922
KL2 = ½ (2,518 + 0,145 + 1,931 − 1) ≈ 1,797 → KL ≈ 2,72 nats
reparamétrisation avec ε = (0,5; −1,0) : z = (0,285 + 0,260 · 0,5; 1,587 − 0,381 · 1,0) = (0,415; 1,206)
Ici, l’essentiel de la pénalité ne vient pas de la distance à l’origine mais de l’étroitesse du nuage : le terme −log σ² contribue pour 2,695 et 1,931. L’encodeur voudrait un code aussi précis que possible, la KL le maintient plus large. La KL moyenne sur les chiffres de test est de 3,42 nats. L’erreur de reconstruction de ce chiffre (depuis le centre μ) est de 34,6 nats sur 64 pixels.
def krok_vae(davka, beta=1.0):
for x in davka:
mu, logvar = koder(x) # k + k nombres
sigma = exp(0.5 * logvar)
eps = normal(0, 1, size=len(mu)) # hasard venu de l’extérieur
z = mu + sigma * eps # astuce de reparamétrisation
y = dekoder(z) # probabilités des pixels
rek = -sum(x * log(y) + (1 - x) * log(1 - y))
kl = 0.5 * sum(mu**2 + exp(logvar) - logvar - 1)
zpetne_sireni(rek + beta * kl) # le gradient passe z → mu, sigma → encodeur
adam_krok() # gradient moyen du lot
Notre réseau : 64 pixels → 32 neurones (tanh) → μ et log σ² (k nombres chacun) → 32 neurones → 64 pixels (sigmoïde), entraîné avec Adam pendant 150 époques sur 600 chiffres synthétiques. Le même réseau, simplement sans le bruit ε et sans la pénalité KL, est un autoencodeur ordinaire — la comparaison est donc équitable.
3. Un espace latent ordonné
Les deux réseaux regroupent les mêmes chiffres. Mais l’autoencodeur les disperse sur une grande surface — les deux cercles de la loi normale sont petits en comparaison. Les codes du VAE se trouvent autour de l’origine, avec des écarts types de 1,14 et 1,04, c’est-à-dire exactement là où nous allons tirer.
À quoi ressemble l’espace entier, une grille le montre : des points de l’espace latent régulièrement répartis, chacun passé par le décodeur.
Avec le VAE, les chiffres se fondent progressivement les uns dans les autres et presque chaque case est un chiffre lisible. Pour être honnête : dans le plan, le décodeur de l’autoencodeur est lui aussi assez lisse, et sa grille contient aussi surtout des chiffres. La différence ne se voit pleinement qu’avec un code à plus de dimensions.
4. Génération : de nouveaux chiffres à partir du bruit
Nous tirons 200 codes et les faisons décoder : pour le VAE à partir de N(0, I), pour l’autoencodeur (pour lui laisser une chance équitable) à partir d’une loi normale ajustée à ses codes réels. Nous mesurons la qualité par la distance (erreur quadratique moyenne) entre l’échantillon et le chiffre d’entraînement le plus proche. Les vrais chiffres de test sont en moyenne à 0,017 du chiffre d’entraînement le plus proche, le bruit aléatoire à 0,159.
Définition — distance au chiffre d’entraînement le plus proche et loi ajustée
Pour un échantillon y ∈ [0, 1]64 et des chiffres d’entraînement x(1), …, x(600), on a
d(y) = mini (1/64) ∑j=164 (yj − x(i)j)²,
c’est-à-dire l’erreur quadratique moyenne (MSE) par rapport au chiffre le plus semblable ; la qualité du générateur est la moyenne de d sur 200 échantillons. Un petit d signifie « ressemble à un vrai chiffre » — attention, un générateur qui se contente de copier les données d’entraînement obtiendrait lui aussi zéro.
L’autoencodeur tire selon zj = mj + sj · εj, où mj et sj sont la moyenne et l’écart type de la j-ième coordonnée des codes des chiffres de test (pour k = 2 : m = (−3,9; −0,9), s = (4,3; 6,0)). Les coordonnées sont tirées indépendamment — une éventuelle dépendance entre elles, la loi ajustée ne la capte pas.
| taille du code k | reconstruction AE (MSE) | reconstruction VAE (MSE) | échantillons AE | échantillons VAE |
|---|---|---|---|---|
| 2 | 0,051 | 0,063 | 0,042 | 0,044 |
| 8 | 0,025 | 0,036 | 0,090 | 0,054 |
| 16 | 0,017 | 0,039 | 0,112 | 0,055 |
Avec un code bidimensionnel, les deux réseaux font à peu près jeu égal (0,042 contre 0,044). Avec huit dimensions, l’autoencodeur s’effondre : ses échantillons ont une distance de 0,090, plus de la moitié du chemin vers le bruit, tandis que le VAE tient à 0,054. Avec seize, c’est 0,112 contre 0,055. En grande dimension, les codes de l’autoencodeur se trouvent sur une surface mince et tordue, et un point aléatoire tiré de la loi normale ajustée ne tombe pratiquement jamais dessus.
Le prix à payer : le VAE reconstruit moins bien. Avec k = 16, son erreur de reconstruction est de 0,039 contre 0,017 pour l’autoencodeur, et ses images sont plus floues. Le code est bruité, si bien que le décodeur apprend à dessiner la « moyenne » de ce qui pourrait se trouver au voisinage du code. Le flou est une faiblesse bien connue des VAE.
5. Reconstruction contre ordre : β
Le poids β détermine avec quelle force la pénalité KL pousse les codes vers la loi normale. Cinq VAE avec un code k = 2 et des β différents :
| β | KL (nats par image) | reconstruction (MSE) | échantillons (distance) |
|---|---|---|---|
| 0,1 | 8,18 | 0,053 | 0,044 |
| 0,5 | 4,71 | 0,057 | 0,044 |
| 1 | 3,42 | 0,063 | 0,044 |
| 2 | 2,07 | 0,076 | 0,052 |
| 5 | 0,05 | 0,131 | 0,075 |
Un petit β laisse les codes plus libres (KL 8,2) et la reconstruction est plus précise. Un grand β les comprime vers zéro. À β = 5, la KL tombe à 0,05 : l’encodeur a cessé de stocker quoi que ce soit dans le code, et le décodeur dessine toujours à peu près la même chose, quel que soit le code reçu. C’est ce qu’on appelle l’effondrement de la loi a posteriori (posterior collapse).
Définition — le nat, et la KL comme prix du code
Le nat est l’unité d’information associée au logarithme naturel : 1 nat = 1 / ln 2 ≈ 1,443 bit. La KL moyenne sur les données majore l’information mutuelle entre l’image et le code :
Ex KL(q(z | x) ‖ p(z)) = I(x; z) + KL(q(z) ‖ p(z)) ≥ I(x; z),
où q(z) = Ex q(z | x) est la loi des codes de toutes les images réunies. La KL est donc le « prix » que l’encodeur paie pour ce qu’il stocke dans le code à propos de l’image.
À β = 0,1, c’est au plus 8,18 nats ≈ 11,8 bits par image, à β = 5 seulement 0,05 nat ≈ 0,07 bit — moins d’un dixième de bit, le code ne dit donc presque rien de l’image et il ne reste au décodeur qu’à dessiner toujours la même chose.
À l’inverse, un β supérieur à 1 est utilisé délibérément par le β-VAE (Irina Higgins et al., 2017) : une pression plus forte vers des coordonnées du code indépendantes fait que les différentes dimensions correspondent souvent à des propriétés compréhensibles (inclinaison, épaisseur du trait).
6. Transitions continues
Nous prenons les codes de deux chiffres de test (réseaux à code de dimension huit ; pour chaque chiffre, l’image que le VAE reconstruit le mieux) et parcourons la droite qui les relie en huit pas : z(t) = (1 − t) · za + t · zb pour t = 0, 1/7, 2/7, …, 1, où za, zb sont les centres μ des deux chiffres. Nous décodons chaque point intermédiaire.
La transition est continue pour les deux réseaux : les étapes intermédiaires sont des formes vraisemblables entre les deux chiffres. Le long d’une droite entre deux codes réels, on ne s’éloigne pas beaucoup des données, si bien qu’ici l’autoencodeur ordinaire n’est pas en reste — sa faiblesse, c’est seulement le tirage à l’aveugle. Le même principe permet, dans les grands modèles, de modifier progressivement par exemple l’expression d’un visage ou le style d’une image.
7. Où l’on utilise les VAE
- Compression pour d’autres générateurs. Stable Diffusion (diffusion latente, Robin Rombach et al., 2022) ne travaille pas sur les pixels mais sur le code d’un autoencodeur à faible pénalité KL : il comprime une image de 512 × 512 × 3 en 64 × 64 × 4, et la diffusion s’exécute dans cet espace plus petit. La diffusion fera l’objet de l’une des prochaines parties.
- Codes discrets. Le VQ-VAE (Aäron van den Oord et al., 2017) choisit, au lieu d’un code continu, des entrées d’un dictionnaire appris. L’image devient ainsi une suite de « tokens » qu’un transformer sait générer.
- Données scientifiques. Molécules, données cellulaires, son — partout où un espace lisse, dans lequel on peut chercher et interpoler, est utile.
Pour la netteté des images, les VAE perdent face aux GAN et aux modèles de diffusion. Mais ils offrent un apprentissage stable, une véritable interprétation probabiliste et un encodeur gratuit.
8. Résumé
- Un modèle génératif apprend la loi des données pour pouvoir en échantillonner ; avec une variable latente, il tire un code et le décode.
- Le VAE encode vers une loi N(μ, σ²), et perte = reconstruction + β · KL par rapport à N(0, I). L’astuce z = μ + σε permet de dériver à travers le tirage.
- Les codes du VAE se trouvent autour de l’origine et remplissent l’espace ; les codes de l’autoencodeur sont dispersés et troués.
- Avec un code à 8 dimensions : échantillons de l’autoencodeur 0,090, du VAE 0,054 (vrais chiffres 0,017, bruit 0,159).
- Le prix est une reconstruction moins bonne et du flou ; un β trop grand mène à l’effondrement (KL 0,05).
Vous voulez l’essayer vous-même ? Demandez à un agent IA d’entraîner un VAE à code bidimensionnel sur le jeu de données MNIST et de tracer une grille d’images décodées — vous verrez toute une « carte » des chiffres manuscrits.
Vous voyez les choses autrement ? Écrivez à support@coden-app.com.