Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Auto-encodeurs

Tous les modèles que nous avons étudiés jusqu’ici étaient supervisés: ils apprenaient à prédire une sortie yy à partir d’une entrée x\mathbf{x}, en utilisant des paires étiquetées. Mais les données étiquetées sont souvent rares et coûteuses à obtenir, tandis que les données non étiquetées sont abondantes. Peut-on apprendre des représentations utiles à partir des données seules, sans étiquettes?

L’idée la plus naturelle est de demander à un réseau de neurones de comprimer une entrée x\mathbf{x} en une représentation z\mathbf{z} de dimension inférieure, puis de reconstruire x\mathbf{x} à partir de z\mathbf{z}. Si la reconstruction est fidèle malgré la compression, c’est que z\mathbf{z} a capturé la structure essentielle des données. Ce principe de compression-reconstruction définit l’auto-encodeur.

Dans ce chapitre, nous formalisons cette idée et montrons qu’elle généralise l’analyse en composantes principales au cas non linéaire. Nous présentons ensuite l’auto-encodeur débruiteur, qui apprend des représentations plus robustes en reconstruisant des entrées corrompues. Le chapitre se termine par un aperçu de l’auto-encodeur variationnel, qui ajoute une structure probabiliste à l’espace latent.

L’auto-encodeur

Architecture

Un auto-encodeur est un réseau de neurones composé de deux parties. L’encodeur fϕ:RD→RLf_{\boldsymbol{\phi}} : \mathbb{R}^D \to \mathbb{R}^L transforme l’entrée en une représentation de dimension réduite (L<DL < D), appelée code latent ou simplement code. Le décodeur gψ:RL→RDg_{\boldsymbol{\psi}} : \mathbb{R}^L \to \mathbb{R}^D reconstruit l’entrée à partir de ce code. L’entrée traverse d’abord l’encodeur, puis le décodeur:

x  →  fϕ    z  →  gψ    x^\mathbf{x} \;\xrightarrow{\;f_{\boldsymbol{\phi}}\;}\; \mathbf{z} \;\xrightarrow{\;g_{\boldsymbol{\psi}}\;}\; \hat{\mathbf{x}}

où z=fϕ(x)∈RL\mathbf{z} = f_{\boldsymbol{\phi}}(\mathbf{x}) \in \mathbb{R}^L est le code latent et x^=gψ(z)∈RD\hat{\mathbf{x}} = g_{\boldsymbol{\psi}}(\mathbf{z}) \in \mathbb{R}^D est la reconstruction.

La contrainte L<DL < D crée un goulot d’étranglement (bottleneck): le réseau ne peut pas copier l’entrée vers la sortie en passant par z\mathbf{z}, car z\mathbf{z} n’a pas assez de dimensions pour stocker toute l’information. Le réseau doit donc apprendre quels aspects de l’entrée sont essentiels pour permettre la reconstruction, et lesquels peuvent être ignorés.

Fonction de perte

L’auto-encodeur est entraîné en minimisant l’erreur de reconstruction sur un ensemble de données {x1,…,xN}\{\mathbf{x}_1, \ldots, \mathbf{x}_N\}:

L(ϕ,ψ)=1N∑n=1N∥xn−gψ(fϕ(xn))∥2\mathcal{L}(\boldsymbol{\phi}, \boldsymbol{\psi}) = \frac{1}{N} \sum_{n=1}^N \|\mathbf{x}_n - g_{\boldsymbol{\psi}}(f_{\boldsymbol{\phi}}(\mathbf{x}_n))\|^2

C’est une perte des moindres carrés entre l’entrée et sa reconstruction. Comme pour les réseaux supervisés des chapitres précédents, on minimise cette perte par descente de gradient stochastique: la rétropropagation calcule les gradients ∇ϕL\nabla_{\boldsymbol{\phi}} \mathcal{L} et ∇ψL\nabla_{\boldsymbol{\psi}} \mathcal{L}, et un optimiseur (Adam, par exemple) met à jour les paramètres de l’encodeur et du décodeur conjointement.

