Generator는 노이즈 벡터 (z \sim p_{z}(z)) 를 받아 이미지와 같은 샘플 (x{G}=G(z)) 를 생성
Discriminator는 입력 이미지(실제 (x{\text{real}}) 혹은 가짜 (x_{G})) 을 받아 진짜/가짜 확률 (D(x)\in(0,1)) 를 출력
Generator:
fc → batchnorm → leaky‑ReLU → conv‑transpose*…* → sigmoid
(전형적 DCGAN 구조)
Discriminator:
conv → batchnorm → leaky‑ReLU*…* → flatten → fc → sigmoid
훈련 단계:
1. D 를 업데이트: 실제 샘플은 1, 가짜 샘플은 0 로 라벨링
2. G 를 업데이트: D(G(z)) 를 1(실제) 에 가깝게 만든다
[
\min {G}\max {D}
\; \mathbb{E}{x\sim p{\text{data}}}!\big[\log D(x)\big]
| 원문 | ❌ 수정 | 정정 내용 |
|---|---|---|
| 생성자는 Fake 를 1(True) 라고 한다. 생성자 데이터는 1이라고 생각하고 데이터를 넣는다. | ❌ 수정: 생성자는 실제라(Real)로 구분하려는 Discriminator 를 속이기 위해 fake 샘플을 “실제”(1) 로 라벨링하도록 학습한다. | Generator는 D(G(z)) 를 1에 가깝게 만들려는 목표를 가짐(즉, Discriminator가 가짜임을 잘 못 판별하도록 만드는 것). |
| 손실이 최소화는 어떻게든 맞춘것이다. 생성자는 손실을 0 값으로 하려고 오차 역전파를 한다. | ❌ 수정: Generator 역전파 시 L_G 를 최소화(=D(G(z))를 1에 가깝게), 이 과정에서 손실이 0에 가까워지도록 학습한다. | Generator 는 D(G(z)) → 1 를 목표로 하고, L_G → 0 이 되는 방향으로 파라미터를 업데이트한다. |
| 생성자의 랜덤데이터가 만들어낸 픽셀값들을 어떻게든 0값이 하려고 노력하는것이다. | ❌ 수정: Generator 가 픽셀값(0~1) 을 억제하려 하지 않는다. 픽셀값은 D 가 판단하는 확률(1/0)과는 무관하다. | Generator 는 이미지 공간에서 실제와 유사한 샘플 을 생성하도록 학습; 픽셀 값은 생성 과정에서 자동으로 결정됨. |
| 판별자는 정확히 판별을 못한 케이스이다. Fake 로 끝난겁니다. 0 에 최대 가까워진것은 생성자가 다 했기때문에 문제가 손실 계산하는거라서 그렇습니다. | ❌ 정정: Real → 1, Fake → 0 이 목표. Generator 가 잘 하면 D(G(z)) → 1 이 되고, D 는 1−D(G(z)) → 0 을 최소화. | 판별자가 잘못 판별하면 D(G(z)) 가 0에 가까워도 Generator 가 더 크게 조정해 1에 가깝게 만듦. |
| 1-G(x) 판별자가 생성된 데이터를 가짜로 판별할 수 있는 확률. 실제 1은 True 이다 True 가 True 이면 생성자가 잘 한 것이다. 생성자는 진짜 데이터를 넣어서 어떻게든 1값으로 하려고 한다. | ❌ 정정: 1-D(G(z)) 는 Generator 가 만들 가짜를 Discriminator 가 가짜(0) 로 판단할 확률 을 의미. D(G(z)) 가 1에 가깝다는 것은 Generator 가 Discriminator 를 속인 경우. | Generator 는 실세계 샘플처럼 보이게 만들기 위해 D(G(z)) 를 1에 가깝게 만드는 방향으로 학습한다. |
핵심:
- Generator는 Discriminator 를 속이는 가치를 최적화 (Fake → 1).
- Discriminator는 실제 → 1, 가짜 → 0 으로 분류 하는가치를 최적화.
| 변형 | 주된 특징 | 대표 논문 |
|---|---|---|
| DCGAN | Convolution 기반 Generator/Discriminator, BatchNorm, Leaky‑ReLU | Radford, Metz, Chintala (2015) |
| WGAN | Wasserstein 거리를 기반, Weight Clipping 또는 Gradient Penalty | Arjovsky, Bottou, Gulrajani (2017) |
| LSGAN | Least‑Squares 손실, 라벨 1, 0 대신 (1, 0) 대신 (1, -1) | Mao et al. (2017) |
| cGAN | 조건 입력(라벨·텍스트 등)을 각 모델에 병합 | Mirza & Osindero (2014) |
| PGGAN | 점진적 해상도 상승, 레이어 추가를 통해 고해상도 이미지 생성 | Karras et al. (2017) |
| StyleGAN | AdaIN(Adaptive Instance Normalization), Style Mixing | Karras et al. (2018) |
주요 한계: Mode Collapse, Gradient vanishing, 불안정한 학습
해결 방안: Mini‑batch discrimination, Unrolled GAN, Spectral Normalization, Gradient Penalty, Label Smoothing, Feature Matching 등
| 문제 | 원인 | 대응책 |
|---|---|---|
| Mode Collapse | Generator 가 몇 가지 샘플만 반복 생성 | Minibatch Discrimination, Feature Matching, PacGAN, Unrolled GAN |
| Gradient Vanishing | D loss saturates, G 받는 정보 부족 | WGAN‑GP (Gradient Penalty), Spectral Normalization, Instance Normalization |
| Training Instability | D 가 너무 강해 G 가 학습 불가 | Weight Clipping, Gradient Clipping, Adam optimizer 설정 조절 |
| Lipschitz Constraint | WGAN 에 의한 Lipschitz 필요 | Weight Clipping 또는 GP (Gradient Penalty) |
| Label Smoothing | D 가 과도한 확신을 가지는 억제 | Discriminator 라벨을 (0.9, 0.1) 대신 (1, 0) 로 부드럽게 |
| Batch Normalization Issues | Generator & Discriminator 가 서로 부정적 영향 | Instance Normalization, Layer Normalization 사용 가능 |
| Mode Dropping | Generator 가 일부 데이터 분포만 대표 | Variational/Hybrid 세대, DCGAN+ KL-Divergence, Entropy Regularization |
실무 팁
- 학습률: D 은 G 보다 약간 높은 학습률 사용 (예: 0.0002 vs 0.0001).
- Epoch 수: 충분한 회전이 필요하나, 과적합 위험 있어 Early‑Stopping 도 검토.
- 로스 변화: D loss 와 G loss 가 기준 범위(0, 1) 안에 있는지 모니터링.
import torch
import torch.nn as nn
import torch.optim as optim
# ----------------------- Generator -----------------------
class Generator(nn.Module):
def __init__(self, nz=100, ngf=64):
super().__init__()
self.net = nn.Sequential(
nn.ConvTranspose2d(nz, ngf*4, 4, 1, 0, bias=False), # 4x4
nn.BatchNorm2d(ngf*4),
nn.ReLU(True),
nn.ConvTranspose2d(ngf*4, ngf*2, 4, 2, 1, bias=False), # 8x8
nn.BatchNorm2d(ngf*2),
nn.ReLU(True),
nn.ConvTranspose2d(ngf*2, ngf, 4, 2, 1, bias=False), # 16x16
nn.BatchNorm2d(ngf),
nn.ReLU(True),
nn.ConvTranspose2d(ngf, 1, 4, 2, 1, bias=False), # 32x32
nn.Tanh()
)
def forward(self, z):
return self.net(z)
# ----------------------- Discriminator -----------------------
class Discriminator(nn.Module):
def __init__(self, ndf=64):
super().__init__()
self.net = nn.Sequential(
nn.Conv2d(1, ndf, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(ndf, ndf*2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf*2),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(ndf*2, ndf*4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf*4),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(ndf*4, 1, 4, 1, 0, bias=False),
nn.Sigmoid()
)
def forward(self, x):
return self.net(x).view(-1)
# ----------------------- Training Loop -----------------------
nz = 100
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
G = Generator(nz).to(device)
D = Discriminator().to(device)
criterion = nn.BCELoss()
optimizerD = optim.Adam(D.parameters(), lr=2e-4, betas=(0.5, 0.999))
optimizerG = optim.Adam(G.parameters(), lr=2e-4, betas=(0.5, 0.999))
num_epochs = 10
for epoch in range(num_epochs):
for imgs, _ in dataloader: # imgs: (N,1,32,32)
imgs = imgs.to(device)
# === Train Discriminator ===
D.zero_grad()
# real
real_labels = torch.ones(imgs.size(0), device=device)
output_real = D(imgs)
loss_real = criterion(output_real, real_labels)
# fake
z = torch.randn(imgs.size(0), nz, 1, 1, device=device)
fake_imgs = G(z).detach()
fake_labels = torch.zeros(imgs.size(0), device=device)
output_fake = D(fake_imgs)
loss_fake = criterion(output_fake, fake_labels)
loss_D = loss_real + loss_fake
loss_D.backward()
optimizerD.step()
# === Train Generator ===
G.zero_grad()
z = torch.randn(imgs.size(0), nz, 1, 1, device=device)
fake_imgs = G(z)
output = D(fake_imgs)
gen_labels = torch.ones(imgs.size(0), device=device) # G wants D=1
loss_G = criterion(output, gen_labels)
loss_G.backward()
optimizerG.step()
print(f"Epoch {epoch+1}/{num_epochs} | D loss: {loss_D.item():.4f} | G loss: {loss_G.item():.4f}")
팁:
- 라벨 스무딩:
real_labels = 0.9→0.9,fake_labels = 0.1- Spectral Normalization:
nn.utils.spectral_norm를 레이어에 삽입- WGAN‑GP:
D의 가중치 클리핑 대신 gradient penalty 추가
-log D(G(z)) → D 의 출력을 1에 가깝게 만들 것 - [ log D(x) + log(1-D(G(z))) ] → Real:1, Fake:0 | 주제 | 설명 | 참고 자료 |
|---|---|---|
| WGAN‑GP | Gradient Penalty 기반 Wasserstein GAN | https://arxiv.org/abs/1704.00028 |
| BigGAN | Large‑scale GAN, Inception Score 높이기 | https://arxiv.org/abs/1809.11096 |
| Self‑Attention GAN | Attention Layer를 도입해 장거리 의존성 학습 | https://arxiv.org/abs/1805.08318 |
| CycleGAN | 번역 딥러닝, 출력을 다른 도메인으로 변환 | https://arxiv.org/abs/1703.10593 |
| DALL·E / Stable Diffusion | 텍스트‑투‑이미지 변환, Diffusion 기반 | https://arxiv.org/abs/2102.12092 |
| GAN 학습 이론 | Minimax 게임 이론, Convex-Concave 수렴 | https://arxiv.org/abs/1707.06370 |
| GAN 응용 사례 | 의료 영상, 예술, 보안(위조지폐 검출) | 박물관, 의료 AI 사전연습 자료 |
실무 팁
- LoRA (Low‑Rank Adaptation) : 파라미터 수 줄이면서 효율 보장
- Stable Diffusion : Diffusion 기반 GAN 대비 더 낮은 GPU 요구량
- GAN in PyTorch Lightning : 학습 스케줄, 체크포인트 자동화
마무리
GAN 은 강력한 생성 모델이지만 동적 적대 구조 특성상 학습 불안정을 자주 겪는다.
위표시된 변형과 기법들을 단계별로 실험해보면서 하이퍼파라미터 튜닝을 수행하면 실무에서 안정적이고 고품질의 생성을 구현할 수 있다.