딥러닝 파이토치 강좌, DCGAN

본 강좌에서는 딥러닝의 한 분야인 생성적 적대 신경망(Generative Adversarial Networks, GAN)의 일종인 DCGAN(Deep Convolutional GAN)에 대해 자세히 알아보겠습니다. DCGAN은 이미지 생성 및 변환 작업에 특화된 모델로, 특히 고해상도 이미지 생성에서 탁월한 성능을 보입니다.

1. GAN의 이해

GAN은 두 개의 신경망, 즉 생성자(Generator)와 판별자(Discriminator)로 구성됩니다. 생성자는 실제 데이터와 유사한 가짜 데이터를 생성하며, 판별자는 생성된 데이터가 실제인지 가짜인지를 구분합니다. 이 두 네트워크는 서로 경쟁하며 학습하게 되며, 이 과정에서 생성자는 점점 더 사실적인 데이터를 생성하게 됩니다.

1.1 GAN의 기본 개념

GAN의 학습 과정은 다음과 같이 이루어집니다:

  • 1. 생성자 G는 무작위 노이즈 벡터 z를 입력받아 가짜 이미지 G(z)를 생성합니다.
  • 2. 판별자 D는 실제 이미지 x와 생성된 이미지 G(z)를 입력 받아 각각의 진짜/가짜 확률을 출력합니다.
  • 3. 생성자는 D가 가짜 이미지를 진짜로 잘못 판단하도록 학습하며, 판별자는 진짜 이미지를 정확히 구별하도록 학습합니다.

2. DCGAN의 개념

DCGAN은 GAN을 심층 컨볼루션 네트워크(Deep Convolutional Network)로 확장한 것입니다. DCGAN은 이미지 생성 작업에서 더 나은 성능을 제공하기 위해 컨볼루션 레이어를 사용하여 공간적 계층 구조를 학습합니다. DCGAN은 다음과 같은 구조적 특징을 가지고 있습니다:

  • 전통적인 풀링 레이어 대신 스트라이드(stride)를 사용하여 다운샘플링합니다.
  • 배치 정규화(Batch Normalization)를 적용하여 학습을 안정화합니다.
  • ReLU 활성화 함수를 사용하며, 생성자의 출력층에서는 Tanh 활성화 함수를 사용합니다.

2.1 DCGAN의 구조

DCGAN의 구조는 다음과 같은 방식으로 이루어집니다:

  • 생성자 G:
    • 입력: 랜덤 노이즈 벡터 z
    • 레이어: 여러 개의 전치 컨볼루션 레이어와 배치 정규화 및 ReLU 활성화 함수
    • 출력: 생성된 이미지
  • 판별자 D:
    • 입력: 이미지 (실제 또는 생성된)
    • 레이어: 여러 개의 컨볼루션 레이어와 배치 정규화 및 Leaky ReLU 활성화 함수
    • 출력: 실제/가짜 확률

3. DCGAN의 파이썬 구현

이제 DCGAN을 파이썬으로 구현해보겠습니다. 파이토치를 사용하여 지원되는 다양한 GPU를 활용하여 고속으로 모델을 훈련시킬 수 있습니다. 다음 코드는 DCGAN의 기본 구조를 수립하는 예제입니다.

3.1 필요한 라이브러리 설치

!pip install torch torchvision

3.2 데이터셋 로딩

이번 예제에서는 MNIST 데이터셋을 사용하여 손글씨 숫자를 생성할 것입니다. 데이터를 로딩하고 전처리 과정을 진행하겠습니다.


import torch
import torchvision
import torchvision.transforms as transforms

# 데이터셋 변환 정의: 이미지를 0-1로 정규화하고, 텐서로 변환
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

# MNIST 데이터셋 로드
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
    

3.3 생성자와 판별자 정의

이제 생성자와 판별자 모델을 구현하겠습니다. 앞서 설명한 것처럼, 생성자는 전치 컨볼루션 레이어를 사용하여 이미지를 생성하고, 판별자는 컨볼루션 레이어를 사용하여 이미지를 판별합니다.


import torch.nn as nn

class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()
        self.model = nn.Sequential(
            nn.ConvTranspose2d(100, 256, 4, 1, 0, bias=False),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            nn.ConvTranspose2d(256, 128, 4, 2, 1, bias=False),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            nn.ConvTranspose2d(128, 1, 4, 2, 1, bias=False),
            nn.Tanh()
        )

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