La perte quadratique n’est pas le seul choix possible. Pour des données binaires (pixels noirs et blancs, par exemple), on utilise l’entropie croisée binaire entre l’entrée et la reconstruction, comme en classification binaire (chapitre 3). Pour des images en niveaux de gris normalisés entre 0 et 1, les deux fonctions de perte donnent des résultats similaires en pratique.

Ce que l’auto-encodeur n’est pas

Un auto-encodeur n’est pas un algorithme de compression au sens informatique du terme: il ne produit pas un code binaire optimal pour des données arbitraires. Il apprend une compression adaptée à la distribution des données d’entraînement. Un auto-encodeur entraîné sur des visages comprimera bien d’autres visages, mais pas des paysages. Cette spécialisation est précisément ce qui rend les représentations apprises utiles pour des tâches en aval (classification, détection d’anomalies, etc.).

L’auto-encodeur linéaire et l’ACP

Que se passe-t-il si l’encodeur et le décodeur sont des transformations linéaires, sans fonction d’activation? L’encodeur devient fϕ(x)=We⊤xf_{\boldsymbol{\phi}}(\mathbf{x}) = \mathbf{W}_e^\top \mathbf{x} et le décodeur gψ(z)=Wd zg_{\boldsymbol{\psi}}(\mathbf{z}) = \mathbf{W}_d\, \mathbf{z}, où We∈RD×L\mathbf{W}_e \in \mathbb{R}^{D \times L} et Wd∈RD×L\mathbf{W}_d \in \mathbb{R}^{D \times L}. La reconstruction est:

x^=Wd We⊤x\hat{\mathbf{x}} = \mathbf{W}_d\, \mathbf{W}_e^\top \mathbf{x}

et la perte de reconstruction (pour des données centrées) devient:

L(We,Wd)=1N∑n=1N∥xn−Wd We⊤xn∥2=1N∥X−XWeWd⊤∥F2\mathcal{L}(\mathbf{W}_e, \mathbf{W}_d) = \frac{1}{N} \sum_{n=1}^N \|\mathbf{x}_n - \mathbf{W}_d\, \mathbf{W}_e^\top \mathbf{x}_n\|^2 = \frac{1}{N}\|\mathbf{X} - \mathbf{X} \mathbf{W}_e \mathbf{W}_d^\top\|_F^2

Cette perte est exactement l’erreur de reconstruction de l’analyse en composantes principales (ACP). On peut montrer que les minimiseurs We∗\mathbf{W}_e^* et Wd∗\mathbf{W}_d^* satisfont Im(Wd∗)=Im(We∗)\text{Im}(\mathbf{W}_d^*) = \text{Im}(\mathbf{W}_e^*), et que ce sous-espace est engendré par les LL vecteurs propres de la matrice de covariance empirique Σ^=1NX⊤X\hat{\boldsymbol{\Sigma}} = \frac{1}{N}\mathbf{X}^\top \mathbf{X} associés aux LL plus grandes valeurs propres Baldi & Hornik (1989). L’erreur de reconstruction minimale est:

L∗=∑k=L+1Dλk\mathcal{L}^* = \sum_{k=L+1}^{D} \lambda_k

où λ1≥λ2≥⋯≥λD\lambda_1 \geq \lambda_2 \geq \cdots \geq \lambda_D sont les valeurs propres de Σ^\hat{\boldsymbol{\Sigma}}. C’est la somme des variances dans les directions ignorées par la projection, le même résultat que l’ACP.

L’auto-encodeur linéaire est donc une reformulation de l’ACP comme problème d’optimisation par gradient. Cette reformulation peut sembler inutile (l’ACP se résout par décomposition spectrale, sans itérations), mais elle ouvre une voie: en remplaçant les transformations linéaires par des réseaux de neurones, on obtient une généralisation non linéaire de l’ACP.

Auto-encodeurs non linéaires

Du linéaire au non linéaire

L’ACP projette les données sur un sous-espace linéaire. Or beaucoup de jeux de données vivent sur des variétés courbes de basse dimension: les images de visages, par exemple, varient le long de directions continues (pose, éclairage, expression) qui ne forment pas un sous-espace linéaire de l’espace des pixels. Un sous-espace linéaire est une mauvaise approximation de cette variété.

