GAN (Generative Adversarial Nets): 생성적 적대 신경망

GAN은 생성적 적대 신경망(Generative Adversarial Network)의 약어로, 데이터 생성 및 생성 모델링에 사용되는 딥러닝 아키텍처입니다.

GAN은 생성자(Generator)와 판별자(Discriminator)라고 불리는 두 개의 신경망으로 구성되며, 이 두 신경망은 서로 경쟁하면서 학습합니다.

GAN에 대해 알아보고 파이썬 텐서 플로우로 작성하여 테스트해보겠습니다.


목차

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

출처: GAN 논문


GAN 이란?

GAN 이란?

생성적 적대 신경망(GAN)은 두 개의 주요 구성 요소인 생성자(G)와 판별자(D)로 이루어져 있습니다. 이 두 신경망은 다중 퍼셉트론으로 구현되며, GAN은 주어진 데이터를 학습하고, 그 데이터와 유사한 새로운 데이터를 생성하는 데 사용됩니다.

GAN의 핵심 아이디어는 생성자와 판별자 간의 미니맥스 게임입니다. 즉, 생성자는 가짜 데이터를 생성하여 판별자를 속이고, 판별자는 실제 데이터와 가짜 데이터를 구별하려고 노력합니다.

GAN의 목표는 생성자가 실제 데이터 분포를 학습하여 실제와 구별하기 어려운 가짜 데이터를 생성하는 것입니다.

이미지 생성 외에도 스타일 변환, 데이터 증강, 이미지 Super-Resolution, 음성 합성 및 다른 응용 분야에서 사용이 될 수 있습니다.

  1. 생성자(G):
    • 생성자는 주어진 무작위 노이즈 벡터를 입력으로 받아 실제 데이터와 유사한 데이터를 생성하는 역할을 합니다.
    • G는 학습 과정에서 생성한 데이터를 판별자(D)에게 전달하고, 이 데이터를 실제 데이터처럼 보이도록 만드는 방향으로 학습합니다.
    • G의 목표는 D를 속이는 것으로, 생성된 데이터가 실제 데이터처럼 판별되도록 노력합니다.
  2. 판별자(D):
    • 판별자는 주어진 데이터가 실제 데이터인지 생성자(G)가 생성한 가짜 데이터인지를 판별하는 역할을 합니다.
    • D는 학습 과정에서 실제 데이터와 생성자가 생성한 데이터를 구별할 수 있도록 훈련됩니다.
    • D의 목표는 실제 데이터를 식별하고 가짜 데이터를 식별하는 데 있어서 정확도를 최대화하는 것입니다.

학습 과정에서는 다음과 같은 단계를 거칩니다:

  1. 무작위 노이즈 벡터를 생성자(G)에 입력으로 주고, G는 가짜 데이터를 생성합니다.
  2. 생성된 가짜 데이터와 실제 데이터를 함께 판별자(D)에 제공합니다.
  3. 판별자(D)는 이 두 데이터를 기반으로 가짜와 실제 데이터를 판별하고 손실을 계산합니다.
  4. 생성자(G)는 판별자를 속이도록 손실을 최소화하기 위해 역전파 알고리즘을 사용하여 가중치를 업데이트합니다.
  5. 판별자(D)도 자신의 손실을 최소화하기 위해 가중치를 업데이트합니다.

이런 과정을 반복하면 생성자는 점차적으로 실제 데이터와 유사한 데이터를 생성하도록 학습하고, 판별자는 생성된 가짜 데이터와 실제 데이터를 더욱 정확하게 판별하도록 학습합니다. GAN은 이 두 신경망 간의 경쟁을 통해 데이터 생성과 판별 능력을 향상시키는 강력한 모델 중 하나입니다.


