IT Repository

(3) GAN (Generative Adversarial Network) 본문

Generative/Study

(3) GAN (Generative Adversarial Network)

IT찬니 2020. 1. 19. 14:26

 

 

 

 

Generative Adversarial Network (GAN)

Introduction에서 소개했던 아래 계층도를 기억하시나요?

  • Generative models
    • Explicit density
      • Tractable density: 실제로 density를 계산할 수 있는 경우
        • NADE
        • MADE
        • PixelRNN/CNN
      • Approximate density: density의 form을 알고 있고, 그를 통해 근사하는 경우
        • Variational (VAE)
        • Markov Chain (Boltzman Machine)
    • Impicit density: density의 form을 알 수 없고, 특정 분포로 전제하지도 않는 경우
      • Direct (GAN)
      • Markov Chain (GSN)

우리가 지난번에 배웠던 VAE는 Explicit density에 속하는 분류였습니다.
즉, 아래와 같이 확률밀도함수를 정의하는 것이 가능하다는 이야기입니다.
물론 확률밀도함수를 계산할 수는 없었기에 ELBO를 계산해서 그를 높이는 방식으로 학습해나갔었죠.

- PixelCNN
$p_\theta(x) = \displaystyle \prod_{i=1}^{n} p_\theta \left( x^i~|~x^1, x^2, \dots, x^{i-1} \right)$
- VAE
$p_\theta(x) = \displaystyle \int p_\theta(z) \cdot p_\theta (x|z) dz$

(PixelCNN은 공부하지 않았으나, 대충 식을 보아하니 이전 인풋이 있을 때의 현재 인풋을 조건부 확률로 구해서 하나의 사건을 독립사건으로 보고 Pi product하는 식 이네요.)

GAN은 위와 같이 가능도 $p(x)$를 정의하지 않고 샘플링만 가능하게 하면 어떨까? 라는 아이디어에서 출발합니다.

Generative model의 목표는 $P_{data}$를 근사하는 $P_{model}$을 찾아내는 것이었죠.
GAN 역시 실제 데이터가 함축하는 복잡한 분포로 Direct하게 매핑하는 함수를 찾아내는 것을 목표로 합니다.
그러나 그 함수를 Explit하게 사용하는 것이 아니라 Implicit하게 있긴 있는데 무엇인지는 우리가 알 수 없는 것이죠.

따라서 Explicit density function을 사용하지 않는 대신에 그 논리적인 근거를 게임 이론에서 찾습니다.

 

Game Theory: Mini Max Algorithm

게임이론에서의 미니 맥스 전략은 게임 상대방의 최대값을 최소화해 나가는 것입니다.

미니 맥스 이론은 초기에는 한정된 자원에서 두 경쟁자가 손익을 합했을 때 0이 되는, 제로섬 게임을 해결하기 위해 발전했습니다.
따라서 이러한 제로섬 게임에서는 결국 지지 않는 것이 이기기 위한 최선의 전략이 됩니다.
상대의 이익(최대값)을 최소화하거나 졌을 때에도 손실을 최소화하기 위한 전략이 최선의 전략이라는 것입니다.

즉, 미니 맥스 게임은 Max risk를 Minimize하는 것을 말합니다.

GAN을 설명하는 대표적인 예시로 지폐위조범과 경찰관의 예시를 들 수 있습니다.
여기에서는 너무나도 흔하고 유명한 예시이기 때문에 여기에서는 언급하지 않습니다만 이 역시 미니 맥스 게임이라는 것을 생각해 볼 수 있습니다.

 

Main Idea of GAN

아래는 GAN을 구성하는 두 개의 네트워크 입니다.

  • Generator: Latent vector z를 통해 진짜같은 가짜 이미지를 생성해서 Discriminator를 속임
  • Discriminator: 진짜와 가짜를 구별함

GAN의 "Adversarial" 은 위의 두 네트워크가 서로 대립하기 때문입니다.
제너레이터는 디스크리미네이터를 속이는 것이 게임을 이기는 것이고, 디스크리미네이터는 제너레이터에게 속지 않는 것이 이기는 것입니다.

두 네트워크가 서로 대립하면서 제너레이터가 점점 더 실제와 유사한 샘플을 만들도록 하는 것이 생성 모델로써 GAN의 목표입니다.
(디스크리미네이터 역시 점점 더 잘 구별할 것이고, 그러면 제너레이터는 더 비슷하고 유사한 샘플을 생성해야 하겠죠.)
이는 즉, 계속해서 이야기했었던 $P_{data}$를 근사하는 $P_{model}$을 찾아내도록 하는 것입니다.

 