L’auto-encodeur non linéaire remplace les projections linéaires par des réseaux de neurones. L’encodeur et le décodeur deviennent des MLP (chapitre 7) avec des fonctions d’activation non linéaires:

z=fϕ(x)=σK(WKσK−1(⋯σ1(W1x+b1)⋯ )+bK)x^=gψ(z)=σK′(WK′σK−1′(⋯σ1′(W1′z+b1′)⋯ )+bK′)\begin{aligned} \mathbf{z} &= f_{\boldsymbol{\phi}}(\mathbf{x}) = \sigma_K(\mathbf{W}_K \sigma_{K-1}(\cdots \sigma_1(\mathbf{W}_1 \mathbf{x} + \mathbf{b}_1) \cdots) + \mathbf{b}_K) \\ \hat{\mathbf{x}} &= g_{\boldsymbol{\psi}}(\mathbf{z}) = \sigma_K'(\mathbf{W}_K' \sigma_{K-1}'(\cdots \sigma_1'(\mathbf{W}_1' \mathbf{z} + \mathbf{b}_1') \cdots) + \mathbf{b}_K') \end{aligned}

où les σk\sigma_k et σk′\sigma_k' sont des fonctions d’activation (ReLU, par exemple). L’encodeur et le décodeur ont chacun leurs propres paramètres; l’entraînement ajuste les deux simultanément par rétropropagation.

Le rôle du goulot d’étranglement

La dimension LL du code latent contrôle le compromis entre fidélité de la reconstruction et compression. Si LL est trop grand (proche de DD), le réseau peut apprendre la fonction identité et le code ne capture aucune structure utile. Si LL est trop petit, la reconstruction sera mauvaise car trop d’information est perdue.

En pratique, on choisit LL par validation: on entraîne des auto-encodeurs avec différentes dimensions latentes et on évalue la qualité des représentations sur une tâche en aval (classification avec un classifieur linéaire sur z\mathbf{z}, par exemple) ou simplement la qualité de reconstruction sur un ensemble de validation.

Architectures courantes

L’encodeur et le décodeur ont souvent des architectures symétriques: si l’encodeur a des couches de tailles D→512→256→LD \to 512 \to 256 \to L, le décodeur aura des couches L→256→512→DL \to 256 \to 512 \to D. Cette symétrie n’est pas requise, mais elle simplifie le choix des hyperparamètres. La dernière couche du décodeur utilise une fonction d’activation adaptée au domaine des données: une sigmoïde pour des données dans [0,1][0, 1] (pixels normalisés), une identité pour des données réelles non bornées.

Source
import numpy as np
import matplotlib.pyplot as plt
import jax
import jax.numpy as jnp
%config InlineBackend.figure_format = 'retina'

# Générer des données sur une variété non linéaire (demi-cercle bruité)
np.random.seed(42)
N = 500
t = np.random.uniform(0, np.pi, N)
noise = np.random.normal(0, 0.05, (N, 2))
X = np.column_stack([np.cos(t), np.sin(t)]) + noise

# --- ACP ---
X_centered = X - X.mean(axis=0)
cov = X_centered.T @ X_centered / N
eigenvalues, eigenvectors = np.linalg.eigh(cov)
w1 = eigenvectors[:, -1]  # eigh trie par ordre croissant
z_pca = X_centered @ w1
X_pca_recon = np.outer(z_pca, w1) + X.mean(axis=0)

# --- Auto-encodeur en JAX ---
# Architecture: 2 -> 64 -> 1 -> 64 -> 2

def init_params(key):
    k1, k2, k3, k4 = jax.random.split(key, 4)
    return {
        'W1': jax.random.normal(k1, (2, 64)) * jnp.sqrt(2.0 / 2),
        'b1': jnp.zeros(64),
        'W2': jax.random.normal(k2, (64, 1)) * jnp.sqrt(2.0 / 64),
        'b2': jnp.zeros(1),
        'W3': jax.random.normal(k3, (1, 64)) * jnp.sqrt(2.0 / 1),
        'b3': jnp.zeros(64),
        'W4': jax.random.normal(k4, (64, 2)) * jnp.sqrt(2.0 / 64),
        'b4': jnp.zeros(2),
    }

def encode(params, x):
    h = jax.nn.relu(x @ params['W1'] + params['b1'])
    return h @ params['W2'] + params['b2']

