Autoencodeur PyTorch

Un autoencodeur (Autoencoder, AE)est un réseau de neurones pour l'apprentissage non supervisé qui compresse les données d'entrée dans un espace latent de faible dimension par apprentissage, puis reconstruit les données d'origine à partir de la représentation compressée.

Les autoencodeurs sont largement utilisés dans des scénarios tels que la réduction de dimensionnalité des données, l'extraction de caractéristiques, la détection d'anomalies, le débruitage d'images et les modèles génératifs.


1. Principes de base de l'autoencodeur

La structure de base d'un autoencodeur se compose de trois parties :

  • Encodeur: projette les données d'entrée \(x\) vers une représentation latente de faible dimension \(z\)
  • Espace latent: le vecteur de faible dimension produit par l'encodeur, également appelé couche de goulot d'étranglement
  • Décodeur: reconstruit la représentation latente \(z\) vers la sortie \(\hat{x}\)

1.1 Structure du réseau

L'objectif d'un autoencodeur est de rendre la sortie \(\hat{x}\) aussi proche que possible de l'entrée \(x\) :

\[ \min_{\theta, \phi} \frac{1}{n} \sum_{i=1}^{n} \| x_i - D_\phi(E_\theta(x_i)) \|^2 \]

où \(\theta\) sont les paramètres de l'encodeur et \(\phi\) sont les paramètres du décodeur.

1.2 Effet de réduction de dimensionnalité

Les autoencodeurs apprennent une représentation compressée des données en forçant les données à passer par une couche de goulot d'étranglement plus petite que la dimension d'entrée. Cette compression préserve les informations principales des données.

Comparés à l'analyse en composantes principales (ACP), les autoencodeurs peuvent apprendre une réduction de dimension non linéaire et capturer des structures de données plus complexes.


2. Implémentation de base d'un autoencodeur

2.1 Autoencodeur simple

Exemple

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

# ── Modèle d'autoencodeur ─────────────────────────────────
class Autoencoder(nn.Module):
    """
Autoencodeur de base : structure symétrique
    """

    def __init__(self, input_dim, hidden_dim, latent_dim):
        super().__init__()

        # Encodeur
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, latent_dim),  # Couche de goulot d'étranglement
        )

        # Décodeur
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, input_dim),
        )

    def forward(self, x):
        z = self.encoder(x)
        x_recon = self.decoder(z)
        return x_recon

    def encode(self, x):
        """Encodage : obtenir la représentation latente"""
        return self.encoder(x)

    def decode(self, z):
        """Décodage : reconstruire à partir de la représentation latente"""
        return self.decoder(z)


# ── Exemple d'utilisation ──────────────────────────────────────
INPUT_DIM = 784   # Par exemple, après avoir aplati une image MNIST
HIDDEN_DIM = 256
LATENT_DIM = 32   # Dimension de l'espace latent, bien plus petite que la dimension d'entrée

model = Autoencoder(INPUT_DIM, HIDDEN_DIM, LATENT_DIM)
print(f"Dimension d'entrée : {INPUT_DIM}")
print(f"Dimension latente : {LATENT_DIM}")
print(f"Taux de compression : {INPUT_DIM / LATENT_DIM:.1f}x")

# Afficher le nombre de paramètres
total_params = sum(p.numel() for p in model.parameters())
print(f"Nombre total de paramètres : {total_params:,}")

2.2 Autoencodeur convolutif

Pour les données d'image, les autoencodeurs utilisant des couches convolutives sont plus performants :

Exemple

import torch
import torch.nn as nn