GAN 이외의 다양한 생성 모델 종류와 비교

  1. Restricted Boltzmann Machines (RBMs):
    • RBMs은 확률적인 생성 모델로, GAN과 유사한 점은 있지만 GAN보다 복잡한 확률 분포 모델을 학습하기 어려울 수 있습니다.
    • GAN은 생성자와 판별자 간의 경쟁으로 학습되는 반면, RBMs는 에너지 기반 확률 모델로 학습됩니다.
  2. Deep Boltzmann Machines (DBMs):
    • DBMs은 여러 층의 제한된 볼츠만 머신(RBM)으로 구성된 생성 모델입니다.
    • DBMs는 GAN과 유사한 확률 모델링을 수행하지만, 더 복잡한 모델 구조를 가질 수 있습니다.
  3. Deep Belief Networks (DBNs):
    • DBNs은 하이브리드 모델로, 단일 비방향 레이어와 여러 방향 레이어로 구성됩니다.
    • GAN과 비교하면 DBNs는 방향과 비방향 모델의 계산적 어려움을 동시에 가질 수 있습니다.
  4. Noise-Contrastive Estimation (NCE):
    • NCE는 생성 모델의 학습을 위한 대안적인 기준 중 하나로, GAN과 유사한 방식으로 생성 모델을 학습합니다.
    • GAN은 경쟁적인 학습을 사용하고 판별자와 생성자를 별도로 훈련하는 반면, NCE는 생성 모델 자체를 사용하여 학습합니다.
  5. Generative Stochastic Network (GSN):
    • GSN은 생성 모델링의 한 방법으로, Markov 체인을 정의하는 매개변수화된 기계를 학습하는 데 사용됩니다.
    • GAN과 비교하면 GSN은 Markov 체인을 필요로하지 않으며, 생성 시에 피드백 루프가 필요하지 않기 때문에 역전파를 더 효과적으로 활용할 수 있습니다.
  6. Adversarial Nets (GAN):
    • GAN은 생성자와 판별자 간의 경쟁을 통해 생성 모델을 학습하는 기술로, GSN과 비교하면 Markov 체인을 사용하지 않습니다.
    • GAN은 피드백 루프 없이 생성하는 데 우수하며, 역전파의 성능을 향상시킬 수 있는 piecewise linear units을 활용 할 수 있습니다.

적대적 네트워크 (Adversarial nets)

적대적 모델링 프레임워크는 모델이 모두 다층 퍼셉트론인 경우 가장 직접적으로 적용할 수 있습니다.

데이터 x에 대한 생성자의 분포 p_g(x)를 학습하기 위해 입력 노이즈 변수 p_z(z)에 대한 사전 분포를 정의하고, 데이터 공간으로의 매핑을 G(z; \theta_g)로 나타냅니다. 여기서 G는 매개변수 \theta_g로 표현되는 미분 가능한 함수입니다. 또한 D(x; \theta_d)라는 두 번째 다층 퍼셉트론을 정의하며, D(x)x가 데이터에서 왔을 확률을 나타냅니다. 우리는 D를 학습하여 훈련 예제와 G에서 생성된 샘플에 올바른 레이블을 할당할 확률을 최대화합니다. 동시에 G를 학습하여 \log(1 - D(G(z)))를 최소화합니다.

다시 말해, DG는 다음과 같은 이분법적 게임(minimax game)을 플레이합니다. 이 게임의 가치 함수 V(G, D)는 다음과 같습니다:

V(G, D) = \mathbb{E}_{x\sim p_{\text{data}}(x)} [\log D(x)] + \mathbb{E}_{z\sim p_z(z)} [\log(1 - D(G(z)))] 입니다.여기서 \mathbb{E}(x)는 확률 가중 평균(Probability Weighted Average) 입니다.

V(G, D) : 이것은 적대적 네트워크(GAN)에서 사용되는 손실 함수입니다. GD는 생성자와 판별자 모델을 나타냅니다. 이 손실 함수는 생성자와 판별자 간의 경쟁적인 게임을 나타냅니다.

위의 식의 각 항들에 대한 설명입니다:

\mathbb{E} {x\sim p_{\text{data}}(x)} [\log D(x)] :
  • x는 실제 데이터(실제 데이터 분포인 p_{data}​에서 추출된)를 나타냅니다.
  • D(x)는 판별자 모델이 실제 데이터 x를 실제 데이터로 판별하는 확률을 나타냅니다. 즉, 판별자가 x가 실제 데이터로 판별될 확률을 예측한 값입니다.
  • \text{log }D(x)는 이 확률을 로그 스케일로 변환한 것입니다.
  • 이 항은 판별자의 목표를 나타냅니다. 즉, 판별자는 실제 데이터를 최대한 실제 데이터로 판별하도록 학습합니다.
\mathbb{E}_{z\sim p_z(z)} [\log(1 - D(G(z)))] :
  • z는 잠재 공간에서 무작위로 샘플링된 잡음 벡터를 나타냅니다. p_z​(z)는 잡음 벡터의 사전 분포입니다.
  • G(z)는 생성자 모델을 통해 잡음 벡터 z에서 생성된 가짜 데이터를 나타냅니다.
  • D(G(z))는 판별자 모델이 가짜 데이터 G(z)를 실제 데이터로 오인할 확률을 나타냅니다. 이는 판별자가 생성된 가짜 데이터를 얼마나 실제 데이터와 유사하다고 판단하는지를 측정합니다.
  • \text{log} (1−D(G(z)))는 판별자가 G(z)가 가짜 데이터일 확률의 로그값을 나타냅니다.
  • 이 항은 생성자의 목표를 나타냅니다. 생성자는 판별자를 속여 가짜 데이터를 최대한 실제 데이터처럼 보이게 만들려고 합니다.

훈련시 주의사항:

  • 판별자(D)를 훈련 중에 완벽하게 최적화하는 것은 계산상 불가능하며, 유한한 데이터셋에서는 과적합 문제가 발생할 수 있습니다. 판별자(D)와 생성자(G)를 번갈아가며 최적화합니다.
  • 판별자(D)를 여러 단계(보통 k 단계) 최적화한 다음 생성자(G)를 1 단계 최적화합니다.
  • SML/PCD(Stochastic Maximum Likelihood/Parallel Contrastive Divergence) 훈련에서 한 학습 단계에서 다음 학습 단계로 샘플을 초기화하지 않고 유지하는 것과 유사합니다.
  • 생성자(G)가 잘 학습되지 않을 때, \text{log}(1−D(G(z))) 항은 충분한 경사를 제공하지 못할 수 있습니다.이런 경우에는 대신 \text{log}D(G(z)) 를 최대화하여 학습을 진행합니다. 이렇게 하면 초기 학습 단계에서 더 강한 경사를 얻을 수 있습니다.

알고리즘 과정

GAN의 미니배치 확률적 경사하강법.
하이퍼 파라미터: 적용시킬 판별자의 스텝수, k
여기서는 k = 1, 최소 1부터 시작합니다.

학습 반복 횟수만큼 다음을 반복합니다:

  • k 단계에 대해 다음을 수행합니다:
    • 판별자의 학습
      • m개의 잡음 샘플 {z^{(1)}, …, z^{(m)}}를 잡음 사전 분포 p_g(z)에서 샘플링합니다.
      • m개의 예제 {x^{(1)}, …, x^{(m)}}를 데이터 생성 분포 p_{data}(x)에서 샘플링합니다.
      • 판별자를 업데이트합니다. 이를 위해 판별자의 가중치 θd 를 경사 상승 방향으로 업데이트합니다. 업데이트는 다음 손실을 최대화하도록 이루어집니다: \nabla{\theta_{d}} \frac{1}{m} \sum_{i=1}^{m} \left[ \log D(x^{(i)}) + \log \left(1 - D(G(z^{(i)}))\right) \right]
    • 생성자의 학습
      • m개의 잡음 샘플 {z^(1), …, z^(m)}를 잡음 사전 분포 pg(z)에서 샘플링합니다.
      • 생성자를 업데이트합니다. 이를 위해 생성자의 가중치 \theta_g 를 경사 하강 방향으로 업데이트합니다. 업데이트는 다음 손실을 최소화하도록 이루어집니다: \nabla_{\theta_{g}} \frac{1}{m} \sum_{i=1}^{m} \log \left(1 - D(G(z^{(i)}))\right)
  • 기울기 기반 업데이트는 표준 기울기 기반 학습 규칙을 사용할 수 있습니다. 실험에서는 모멘텀(momentum)을 사용했습니다.
    • 모멘텀 벡터 계산하기: v_{t}=\beta v_{t-1} + (1-\beta) \nabla J(\theta_t) 여기서 v_{t}는 모멘텀 벡터 \beta 는하이퍼 파라미터 (보통 0.9로 설정) \nabla J(\theta_t)는 현재 단계에서의 그래디언트 (미분) 입니다.
    • 모멘텀 벡터를 이용하여 파라미터 업데이트: \theta_{t+1}=\theta_{t} - \text{Learning Rate} \times v_t
      위의 수식은 GAN의 학습 알고리즘을 설명합니다. 판별자와 생성자가 번갈아가며 업데이트 되며, 이러한 업데이트는 판별자가 실제 데이터와 가짜 데이터를 올바르게 분류하도록 하고, 생성자가 판별자를 속이도록 하는 균형을 찾는 데 도움이 됩니다.