Workflow of GAN Idea

위의 메인 아이디어를 도식화해서 작업 흐름도로 표현해보겠습니다.

  1. Gaussian 분포에서 랜덤 벡터 z(랜덤 노이즈)를 샘플링
  2. z를 Generator에 통과시켜 Fake data 생성
  3. Fake data와 Real data를 함께 Discriminator에 통과시켜 Real과 Fake를 구별
 

Loss Function: Minimax Objective Function

이제 위의 구조를 학습하기 위한 손실함수가 필요하겠죠?
두 네트워크의 목표인 Max risk를 Minimize하는 수식을 여기에서는 손실함수로 사용합니다.

아래의 Minimax Objective Function을 하나씩 살펴보겠습니다.

($\theta_g$: params of Generator, $\theta_d$: params of Discriminator)

${\text{min} \\ ~~\scriptsize {\theta_g}}~{\text{max} \\ ~~\scriptsize {\theta_d}} \bigg[ \mathbb{E}_{x \sim p_{data}}~~\log D_{\theta_d}~\big( x \big) + \mathbb{E}_{z \sim p(z)}\log \big( 1 - D_{\theta_d}\big( G_{\theta_g} \big( z \big) \big) \big) \bigg]$

 

각 항에 대한 설명은 아래와 같습니다.

(1번째 항) $\mathbb{E}_{x \sim p_{data}}~~\log D_{\theta_d}~\big( x \big)$: 실제 데이터에서 샘플링한 x를 Discriminator에 넣었을 때의 결과에 대한 기대값
(2번째 항) $\mathbb{E}_{z \sim p(z)}\log \big( 1 - D_{\theta_d}\big( G_{\theta_g} \big( z \big) \big) \big)$: 랜덤 벡터에서 샘플링한 z를 통해 Generator가 생성한 Fake data를 Discriminator가 구별할 때의 결과에 (1에서 결과를 뺀 것에) 대한 기대값

(Discriminator의 아웃풋이 Fake면 0, Real이면 1입니다.)

In [2]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0.0001, 2, 201)
y = np.log10(x)
xticks = [0, 1]
yticks = []
xlim = [-0.1, 1.5]
ylim = [-1.5, 0.1]

plt.title("Graph of Log(x)")

plt.plot([0,0], [-2,2], "black")
plt.plot([-2,2], [0,0], "black")

plt.plot(x, y)
plt.gca().xaxis.tick_top()
plt.xticks(xticks); plt.yticks(yticks)
plt.xlim(xlim); plt.ylim(ylim)

for direction in ["left", "right", "top", "bottom"]:
    plt.gca().spines[direction].set_visible(False)
    
plt.grid(axis="x")
plt.show()
 
 

(이해를 돕기 위해, 위의 log 그래프를 참고하세요.)

(1)
자, 그러면 먼저 max 부분을 먼저 확인하겠습니다.

디스크리미네이터가 실제 데이터 x를 Real로 잘 구별하는 경우에 1번째 항에 있는 $D(x)$의 값은 1이고, log 1은 0 입니다.
이번에는 제너레이터가 생성한 가짜 데이터를 Fake로 잘 구별하는 경우에 2번째 항에 있는 (1 - D(G(z)))의 값은 1이고, 마찬가지로 log 1은 0 입니다.

즉, 디스크리미네이터가 Real을 Real로 잘 구별하고, Fake를 Fake로 잘 구별할 때에 기대값이 최대가 됩니다.
이를 해석하면, 디스크리미네이터는 Objective Function을 Maximize하는 것이 목표라고 할 수 있겠네요.

(2)
이번에는 min 부분을 확인하겠습니다.

디스크리미네이터가 실제 데이터 x를 Real로 잘 구별하지 못하는 경우에 $D(x)$의 값은 0이고, log 0은 $-\infty$로 향합니다.
그리고 제너레이터가 생성한 가짜 데이터를 Real이라고 잘못 구별하는 경우에 2번째 항에 있는 (1 - D(G(z)))의 값은 0이고, 마찬가지로 log 0은 $-\infty$로 향합니다.

즉, 디스크리미네이터가 Real을 잘 구별하지 못하고, Fake를 잘 구별하지 못할때에 기대값이 최소가 됩니다.
이 말을 뒤집어서 생각하면, 제너레이터가 디스크리미네이터를 속이는데에 성공했을 때에 기대값이 최소가 된다는 이야기입니다.
마찬가지로 해석했을 때, 제너레이터는 Objective Function을 Minimize하는 것이 목표라고 할 수 있습니다.

 

