1. 서론
딥러닝은 컴퓨터 비전, 자연어 처리, 음성 인식 등 다양한 분야에서 혁신적인 발전을 이루고 있습니다. 그중에서도 생성적 적대 신경망(Generative Adversarial Network, GAN)은 특별한 주목을 받고 있는 기술입니다. GAN은 두 개의 신경망, 즉 생성자(Generator)와 판별자(Discriminator)가 서로 경쟁하는 구조를 가지고 있으며, 이를 통해 사실적인 데이터를 생성하는 능력을 갖추게 됩니다.
이번 글에서는 GAN의 변형 중 하나인 조건부 생성적 적대 신경망(Conditional GAN, cGAN)에 대해 자세히 알아보겠습니다. cGAN은 생성 과정에 조건을 주어 특정 클래스의 이미지를 생성할 수 있도록 합니다. 예를 들어, 숫자 이미지 데이터셋인 MNIST를 활용해 특정 숫자의 이미지를 생성하는 방법에 대해 살펴보겠습니다.
2. cGAN의 개요
2.1 GAN 기본 구조
GAN은 기본적으로 두 개의 신경망으로 구성됩니다. 생성자는 무작위 노이즈 벡터를 입력받아 가짜 이미지를 생성하고, 판별자는 입력된 이미지가 진짜인지 가짜인지 판별하는 역할을 합니다. 이 둘은 다음과 같이 상호작용합니다:
- 생성자는 무작위 노이즈를 입력받아 이미지를 생성
- 생성된 이미지는 판별자에게 전송되어 진짜 이미지와 비교
- 판별자는 진짜 이미지를 ‘1’, 가짜 이미지를 ‘0’으로 판단
- 이 과정이 반복되면서 생성자는 점점 더 사실적인 이미지를 생성
2.2 cGAN의 구조
cGAN은 GAN의 개념을 확장하여, 생성자와 판별자 모두에 조건 정보를 추가합니다. 이는 특정 클래스에 대한 이미지를 생성할 수 있도록 합니다. 예를 들어, 숫자 이미지 생성에서 숫자 ‘3’을 조건으로 설정하면, 생성자는 ‘3’에 해당하는 이미지를 생성하게 됩니다. cGAN의 구조는 다음과 같습니다:
- 생성자는 조건 정보를 입력으로 받아들이고, 이를 통해 이미지를 생성
- 판별자는 입력된 이미지와 조건 정보를 함께 받아들이고 진짜인지 가짜인지 판별
3. cGAN 구현을 위한 파이토치 기본 설정
3.1 필수 라이브러리 설치
cGAN을 구현하기 위해 필요한 파이썬 라이브러리를 설치합니다. 기본적으로 PyTorch와 NumPy, Matplotlib 라이브러리를 사용할 것입니다. 아래 명령어로 설치할 수 있습니다.
pip install torch torchvision numpy matplotlib
3.2 데이터셋 준비
cGAN을 구현하기 위해 MNIST 데이터셋을 사용할 것입니다. MNIST는 0부터 9까지의 손글씨 숫자 이미지로 구성된 데이터셋입니다. PyTorch의 torchvision에서 해당 데이터셋을 불러올 수 있습니다.
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, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
4. cGAN 아키텍처 구현
4.1 생성자(Generator)
생성자는 랜덤 노이즈와 조건 정보를 입력으로 받아 이미지를 생성합니다. 생성자 모델은 주로 여러 개의 선형 계층과 ReLU 활성화 함수를 사용하여 구축됩니다.
import torch.nn as nn
class Generator(nn.Module):
def __init__(self, z_dim, num_classes):
super(Generator, self).__init__()
self.label_embedding = nn.Embedding(num_classes, num_classes)
self.model = nn.Sequential(
nn.Linear(z_dim + num_classes, 128),
nn.ReLU(),
nn.Linear(128, 256),
nn.ReLU(),
nn.Linear(256, 512),
nn.ReLU(),
nn.Linear(512, 1 * 28 * 28),
nn.Tanh()
)
def forward(self, noise, labels):
label_input = self.label_embedding(labels)
input = torch.cat((noise, label_input), dim=1)
img = self.model(input)
img = img.view(img.size(0), 1, 28, 28)
return img
4.2 판별자(Discriminator)
판별자는 이미지와 조건 정보를 함께 받아들여 진짜와 가짜를 판별하는 역할을 합니다. 바닥층에서 시작하여 점차 깊어지는 구조로 설계할 수 있습니다.
class Discriminator(nn.Module):
def __init__(self, num_classes):
super(Discriminator, self).__init__()
self.label_embedding = nn.Embedding(num_classes, num_classes)
self.model = nn.Sequential(
nn.Linear(1 * 28 * 28 + num_classes, 512),
nn.LeakyReLU(0.2),
nn.Linear(512, 256),
nn.LeakyReLU(0.2),
nn.Linear(256, 1),
nn.Sigmoid()
)
def forward(self, img, labels):
label_input = self.label_embedding(labels)
img_flat = img.view(img.size(0), -1)
input = torch.cat((img_flat, label_input), dim=1)
validity = self.model(input)
return validity
5. 손실 함수 및 최적화
cGAN의 손실 함수는 생성자와 판별자의 성능을 평가합니다. 주로 이진_cross-entropy 손실 함수를 사용하며, 생성자와 판별자는 서로 반대되는 목표를 가집니다.
import torch.optim as optim
def build_optimizers(generator, discriminator, lr=0.0002, beta1=0.5):
g_optimizer = optim.Adam(generator.parameters(), lr=lr, betas=(beta1, 0.999))
d_optimizer = optim.Adam(discriminator.parameters(), lr=lr, betas=(beta1, 0.999))
return g_optimizer, d_optimizer
6. cGAN 훈련
생성자와 판별자는 서로 경쟁하며 훈련됩니다. 매 반복마다 판별자는 실제 이미지에 대해 높은 신뢰도를 보이는 동시에 생성자가 만든 이미지에 대해서는 낮은 신뢰도를 가지도록 수정됩니다. 다음은 훈련 루프 예제입니다.
num_classes = 10
z_dim = 100
generator = Generator(z_dim, num_classes)
discriminator = Discriminator(num_classes)
g_optimizer, d_optimizer = build_optimizers(generator, discriminator)
criterion = nn.BCELoss()
# 훈련 루프
num_epochs = 200
for epoch in range(num_epochs):
for imgs, labels in train_loader:
batch_size = imgs.size(0)
# 진짜 이미지와 가짜 이미지 레이블 준비
real_labels = torch.ones(batch_size, 1)
fake_labels = torch.zeros(batch_size, 1)
# 판별자 훈련
discriminator.zero_grad()
outputs = discriminator(imgs, labels)
d_loss_real = criterion(outputs, real_labels)
d_loss_real.backward()
noise = torch.randn(batch_size, z_dim)
random_labels = torch.randint(0, num_classes, (batch_size,))
generated_imgs = generator(noise, random_labels)
outputs = discriminator(generated_imgs, random_labels)
d_loss_fake = criterion(outputs, fake_labels)
d_loss_fake.backward()
d_optimizer.step()
d_loss = d_loss_real + d_loss_fake
# 생성자 훈련
generator.zero_grad()
noise = torch.randn(batch_size, z_dim)
generated_imgs = generator(noise, random_labels)
outputs = discriminator(generated_imgs, random_labels)
g_loss = criterion(outputs, real_labels)
g_loss.backward()
g_optimizer.step()
print(f'Epoch [{epoch}/{num_epochs}], d_loss: {d_loss.item()}, g_loss: {g_loss.item()}')
7. 결과 시각화
훈련이 완료된 후, 생성된 이미지를 시각화할 수 있습니다. Matplotlib를 사용하여 특정 클래스의 이미지를 생성하고 보여줄 수 있습니다.
import matplotlib.pyplot as plt
def generate_and_show_images(generator, num_images=10):
noise = torch.randn(num_images, z_dim)
labels = torch.randint(0, num_classes, (num_images,))
generated_images = generator(noise, labels)
for i in range(num_images):
img = generated_images[i].detach().numpy().reshape(28, 28)
plt.subplot(2, 5, i + 1)
plt.imshow(img, cmap='gray')
plt.axis('off')
plt.show()
generate_and_show_images(generator)
8. 결론
본 글에서는 조건부 생성적 적대 신경망(cGAN)의 개념 및 구현 방법을 살펴보았습니다. cGAN은 특정 조건에 따라 이미지를 생성할 수 있는 강력한 방법으로, 다양한 분야에서 응용될 수 있습니다. 특히, 이미지를 생성하는 것뿐만 아니라 이미지 변환, 스타일 전이 등의 작업에서도 활용될 수 있습니다. 파이토치를 사용하여 cGAN을 구현하는 방법에 대해 자세히 알아보았으므로, 앞으로 더 발전된 모델이나 다양한 응용이 이루어질 수 있기를 기대합니다.