Global Optimality (전역 최적화) of p_{g} = p_{data}

(주요한 식들만 나열해 보겠습니다.)

  1. G가 고정된 상황에서 최적의 판별자 D:
    이 식은 생성자 G가 고정된 상황에서, 최적의 판별자 D를 나타냅니다. 이 판별자는 데이터 분포 p_{\text{data}}와 생성된 데이터 분포 p_g를 고려하여 주어진 입력 데이터 x가 진짜 데이터인지 (1을 출력) 아니면 생성된 데이터인지 (0을 출력)를 결정하는 함수입니다. 최적의 판별자는 주어진 데이터에 대해 진짜 데이터와 생성된 데이터 간의 확률 비율을 고려하여 판단합니다. D^*G(x) = \frac{p{\text{data}}(x)}{p_{\text{data}}(x) + p_g(x)}
  2. 생성자와 판별자 간의 훈련 목적 함수 C(G):
    이 함수는 생성자 G와 판별자 D 간의 훈련을 지도하기 위한 목적 함수로 사용됩니다. 이 함수는 두 부분으로 나뉘어 있으며, 첫 번째 부분은 실제 데이터 분포 p_{\text{data}}에서 샘플링된 데이터에 대한 기댓값을 계산하고 두 번째 부분은 생성된 데이터 분포 p_g에서 샘플링된 데이터에 대한 기댓값을 계산합니다. 이 함수를 최대화하는 것은 생성자 G가 실제 데이터 분포와 유사한 데이터를 생성하도록 하는 것을 목표로 합니다. C(G) = \mathbb{E}_{x \sim p_{\text{data}}} \left[\log \frac{p_{\text{data}}(x)}{p_{\text{data}}(x) + p_g(x)}\right] + \mathbb{E}_{x \sim p_g} \left[\log \frac{p_g(x)}{p_{\text{data}}(x) + p_g(x)}\right]
  3. C(G) 와 Jensen-Shannon 발산 \text{JSD}(p_{\text{data}} | p_g) 간의 관계:
    이 식은 목적 함수 C(G)와 Jensen-Shannon 발산 (Jensen-Shannon Divergence, JSD) 사이의 관계를 나타냅니다. C(G) = -\log(4) + 2 \cdot \text{JSD}(p_{\text{data}} | p_g) Jensen-Shannon 발산은 두 확률 분포 간의 유사도를 측정하는 지표로, 두 분포가 유사할수록 값이 작아집니다. 이 관계식은 목적 함수 C(G)를 Jensen-Shannon 발산과 상수 -\log(4)의 합으로 나타냅니다. Jensen-Shannon 발산 (Jensen-Shannon Divergence):
    Jensen-Shannon 발산은 두 확률 분포 P(x)Q(x) 사이의 발산을 측정하는 방법 중 하나로, 두 분포의 평균 분포와의 발산을 나타냅니다. Jensen-Shannon 발산은 다음과 같이 정의됩니다: JS(P∥Q)=\frac{1}{2} D(P∥M)+\frac{1}{2}​D(Q∥M)
    및 의 식의 MPQ의 평균 분포를 나타냅니다: M(x)=\frac{1}{2} ​(P(x)+Q(x))
    Jensen-Shannon 발산은 항상 0 이상의 값을 가지며, 두 분포가 정확히 같을 때에만 0 이 됩니다. Jensen-Shannon 발산은 발산의 대칭성을 가지며, 두 분포가 얼마나 유사한지를 측정하는 데 사용됩니다. 따라서 GAN(Generative Adversarial Network)에서 생성자와 실제 데이터 분포 간의 유사성을 측정하는 데 많이 활용됩니다.
  4. C(G)의 전역 최소값:
    이 식은 목적 함수 C(G)가 최소값을 가질 때의 값을 나타냅니다. C^*C(G)의 최소값을 나타내며, 이 값은 p_gp_{\text{data}}와 거의 일치할 때 달성됩니다. 이것은 생성자가 실제 데이터 분포를 거의 완벽하게 모방했을 때의 상황을 나타내며, C^*는 목적 함수가 최소가 되는 값으로 정의됩니다. 즉, 생성된 데이터 분포 p_g가 실제 데이터 분포 p_{\text{data}}와 구별할 수 없을 때 목적 함수가 최소값이 됩니다. 이 최소값을 구체적으로 계산하면 C^* = -\log(4)가 됩니다. 이 값일 때, 생성된 분포와 실제 분포가 완전히 일치하고 목적 함수 C(G)는 최소가 됩니다.