class ConvAutoencoder(nn.Module):
    """
Autoencodeur convolutif : adapté aux images
    """

    def __init__(self, channels=3, latent_dim=128):
        super().__init__()

        # Encodeur : réduire progressivement la taille, augmenter le nombre de canaux
        # Entrée : (batch, channels, 64, 64)
        self.encoder = nn.Sequential(
            # 32 -> 16
            nn.Conv2d(channels, 32, kernel_size=3, stride=2, padding=1),
            nn.ReLU(),
            # 16 -> 8
            nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1),
            nn.ReLU(),
            # 8 -> 4
            nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1),
            nn.ReLU(),
            # 4 -> 2
            nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1),
            nn.ReLU(),
        )

        # Mappage de l'espace latent
        self.to_latent = nn.AdaptiveAvgPool2d((1, 1))

        # Décodeur : augmenter progressivement la taille
        # Entrée : (batch, 256, 2, 2)
        self.from_latent = nn.Sequential(
            nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),
            nn.Conv2d(256, 128, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),
            nn.Conv2d(128, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),
            nn.Conv2d(64, 32, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),
            nn.Conv2d(32, channels, kernel_size=3, padding=1),
            nn.Sigmoid()  # Sortie [0, 1]
        )

    def forward(self, x):
        z = self.encode(x)
        x_recon = self.decode(z)
        return x_recon

    def encode(self, x):
        """Encoder"""
        features = self.encoder(x)
        z = self.to_latent(features)
        z = z.view(z.size(0), -1)  # (batch, 256)
        return z

    def decode(self, z):
        """Décoder"""
        # Redimensionner le vecteur en carte de caractéristiques
        batch_size = z.size(0)
        z = z.view(batch_size, 256, 1, 1)
        z = z.expand(-1, -1, 2, 2)  # Suréchantillonner à 2x2
        x_recon = self.from_latent(z)
        return x_recon


# Test
model = ConvAutoencoder(channels=3, latent_dim=128)
x = torch.randn(4, 3, 64, 64)
x_recon = model(x)

print(f"Forme de l'entrée : {x.shape}")
print(f"Forme de sortie : {x_recon.shape}")
print(f"Forme du vecteur latent : {model.encode(x).shape}")

2.3 Entraînement et reconstruction

Exemple

import torch
import torch.nn as nn
import torch.optim as optim

# ── Configuration d'entraînement ─────────────────────────────────────
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = ConvAutoencoder(channels=3, latent_dim=128).to(device)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)

# ── Boucle d'entraînement ─────────────────────────────────────
def train_autoencoder(model, dataloader, criterion, optimizer, num_epochs=10):
    model.train()
    for epoch in range(num_epochs):
        total_loss = 0
        for batch in dataloader:
            images = batch[0].to(device)

            # Propagation avant
            outputs = model(images)
            loss = criterion(outputs, images)

            # Rétropropagation
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            total_loss += loss.item()

        avg_loss = total_loss / len(dataloader)
        print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.6f}")

    return model


# On suppose qu'un chargeur de données existe déjà
# train_autoencoder(model, train_loader, criterion, optimizer, num_epochs=10)
print("Entraînement de l'autoencodeur terminé !")

3. Autoencodeur débruiteur (DAE)

L'autoencodeur débruiteur (DAE)ajoute du bruit à l'entrée pendant l'entraînement, puis apprend à supprimer le bruit et à récupérer l'entrée d'origine. Cela permet au modèle d'apprendre des représentations de caractéristiques plus robustes.

3.1 Implémentation de l'autoencodeur débruiteur

Exemple

import torch
import torch.nn as nn

class DenoisingAutoencoder(nn.Module):
    """
Autoencodeur débruiteur
    """

    def __init__(self, input_dim, hidden_dim, latent_dim):
        super().__init__()

        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, latent_dim),
        )

        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, input_dim),
            nn.Sigmoid()  # Sortie [0, 1]
        )

    def forward(self, x):
        z = self.encode(x)
        return self.decode(z)

    def encode(self, x):
        return self.encoder(x)

    def decode(self, z):
        return self.decoder(z)


def add_noise(x, noise_factor=0.3):
    """
Ajouter un bruit gaussien
    """

    noise = torch.randn_like(x) * noise_factor
    noisy_x = x + noise
    return torch.clamp(noisy_x, 0.0, 1.0)


# Entraîner l'autoencodeur de débruitage
def train_dae(model, dataloader, noise_factor=0.3, lr=1e-3):
    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=lr)

    model.train()
    for epoch in range(10):
        for batch in dataloader:
            images = batch[0]

            # Ajouter du bruit
            noisy_images = add_noise(images, noise_factor)
            noisy_images = noisy_images.to(next(model.parameters()).device)
            images = images.to(next(model.parameters()).device)

            # Propagation avant
            outputs = model(noisy_images)
            loss = criterion(outputs, images)  # Comparer à l'image originale, pas à l'image bruitée

            # Rétropropagation
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

    return model

