Data Analyst KIM

[Deep Learning] GAN이란 무엇인가? 본문

데이터 분석/ML&DL&NLP

[Deep Learning] GAN이란 무엇인가?

김두연 2023. 10. 26. 17:06
반응형

생성적 적대 신경망(Generative Adversarial Network,GAN)

  • 비지도학습 알고리즘
  • 딥러닝의원리를 활용해 가상의 이미지를 생성하는 알고리즘
  • 적대 (Adversarial, 서로 대립 관계에 있는)이란 단어는 GAN 알고리즘의성격을 잘 표현함
  • 진짜 같은 가짜 이미지를 생성하기 위해 GAN 알고리즘 내부에서는‘적대적’인경합을 진행하기 때문임

 

생성자(Generator)
  • 가짜 이미지를만들어 내는 공장
  • 초기에는 무작위한 픽셀 값으로 채워진 가짜 이미지로 시작해서 판별 결과에 따라 지속적으로 학습
  • 점차 진짜 같은 가짜 이미지를 생성
  • 페이스북의 AI 연구팀이 만들어 발표한 DCGAN은 생성자 신경망으로 CNN을 사용
  • CNN 구조의 생성자
    • 패딩 : 이미지 크기를 조절하는 이유는 판별자가 비교할 '진짜'와 똑같은 크기여야 하기 때문임
    • 배치 정규화 : 입력 데이터의 평균이 0, 분산이 1이 되도록 재배치하는 것, 다음 층으로 입력될 값을 일정하게 재배치하는 역할 / BatchNomalization() 사용
    • 활성화 함 : 생성자 활성화 함수는 ReLU()함수 사용 / 판별자로 넘겨 주기 직전에는 tanh() 함수 사용

 

생성자 모델 만들기

 

# 생성자 모델 만들기
generator = Sequential()
generator.add(Dense(128*7*7, input_dim=100, activation=LeakyReLU(0.2)))
generator.add(BatchNormalization())

generator.add(Reshape((7, 7, 128)))
generator.add(UpSampling2D())
generator.add(Conv2D(64, kernel_size=5, padding='same'))
generator.add(BatchNormalization())

generator.add(Activation(LeakyReLU(0.2)))
generator.add(UpSampling2D())
generator.add(Conv2D(1, kernel_size=5, padding='same', activation='tanh'))

128*7*7 : 노드(128) / 이미지 최초 크기(7*7)
imput_dim=100 : 100차원 크기의 랜덤 벡터를 준비해 집어넣음
Reshape : 컨볼루션 레이어가 받아들일 수 있는 형태로 바꾸기
UpSamling2D : 이미지 크기를 7*7 → 14*14로 만들어줌
padding='same' : 모자라는 부분은 자동으로 0을 채움

 

활성화 함수 : LeakyReLU 사용
ReLU를 사용할 경우 학습이 불안정해지는 경우가 많음

마지막 활성화 함수 : tanh 사용
컨볼루션 과정을 거친 후 판별자로 값을 넘길 준비를 마침


판별자(Discriminator)
  • 이미지가 가짜인지진짜인지를 판별해주는 장치
  • CNN 구조를 사용
  • 손실 함수: binary_crossentropy
  • 최적화 함수 : adam
  • 주의할 점 : 판별자를만들 때는 가중치를 저장하는 학습 기능을 꺼주어야함
    • 판별자는 진위 여부만 판단하기 때문에 자기 자신이 학습을 하면 안됨
    • 판별자가 얻은 가중치는 판별자 자신이 학습하는데 쓰이는 것이 아니라, 생성자로 넘겨서 생성자가 업데이트된 이미지를 만들도록 해야함

 

판별자 모델 만들기

 

노드 수 : 64 , 128개
커널 크기 : 5
stride : 2칸씩 이동하는 옵션
활성화 함수 : LeakyReLU()
flatten() : 2차원 → 1차원
마지막 활성화 함수 : sigmoid()

sigmoid() 쓰는 이유
- 판별 결과가 둘 중에 하나가 되야하므로

