PyTorch-Autoencoder

Ein Autoencoder (Autoencoder, AE)ist ein neuronales Netz für unüberwachtes Lernen, das Eingabedaten durch Lernen in einen niedrigdimensionalen latenten Raum komprimiert und anschließend die Originaldaten aus der komprimierten Repräsentation rekonstruiert.

Autoencoder werden häufig in Szenarien wie Dimensionsreduktion von Daten, Merkmalsextraktion, Anomalieerkennung, Bildentrauschung und generativen Modellen eingesetzt.


1. Grundprinzipien des Autoencoders

Die Grundstruktur eines Autoencoders besteht aus drei Teilen:

  • Encoder: bildet Eingabedaten \(x\) auf eine niedrigdimensionale latente Repräsentation \(z\) ab
  • Latenter Raum: der niedrigdimensionale Vektor, der vom Encoder ausgegeben wird, auch Bottleneck-Schicht genannt
  • Decoder: rekonstruiert die latente Repräsentation \(z\) zur Ausgabe \(\hat{x}\)

1.1 Netzwerkstruktur

Das Ziel eines Autoencoders ist es, die Ausgabe \(\hat{x}\) so nah wie möglich an die Eingabe \(x\) zu bringen:

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

wobei \(\theta\) die Encoder-Parameter und \(\phi\) die Decoder-Parameter sind.

1.2 Effekt der Dimensionsreduktion

Autoencoder lernen eine komprimierte Repräsentation der Daten, indem sie die Daten durch eine Bottleneck-Schicht leiten, die kleiner als die Eingabedimension ist. Diese Kompression bewahrt die wesentlichen Informationen der Daten.

Im Vergleich zur Hauptkomponentenanalyse (PCA) können Autoencoder nichtlineare Dimensionsreduktion lernen und komplexere Datenstrukturen erfassen.


2. Grundlegende Autoencoder-Implementierung

2.1 Einfacher Autoencoder

Beispiel

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

# ── Autoencoder-Modell ─────────────────────────────────
class Autoencoder(nn.Module):
    """
Einfacher Autoencoder: symmetrische Struktur
    """

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

        # Encoder
        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),  # Bottleneck-Schicht
        )

        # Decoder
        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):
        """Kodierung: latente Repräsentation erhalten"""
        return self.encoder(x)

    def decode(self, z):
        """Dekodierung: aus latenter Repräsentation rekonstruieren"""
        return self.decoder(z)


# ── Anwendungsbeispiel ──────────────────────────────────────
INPUT_DIM = 784   # Zum Beispiel nach dem Abflachen eines MNIST-Bildes
HIDDEN_DIM = 256
LATENT_DIM = 32   # Dimension des latenten Raums, viel kleiner als die Eingabedimension

model = Autoencoder(INPUT_DIM, HIDDEN_DIM, LATENT_DIM)
print(f"Eingabedimension: {INPUT_DIM}")
print(f"Latente Dimension: {LATENT_DIM}")
print(f"Kompressionsrate: {INPUT_DIM / LATENT_DIM:.1f}x")

# Parameteranzahl anzeigen
total_params = sum(p.numel() for p in model.parameters())
print(f"Gesamtparameter: {total_params:,}")

2.2 Faltungs-Autoencoder

Für Bilddaten sind Autoencoder mit Faltungsschichten besser geeignet:

Beispiel

import torch
import torch.nn as nn

class ConvAutoencoder(nn.Module):
    """
Faltungs-Autoencoder: für Bilder geeignet
    """

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

        # Encoder: Größe schrittweise verringern, Anzahl der Kanäle erhöhen
        # Eingabe: (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(),
        )

        # Abbildung in den latenten Raum
        self.to_latent = nn.AdaptiveAvgPool2d((1, 1))

        # Decoder: Größe schrittweise erhöhen
        # Eingabe: (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()  # Ausgabe [0, 1]
        )

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

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

    def decode(self, z):
        """Dekodieren"""
        # Vektor zu Feature Map umformen
        batch_size = z.size(0)
        z = z.view(batch_size, 256, 1, 1)
        z = z.expand(-1, -1, 2, 2)  # Auf 2x2 hochskalieren
        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"Form der Eingabe: {x.shape}")
print(f"Ausgabeform: {x_recon.shape}")
print(f"Form des latenten Vektors: {model.encode(x).shape}")

