← Назад к вопросам

Что такое autoencoders и VAE?

3.0 Senior🔥 121 комментариев
#Глубокое обучение#Машинное обучение

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое autoencoders и VAE?

Autoencoders (автокодировщики) и Variational Autoencoders (VAE) — это типы нейронных сетей, которые используются для неконтролируемого обучения. Они особенно полезны для снижения размерности, генерации новых данных и обнаружения аномалий.

Autoencoders (Автокодировщики)

Autoencoder — это архитектура нейронной сети, которая учится кодировать входные данные в скрытое представление (latent representation), а затем декодировать его обратно в исходные данные.

Структура Autoencoder

Входные данные (X) -> Encoder -> Latent code (z) -> Decoder -> Выходные данные (X')

Цель: минимизировать ошибку реконструкции: L = ||X - X'||^2

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt

# Определение Autoencoder
class Autoencoder(nn.Module):
    def __init__(self, input_dim=784, hidden_dim=256, latent_dim=32):
        super(Autoencoder, self).__init__()
        
        # Encoder
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            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()  # Для изображений, значения от 0 до 1
        )
    
    def forward(self, x):
        # Кодирование
        z = self.encoder(x)
        
        # Декодирование
        x_reconstructed = self.decoder(z)
        
        return x_reconstructed, z

# Загрузка данных
train_dataset = MNIST(root='.', train=True, transform=ToTensor(), download=False)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)

# Инициализация модели
model = Autoencoder(input_dim=784, latent_dim=32)
optimizer = optim.Adam(model.parameters(), lr=1e-3)
loss_fn = nn.MSELoss()

# Обучение
model.train()
for epoch in range(10):
    total_loss = 0
    for images, _ in train_loader:
        # Flatten изображения: [batch, 1, 28, 28] -> [batch, 784]
        images = images.view(-1, 784)
        
        # Forward pass
        reconstructed, latent = model(images)
        loss = loss_fn(reconstructed, images)
        
        # Backward pass
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        total_loss += loss.item()
    
    print(f"Epoch {epoch+1}, Loss: {total_loss/len(train_loader):.4f}")

# Использование для визуализации
model.eval()
with torch.no_grad():
    sample_images, _ = next(iter(train_loader))
    sample_images = sample_images.view(-1, 784)
    reconstructed, latent = model(sample_images)
    
    # Визуализация
    fig, axes = plt.subplots(2, 10, figsize=(15, 3))
    for i in range(10):
        # Оригинальные изображения
        axes[0, i].imshow(sample_images[i].view(28, 28).numpy(), cmap='gray')
        axes[0, i].axis('off')
        
        # Реконструированные изображения
        axes[1, i].imshow(reconstructed[i].view(28, 28).numpy(), cmap='gray')
        axes[1, i].axis('off')
    plt.suptitle('Top: Original, Bottom: Reconstructed')
    plt.show()

Применение Autoencoders

  1. Снижение размерности: как альтернатива PCA
  2. Шумоподавление (Denoising): восстановление чистых данных из зашумленных
  3. Обнаружение аномалий: высокая ошибка реконструкции = аномалия
  4. Генерация данных: используя только decoder
# Пример: Denoising Autoencoder
class DenoisingAutoencoder(Autoencoder):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
    
    def add_noise(self, x, noise_level=0.2):
        return x + torch.randn_like(x) * noise_level

# Обучение
model = DenoisingAutoencoder()
for epoch in range(10):
    for images, _ in train_loader:
        images = images.view(-1, 784)
        
        # Добавляем шум к входу
        noisy_images = model.add_noise(images)
        
        # Модель учится восстанавливать чистые изображения
        reconstructed, _ = model(noisy_images)
        loss = loss_fn(reconstructed, images)  # Compare with clean images
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

Variational Autoencoders (VAE)

VAE — это расширение обычного autoencoder с вероятностной интерпретацией. Вместо того, чтобы просто кодировать данные в вектор, VAE кодирует их в распределение (mean и variance).

Основная идея VAE

VAE предполагает, что скрытые переменные следуют стандартному нормальному распределению N(0, I). Это позволяет:

  1. Генерировать новые данные: сэмплировать из N(0, I) и декодировать
  2. Интерполировать между примерами в latent space
class VAE(nn.Module):
    def __init__(self, input_dim=784, hidden_dim=256, latent_dim=32):
        super(VAE, self).__init__()
        
        # Encoder
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU()
        )
        
        # Latent space: mean и logvar (log variance)
        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, 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):
        # Трюк переформатирования: сэмплируем z = mu + eps * sqrt(exp(logvar))
        # Это позволяет распространить градиент через случайную операцию
        std = torch.exp(0.5 * logvar)
        eps = torch.randn_like(std)
        z = mu + eps * std
        return z
    
    def decode(self, z):
        return self.decoder(z)
    
    def forward(self, x):
        mu, logvar = self.encode(x)
        z = self.reparameterize(mu, logvar)
        return self.decode(z), mu, logvar

