Что такое autoencoders и VAE?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое 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
- Снижение размерности: как альтернатива PCA
- Шумоподавление (Denoising): восстановление чистых данных из зашумленных
- Обнаружение аномалий: высокая ошибка реконструкции = аномалия
- Генерация данных: используя только 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). Это позволяет:
- Генерировать новые данные: сэмплировать из N(0, I) и декодировать
- Интерполировать между примерами в 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
| Аспект | Autoencoder | VAE |
|---|---|---|
| Latent representation | Детерминированный вектор | Распределение (mean, variance) |
| Loss function | Только reconstruction loss | Reconstruction + 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)
)
Продвинутые техники
- Beta-VAE: взвешивание KL-дивергенции для контроля over compression
- Adversarial Autoencoder: использование adversarial loss для лучшей генерации
- Hierarchical VAE: иерархические latent variables
Вывод: Autoencoders — простой инструмент для снижения размерности и аномалий, VAE — более мощный метод для генеративного моделирования. Выбор зависит от задачи: для реконструкции и аномалий — autoencoder, для генерации и интерполяции — VAE.