📌 목차

1. DCGAN (Deep Convolutional GAN)
2. WGAN-GP (Wasserstein GAN-Gradient Penalty)

3. CGAN (Conditional GAN)
4. 요약

 

🧐  preview: 

GAN은 생성자와 판별자라는 두 모듈간의 싸움이다.
생성자: random noise를 기존 dataset에서 sampling한 것처럼 보이도록 변환
판별자: sample이 기존 dataset에서인지, 생성자에서나왔는지 예측.

 

 

 

 

 

 

 

 


1.  DCGAN (Deep Convolutinal GAN)

DCGAN 

2015년에 나온 논문 참고.


💸 Generator

목표: 판별자가 판별 불가능한 img생성
input: 다변량표준정규분포에서 뽑은 벡터
output: 원본 train data에 있는 img와 동일한 크기의 img

위 설명이 마치 VAE같다면?
실제로 VAE의 Decoder와 동일한 목적을 수행한다.
latent space의 벡터를 조작, 기존 domain에서 img의 고수준 특성을 바꾸는 기능을 제공하기 때문.
Gen = nn.Sequential(
    nn.ConvTranspose2d(input_latent, 512, 4, 1, 0, bias=False),
    nn.BatchNorm2d(512),
    nn.ReLU(True),

    nn.ConvTranspose2d(512, 256, 4, 2, 1, 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, 64, 4, 2, 1, bias=False),
    nn.BatchNorm2d(64),
    nn.ReLU(True),

    nn.ConvTranspose2d(64, 3, 4, 2, 1, bias=False),
    nn.Tanh()
)
     

Gen = Gen.to(device)
Gen.apply(initialize_weights)





🔍 Discriminator

목표: img가 진짜인지, 가짜인지 예측.
마지막 Conv2D층에서 Sigmoid를 이용해 0과 1사이 숫자로 출력.
Dis = nn.Sequential(
    nn.Conv2d(3, 64, 4, 2, 1, bias=False),
    nn.LeakyReLU(0.2, inplace=True),

    nn.Conv2d(64, 128, 4, 2, 1, bias=False),
    nn.BatchNorm2d(128),
    nn.LeakyReLU(0.2, inplace=True),

    nn.Conv2d(128, 256, 4, 2, 1, bias=False),
    nn.BatchNorm2d(256),
    nn.LeakyReLU(0.2, inplace=True),

    nn.Conv2d(256, 512, 4, 2, 1, bias=False),
    nn.BatchNorm2d(512),
    nn.LeakyReLU(0.2, inplace=True),
            
    nn.Conv2d(512, 1, 4, 1, 0, bias=False),
    nn.Sigmoid()
)


Dis=Dis.to(device)
Dis.apply(initialize_weights)​




🔨 Train

batch img생성→판별자에 통과→각 img에 대한 점수 get.
∙ G_Loss: BCELoss (0: fake img  /  1: real img)
∙ D_Loss: BCELoss (0: fake img  /  1: real img)
이때, 한번에 한 신경망 가중치만 update되도록 두 신경망을 번갈아 train해줘야함.
criterion = nn.BCELoss()

Gen_optimizer = torch.optim.Adam(Gen.parameters(), lr=0.0002, betas=(0.5, 0.999))
Dis_optimizer = torch.optim.Adam(Dis.parameters(), lr=0.0002, betas=(0.5, 0.999))​

 

기타 train코드 참고: https://github.com/V2LLAIN/Vision_Generation/blob/main/Implicit_Density/DCGAN/train.py


이때, DCGAN훈련과정이 불안정할 수 있다. (∵ 판별자와 생성자가 우위를 차지하려 서로 계속 경쟁하기 때문.)
시간이 충분히 지나면, 판별자가 우세해지는 경향이 있다.
다만, 이시점에는 생성자가 충분히 고품질 Img생성이 가능해서 큰 문제는 되지 않는다.


Label Smoothing

또한, GAN에 random noise를 조금 추가하면 유용한데, train과정의 안정성 개성 및 img선명도가 증가한다.
(마치 Denoise Auto Encoder와 같은 느낌.)

GAN 훈련 팁 &  Trick

∙ D >> G 인 경우.

판별자가 너무 강하면 Loss신호가 너무 약해진다.
이로 인해 생성자에서 의미있는 향상을 도모하기 어려워진다.
따라서 다음과 같이 판별자를 약화할 방법이 필요하다.
∙ 판별자에 Dropout rate 증가.
∙ 판별자의 LR 감소.
∙ 판별자의 Conv filter 수 감소.
∙ 판별자 훈련 시, Label에 Noise추가. (Label Smoothing)
∙ 판별자 훈련 시, 일부 img의 label을 random으로 뒤집는다.​


∙ G >> D 인 경우.

mode collapse: 생성자가 거의 동일한 몇개의 img로 판별자를 "쉽게 속이는 방법"
mode: 판별자를 항상 속이는 하나의 sample.

생성자는 이런 mode를 찾으려는 경향이 있고,
latent space의 모든 point를 이 img에 mapping가능하다.
또한, 손실함수의 Gradient가 0에 가까운값으로 붕괴(collapse)하기에 이상태에서 벗어나기 어려워진다.