3.2 Autres types de bruit

Exemple

def salt_pepper_noise(x, prob=0.1):
    """Bruit poivre et sel"""
    random_mask = torch.rand_like(x)
    noisy = x.clone()
    noisy[random_mask < prob / 2] = 0.0
    noisy[random_mask > 1 - prob / 2] = 1.0
    return noisy


def mask_noise(x, prob=0.1):
    """Bruit de masquage (mis aléatoirement à zéro)"""
    mask = torch.rand_like(x) > prob
    return x * mask.float()


def dropout_noise(x, rate=0.2):
    """Bruit de dropout"""
    mask = torch.rand_like(x) > rate
    return x * mask.float() / (1 - rate)

4. Auto-encodeur variationnel (VAE)

Auto-encodeur variationnel (VAE)est un modèle génératif qui encode les données en une distribution de probabilité dans l'espace latent plutôt qu'en un vecteur fixe. Cela nous permet d'échantillonner à partir de l'espace latent pour générer de nouvelles données.

4.1 Principes fondamentaux du VAE

L'innovation clé du VAE est d'apprendre la distribution de probabilité des variables latentes :

  • L'encodeur produit la moyenne \(\mu\) et l'écart-type \(\sigma\)
  • Échantillonner le vecteur latent \(z\) à partir de la distribution normale \(\mathcal{N}(\mu, \sigma)\)
  • Le décodeur reconstruit les données à partir de \(z\)

Pour obtenir un processus d'échantillonnage différentiable, l'astuce de reparamétrisation est utilisée.:

\[ z = \mu + \sigma \cdot \epsilon, \quad \epsilon \sim \mathcal{N}(0, 1) \]

4.2 Implémentation du VAE

Exemple

import torch
import torch.nn as nn
import torch.optim as optim

class VAE(nn.Module):
    """
Autoencodeur variationnel
    """

    def __init__(self, input_dim, hidden_dim, latent_dim):
        super().__init__()

        # Encodeur : moyenne et variance en sortie
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
        )
        self.fc_mu = nn.Linear(hidden_dim, latent_dim)
        self.fc_logvar = nn.Linear(hidden_dim, latent_dim)

        # Décodeur
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, input_dim),
            nn.Sigmoid()
        )

    def encode(self, x):
        h = self.encoder(x)
        mu = self.fc_mu(h)
        logvar = self.fc_logvar(h)
        return mu, logvar

    def reparameterize(self, mu, logvar):
        """Astuce de reparamétrisation"""
        std = torch.exp(0.5 * logvar)
        eps = torch.randn_like(std)
        return mu + eps * std

    def decode(self, z):
        return self.decoder(z)

    def forward(self, x):
        mu, logvar = self.encode(x)
        z = self.reparameterize(mu, logvar)
        x_recon = self.decode(z)
        return x_recon, mu, logvar


def vae_loss(x_recon, x, mu, logvar, beta=1.0):
    """
Fonction de perte VAE
Perte de reconstruction + divergence KL
    """

    # Perte de reconstruction
    recon_loss = nn.functional.mse_loss(x_recon, x, reduction='sum')

    # Divergence KL : -0.5 * sum(1 + log(sigma^2) - mu^2 - sigma^2)
    kl_loss = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())

    return recon_loss + beta * kl_loss, recon_loss, kl_loss


# Exemple d'utilisation
INPUT_DIM = 784
HIDDEN_DIM = 256
LATENT_DIM = 2  # Espace latent bidimensionnel pour une visualisation facile

model = VAE(INPUT_DIM, HIDDEN_DIM, LATENT_DIM)

# Test
x = torch.randn(32, 784)
x_recon, mu, logvar = model(x)

print(f"Forme de l'entrée : {x.shape}")
print(f"Forme reconstruite : {x_recon.shape}")
print(f"Forme de la moyenne : {mu.shape}")       # (32, 2)
print(f"Forme de la variance : {logvar.shape}")   # (32, 2)

4.3 Génération et visualisation VAE

Exemple

import matplotlib.pyplot as plt