2.3 Training und Rekonstruktion

Beispiel

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

# ── Trainingskonfiguration ─────────────────────────────────────
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)

# ── Trainingsschleife ─────────────────────────────────────
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)

            # Vorwärtspropagierung
            outputs = model(images)
            loss = criterion(outputs, images)

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

            total_loss += loss.item()

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

    return model


# Angenommen, ein Datenlader existiert bereits
# train_autoencoder(model, train_loader, criterion, optimizer, num_epochs=10)
print("Autoencoder-Training abgeschlossen!")

3. Denoising-Autoencoder (DAE)

Denoising-Autoencoder (DAE)fügt während des Trainings dem Eingang Rauschen hinzu, lernt dann, das Rauschen zu entfernen und die ursprüngliche Eingabe wiederherzustellen. Dadurch kann das Modell robustere Merkmalsrepräsentationen erlernen.

3.1 Implementierung des Denoising-Autoencoders

Beispiel

import torch
import torch.nn as nn

class DenoisingAutoencoder(nn.Module):
    """
Denoising-Autoencoder
    """

    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()  # Ausgabe [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):
    """
Gaußsches Rauschen hinzufügen
    """

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


# Denoising-Autoencoder trainieren
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]

            # Rauschen hinzufügen
            noisy_images = add_noise(images, noise_factor)
            noisy_images = noisy_images.to(next(model.parameters()).device)
            images = images.to(next(model.parameters()).device)

            # Vorwärtspropagierung
            outputs = model(noisy_images)
            loss = criterion(outputs, images)  # Mit dem Originalbild vergleichen, nicht mit dem verrauschten Bild

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

    return model

3.2 Andere Rauscharten

Beispiel

def salt_pepper_noise(x, prob=0.1):
    """Salz-und-Pfeffer-Rauschen"""
    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):
    """Maskierungsrauschen (zufällig auf null gesetzt)"""
    mask = torch.rand_like(x) > prob
    return x * mask.float()


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

4. Variational Autoencoder (VAE)

Variational Autoencoder (VAE)ist ein generatives Modell, das Daten in eine Wahrscheinlichkeitsverteilung im latenten Raum statt in einen festen Vektor kodiert. Dadurch können wir Stichproben aus dem latenten Raum ziehen, um neue Daten zu erzeugen.

4.1 Kernprinzipien von VAE

Die zentrale Innovation von VAE ist das Erlernen der Wahrscheinlichkeitsverteilung latenter Variablen:

  • Der Encoder gibt den Mittelwert \(\mu\) und die Standardabweichung \(\sigma\) aus
  • Ziehe den latenten Vektor \(z\) als Stichprobe aus der Normalverteilung \(\mathcal{N}(\mu, \sigma)\)
  • Der Decoder rekonstruiert Daten aus \(z\)

Um einen differenzierbaren Sampling-Prozess zu erreichen, wird derReparametrisierungstrick verwendet.:

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

4.2 VAE-Implementierung

Beispiel

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

class VAE(nn.Module):
    """
Variational Autoencoder
    """

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

        # Encoder: Mittelwert und Varianz ausgeben
        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)

        # Decoder
        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):
        """Reparametrisierungstrick"""
        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):
    """
VAE-Verlustfunktion
Rekonstruktionsverlust + KL-Divergenz
    """

    # Rekonstruktionsverlust
    recon_loss = nn.functional.mse_loss(x_recon, x, reduction='sum')

    # KL-Divergenz: -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


# Verwendungsbeispiel
INPUT_DIM = 784
HIDDEN_DIM = 256
LATENT_DIM = 2  # Zweidimensionaler latenter Raum zur einfachen Visualisierung

model = VAE(INPUT_DIM, HIDDEN_DIM, LATENT_DIM)

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

print(f"Form der Eingabe: {x.shape}")
print(f"Form der Rekonstruktion: {x_recon.shape}")
print(f"Form des Mittelwerts: {mu.shape}")       # (32, 2)
print(f"Form der Varianz: {logvar.shape}")   # (32, 2)

4.3 VAE-Generierung und Visualisierung

Beispiel

import matplotlib.pyplot as plt

def visualize_latent_space(model, dataloader, device):
    """Visualisierung des latenten Raums"""
    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('Latente Dimension 1')
    plt.ylabel('Latente Dimension 2')
    plt.title('VAE Latentraum')
    plt.show()


