파이토치를 활용한 GAN 딥러닝, 인코더-디코더 모델

오늘은 Generative Adversarial Networks(GAN)와 인코더-디코더 모델의 개념을 깊이 있게 살펴보도록 하겠습니다. 우리는 파이토치(PyTorch) 프레임워크를 사용하여 이 두 가지 모델을 구현할 것입니다. GAN은 두 개의 신경망을 통해 데이터를 생성하는 딥러닝 기술이며, 인코더-디코더 모델은 데이터의 구조를 변환하는 데 사용됩니다.

1. GAN(Generative Adversarial Networks)

GAN은 이안 굿펠로(Ian Goodfellow)가 2014년에 제안한 생성적 모델로, 주로 생성 관련 작업에 사용됩니다. GAN은 두 개의 주요 구성 요소인 생성자(Generator)와 판별자(Discriminator)로 이루어져 있습니다. 생성자는 가짜 데이터를 생성하고, 판별자는 데이터가 진짜인지 가짜인지를 판별합니다.

1.1 GAN의 작동 원리

GAN의 작동 원리는 다음과 같이 요약할 수 있습니다.

  1. 생성자는 무작위 노이즈 벡터를 입력받아 가짜 데이터를 생성합니다.
  2. 판별자는 실제 데이터와 생성된 데이터를 비교하여 진짜인지 가짜인지 판단합니다.
  3. 생성자는 판별자를 속이기 위해 지속적으로 개선됩니다.
  4. 판별자는 생성자의 개선에 대응하여 판별 능력을 향상입니다.

1.2 GAN의 수학적 정의

GAN의 목표는 다음과 같은 두 가지 신경망을 최적화하는 것입니다:

min_G max_D V(D, G) = E[log(D(x))] + E[log(1 - D(G(z)))].

여기서 D(x)는 실제 데이터에 대한 판별자의 출력, G(z)는 생성자가 만들어낸 가짜 데이터입니다.

2. 파이토치에서의 GAN 구현

2.1 환경 설정

!pip install torch torchvision

2.2 데이터셋 준비

MNIST 데이터셋을 사용하여 손글씨 숫자를 생성할 것입니다.

import torch
from torchvision import datasets, transforms

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)

2.3 GAN 모델 정의

import torch.nn as nn

class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(100, 256),
            nn.ReLU(),
            nn.Linear(256, 512),
            nn.ReLU(),
            nn.Linear(512, 1024),
            nn.ReLU(),
            nn.Linear(1024, 784),
            nn.Tanh()  # MNIST의 픽셀 값은 -1~1 범위
        )

    def forward(self, z):
        return self.model(z)

class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(784, 1024),
            nn.LeakyReLU(0.2),
            nn.Linear(1024, 512),
            nn.LeakyReLU(0.2),
            nn.Linear(512, 256),
            nn.LeakyReLU(0.2),
            nn.Linear(256, 1),
            nn.Sigmoid()
        )

    def forward(self, img):
        return self.model(img)

2.4 훈련 루프 구현

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

generator = Generator().to(device)
discriminator = Discriminator().to(device)

criterion = nn.BCELoss()
optimizer_G = torch.optim.Adam(generator.parameters(), lr=0.0002, betas=(0.5, 0.999))
optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))

for epoch in range(50):
    for i, (imgs, _) in enumerate(train_loader):
        imgs = imgs.view(imgs.size(0), -1).to(device)
        z = torch.randn(imgs.size(0), 100).to(device)

        real_labels = torch.ones(imgs.size(0), 1).to(device)
        fake_labels = torch.zeros(imgs.size(0), 1).to(device)

        # 판별자 훈련
        optimizer_D.zero_grad()
        outputs = discriminator(imgs)
        d_loss_real = criterion(outputs, real_labels)
        d_loss_real.backward()

        fake_imgs = generator(z)
        outputs = discriminator(fake_imgs.detach())
        d_loss_fake = criterion(outputs, fake_labels)
        d_loss_fake.backward()

        optimizer_D.step()

        # 생성자 훈련
        optimizer_G.zero_grad()
        outputs = discriminator(fake_imgs)
        g_loss = criterion(outputs, real_labels)
        g_loss.backward()
        optimizer_G.step()

    print(f'Epoch [{epoch+1}/50], d_loss: {d_loss_real.item() + d_loss_fake.item():.4f}, g_loss: {g_loss.item():.4f}')

3. 인코더-디코더 모델

인코더-디코더 모델은 입력 데이터를 압축하고, 압축된 데이터를 기반으로 다시 생성하는 두 개의 신경망 구조입니다. 주로 자연어 처리(NLP), 이미지 변환 등의 작업에서 사용됩니다.

3.1 인코더-디코더 구조

인코더는 입력 데이터를 잠재 공간(latent space)으로 변환하고, 디코더는 잠재 공간에서 다시 원래의 데이터로 복원합니다. 이 구조는 기계 번역, 이미지 캡셔닝 등에서 매우 유용하게 사용됩니다.

3.2 모델 구현

class Encoder(nn.Module):
        def __init__(self):
            super(Encoder, self).__init__()
            self.model = nn.Sequential(
                nn.Linear(784, 256),
                nn.ReLU(),
                nn.Linear(256, 64)
            )

        def forward(self, x):
            return self.model(x)

    class Decoder(nn.Module):
        def __init__(self):
            super(Decoder, self).__init__()
            self.model = nn.Sequential(
                nn.Linear(64, 256),
                nn.ReLU(),
                nn.Linear(256, 784),
                nn.Sigmoid()
            )

        def forward(self, z):
            return self.model(z)

3.3 훈련 루프

encoder = Encoder().to(device)
decoder = Decoder().to(device)

optimizer = torch.optim.Adam(list(encoder.parameters()) + list(decoder.parameters()), lr=0.001)
criterion = nn.BCELoss()

for epoch in range(50):
    for imgs, _ in train_loader:
        imgs = imgs.view(imgs.size(0), -1).to(device)
        z = encoder(imgs)

        optimizer.zero_grad()
        reconstructed = decoder(z)
        loss = criterion(reconstructed, imgs)
        loss.backward()
        optimizer.step()

    print(f'Epoch [{epoch+1}/50], Loss: {loss.item():.4f}') 

결론

이번 글에서는 GAN 및 인코더-디코더 모델에 대한 상세한 설명과 파이토치로 구현하는 방법에 대해 알아보았습니다. GAN의 구조와 작동 원리를 이해하고, 이를 통해 이미지 생성 작업을 수행할 수 있었습니다. 또한, 인코더-디코더 모델을 통해 입력 데이터를 효율적으로 처리하는 방법도 배웠습니다. 이러한 모델들은 딥러닝의 다양한 분야에서 응용될 수 있으며, 앞으로 발전 가능성이 큰 기술들입니다.

이 강좌를 통해 독자 여러분이 딥러닝의 심화 과정에 대해 더 깊이 이해할 수 있기를 바랍니다.