def visualize_latent_space(model, dataloader, device):
    """Visualisation de l'espace latent"""
    model.eval()
    all_mu = []
    all_labels = []

    with torch.no_grad():
        for batch in dataloader:
            images, labels = batch[0].to(device), batch[1]
            mu, _ = model.encode(images)
            all_mu.append(mu.cpu())
            all_labels.append(labels)

    all_mu = torch.cat(all_mu, dim=0).numpy()
    all_labels = torch.cat(all_labels, dim=0).numpy()

    plt.figure(figsize=(10, 8))
    scatter = plt.scatter(all_mu[:, 0], all_mu[:, 1], c=all_labels,
                          cmap='tab10', alpha=0.5, s=10)
    plt.colorbar(scatter)
    plt.xlabel('Dimension latente 1')
    plt.ylabel('Dimension latente 2')
    plt.title('Espace latent du VAE')
    plt.show()


def generate_from_latent(model, z, device):
    """Génération d'images à partir de vecteurs latents"""
    model.eval()
    with torch.no_grad():
        z = z.to(device)
        generated = model.decode(z)
    return generated


def interpolate_latent(model, z1, z2, steps=10, device):
    """Génération par interpolation dans l'espace latent"""
    model.eval()

    # Interpolation linéaire
    alphas = torch.linspace(0, 1, steps)
    interpolated = []

    with torch.no_grad():
        for alpha in alphas:
            z = z1 * (1 - alpha) + z2 * alpha
            generated = model.decode(z)
            interpolated.append(generated)

    return torch.cat(interpolated, dim=0)


# Générer de nouvelles images
def generate_new_images(model, num_images, latent_dim, device):
    """Générer de nouvelles images à partir de vecteurs latents aléatoires"""
    model.eval()
    with torch.no_grad():
        # Échantillonner depuis une distribution normale standard
        z = torch.randn(num_images, latent_dim).to(device)
        generated = model.decode(z)
    return generated

L'espace latent d'un VAE est continu, permettant une interpolation dans l'espace latent pour générer des images avec des transitions fluides. Cependant, les images générées par un VAE sont souvent floues car il optimise une borne inférieure plutôt que la log-vraisemblance exacte.


5. Autoencodeur parcimonieux

Autoencodeur parcimonieuxAjoutez une contrainte de parcimonie à la fonction de perte, limitant le nombre d'activations dans le vecteur latent. Cela permet au modèle d'apprendre des caractéristiques plus significatives.

5.1 Implémentation de l'autoencodeur parcimonieux

< h2 class="example">Exemple
import torch
import torch.nn as nn
import torch.nn.functional as F

class SparseAutoencoder(nn.Module):
    """
Autoencodeur parcimonieux
    """

    def __init__(self, input_dim, hidden_dim, latent_dim):
        super().__init__()

        # Encodeur
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
        )
        # Couche latente
        self.bottleneck = nn.Linear(hidden_dim, latent_dim)

        # Décodeur
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, input_dim),
            nn.Sigmoid()
        )

    def forward(self, x):
        h = self.encoder(x)
        z = self.bottleneck(h)
        z_activated = F.relu(z)  # Activation parcimonieuse
        x_recon = self.decoder(z_activated)
        return x_recon, z_activated


def sparse_loss(z, rho=0.05, beta=1.0):
    """
Perte de parcimonie : divergence KL
rho : parcimonie cible (par ex., 0.05 signifie que seulement 5% des neurones devraient être actifs)
beta : poids du terme de parcimonie
    """

    # Calculer l'activation moyenne
    rho_hat = torch.mean(z, dim=0)

    # Divergence KL
    kl = rho * torch.log(rho / (rho_hat + 1e-8)) + \
         (1 - rho) * torch.log((1 - rho) / (1 - rho_hat + 1e-8))

    return beta * torch.sum(kl)


def total_sparse_loss(x_recon, x, z, rho=0.05, beta=1.0):
    """Perte totale = perte de reconstruction + perte de parcimonie"""
    recon_loss = F.mse_loss(x_recon, x)
    sparsity = sparse_loss(z, rho, beta)
    return recon_loss + sparsity

6. Autoencodeur séquence à séquence

Pour les données séquentielles (comme le texte, les séries temporelles), utilisez RNN/LSTM comme encodeur et décodeur.

6.1 Implémentation de l'auto-encodeur de séquence