def decode(params, z):
    h = jax.nn.relu(z @ params['W3'] + params['b3'])
    return h @ params['W4'] + params['b4']

def loss_fn(params, x):
    z = encode(params, x)
    x_hat = decode(params, z)
    return jnp.mean((x - x_hat) ** 2)

# Adam
def adam_init(params):
    return {k: {'m': jnp.zeros_like(v), 'v': jnp.zeros_like(v)}
            for k, v in params.items()}

@jax.jit
def adam_step(params, state, grads, lr=1e-3, b1=0.9, b2=0.999, eps=1e-8, t=1):
    new_params, new_state = {}, {}
    for k in params:
        m = b1 * state[k]['m'] + (1 - b1) * grads[k]
        v = b2 * state[k]['v'] + (1 - b2) * grads[k] ** 2
        m_hat = m / (1 - b1 ** t)
        v_hat = v / (1 - b2 ** t)
        new_params[k] = params[k] - lr * m_hat / (jnp.sqrt(v_hat) + eps)
        new_state[k] = {'m': m, 'v': v}
    return new_params, new_state

grad_fn = jax.jit(jax.grad(loss_fn))

X_jax = jnp.array(X_centered)
params = init_params(jax.random.key(0))
state = adam_init(params)

for i in range(3000):
    grads = grad_fn(params, X_jax)
    params, state = adam_step(params, state, grads, t=i + 1)

# Reconstruction
X_ae_recon = np.array(decode(params, encode(params, X_jax))) + X.mean(axis=0)

# --- Tracé ---
fig, axes = plt.subplots(1, 3, figsize=(12, 3.5))

axes[0].scatter(X[:, 0], X[:, 1], s=8, c=t, cmap='viridis', alpha=0.7)
axes[0].set_title('Données originales')
axes[0].set_xlabel('$x_1$')
axes[0].set_ylabel('$x_2$')
axes[0].set_aspect('equal')

axes[1].scatter(X[:, 0], X[:, 1], s=8, c=t, cmap='viridis', alpha=0.2)
axes[1].scatter(X_pca_recon[:, 0], X_pca_recon[:, 1], s=8, c=t, cmap='viridis', alpha=0.7)
for i in range(0, N, 15):
    axes[1].plot([X[i, 0], X_pca_recon[i, 0]], [X[i, 1], X_pca_recon[i, 1]],
                 'k-', alpha=0.1, linewidth=0.5)
axes[1].set_title('Reconstruction par ACP ($L=1$)')
axes[1].set_xlabel('$x_1$')
axes[1].set_ylabel('$x_2$')
axes[1].set_aspect('equal')

axes[2].scatter(X[:, 0], X[:, 1], s=8, c=t, cmap='viridis', alpha=0.2)
axes[2].scatter(X_ae_recon[:, 0], X_ae_recon[:, 1], s=8, c=t, cmap='viridis', alpha=0.7)
for i in range(0, N, 15):
    axes[2].plot([X[i, 0], X_ae_recon[i, 0]], [X[i, 1], X_ae_recon[i, 1]],
                 'k-', alpha=0.1, linewidth=0.5)
axes[2].set_title('Reconstruction par auto-encodeur ($L=1$)')
axes[2].set_xlabel('$x_1$')
axes[2].set_ylabel('$x_2$')
axes[2].set_aspect('equal')

plt.tight_layout()
W0409 22:49:32.675388 39650675 cpp_gen_intrinsics.cc:74] Empty bitcode string provided for eigen. Optimizations relying on this IR will be disabled.
<Figure size 1200x350 with 3 Axes>

La figure ci-dessus illustre la différence entre l’ACP et un auto-encodeur non linéaire sur des données disposées en demi-cercle. L’ACP projette les données sur une droite (la direction de variance maximale), ce qui écrase la structure courbe. L’auto-encodeur, grâce à ses non-linéarités, apprend à projeter sur la variété courbe elle-même: chaque point est reconstruit près de sa position originale sur le demi-cercle. La dimension latente est L=1L = 1 dans les deux cas, mais l’auto-encodeur utilise cette unique dimension pour paramétrer la position le long de la courbe.