def generate_from_latent(model, z, device):
    """Bilder aus latenten Vektoren generieren"""
    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):
    """Generierung durch Interpolation im latenten Raum"""
    model.eval()

    # Lineare Interpolation
    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)


# Neue Bilder generieren
def generate_new_images(model, num_images, latent_dim, device):
    """Neue Bilder aus zufälligen latenten Vektoren generieren"""
    model.eval()
    with torch.no_grad():
        # Stichprobe aus der Standardnormalverteilung ziehen
        z = torch.randn(num_images, latent_dim).to(device)
        generated = model.decode(z)
    return generated

Der latente Raum von VAE ist kontinuierlich, was Interpolation im latenten Raum ermöglicht, um sanft übergehende Bilder zu erzeugen. Allerdings sind von VAE erzeugte Bilder oft unscharf, weil es eine untere Schranke statt der exakten Log-Likelihood optimiert.


5. Sparse Autoencoder

Sparse AutoencoderFüge der Verlustfunktion eine Sparsamkeitsbeschränkung hinzu, die die Anzahl der Aktivierungen im latenten Vektor begrenzt. Dadurch kann das Modell aussagekräftigere Merkmale lernen.

5.1 Implementierung des Sparse Autoencoders

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

class SparseAutoencoder(nn.Module):
    """
Sparse-Autoencoder
    """

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

        # Encoder
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
        )
        # Latente Schicht
        self.bottleneck = nn.Linear(hidden_dim, latent_dim)

        # Decoder
        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)  # Sparse-Aktivierung
        x_recon = self.decoder(z_activated)
        return x_recon, z_activated


def sparse_loss(z, rho=0.05, beta=1.0):
    """
Sparse-Verlust: KL-Divergenz
rho: Ziel-Sparsamkeit (z. B. 0.05 bedeutet, dass nur 5% der Neuronen aktiv sein sollten)
beta: Gewicht des Sparsamkeitsterms
    """

    # Durchschnittliche Aktivierung berechnen
    rho_hat = torch.mean(z, dim=0)

    # KL-Divergenz
    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):
    """Gesamtverlust = Rekonstruktionsverlust + Sparsitätsverlust"""
    recon_loss = F.mse_loss(x_recon, x)
    sparsity = sparse_loss(z, rho, beta)
    return recon_loss + sparsity

6. Sequenz-zu-Sequenz-Autoencoder

Verwenden Sie für Sequenzdaten (wie Text, Zeitreihen) RNN/LSTM als Encoder und Decoder.

6.1 Implementierung eines Sequenz-Autoencoders

Beispiel

import torch
import torch.nn as nn

class Seq2SeqAutoencoder(nn.Module):
    """
Sequenz-zu-Sequenz-Autoencoder: wird für Sequenzdaten verwendet
    """

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

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

        # Abbildung in den latenten Raum
        # Die Ausgabe des bidirektionalen LSTM ist hidden_size * 2
        self.to_latent = nn.Linear(hidden_size * 2, latent_size)
        self.from_latent = nn.Linear(latent_size, hidden_size * 2)

        # Decoder-LSTM
        self.decoder = nn.LSTM(
            input_size=hidden_size,
            hidden_size=hidden_size,
            num_layers=num_layers,
            batch_first=True,
            bidirectional=True
        )

        # Ausgabezuordnung
        self.output_proj = nn.Linear(hidden_size * 2, input_size)

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

        # Bidirektionale finale verborgene Zustände verketten
        # 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)

        # In den latenten Raum abbilden
        z = self.to_latent(h_combined)

        # Vom latenten Raum zurück abbilden
        decoder_init = self.from_latent(z)
        decoder_init = decoder_init.view(2, decoder_init.size(0), -1)  # (2, batch, hidden_size*2)

        # Dekodieren (unter Verwendung der ursprünglichen Eingabelänge)
        decoder_output, _ = self.decoder(
            x,
            (decoder_init, torch.zeros_like(decoder_init))
        )

        # Ausgabezuordnung
        output = self.output_proj(decoder_output)
        return output, z


# Verwendungsbeispiel
model = Seq2SeqAutoencoder(
    input_size=128,   # Dimension der Eingabemerkmale
    hidden_size=256,  # Verborgene Dimension des LSTM
    latent_size=64,   # Dimension des latenten Raums
    num_layers=2
)

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

