Skip to main content

오토인코더 (Autoencoder)

학습 목표

  • 오토인코더의 인코더-디코더 구조와 병목(Bottleneck) 개념을 이해한다
  • 재구성 손실(Reconstruction Loss)의 역할을 설명할 수 있다
  • Denoising Autoencoder의 원리와 장점을 안다
  • 오토인코더와 PCA의 차이를 비교할 수 있다

왜 중요한가

오토인코더(Autoencoder, AE)는 입력을 저차원 잠재 공간(Latent Space)으로 압축한 뒤 다시 복원하는 구조입니다. 이 과정에서 학습된 잠재 표현은 차원 축소, 이상 탐지, 사전학습에 활용됩니다. VAE, Diffusion 등 현대 생성 모델의 기초가 됩니다.

구조

LAE=xx^2=xD(E(x))2\mathcal{L}_{\text{AE}} = \| x - \hat{x} \|^2 = \| x - D(E(x)) \|^2
  • EE: 인코더, DD: 디코더
  • z=E(x)z = E(x): 잠재 표현
  • x^=D(z)\hat{x} = D(z): 재구성

구현

import torch
import torch.nn as nn

class Autoencoder(nn.Module):
    """기본 오토인코더"""
    def __init__(self, input_dim=784, latent_dim=32):
        super().__init__()

        # 인코더: 입력 → 잠재 공간
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, 256),
            nn.ReLU(),
            nn.Linear(256, 128),
            nn.ReLU(),
            nn.Linear(128, latent_dim),
        )

        # 디코더: 잠재 공간 → 재구성
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, 128),
            nn.ReLU(),
            nn.Linear(128, 256),
            nn.ReLU(),
            nn.Linear(256, input_dim),
            nn.Sigmoid(),  # 픽셀 값 [0, 1] 범위
        )

    def forward(self, x):
        z = self.encoder(x)       # 인코딩
        x_hat = self.decoder(z)   # 디코딩
        return x_hat

    def encode(self, x):
        """잠재 표현 추출 (다운스트림 태스크용)"""
        return self.encoder(x)

합성곱 오토인코더

이미지 데이터에는 합성곱 구조가 더 효과적입니다.
class ConvAutoencoder(nn.Module):
    """합성곱 오토인코더 (MNIST용)"""
    def __init__(self, latent_dim=16):
        super().__init__()

        # 인코더: (1, 28, 28) → (latent_dim,)
        self.encoder = nn.Sequential(
            nn.Conv2d(1, 16, 3, stride=2, padding=1),  # (16, 14, 14)
            nn.ReLU(),
            nn.Conv2d(16, 32, 3, stride=2, padding=1), # (32, 7, 7)
            nn.ReLU(),
            nn.Flatten(),
            nn.Linear(32 * 7 * 7, latent_dim),
        )

        # 디코더: (latent_dim,) → (1, 28, 28)
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, 32 * 7 * 7),
            nn.Unflatten(1, (32, 7, 7)),
            nn.ConvTranspose2d(32, 16, 3, stride=2, padding=1, output_padding=1),  # (16, 14, 14)
            nn.ReLU(),
            nn.ConvTranspose2d(16, 1, 3, stride=2, padding=1, output_padding=1),   # (1, 28, 28)
            nn.Sigmoid(),
        )

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

학습

import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

# 데이터 준비
transform = transforms.Compose([
    transforms.ToTensor(),
])
train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
train_loader = DataLoader(train_dataset, batch_size=128, shuffle=True)

# 모델, 옵티마이저
model = ConvAutoencoder(latent_dim=16).to('cuda')
optimizer = optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.MSELoss()  # 재구성 손실

# 학습 루프
for epoch in range(20):
    total_loss = 0
    for images, _ in train_loader:  # 레이블 사용 안 함
        images = images.to('cuda')

        # 순전파
        reconstructed = model(images)
        loss = criterion(reconstructed, images)

        # 역전파
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        total_loss += loss.item()

    avg_loss = total_loss / len(train_loader)
    print(f"Epoch {epoch+1}: 재구성 손실 = {avg_loss:.4f}")

Denoising Autoencoder (DAE)

입력에 노이즈를 추가한 뒤, 원본을 복원하도록 학습합니다. 노이즈에 강건한 표현을 학습하며, 더 의미 있는 잠재 표현을 추출합니다.
def add_noise(images, noise_factor=0.3):
    """가우시안 노이즈 추가"""
    noisy = images + noise_factor * torch.randn_like(images)
    return torch.clamp(noisy, 0., 1.)  # [0, 1] 범위 유지

# Denoising AE 학습 루프
for epoch in range(20):
    for images, _ in train_loader:
        images = images.to('cuda')

        # 노이즈 추가
        noisy_images = add_noise(images, noise_factor=0.3)

        # 노이즈 입력 → 원본 복원
        reconstructed = model(noisy_images)
        loss = criterion(reconstructed, images)  # 원본과 비교

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

비교

방법비선형학습잠재 공간 구조
PCA선형 변환고유값 분해직교 축
AE비선형역전파비구조적
DAE비선형역전파 (노이즈)강건한 표현
VAE비선형역전파 + KL연속적, 구조적 (가우시안)
기본 오토인코더의 잠재 공간은 구조가 없습니다. 잠재 벡터를 임의로 샘플링하면 의미 없는 출력이 나옵니다. 이 한계를 극복한 것이 VAE(Variational Autoencoder)로, 잠재 공간에 확률 분포 구조를 부여하여 의미 있는 생성이 가능합니다.

체크리스트

  • 인코더-디코더 구조와 병목의 역할을 설명할 수 있다
  • 재구성 손실의 의미를 이해한다
  • Denoising AE가 강건한 표현을 학습하는 원리를 안다
  • 오토인코더와 PCA의 차이를 비교할 수 있다

다음 문서