Auto-encodeur débruiteur

Motivation

Un auto-encodeur avec une capacité suffisante peut apprendre à reconstruire ses entrées presque parfaitement, sans pour autant apprendre des représentations utiles. Si le réseau mémorise chaque exemple individuellement, le code latent ne capture pas les régularités de la distribution, il encode simplement l’identité de chaque point.

L’auto-encodeur débruiteur (denoising autoencoder, DAE) contourne ce problème en modifiant la tâche d’entraînement Vincent et al. (2008). Au lieu de reconstruire x\mathbf{x} à partir de x\mathbf{x}, on corrompt d’abord l’entrée en x~\tilde{\mathbf{x}}, puis on entraîne le réseau à reconstruire l’entrée originale (propre) à partir de l’entrée corrompue:

x~  →  fϕ    z  →  gψ    x^≈x\tilde{\mathbf{x}} \;\xrightarrow{\;f_{\boldsymbol{\phi}}\;}\; \mathbf{z} \;\xrightarrow{\;g_{\boldsymbol{\psi}}\;}\; \hat{\mathbf{x}} \approx \mathbf{x}

La perte reste la même qu’en (2), mais l’entrée de l’encodeur est x~\tilde{\mathbf{x}} alors que la cible de reconstruction est x\mathbf{x}:

LDAE(ϕ,ψ)=1N∑n=1NEx~n∼q(x~∣xn)∥xn−gψ(fϕ(x~n))∥2\mathcal{L}_{\text{DAE}}(\boldsymbol{\phi}, \boldsymbol{\psi}) = \frac{1}{N} \sum_{n=1}^N \mathbb{E}_{\tilde{\mathbf{x}}_n \sim q(\tilde{\mathbf{x}} | \mathbf{x}_n)} \|\mathbf{x}_n - g_{\boldsymbol{\psi}}(f_{\boldsymbol{\phi}}(\tilde{\mathbf{x}}_n))\|^2

où q(x~∣x)q(\tilde{\mathbf{x}} | \mathbf{x}) est le processus de corruption. En pratique, l’espérance est estimée en tirant une corruption différente à chaque passage d’un exemple dans le réseau (comme le dropout au chapitre 8, une nouvelle corruption est tirée à chaque itération).

Processus de corruption

Deux corruptions sont courantes:

Le bruit additif gaussien ajoute un bruit ϵ∼N(0,σ2I)\boldsymbol{\epsilon} \sim \mathcal{N}(\mathbf{0}, \sigma^2 \mathbf{I}) à l’entrée: x~=x+ϵ\tilde{\mathbf{x}} = \mathbf{x} + \boldsymbol{\epsilon}. L’écart-type σ\sigma contrôle l’intensité de la corruption.

La corruption par masquage met à zéro une fraction pp des composantes de x\mathbf{x}, choisies aléatoirement. Chaque composante est indépendamment mise à zéro avec probabilité pp. Cette corruption force le réseau à inférer les composantes manquantes à partir des composantes restantes, un peu comme on compléterait un mot à trous.

Interprétation géométrique

Pour reconstruire x\mathbf{x} à partir de x~=x+ϵ\tilde{\mathbf{x}} = \mathbf{x} + \boldsymbol{\epsilon}, le réseau doit apprendre à ramener les points bruités vers la variété des données. La reconstruction x^\hat{\mathbf{x}} se trouve (idéalement) sur la variété, ou du moins plus près de celle-ci que x~\tilde{\mathbf{x}}. Le vecteur x^−x~\hat{\mathbf{x}} - \tilde{\mathbf{x}} pointe donc approximativement vers la variété.

On peut montrer que, dans la limite d’un bruit gaussien de faible variance, la fonction de reconstruction optimale estime le gradient du logarithme de la densité des données Vincent (2011):

gψ(fϕ(x~))−x~  ≈  σ2∇xlog⁡p(x~)g_{\boldsymbol{\psi}}(f_{\boldsymbol{\phi}}(\tilde{\mathbf{x}})) - \tilde{\mathbf{x}} \;\approx\; \sigma^2 \nabla_{\mathbf{x}} \log p(\tilde{\mathbf{x}})