손실함수 : binary_crossentropy
최적화 함수 : adam

학습 기능 끄기 : 
discriminator.trainable = False

 


적대적 신경망 실행하기
  • 생성자와 판별자를 연결시킨다는 것은 진위 여부를 판별하게 만든다는 뜻임

 

생성자와 판별자 모델을 연결시키는 gan 모델 만들기

# 생성자와 판별자 모델을 연결시키는 gan 모델 만들기
ginput = Input(shape=(100,))
dis_output = discriminator(generator(ginput))
gan = Model(ginput, dis_output)
gan.compile(loss='binary_crossentropy', optimizer='adam')
gan.summary()

# 신경망을 실행시키는 함수를 만듭니다.
def gan_train(epoch, batch_size, saving_interval):

  # MNIST 데이터를 불러옵니다.

  (X_train, _), (_, _) = mnist.load_data()  # 앞서 불러온 적 있는 MNIST를 다시 이용합니다. 단, 테스트 과정은 필요 없고 이미지만 사용할 것이기 때문에 X_train만 불러왔습니다.
  X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32')
  X_train = (X_train - 127.5) / 127.5  # 픽셀 값은 0에서 255 사이의 값입니다. 이전에 255로 나누어 줄때는 이를 0~1 사이의 값으로 바꾸었던 것인데, 여기서는 127.5를 빼준 뒤 127.5로 나누어 줌으로 인해 -1에서 1사이의 값으로 바뀌게 됩니다.
  # X_train.shape, Y_train.shape, X_test.shape, Y_test.shape

  true = np.ones((batch_size, 1))
  fake = np.zeros((batch_size, 1))

  for i in range(epoch):
          # 실제 데이터를 판별자에 입력하는 부분입니다.
          idx = np.random.randint(0, X_train.shape[0], batch_size)
          imgs = X_train[idx]
          d_loss_real = discriminator.train_on_batch(imgs, true)

          # 가상 이미지를 판별자에 입력하는 부분입니다.
          noise = np.random.normal(0, 1, (batch_size, 100))
          gen_imgs = generator.predict(noise)
          d_loss_fake = discriminator.train_on_batch(gen_imgs, fake)

          # 판별자와 생성자의 오차를 계산합니다.
          d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
          g_loss = gan.train_on_batch(noise, true)

          print('epoch:%d' % i, ' d_loss:%.4f' % d_loss, ' g_loss:%.4f' % g_loss)

        # 이 부분은 중간 과정을 이미지로 저장해 주는 부분입니다. 이 장의 주요 내용과 관련이 없어
        # 소스 코드만 첨부합니다. 만들어진 이미지들은 gan_images 폴더에 저장됩니다.
          if i % saving_interval == 0:
              #r, c = 5, 5
              noise = np.random.normal(0, 1, (25, 100))
              gen_imgs = generator.predict(noise)

              # Rescale images 0 - 1
              gen_imgs = 0.5 * gen_imgs + 0.5

              fig, axs = plt.subplots(5, 5)
              count = 0
              for j in range(5):
                  for k in range(5):
                      axs[j, k].imshow(gen_imgs[count, :, :, 0], cmap='gray')
                      axs[j, k].axis('off')
                      count += 1
              fig.savefig("./gan_mnist_%d.png" % i)

gan_train(2001, 32, 200)  # 2000번 반복되고, 배치 사이즈는 32,  200번마다 결과가 저장되게 하였습니다.

 

(전체코드 더보기 클릭)

더보기
from tensorflow.keras.datasets import mnist
from tensorflow.keras.layers import Input, Dense, Reshape, Flatten, Dropout
from tensorflow.keras.layers import BatchNormalization, Activation, LeakyReLU, UpSampling2D, Conv2D
from tensorflow.keras.models import Sequential, Model

import numpy as np
import matplotlib.pyplot as plt