파이썬 과 텐서플로우 라이브러리를 이용한 GAN 구현

GAN 논문을 바탕으로 파이썬과 텐서 플로우로 구현을 시작해 보겠습니다. 이 예제에서는 기본적인 Mnist 데이터 셋을 이용하였습니다.

1. 라이브러리 임포트

우선 기본적으로 필요한 라이브러리를 불러옵니다.

Python
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf

2. 생성자와 판별자 메서드.

텐서플로우의 케라스를 기준으로 생성자와 판별자의 모델을 정의 했습니다.

Python
# 생성자 구현
def build_generator(input_dim, output_dim, hidden_dim):
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(hidden_dim, input_dim=input_dim, activation='relu'))
    model.add(tf.keras.layers.Dense(output_dim, activation='sigmoid'))
    return model

# 판별자 구현
def build_discriminator(input_dim, hidden_dim):
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(hidden_dim, input_dim=input_dim, activation='relu'))
    model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
    return model

3. 초기화

초기에 필요한 값들을 초기화 해 줍니다.

Python
random_dim = 100  # 잡음 벡터의 차원
data_dim = 784  # MNIST 데이터의 차원
hidden_dim = 128  # 은닉층의 차원
batch_size = 64  # 미니배치 크기
epochs = 20000  # 학습 에포크 수

# 생성자와 판별자 모델 생성
generator = build_generator(random_dim, data_dim, hidden_dim)
discriminator = build_discriminator(data_dim, hidden_dim)

# 판별자 컴파일
discriminator_optimizer = tf.keras.optimizers.SGD(learning_rate=0.001, momentum=0.9)  # 모멘텀 옵티마이저 설정
discriminator.compile(loss='binary_crossentropy', optimizer=discriminator_optimizer)

# GAN 모델 생성
discriminator.trainable = False  # 판별자는 학습하지 않도록 설정
gan_input = tf.keras.Input(shape=(random_dim,))
x = generator(gan_input)
gan_output = discriminator(x)
gan = tf.keras.Model(gan_input, gan_output)

gan_optimizer = tf.keras.optimizers.SGD(learning_rate=0.001, momentum=0.9)  # 모멘텀 옵티마이저 설정
gan.compile(loss='binary_crossentropy', optimizer=gan_optimizer)

4. 자료 로드

학습할 자료를 로드합니다. 여기서는 기본으로 Mnist데이터를 이용했습니다.