Ce gradient, appelé fonction de score (score function), indique la direction dans laquelle la densité des données augmente le plus vite. L’auto-encodeur débruiteur apprend donc implicitement la structure de la distribution des données, pas seulement une compression.

Source
# Illustration de l'auto-encodeur débruiteur sur des données 2D
np.random.seed(42)
N_dae = 300
t_dae = np.random.uniform(0, np.pi, N_dae)
X_clean = np.column_stack([np.cos(t_dae), np.sin(t_dae)])

# Corruption par bruit gaussien
sigma_noise = 0.3
X_noisy = X_clean + np.random.normal(0, sigma_noise, X_clean.shape)

fig, axes = plt.subplots(1, 2, figsize=(8, 3.5))

axes[0].scatter(X_clean[:, 0], X_clean[:, 1], s=10, alpha=0.5, label='Originales')
axes[0].scatter(X_noisy[:, 0], X_noisy[:, 1], s=10, alpha=0.3, marker='x', label='Corrompues')
axes[0].set_title('Données et corruption')
axes[0].set_xlabel('$x_1$')
axes[0].set_ylabel('$x_2$')
axes[0].set_aspect('equal')
axes[0].legend(fontsize=8)

# Flèches de reconstruction: de x_noisy vers x_clean (cible idéale)
# On montre un sous-ensemble pour la lisibilité
indices = np.random.choice(N_dae, 50, replace=False)
axes[1].scatter(X_noisy[:, 0], X_noisy[:, 1], s=10, alpha=0.2, color='C1')
for i in indices:
    axes[1].annotate('', xy=X_clean[i], xytext=X_noisy[i],
                     arrowprops=dict(arrowstyle='->', color='C0', alpha=0.4, lw=0.8))
# Tracer la variété
t_curve = np.linspace(0, np.pi, 200)
axes[1].plot(np.cos(t_curve), np.sin(t_curve), 'k-', linewidth=1.5, alpha=0.5, label='Variété')
axes[1].set_title('Vecteurs de reconstruction')
axes[1].set_xlabel('$x_1$')
axes[1].set_ylabel('$x_2$')
axes[1].set_aspect('equal')
axes[1].legend(fontsize=8)

plt.tight_layout()
<Figure size 800x350 with 2 Axes>

La figure de droite montre les vecteurs de reconstruction idéaux: chaque flèche part d’un point corrompu et pointe vers le point propre correspondant, c’est-à-dire vers la variété des données. Ces vecteurs approximent le gradient de la log-densité: ils indiquent la direction dans laquelle les données sont les plus probables.

Vers l’auto-encodeur variationnel

L’auto-encodeur standard apprend un encodeur déterministe: à chaque entrée x\mathbf{x} correspond un unique code z=fϕ(x)\mathbf{z} = f_{\boldsymbol{\phi}}(\mathbf{x}). Mais l’espace latent résultant n’a pas de structure particulière. Deux points proches dans l’espace latent ne correspondent pas nécessairement à des données similaires, et un point z\mathbf{z} tiré au hasard dans l’espace latent ne correspond généralement à aucune donnée réaliste. On ne peut donc pas utiliser un auto-encodeur standard pour générer de nouvelles données.

L’auto-encodeur variationnel (VAE) Kingma & Welling (2014) résout ce problème en imposant une structure probabiliste à l’espace latent. Au lieu de produire un code déterministe z\mathbf{z}, l’encodeur produit les paramètres d’une distribution: une moyenne μ\boldsymbol{\mu} et une variance σ2\boldsymbol{\sigma}^2 pour chaque dimension latente. Le code z\mathbf{z} est ensuite tiré aléatoirement de cette distribution:

z∼N(μϕ(x),  diag(σϕ2(x)))\mathbf{z} \sim \mathcal{N}(\boldsymbol{\mu}_{\boldsymbol{\phi}}(\mathbf{x}),\; \text{diag}(\boldsymbol{\sigma}^2_{\boldsymbol{\phi}}(\mathbf{x})))