# 생성자 모델 만들기
generator = Sequential()
generator.add(Dense(128*7*7, input_dim=100, activation=LeakyReLU(0.2)))
generator.add(BatchNormalization())
generator.add(Reshape((7, 7, 128)))
generator.add(UpSampling2D())
generator.add(Conv2D(64, kernel_size=5, padding='same'))
generator.add(BatchNormalization())
generator.add(Activation(LeakyReLU(0.2)))
generator.add(UpSampling2D())
generator.add(Conv2D(1, kernel_size=5, padding='same', activation='tanh'))

# 판별자 모델 만들기
discriminator = Sequential()
discriminator.add(Conv2D(64, kernel_size=5, strides=2, input_shape=(28,28,1), padding="same"))
discriminator.add(Activation(LeakyReLU(0.2)))
discriminator.add(Dropout(0.3))
discriminator.add(Conv2D(128, kernel_size=5, strides=2, padding="same"))
discriminator.add(Activation(LeakyReLU(0.2)))
discriminator.add(Dropout(0.3))
discriminator.add(Flatten())
discriminator.add(Dense(1, activation='sigmoid'))
discriminator.compile(loss='binary_crossentropy', optimizer='adam')
discriminator.trainable = False

# 생성자와 판별자 모델을 연결시키는 gan 모델 만들기
ginput = Input(shape=(100,))
dis_output = discriminator(generator(ginput))
gan = Model(ginput, dis_output)
gan.compile(loss='binary_crossentropy', optimizer='adam')
gan.summary()

# 신경망을 실행시키는 함수를 만듭니다.
def gan_train(epoch, batch_size, saving_interval):

  # MNIST 데이터를 불러옵니다.

  (X_train, _), (_, _) = mnist.load_data()  # 앞서 불러온 적 있는 MNIST를 다시 이용합니다. 단, 테스트 과정은 필요 없고 이미지만 사용할 것이기 때문에 X_train만 불러왔습니다.
  X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32')
  X_train = (X_train - 127.5) / 127.5  # 픽셀 값은 0에서 255 사이의 값입니다. 이전에 255로 나누어 줄때는 이를 0~1 사이의 값으로 바꾸었던 것인데, 여기서는 127.5를 빼준 뒤 127.5로 나누어 줌으로 인해 -1에서 1사이의 값으로 바뀌게 됩니다.
  # X_train.shape, Y_train.shape, X_test.shape, Y_test.shape

  true = np.ones((batch_size, 1))
  fake = np.zeros((batch_size, 1))

  for i in range(epoch):
          # 실제 데이터를 판별자에 입력하는 부분입니다.
          idx = np.random.randint(0, X_train.shape[0], batch_size)
          imgs = X_train[idx]
          d_loss_real = discriminator.train_on_batch(imgs, true)

          # 가상 이미지를 판별자에 입력하는 부분입니다.
          noise = np.random.normal(0, 1, (batch_size, 100))
          gen_imgs = generator.predict(noise)
          d_loss_fake = discriminator.train_on_batch(gen_imgs, fake)

          # 판별자와 생성자의 오차를 계산합니다.
          d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
          g_loss = gan.train_on_batch(noise, true)

          print('epoch:%d' % i, ' d_loss:%.4f' % d_loss, ' g_loss:%.4f' % g_loss)

        # 이 부분은 중간 과정을 이미지로 저장해 주는 부분입니다. 이 장의 주요 내용과 관련이 없어
        # 소스 코드만 첨부합니다. 만들어진 이미지들은 gan_images 폴더에 저장됩니다.
          if i % saving_interval == 0:
              #r, c = 5, 5
              noise = np.random.normal(0, 1, (25, 100))
              gen_imgs = generator.predict(noise)

              # Rescale images 0 - 1
              gen_imgs = 0.5 * gen_imgs + 0.5

              fig, axs = plt.subplots(5, 5)
              count = 0
              for j in range(5):
                  for k in range(5):
                      axs[j, k].imshow(gen_imgs[count, :, :, 0], cmap='gray')
                      axs[j, k].axis('off')
                      count += 1
              fig.savefig("./gan_mnist_%d.png" % i)

gan_train(2001, 32, 200)  # 2000번 반복되고, 배치 사이즈는 32,  200번마다 결과가 저장되게 하였습니다.
반응형