Problem in Optimizing Generator

여기까지 왔을 때, GAN을 Optimization을 하기 위해서 결론적으로 아래와 같이 생각할 수 있습니다.

- $\theta_d$에 대해서는 Objective Function을 Maximize 해야하므로, 1 + 2 번째 항에 대해서 Gradient Ascent한다.
- $\theta_g$에 대해서는 Objective Function을 Minimize 해야하므로, 2번째 항에 대해서 Gradient Descent한다.

그러나 이와 같이 파라미터를 업데이트 해 나가는 경우에 한가지 문제점이 생깁니다.

 

제너레이터를 업데이트하는 $\theta_g$ Optimization 부분을 살펴봅시다.
$\theta_g$를 Optimize하기 위해서는 $\log (1-D(G(z)))$를 미분해서 Gradient를 구할 것입니다.

이를 $\log (1-x)$로 간략화해서 생각해봅시다.
x값이 0에 가까운 경우는 제너레이터가 디스크리미네이터를 속이는 데에 실패한 경우입니다.
즉, Global Minimum에서 멀리 떨어진 상황입니다.

이 경우에 아래 그래프를 참고하면, Gradient가 작아 학습이 느려지는 문제가 발생하는 것을 확인할 수 있습니다.
반대로 Global Minimum 근처에서는 Gradient가 너무 커서 스텝을 빨리 밟아버리는 문제가 발생합니다. (x값이 1에 가까운 경우)

In [2]:
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-2, 0.999, 301)
y = np.log(1 - x)
xticks = [0, 1]
yticks = [0]
xlim = [-0.1, 1.1]
ylim = [-5, 0.5]

plt.title("Graph of Log(1-x)")

plt.plot([0,0], [-10,10], "black")
plt.plot([-10,10], [0,0], "black")

plt.plot(x, y)
plt.gca().xaxis.tick_top()
plt.xticks(xticks); plt.yticks(yticks)
plt.xlim(xlim); plt.ylim(ylim)

for direction in ["left", "right", "top", "bottom"]:
    plt.gca().spines[direction].set_visible(False)
    
plt.grid(axis="x")
plt.show()
 
 

To Solve Problem in Optimizing Generator

따라서, 실제로는 $\log (1-x)$에 대해 Gradient Descent하지 않습니다.
그 대신에 $\log (1-x)$에 대해 선대칭인 $\log (x)$ 그래프를 Gradient Ascent합니다.

아래 그래프는 동일한 x값일 때의 Gradient의 차이를 확인할 수 있습니다.

In [35]:
import numpy as np
import matplotlib.pyplot as plt

x1 = np.linspace(-2, 0.999, 301)
y1 = np.log(1 - x1)

x2 = np.linspace(0.001, 2, 201)
y2 = np.log(x2)

plt.title("Comparison of Log(1-x) with Log(x)")

plt.plot([-1,2], [0,0], color="black", linewidth=0.7)
plt.plot(x1, y1, label="Log(1-x)")
plt.plot(x2, y2, "g", label="Log(x)")

plt.quiver(0.3, 1, 0.4, 0, scale_units='xy', angles='xy', scale=1, width=0.005)
plt.text(0.15, 1.5, "To update params for x to move right ")

plt.xticks([0, 1]); plt.yticks([0])
plt.xlim([-0.1, 1.1]); plt.ylim([-5, 5])
plt.xlabel("x = D(G(z))")
    
plt.grid(axis="x")
plt.legend()
plt.show()
 
 

조금 더 구체적으로 설명해보겠습니다.
(위 그래프를 참고하세요.)

(1)
$\log (1 - x)$를 최소화하면 $\theta_g$를 Optimize할 수 있었습니다.
$\log (1 - x)$가 작아지려면 x값이 양의 방향으로 움직여야 했었구요.
그리고 x값이 양의 방향으로 움직이려면 Gradient Descent를 해야 했습니다.
(파란색 그래프입니다.)

결국 x값만 양의 방향으로 움직여주면 됩니다.

(2)
이번에는 $\log(x)$의 Gradient를 Ascent한다고 생각해봅시다.
(초록색 그래프입니다.)

그러면 x값이 양의 방향으로 움직이도록 파라미터를 업데이트할 것입니다.
게다가 이전과 같은 업데이트 스텝의 크기 문제도 해결이 됩니다.

그러면 $\log(x)$를 Gradient Ascent하면, 결국 $\log (1 - x)$를 최소화할 수 있겠네요.

 

Training Procedure of GAN