L’entraînement du VAE ajoute un terme de régularisation qui force la distribution des codes à rester proche d’une gaussienne standard N(0,I)\mathcal{N}(\mathbf{0}, \mathbf{I}). Ce terme, une divergence de Kullback-Leibler, garantit que l’espace latent est continu et structuré: des points proches dans l’espace latent correspondent à des données similaires, et on peut générer de nouvelles données en décodant des points tirés de N(0,I)\mathcal{N}(\mathbf{0}, \mathbf{I}).

Le VAE se situe à l’intersection des auto-encodeurs et de l’inférence variationnelle que nous avons brièvement rencontrée au chapitre 6 (dans le contexte de l’algorithme EM et de l’ELBO). L’encodeur joue le rôle de la distribution variationnelle qϕ(z∣x)q_{\boldsymbol{\phi}}(\mathbf{z} | \mathbf{x}) qui approxime l’a posteriori intractable p(z∣x)p(\mathbf{z} | \mathbf{x}). L’étude détaillée du VAE dépasse le cadre de ce cours; nous renvoyons le lecteur intéressé à des références spécialisées Kingma & Welling (2019).

Auto-encodeurs et pré-entraînement

Au chapitre 8, nous avons vu que le pré-entraînement couche par couche a été la première technique permettant d’entraîner des réseaux profonds. L’auto-encodeur a joué un rôle central dans cette approche Hinton & Salakhutdinov (2006)Bengio et al. (2007): chaque couche du réseau était pré-entraînée comme un auto-encodeur, apprenant à reconstruire ses entrées avant de passer à la couche suivante.

Cette technique a perdu de son importance avec l’arrivée de ReLU, de la normalisation par lots et des connexions résiduelles, qui permettent d’entraîner des réseaux profonds directement. Mais le principe sous-jacent reste pertinent: apprendre des représentations non supervisées, puis les transférer vers une tâche supervisée. Les représentations apprises par un auto-encodeur (ou un auto-encodeur débruiteur) sur un grand ensemble de données non étiquetées peuvent servir d’initialisation pour un réseau supervisé entraîné sur un petit ensemble étiqueté, suivant le schéma tronc-tête du chapitre 8.

Résumé

L’auto-encodeur apprend des représentations de basse dimension en minimisant l’erreur de reconstruction entre l’entrée et sa version comprimée puis reconstruite. Le goulot d’étranglement force le réseau à capturer la structure essentielle des données.

Quand l’encodeur et le décodeur sont linéaires, l’auto-encodeur retrouve la solution de l’ACP: la projection sur le sous-espace de variance maximale. Le passage aux non-linéarités permet de capturer des variétés courbes que l’ACP ne peut pas représenter.

L’auto-encodeur débruiteur renforce les représentations en entraînant le réseau à reconstruire des entrées propres à partir de versions corrompues. Dans la limite de faible bruit, cette tâche revient à estimer le gradient de la log-densité des données.

L’auto-encodeur variationnel ajoute une structure probabiliste à l’espace latent, ce qui permet de générer de nouvelles données et relie l’auto-encodeur au cadre de l’inférence variationnelle.

Exercices

References
  1. Baldi, P., & Hornik, K. (1989). Neural Networks and Principal Component Analysis: Learning from Examples Without Local Minima. Neural Networks, 2(1), 53–58.
  2. Vincent, P., Larochelle, H., Bengio, Y., & Manzagol, P.-A. (2008). Extracting and Composing Robust Features with Denoising Autoencoders. Proceedings of the 25th International Conference on Machine Learning (ICML), 1096–1103.
  3. Vincent, P. (2011). A Connection Between Score Matching and Denoising Autoencoders. Neural Computation, 23(7), 1661–1674.
  4. Kingma, D. P., & Welling, M. (2014). Auto-Encoding Variational Bayes. arXiv Preprint arXiv:1312.6114.
  5. Kingma, D. P., & Welling, M. (2019). An Introduction to Variational Autoencoders. Foundations and Trends in Machine Learning, 12(4), 307–392.
  6. Hinton, G. E., & Salakhutdinov, R. R. (2006). Reducing the Dimensionality of Data with Neural Networks. Science, 313(5786), 504–507.
  7. Bengio, Y., Lamblin, P., Popovici, D., & Larochelle, H. (2007). Greedy Layer-Wise Training of Deep Networks. Advances in Neural Information Processing Systems (NeurIPS).