Exemple

import torch
import torch.nn as nn

class Seq2SeqAutoencoder(nn.Module):
    """
Auto-encodeur séquence à séquence : utilisé pour les données séquentielles
    """

    def __init__(self, input_size, hidden_size, latent_size, num_layers=2):
        super().__init__()

        # Encodeur LSTM
        self.encoder = nn.LSTM(
            input_size=input_size,
            hidden_size=hidden_size,
            num_layers=num_layers,
            batch_first=True,
            bidirectional=True
        )

        # Mappage de l'espace latent
        # La sortie du LSTM bidirectionnel est hidden_size * 2
        self.to_latent = nn.Linear(hidden_size * 2, latent_size)
        self.from_latent = nn.Linear(latent_size, hidden_size * 2)

        # LSTM décodeur
        self.decoder = nn.LSTM(
            input_size=hidden_size,
            hidden_size=hidden_size,
            num_layers=num_layers,
            batch_first=True,
            bidirectional=True
        )

        # Projection de sortie
        self.output_proj = nn.Linear(hidden_size * 2, input_size)

    def forward(self, x):
        # Encodage
        _, (h_n, _) = self.encoder(x)

        # Concaténer les états cachés finaux bidirectionnels
        # h_n: (num_layers * 2, batch, hidden_size)
        h_forward = h_n[-2]
        h_backward = h_n[-1]
        h_combined = torch.cat([h_forward, h_backward], dim=-1)

        # Mapper vers l'espace latent
        z = self.to_latent(h_combined)

        # Remapper depuis l'espace latent
        decoder_init = self.from_latent(z)
        decoder_init = decoder_init.view(2, decoder_init.size(0), -1)  # (2, batch, hidden_size*2)

        # Décoder (en utilisant la longueur d'entrée d'origine)
        decoder_output, _ = self.decoder(
            x,
            (decoder_init, torch.zeros_like(decoder_init))
        )

        # Projection de sortie
        output = self.output_proj(decoder_output)
        return output, z


# Exemple d'utilisation
model = Seq2SeqAutoencoder(
    input_size=128,   # Dimension des caractéristiques d'entrée
    hidden_size=256,  # Dimension cachée du LSTM
    latent_size=64,   # Dimension de l'espace latent
    num_layers=2
)

# Test
x = torch.randn(8, 20, 128)  # (batch, seq_len, input_size)
output, z = model(x)

print(f"Forme de l'entrée : {x.shape}")        # (8, 20, 128)
print(f"Forme de sortie : {output.shape}")  # (8, 20, 128)
print(f"Forme du vecteur latent : {z.shape}")    # (8, 64)

7. Scénarios d'application des autoencodeurs

7.1 Détection d'anomalies

Les autoencodeurs peuvent être utilisés pour détecter des données anormales. Les données normales ont une faible erreur de reconstruction, tandis que les données anormales ont une erreur de reconstruction élevée :

Exemple

import torch
import torch.nn as nn

def detect_anomalies(model, data_loader, threshold=None, device='cpu'):
    """
Détection d'anomalies à l'aide d'autoencodeurs
    """

    model.eval()
    reconstruction_errors = []

    with torch.no_grad():
        for batch in data_loader:
            images = batch[0].to(device)
            outputs = model(images)

            # Calculer l'erreur de reconstruction (erreur quadratique moyenne)
            errors = torch.mean((outputs - images) ** 2, dim=(1, 2, 3))
            reconstruction_errors.extend(errors.cpu().numpy())

    reconstruction_errors = torch.tensor(reconstruction_errors)

    # Si aucun seuil n'est fourni, utiliser une méthode statistique
    if threshold is None:
        # Utiliser le percentile 95%
        threshold = torch.quantile(reconstruction_errors, 0.95).item()

    # Marquer les anomalies
    anomalies = reconstruction_errors > threshold

    return anomalies, reconstruction_errors, threshold


# Entraîner le modèle de détection d'anomalies
def train_anomaly_detector(normal_data_loader):
    """Entraîner l'auto-encodeur en utilisant uniquement des données normales"""
    model = ConvAutoencoder(channels=1, latent_dim=32)
    criterion = nn.MSELoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

    model.train()
    for epoch in range(10):
        for batch in normal_data_loader:
            images = batch[0]
            outputs = model(images)
            loss = criterion(outputs, images)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

    return model

