Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks, Alec Radford et al, ICLR2016
DCGAN contribution
- Convolutional GAN architecture를 사용하면 학습시에 더 안정적하다.
- GAN에 의해 학습된 필터들를 시각화하고, 특정한 필터가 특정한 물체를 그리는데 학습되었다는 것을 실험적으로 보여준다.
- 학습된 discriminator를 image classification task에 사용했을 때, 다른 비지도 학습 알고리즘과 비슷한 성능을 보인다.
- Generator가 벡터 연산적 특성을 가지고 있어서 생성된 샘플들에 대한 semantic qualities에 대한 조절이 가능하다.
기존 GAN의 한계
-
기존 GAN이 생성한 이미지는 학습이 불안정하다.
-
GAN이 생성한 이미지는 noise가 껴있고 incomprehensible하다.
-
black-box methods. 뉴럴 네트워크가 내부에서 어떤 작동을 하는지 우리는 정확히 모른다.
-
뉴럴넷은 사람이 소비할 수 있는 알고리즘의 수준에서 네트워크가 무엇을 생성하는지 이해하기 어려운 black-box methods 라는 비판이 있다.
-
Low resolution upsampling 힘들다.
DCGAN Architecture
Higher resolution and deeper generative models
- 모든 convolution layer에서 pooling layer를 strided convolution으로 대체한다.
- Batch normalization을 사용한다.
- fully connected layer를 사용하지 않는다.
- ReLU activation을 사용하고 generator의 마지막 output에만 Tanh function을 사용한다.
- Discriminator는 leaky ReLU activation을 사용한다.
미분가능하지 않은 spatial pooling layer를 convolution layer로 대체한다. generator, discriminator 네트워크가 자신의 spatial upsampling, downsampling을 학습할 수 있게 된다.
Batch Normalization을 적용하면 학습이 더 안정화된다. 더 깊은 모델에서 gradient가 흐르는 것을 도와준다. GAN에서 generator가 모든 샘플을 하나로 만드는 collapsing하는 것을 막는데 중요한 역할을 한다. 모든 layer에 batch norm을 사용하면 sample이 oscillation되고 모델이 불안정해지는 결과가 발생한다. 이것을 막기 위해서 Generator의 output layer와 discriminator의 input layer에는 batch norm을 사용하지 않는다.
논문에 의하면 fully connected layer를 사용하지 않는 추세라고 한다.
DCGAN pytorch 모델 구현
논문에 제시된 generator의 모델은 다음과 같다.
import torch
import torch.nn as nn
class Generator(nn.Module):
def __init__(self):
super().__init__()
self.features = nn.Sequential(
nn.ConvTranspose2d(100, 1024, kernel_size=4, stride=1, padding=0),
nn.BatchNorm2d(1024),
nn.ReLU(inplace=True),
nn.ConvTranspose2d(1024, 512, kernel_size=4, stride=2, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.ConvTranspose2d(512, 256, kernel_size=4, stride=2, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(inplace=True),
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.ConvTranspose2d(128, 3, kernel_size=4, stride=2, padding=1),
nn.Tanh(),
)
def forward(self, x):
x = self.features(x)
return x
class Discriminator(nn.Module):
def __init__(self):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 128, kernel_size=4, stride=2, padding=1),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1),
nn.BatchNorm2d(256),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(256, 512, kernel_size=4, stride=2, padding=1),
nn.BatchNorm2d(512),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(512, 1024, kernel_size=4, stride=2, padding=1),
nn.BatchNorm2d(1024),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(1024, 1, kernel_size=4, stride=1, padding=0),
nn.Sigmoid()
)
def forward(self, x):
x = self.features(x)
return x