아래는 GAN을 트레이닝하는 순서입니다.
중요한 것은 디스크리미네이터를 먼저 업데이트하고 파라미터를 고정시킨 후에 제너레이터를 훈련시킨다는 점입니다.

  1. Discriminator 업데이트
    1. Gaussian 분포 $\mathcal{N} (0, 1)$에서 랜덤 노이즈 벡터를 m개 샘플링한다. ($z \sim p_{g}(z)$)
    2. 실제 데이터셋에서 이미지를 m개 샘플링한다. ($x \sim p_{data}(x)$)
    3. 디스크리미네이터를 k번 업데이트 한다. (Gradient Ascent)
      (디스크리미네이터를 업데이트하는 횟수를 결정하는 파라미터 k는 고정된 값은 없고 알맞게 조정한다.)
      $\nabla_{\theta_d}~\dfrac{1}{m} \displaystyle \sum_{i=1}^{n} \bigg[ \log D_{\theta_d}~\big( x^{(i)} \big) + \log \big( 1 - D_{\theta_d}\big( G_{\theta_g} \big( z^{(i)} \big) \big) \big) \bigg]$

  2. Generator 업데이트
    1. 디스크리미네이터의 파라미터를 Freeze 시킨다.
    2. Gaussian 분포 $\mathcal{N} (0, 1)$에서 랜덤 노이즈 벡터를 m개 샘플링한다. ($z \sim p_{g}(z)$)
    3. 제너레이터를 업데이트 한다. (Gradient Ascent)
      $\nabla_{\theta_g}~\dfrac{1}{m} \displaystyle \sum_{i=1}^{n} \log \big( D_{\theta_d}\big( G_{\theta_g} \big( z \big) \big) \big)$

  3. 1과 2를 반복한다.
 

Summary of GAN

GAN은 Explicit한 Density Function을 사용하지 않습니다.
대신에 게임 이론에 입각하여 문제를 해결합니다.

  • Pros
    • 출력물의 결과가 좋다.
  • Cons
    • 학습이 어렵다.
    • feature representation에는 사용할 수 없다.
 

Other objective functions of GAN

위에 설명한 Objective Function은 Vanilla GAN (2014)의 Function 입니다.
최근에는 이 Function을 사용했을 때에 Model Collapse가 자주 발생한다고 합니다.
(Model Collapse: 모델이 완전히 잘못된 데이터를 생성하는 현상)

이에 따라 더 안정적으로 트레이닝할 수 있는 새로운 Objective Function을 사용하는 시도가 있고, GAN 분야에서의 연구과제 입니다.
이러한 시도를 통해 발표된 최근의 State-of-the-art Model은 WGAN(Wasserstein GAN), WGAN-GP, LSGAN 등이 있습니다.

Active areas of research
더 좋은 objective function을 사용하거나 더 안정적인 학습방법: Wasserstein GAN, LSGAN, ...
Property control을 할 수 있는 모델: Conditional GAN

다양한 GAN 모델의 Implementation: The GAN Zoo

 

DCGAN (Deep Convolutional GAN)

초기 GAN의 아키텍처는 MLP 만을 사용했습니다.
그러나 이후에는 이미지를 생성하는데 알맞도록, MLP 대신에 Convolution Layer를 사용한 모델을 사용하기 시작했습니다.

디스크리미네이터는 일반적인 CNN 모델과 다르지 않습니다.
하지만 제너레이터는 Latent vector z로부터 고차원의 이미지를 만들기 위한 네트워크이므로, Upsampling Network가 되어야 합니다.
(이를 위해 Transposed Conv Layer(= Fractional-strided Conv Layer)를 사용합니다.)

  • Architecture guidelines for stable DCGAN
    • pooling layer를 디스크리미네이터는 strided conv, 제너레이터는 fractional-strided conv로 바꾼다.
    • 디스크리미네이터와 제너레이터에 BatchNorm을 사용한다.
    • FC 은닉층들을 제거한다.
    • 아웃풋 레이어를 제외하고, 제너레이터에 있는 모든 레이어에 ReLU 액티베이션 한다. (아웃풋 레이어는 Tanh를 사용)
    • 디스크리미네이터에 있는 모든 레이어에 LeakyReLU 액티베이션 한다.

Quote: Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks (arXiv:1511.06434v2 [cs.LG] 7 Jan 2016)

'Generative > Study' 카테고리의 다른 글

(2) VAE (Variational AutoEncoder)  (12) 2020.01.18
(1) Introduction of Generative Models  (0) 2020.01.18
(0) Overview of Generative Models  (0) 2020.01.14
Comments