print(f"Form der Eingabe: {x.shape}")        # (8, 20, 128)
print(f"Ausgabeform: {output.shape}")  # (8, 20, 128)
print(f"Form des latenten Vektors: {z.shape}")    # (8, 64)

7. Anwendungsszenarien von Autoencodern

7.1 Anomalieerkennung

Autoencoder können verwendet werden, um anomale Daten zu erkennen. Normale Daten haben einen kleinen Rekonstruktionsfehler, während anomale Daten einen großen Rekonstruktionsfehler haben:

Beispiel

import torch
import torch.nn as nn

def detect_anomalies(model, data_loader, threshold=None, device='cpu'):
    """
Erkennung von Anomalien mit Autoencodern
    """

    model.eval()
    reconstruction_errors = []

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

            # Rekonstruktionsfehler berechnen (mittlerer quadratischer Fehler)
            errors = torch.mean((outputs - images) ** 2, dim=(1, 2, 3))
            reconstruction_errors.extend(errors.cpu().numpy())

    reconstruction_errors = torch.tensor(reconstruction_errors)

    # Wenn kein Schwellenwert angegeben ist, statistische Methode verwenden
    if threshold is None:
        # 95%-Perzentil verwenden
        threshold = torch.quantile(reconstruction_errors, 0.95).item()

    # Anomalien markieren
    anomalies = reconstruction_errors > threshold

    return anomalies, reconstruction_errors, threshold


# Anomalieerkennungsmodell trainieren
def train_anomaly_detector(normal_data_loader):
    """Trainiere den Autoencoder nur mit normalen Daten"""
    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 Bildkolorierung und Stilübertragung

Beispiel

class ColorizationAutoencoder(nn.Module):
    """
Bildkolorierungs-Autoencoder
Eingabe: Graustufenbild (batch, 1, H, W)
Ausgabe: Farbbild (batch, 2, H, W) (ab-Farbraum)
    """

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

        # Encoder: schrittweise Merkmale extrahieren
        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(),
        )

        # Decoder: Upsampling zur Erzeugung von Farben
        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),  # Ausgabe der ab-Kanäle
            nn.Sigmoid()  # Wertebereich der ab-Kanäle [0, 1]
        )

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

7.3 Visualisierung der Dimensionalitätsreduktion von Daten

Beispiel

import matplotlib.pyplot as plt

def visualize_latent_2d(model, dataloader, device, num_samples=1000):
    """
Autoencoder verwenden, um Daten zur Visualisierung auf 2 Dimensionen zu reduzieren
    """

    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)

            # Wenn es ein 2D-AE ist, direkt verwenden
            # Wenn die Dimensionalität höher ist, zuerst auf 2D projizieren
            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('Latente Dimension 1')
    plt.ylabel('Latente Dimension 2')
    plt.title('Visualisierung des 2D-Latent-Raums des Autoencoders')
    plt.show()

8. API-Kurzreferenz

8.1 Gängige Autoencoder-Typen

Type Eigenschaften Geeignete Szenarien
Einfacher Autoencoder Einfache symmetrische Struktur Dimensionsreduktion, Merkmalsextraktion
Faltungs-Autoencoder Verwendet Faltungsschichten, um die räumliche Struktur zu erhalten Bildverarbeitung
Entrauschungs-Autoencoder Lernt, Rauschen zu entfernen Bildentrauschung, robuste Merkmale
Variational Autoencoder Erlernt Wahrscheinlichkeitsverteilungen, kann neue Daten generieren Generative Modelle, Datengenerierung
Sparse-Autoencoder Sparsamkeitsbedingung, erlernt interpretierbare Merkmale Merkmalsentflechtung, Interpretierbarkeit
Sequenz-Autoencoder Verwendet RNN/LSTM zur Verarbeitung von Sequenzen Text, Zeitreihen

8.2 Auswahl der Verlustfunktion

Aufgabe Empfohlene Verlustfunktion
Bildrekonstruktion MSELoss、SSIMLoss
Binäre Bilder BCELoss、BCEWithLogitsLoss
Textrekonstruktion CrossEntropyLoss
VAE MSE + KL-Divergenz
Anomalieerkennung MSE, MAE

8.3 Auswahl der latenten Dimension

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

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

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

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

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