∙ 유용하지 않은 Loss

손실이 작을수록 생성된 img품질이 더 좋을 것이라 생각할 수 있다.
하지만 생성자는 현재 판별자에 의해서만 평가된다.
판별자는 계속 향상되기에 train과정의 다른지점에서 평가된 손실을 비교할 수 없다.
즉, 판별Loss는 감소하고, 생성Loss는 증가한다.→ GAN train과정 모니터링이 어려운 이유.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


2. WGAN-GP (Wasserstein GAN with Gradient Penalty)

GAN Loss

GAN의 판별자∙생성자 훈련 시 사용한 BCE Loss를 살펴보자.

판별자 D훈련: real_img에 대한 예측 pi=D(xi)와 target yi=1을 비교.
생성자 G훈련: 생성_img에 대한 예측 pi=D(G(zi))와 target yi=0을 비교.

[GAN D_Loss 최대화 식]:

[GAN G_Loss 최소화 식]:




Wesserstein Loss

[GAN Loss와의 차이점]:
∙ 1과 0대신, yi = 1, yi = -1을 사용.
D의 마지막층에서 sigmoid제거.
→ 예측 pi가 [0,1]범위에 국한되지 않고 [-∞,∞] 범위의 어떤 숫자도 될 수 있게함.
위의 이유들로 WGAN의 판별자는 보통 비평자(Critic)라 부르며, 확률대신 점수"score"를 반환한다.

[Wesserstein Loss함수]:
WGAN의 critic D를 훈련하기위해 
real_img에 대한 예측(D(xi))과 타겟(= 1)을 비교.
생성_img에 대한 예측(D(G(zi)))과 타겟(= -1)을 비교.
∴ 손실을 계산


[WGAN Critic D_Loss 최소화]: real과 생성간의 예측차이 최대화.

[WGAN G_Loss 최소화]: Critic에서 가능한 높은 점수를 받는 img생성.
(= Critic을 속여 real_img라 생각하게 만드는 것.)

1-Lipshitz Continuous function

sigmoid로 [0,1]범위에 국한하지 않고
Critic이 [-∞,∞] 범위의 어떤 숫자도 될 수 있게한다는 점은 Wessertein Loss가 제한없이 아주 큰 값일 수 있다는 것인데, 보통 신경망에서 큰 수는 피해야한다.

그렇기에, "Critic에 추가적인 제약이 필요"하다.
특히, Critic은 1-Lipshitz 연속함수여야 하는데, 이에대해 살펴보자.

Critic은 하나의 img를 하나의 예측으로 변환하는 함수 D이다.
임의의 두 input_img x1, x2에 대해 다음 부등식을 만족하면, 이 함수를 1-Lipshitz라 한다:
|x1-x2| : 두 img 픽셀의 평균적인 절댓값 차이
|D(x1) - D(x2)| : Critic 예측간의 절댓값 차이
Lipshitz Continuous Function

기본적으로 기울기의 절댓값이 어디에서나 최대 1이어야한다
= 두 img간 Critic예측변화비율 제한이 필요하다는 의미.

 

WGAN-GP

WGAN의 Critic의 가중치를 작은 [-0.01, 0.01]범위에 놓이도록
train batch 이후 weight clipping으로 Lipshitz제약을 부과한다.

이때, 학습속도가 크게 감소하기에 Lipshitz제약을 위해 다른 방법을 적용한다:
바로 Wesserstein GAN-Gradient Penalty이다.

[WGAN-GP]: Gradient Norm이 1에서 벗어나면 모델에 불이익을 주는 방식이다.

[Gradient Penalty Loss]:
input_img에 대한 예측의 Gradient Norm과 1사이 차이를 제곱한 것. 
모델은 자연스레 GP항을 최소화하는 가중치를 찾으려하기에 이 모델은 립시츠 제약을 따르게 한다.

Train과정동안 모든곳에서 Gradient계산은 힘들기에 WGAN-GP는 일부지점에서만 Gradient를 계산한다.
이때, real_img와 fake_img쌍 간의 interpolation img를 사용한다.
from torch.autograd import Variable
from torch.autograd import grad as torch_grad

def gradient_penalty(self, real_data, generated_data):
        batch_size = real_data.size()[0]

        # Calculate interpolation
        alpha = torch.rand(batch_size, 1, 1, 1)
        alpha = alpha.expand_as(real_data)

        interpolated = alpha*real_data.data + (1 - alpha)*generated_data.data
        interpolated = Variable(interpolated, requires_grad=True)

        # Calculate probability of interpolated examples
        prob_interpolated = self.D(interpolated)

        # Calculate gradients of probabilities with respect to examples
        gradients = torch_grad(outputs=prob_interpolated, inputs=interpolated,
                               grad_outputs=torch.ones(prob_interpolated.size()).cuda() if self.use_cuda else torch.ones(
                               prob_interpolated.size()),
                               create_graph=True, retain_graph=True)[0]

        # Gradients have shape (B,C,W,H)
        # so flatten to easily take norm per example in batch
        gradients = gradients.view(batch_size, -1)
        self.losses['gradient_norm'].append(gradients.norm(2, dim=1).mean().data[0])

        # Derivatives of the gradient close to 0 can cause problems because of
        # the square root, so manually calculate norm and add epsilon
        gradients_norm = torch.sqrt(torch.sum(gradients**2, dim=1) + 1e-12)

        # Return gradient penalty
        return self.gp_weight * ((gradients_norm-1)**2).mean()