class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.Conv2d(1, 128, 4, 2, 1, bias=False),
            nn.LeakyReLU(0.2),
            nn.Conv2d(128, 256, 4, 2, 1, bias=False),
            nn.BatchNorm2d(256),
            nn.LeakyReLU(0.2),
            nn.Conv2d(256, 1, 4, 1, 0, bias=False),
            nn.Sigmoid()
        )

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

3.4 모델 초기화

생성자와 판별자 모델을 인스턴스화하고, 손실 함수 및 최적화 알고리즘을 정의합니다. 여기서는 바이너리 크로스 엔트로피 손실 함수와 Adam 최적화기를 사용할 것입니다.


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

# 모델 인스턴스화
generator = Generator().to(device)
discriminator = Discriminator().to(device)

# 손실 함수 및 최적화기 정의
criterion = nn.BCELoss()
optimizerG = torch.optim.Adam(generator.parameters(), lr=0.0002, betas=(0.5, 0.999))
optimizerD = torch.optim.Adam(discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))
    

3.5 학습 루프

DCGAN의 학습을 진행하겠습니다. 각 이터레이션마다 생성자와 판별자의 손실을 기록하고, 몇 가지 샘플 이미지를 출력하여 모델이 제대로 학습되고 있는지 확인할 것입니다.


num_epochs = 50
for epoch in range(num_epochs):
    for i, (images, _) in enumerate(train_loader):
        # 훈련 데이터 준비
        images = images.to(device)

        # 레이블 정의
        batch_size = images.size(0)
        labels = torch.full((batch_size,), 1, device=device)  # 실제 이미지에 대한 레이블
        noise = torch.randn(batch_size, 100, 1, 1, device=device)  # 생성자 입력 노이즈

        # ------------------- 판별자 학습 -------------------
        optimizerD.zero_grad()

        # 실제 이미지의 손실
        output = discriminator(images).view(-1)
        lossD_real = criterion(output, labels)
        lossD_real.backward()

        # 가짜 이미지 생성 및 손실 계산
        fake_images = generator(noise)
        labels.fill_(0)  # 가짜 이미지에 대한 레이블
        output = discriminator(fake_images.detach()).view(-1)
        lossD_fake = criterion(output, labels)
        lossD_fake.backward()

        # 판별자 최적화
        optimizerD.step()

        # ------------------- 생성자 학습 -------------------
        optimizerG.zero_grad()
        labels.fill_(1)  # 생성자는 가짜 이미지를 실제로 분류하고 싶음
        output = discriminator(fake_images).view(-1)
        lossG = criterion(output, labels)
        lossG.backward()
        optimizerG.step()

    # 결과 출력
    print(f'Epoch [{epoch+1}/{num_epochs}], Loss D: {lossD_real.item() + lossD_fake.item()}, Loss G: {lossG.item()}')
    

3.6 결과 시각화

학습이 진행된 후 생성된 이미지를 시각화하여 결과를 확인할 수 있습니다. 예를 들어, matplotlib를 사용하여 몇 가지 샘플 이미지를 출력할 수 있습니다.


import matplotlib.pyplot as plt

def show_generated_images(num_images=25):
    noise = torch.randn(num_images, 100, 1, 1, device=device)
    with torch.no_grad():
        generated_images = generator(noise).cpu().detach().numpy()
    generated_images = (generated_images + 1) / 2  # [0, 1] 범위로 변환

    plt.figure(figsize=(10, 10))
    for i in range(num_images):
        plt.subplot(5, 5, i + 1)
        plt.imshow(generated_images[i][0], cmap='gray')
        plt.axis('off')
    plt.show()

show_generated_images()
    

4. 결론

본 강좌에서는 DCGAN의 이론과 구현 과정을 살펴보았습니다. GAN은 생성적 모델링에 있어 많은 가능성을 가지고 있으며, DCGAN은 이미지 생성의 영역에서 특히 강력한 성능을 발휘합니다. 실제 사례를 적용해보며 모델의 훈련과정을 직접 경험해보시기 바랍니다.

DCGAN을 활용하여 여러 가지 다양한 이미지 생성 작업에 도전해보세요!