7.2 Colorisation d'images et transfert de style

Exemple

class ColorizationAutoencoder(nn.Module):
    """
Auto-encodeur de colorisation d'images
Entrée : image en niveaux de gris (batch, 1, H, W)
Sortie : image couleur (batch, 2, H, W) (espace colorimétrique ab)
    """

    def __init__(self):
        super().__init__()

        # Encodeur : extraire progressivement les caractéristiques
        self.encoder = nn.Sequential(
            nn.Conv2d(1, 64, kernel_size=4, stride=2, padding=1),   # H/2
            nn.ReLU(),
            nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1),  # H/4
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1), # H/8
            nn.BatchNorm2d(256),
            nn.ReLU(),
        )

        # Décodeur : suréchantillonner pour générer des couleurs
        self.decoder = nn.Sequential(
            nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1),
            nn.ReLU(),
            nn.Conv2d(32, 2, kernel_size=3, padding=1),  # Canaux ab en sortie
            nn.Sigmoid()  # plage des canaux ab [0, 1]
        )

    def forward(self, x):
        z = self.encoder(x)
        color = self.decoder(z)
        return color

7.3 Visualisation de la réduction de dimensionnalité des données

Exemple

import matplotlib.pyplot as plt

def visualize_latent_2d(model, dataloader, device, num_samples=1000):
    """
Utiliser un autoencodeur pour réduire les données à 2 dimensions pour la visualisation
    """

    model.eval()
    all_latents = []
    all_labels = []

    with torch.no_grad():
        count = 0
        for batch in dataloader:
            if count >= num_samples:
                break
            images, labels = batch[0], batch[1]
            images = images.to(device)

            # S’il s’agit d’un AE 2D, utilisez-le directement
            # Si la dimensionnalité est supérieure, projetez d’abord en 2D
            z = model.encode(images)

            all_latents.append(z.cpu())
            all_labels.append(labels)
            count += images.size(0)

    latents = torch.cat(all_latents, dim=0)[:num_samples].numpy()
    labels = torch.cat(all_labels, dim=0)[:num_samples].numpy()

    plt.figure(figsize=(10, 8))
    scatter = plt.scatter(latents[:, 0], latents[:, 1], c=labels,
                          cmap='tab10', alpha=0.6, s=20)
    plt.colorbar(scatter)
    plt.xlabel('Dimension latente 1')
    plt.ylabel('Dimension latente 2')
    plt.title('Visualisation de l’espace latent 2D de l’autoencodeur')
    plt.show()

8. Référence rapide de l’API

8.1 Types courants d’autoencodeurs

Type Caractéristiques Scénarios applicables
Autoencodeur de base Structure symétrique simple Réduction de dimensionnalité, extraction de caractéristiques
Autoencodeur convolutif Utilise des couches convolutives pour préserver la structure spatiale Traitement d'images
Autoencodeur de débruitage Apprend à supprimer le bruit Débruitage d'images, caractéristiques robustes
Autoencodeur variationnel Apprend les distributions de probabilité, peut générer de nouvelles données Modèles génératifs, génération de données
Autoencodeur parcimonieux Contrainte de parcimonie, apprend des caractéristiques interprétables Désenchevêtrement des caractéristiques, interprétabilité
Autoencodeur de séquence Utilise RNN/LSTM pour traiter des séquences Texte, séries temporelles

8.2 Sélection de la fonction de perte

Tâche Fonction de perte recommandée
Reconstruction d'image MSELoss、SSIMLoss
Images binaires BCELoss、BCEWithLogitsLoss
Reconstruction de texte CrossEntropyLoss
VAE MSE + divergence KL
Détection d'anomalies MSE、MAE

8.3 Sélection de la dimension latente

数据维度低(<100维)
    -> 潜在维度设为 2~10

数据维度中等(100~1000维)
    -> 潜在维度设为 10~50

数据维度高(>1000维)
    -> 潜在维度设为 50~200

生成任务(VAE)
    -> 潜在维度 2~32(便于采样和可视化)

异常检测
    -> 潜在维度 16~64(保留足够信息检测异常)
Autres extensions