# Loss function для VAE
def vae_loss(x_reconstructed, x, mu, logvar, beta=1.0):
    # Reconstruction loss
    reconstruction_loss = nn.functional.binary_cross_entropy(
        x_reconstructed, x, reduction='sum'
    )
    
    # KL divergence: KL(N(mu, logvar) || N(0, 1))
    kl_divergence = -0.5 * torch.sum(
        1 + logvar - mu.pow(2) - logvar.exp()
    )
    
    # Total loss
    total_loss = reconstruction_loss + beta * kl_divergence
    
    return total_loss, reconstruction_loss, kl_divergence

# Обучение VAE
model = VAE(latent_dim=32)
optimizer = optim.Adam(model.parameters(), lr=1e-3)

for epoch in range(20):
    total_loss = 0
    for images, _ in train_loader:
        images = images.view(-1, 784)
        
        # Forward pass
        x_reconstructed, mu, logvar = model(images)
        loss, recon_loss, kl_loss = vae_loss(x_reconstructed, images, mu, logvar)
        
        # Backward pass
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        total_loss += loss.item()
    
    print(f"Epoch {epoch+1}, Total Loss: {total_loss/len(train_loader):.4f}")

Генерация новых данных с VAE

# Генерация новых изображений
model.eval()
with torch.no_grad():
    # Сэмплируем из стандартного нормального распределения
    z = torch.randn(16, 32)  # 16 новых примеров
    
    # Декодируем
    generated_images = model.decode(z)
    
    # Визуализация
    fig, axes = plt.subplots(4, 4, figsize=(8, 8))
    for i in range(16):
        axes[i//4, i%4].imshow(generated_images[i].view(28, 28).numpy(), cmap='gray')
        axes[i//4, i%4].axis('off')
    plt.suptitle('Generated Images')
    plt.show()

# Интерполяция между двумя примерами
with torch.no_grad():
    # Кодируем два примера
    x1, x2 = sample_images[0:1], sample_images[1:2]
    mu1, _ = model.encode(x1)
    mu2, _ = model.encode(x2)
    
    # Интерполируем в latent space
    fig, axes = plt.subplots(1, 11, figsize=(15, 2))
    for i, alpha in enumerate(torch.linspace(0, 1, 11)):
        z_interp = (1 - alpha) * mu1 + alpha * mu2
        x_interp = model.decode(z_interp)
        axes[i].imshow(x_interp.view(28, 28).numpy(), cmap='gray')
        axes[i].axis('off')
    plt.suptitle('Interpolation in Latent Space')
    plt.show()

Сравнение Autoencoder и VAE

АспектAutoencoderVAE
Latent representationДетерминированный векторРаспределение (mean, variance)
Loss functionТолько reconstruction lossReconstruction + KL divergence
ГенерацияТребует явного decoderЕстественная генерация
ИнтерпретируемостьМеньшеЛучше (распределение в latent space)
Качество реконструкцииОбычно лучшеНемного хуже (trade-off)
Стабильность обученияПроще обучатьМожет быть сложнее (KL collapse)

Типичные применения

# 1. Обнаружение аномалий (Autoencoder)
reconstruction_error = torch.nn.functional.mse_loss(
    x_reconstructed, x, reduction='none'
).mean(dim=1)

threshold = reconstruction_error.quantile(0.95)
anomaly_mask = reconstruction_error > threshold

# 2. Снижение размерности (Autoencoder)
latent_representation = model.encoder(data)

# 3. Генерация (VAE)
generated_data = model.decode(torch.randn(100, latent_dim))

# 4. Transfer learning (использование encoder)
pre_trained_encoder = model.encoder
for param in pre_trained_encoder.parameters():
    param.requires_grad = False  # Freeze encoder

# Добавляем классификатор на top
classifier = nn.Sequential(
    pre_trained_encoder,
    nn.Linear(32, 10)
)

Продвинутые техники

  1. Beta-VAE: взвешивание KL-дивергенции для контроля over compression
  2. Adversarial Autoencoder: использование adversarial loss для лучшей генерации
  3. Hierarchical VAE: иерархические latent variables

Вывод: Autoencoders — простой инструмент для снижения размерности и аномалий, VAE — более мощный метод для генеративного моделирования. Выбор зависит от задачи: для реконструкции и аномалий — autoencoder, для генерации и интерполяции — VAE.