Python
# MNIST 데이터 로드
(X_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
X_train = X_train / 255.0  # 0~1 범위로 정규화
X_train = X_train.reshape(X_train.shape[0], data_dim)

5. 학습 하기

이제 학습을 시작하면 됩니다.

Python

# GAN 학습
for epoch in range(epochs):
    # 판별자 학습
    noise = np.random.normal(0, 1, size=[batch_size, random_dim])  # 잡음 생성
    generated_data = generator.predict(noise)  # 생성자로 가짜 데이터 생성

    real_data = X_train[np.random.randint(0, X_train.shape[0], size=batch_size)]  # 실제 데이터 샘플링

    X = np.concatenate([real_data, generated_data])  # 실제 데이터와 가짜 데이터 병합
    y_dis = np.zeros(2 * batch_size)
    y_dis[:batch_size] = 1  # 실제 데이터는 1로 레이블

    discriminator.trainable = True
    d_loss = discriminator.train_on_batch(X, y_dis)  # 판별자 학습

    # 생성자 학습
    noise = np.random.normal(0, 1, size=[batch_size, random_dim])
    y_gen = np.ones(batch_size)  # 생성자는 가짜 데이터를 생성하므로 레이블은 1

    discriminator.trainable = False
    g_loss = gan.train_on_batch(noise, y_gen)  # 생성자 학습

    # 매 1000 에포크마다 결과 출력
    if epoch % 100 == 0:
        print(f"Epoch {epoch}, D Loss: {d_loss}, G Loss: {g_loss}")

        # 생성된 이미지를 저장
        # 생성된 이미지를 저장
        generated_images = generator.predict(noise)

        # 이미지를 4x4 배열로 표시
        fig, axes = plt.subplots(4, 4, figsize=(8, 8))
        for i in range(4):
            for j in range(4):
                ax = axes[i, j]
                ax.imshow(generated_images[i * 4 + j].reshape(28, 28), interpolation='nearest', cmap='gray_r')
                ax.axis('off')

        plt.subplots_adjust(wspace=0.1, hspace=0.1)
        plt.savefig(f"gan_generated_image_epoch_{epoch}.png")
        plt.close()

6. 결과 확인

이제 학습한 결과를 보면 아래와 같은 결과가 나오게 됩니다.


결론

“이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.”

GAN (Generative Adversarial Network)은 생성자와 판별자로 이루어진 딥러닝 아키텍처로, 데이터 생성과 생성 모델링에 사용됩니다. 생성자는 가짜 데이터를 생성하고, 판별자는 실제 데이터와 가짜 데이터를 구별하려고 학습합니다. 이를 통해 생성자는 실제 데이터와 유사한 가짜 데이터를 생성하도록 학습하고, 판별자는 더욱 정확하게 가짜 데이터와 실제 데이터를 판별하도록 학습합니다. GAN은 이미지 생성부터 스타일 변환, 데이터 증강, 이미지 Super-Resolution, 음성 합성 등 다양한 응용 분야에서 사용됩니다.

또한 GAN의 훈련 과정은 어려움을 겪을 수 있으며, 모드 붕괴와 같은 문제가 발생할 수 있습니다. 이러한 문제에 대한 해결 방법과 최신 GAN 변형 모델에 대한 연구 동향이 중요합니다. 또한 GAN과 같은 생성 모델의 윤리적 고려 사항에 대한 인식도 필요합니다. 개인 정보 보호와 딥페이크와 같은 기술을 통한 위조에 대한 주의가 필요합니다.

또한 파이썬과 텐서플로우로 구현을 해보았습니다. GAN은 생성 모델링 분야에서 혁신적인 모델 중 하나로, 더욱 발전해나가며 다양한 응용 분야에 적용될 것으로 기대됩니다.


함께 참고하면 좋은 글

딥러닝 학습을 위한 텐서플로우 라이브러리(Tensorflow)

인공지능 을 위한 아나콘다 와 VS code 설치법


1 thought on “GAN (Generative Adversarial Nets): 생성적 적대 신경망”

Comments are closed.