[WGAN-GP에서의 Batch Normalization]

BN은 같은 batch안의 img간의 correlation을 만든다.
그렇기에 gradient penalty loss의 효과가 떨어지에
WGAN-GP는 Critic에서 BN을 사용해서는 안된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 


3.  CGAN (Conditional GAN)

prev.

앞서 설명한 모델들은 "주어진 trainset에서 사실적인 img를 생성하는 GAN"이었다.
하지만, "생성하려는 img의 유형을 제어할 수 는 없었다."
(ex. 생성하려는 img유형: 크거나 작은 벽돌, 흑발/금발 등등)

latent space에서 random한 하나의 point sampling은 가능하다.
latent variable을 선택하면 어떤 종류의 img가 생성될 지 쉽게 파악가능하다.

 

CGAN

[GAN v.s CGAN]:
CGAN은 GAN과 달리 "label과 관련된 추가정보를 생성자와 critic에 전달한다는 점"이다.
∙ 생성자: 이 정보를 one-hot encoding vector로 latent space sample에 단순히 추가.
∙ Critic: label 정보를 RGB img의 채널에 추가채널로 추가.
→ input img가 동일한 크기가 될 때 까지 one-hot encoding vector를 반복.

[유일한 구조 변경사항]:
label정보를 G,D의 기존 입력에 연결하는 것.
class Generator(nn.Module):
    def __init__(self, generator_layer_size, z_size, img_size, class_num):
        super().__init__()
        
        self.z_size = z_size
        self.img_size = img_size
        
        self.label_emb = nn.Embedding(class_num, class_num)
     
        self.model = nn.Sequential(
            nn.Linear(self.z_size + class_num, generator_layer_size[0]),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(generator_layer_size[0], generator_layer_size[1]),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(generator_layer_size[1], generator_layer_size[2]),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(generator_layer_size[2], self.img_size * self.img_size),
            nn.Tanh()
        )
    
    def forward(self, z, labels):
        
        # Reshape z
        z = z.view(-1, self.z_size)
        
        # One-hot vector to embedding vector
        c = self.label_emb(labels)
        
        # Concat image & label
        x = torch.cat([z, c], 1)
        
        # Generator out
        out = self.model(x)
        
        return out.view(-1, self.img_size, self.img_size)​

class Discriminator(nn.Module):
    def __init__(self, discriminator_layer_size, img_size, class_num):
        super().__init__()
        
        self.label_emb = nn.Embedding(class_num, class_num)
        self.img_size = img_size
        
        self.model = nn.Sequential(
            nn.Linear(self.img_size * self.img_size + class_num, discriminator_layer_size[0]),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Dropout(0.3),
            nn.Linear(discriminator_layer_size[0], discriminator_layer_size[1]),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Dropout(0.3),
            nn.Linear(discriminator_layer_size[1], discriminator_layer_size[2]),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Dropout(0.3),
            nn.Linear(discriminator_layer_size[2], 1),
            nn.Sigmoid()
        )
    
    def forward(self, x, labels):
        
        # Reshape fake image
        x = x.view(-1, self.img_size * self.img_size)
        
        # One-hot vector to embedding vector
        c = self.label_emb(labels)
        
        # Concat image & label
        x = torch.cat([x, c], 1)
        
        # Discriminator out
        out = self.model(x)
        
        return out.squeeze()​

 

 

 

 

 

 

 

 

 

 

 

 

 


4.  요약

앞서 Data를 직접생성하는 확률적과정으로 밀도함수를 암묵적으로 모델링방식이라 했었다. 

이번에 소개한 GAN은 총 3가지가 있다.(참고)
① DCGAN: mode collapse 및 gradient vanishing problem존재.

② WGAN: DCGAN문제 해결을 위해 안정화 진행.
WGAN-GP: 훈련과정중 1-Lipshitz조건(손실함수에 Gradient Norm이 1이 되도록 끌어당기는 항)을 추가.

③ CGAN: 생성된 출력 Img유형을 제어하는데 필요한 추가정보가 신경망에 제공.
다음에는 sequential data modeling 시, 이상적인 AR Model을 알아볼 것이다.

'Gain Study > Generation' 카테고리의 다른 글

[G]Part 2-5. Energy-based Model  (0) 2024.01.29
[G]Part 2-4. Normalizing Flows  (2) 2024.01.29
[G]Part 2-3. Auto Regressive Models  (0) 2024.01.26
[G]Part 2-1. VAE  (2) 2024.01.25
[G]Part 1. Intro. Generative Deep Learning  (0) 2024.01.25

+ Recent posts