딥러닝 기초 #16 - GAN

OilyHand·2024년 1월 26일
0

딥러닝 기초

목록 보기
16/17

1. GAN의 요소


생성적 적대 신경망(Generative Adversarial Networks, GAN)은 딥러닝을 이용하여 가상 이미지를 생성하는 알고리즘입니다. GAN은 생성자와 판별자의 적대적 경합을 이용하여 이미지를 생성합니다.

1.1. 생성자

생성자(generator)는 가상의 이미지를 만들어내는 것입니다. 랜덤한 픽셀값으로 채워진 이미지로 시작하여 판별자의 판별 결과에 따라 이미지를 수정하면서 목표로 하는 이미지를 만들어냅니다.

1.1.1. DCGAN

DCGAN(Deep Convolution GAN)은 컨볼루션 신경망을 이용하여 초기 GAN의 여러 불안정한 점들을 보완한 GAN 알고리즘입니다. 이전에 다루었던 컨볼루션 신경망에서는 옵티마이저를 사용하는 최적화 및 컴파일 과정이 있었지만 DCGAN에서는 생성자에서 판별과 학습이 이루어지지 않아 그러한 과정들이 필요없습니다.

또한 GAN의 컨볼루션 신경망에서는 이미지의 크기가 줄어드는 풀링이 아닌 패딩(padding)을 사용하여 연산을 진행합니다.

1.1.2. Batch Normalization

배치 정규화(Batch Normalization)은 입력 데이터의 평균이 0, 분산이 1이 되도록 재배치하는 것입니다. 이 과정을 추가하면 층의 수가 많아도 안정적인 학습을 진행할 수 있습니다.

1.1.3. 생성자 구조

위의 과정을 적용한 생성자의 구조를 Keras API를 이용한 코드로 작성하면 다음과 같습니다.

# generator라는 이름으로 모델 생성
generator = Sequential()

## 입력층
# 128*7*7 : (임의로 정한 노드의 수) * (이미지 사이즈)
	# 이미지 사이즈는 Conv2DTranspose를 거치면서 두배씩 확장됨
# input_dim=100 (100차원 크기의 랜덤 벡터를 준비)
generator.add(Dense(128*7*7, input_dim=100, activation=LeakyReLU(0.2)))

## 배치 정규화 층
generator.add(BatchNormalization()

## Reshape 층
# 컨볼루션 층에서 사용할 수 있는 형태로 변환
generator.add(Reshape((7, 7, 128)))

## 컨볼루션 층
# 두 배씩 업 샘플링을 한 후 컨볼루션 계산을 진행
# zero-padding을 사용
generator.add(Conv2DTranspose(64, kernel_size=5, padding='same'))

## 배치 정규화 층
generator.add(BatchNormalization())

## 활성화함수
generator.add(Activation(LeakyReLU(0.2)))

## 컨볼루션 층
# 활성화 함수로 쌍곡 탄젠트 함수를 사용
generator.add(Conv2DTranspose(1, kernel_size=5, padding='same', activation='tanh'))

 

1.2. 판별자

판별자(discriminator)는 생성자가 만든 이미지가 진짜인지 가짜인지 판별하는 부분입니다. 이미지를 판별하는 부분이므로 이전에 사용했던 CNN 구조를 사용할 수 있습니다. 하지만 판별자는 진위를 가려주는 역할만 하고 자기 자신이 학습을 진행하면 안됩니다. 판별자가 얻은 가중치를 스스로 학습하는 데 사용하지 않고 생성자에게 넘겨주기만 해야 합니다.

1.2.1. 판별자 구조

CNN 구조에 위의 상황을 고려하여 Keras API를 이용한 코드로 작성하면 다음과 같습니다.

# disriminator라는 이름으로 모델 생성
discriminator = Sequential()

## 컨볼루션 층
# zero-padding
discriminator.add(Conv2D(64, kernel_size=5, strides=2, input_shape=(28,28,1), padding="same"))
# LeakyReLU 활성화 함수 사용
discriminator.add(Activation(LeakyReLU(0.2)))
# 드롭아웃 비율을 0.3으로 설정
discriminator.add(Dropout(0.3))

## 컨볼루션 층
# zero-padding
discriminator.add(Conv2D(128, kernel_size=5, strides=2, padding="same"))
# LeakyReLU 활성화 함수 사용
discriminator.add(Activation(LeakyReLU(0.2)))
# 드롭아웃 비율을 0.3으로 설정
discriminator.add(Dropout(0.3))

## 출력층
# 1차원으로 변환
discriminator.add(Flatten())
# 참, 거짓 판별
discriminator.add(Dense(1, activation='sigmoid'))

## 모델 컴파일
discriminator.compile(loss='binary_crossentropy', optimizer='adam')
#학습 기능 꺼주기
discriminator.trainable = False

 

1.3. 적대적 신경망

적대적 신경망 부분에서는 생성자와 판별자를 연결하여 학습을 진행하며 여러 옵션을 설정합니다.
생성자에서 나온 결과를 판별자가 판별을 하고 판별 결과를 다시 생성자에게 전달하여 정확도가 0.5에 가까워질 때까지 생성하고 판별하고를 반복합니다.

1.3.1. 적대적 신경망 구조

(1) Keras의 Input() 함수를 이용하여 랜덤한 100개의 백터를 만들어 생성자의 입력을 생성한다.

ginput = Input(shape=(100,))

 
(2) 생성자에 ginput을 입력하고 출력을 판별자의 입력으로 넘긴다.

dis_output = discriminator(generator(ginput))

 
(3) Keras의 Model() 함수를 이용하여 ginput값과 dis_out 값을 넣어 gan이라는 이름의 모델을 만든다.

gan = Model(ginput, dis_output)

 
(4) 모델을 컴파일 한다.

  • 손실함수: 이진 크로스엔트로피 함수
  • 옵티마이저: adam
gan.compile(loss='binary_crossentropy', optimizer='adam')

 

1.3.2. 학습 실행 함수

학습의 진행은 다음과 같이 함수를 만들어 MNIST 손글씨 데이터를 이용하여 학습을 진행하도록 합니다. 함수의 구조는 다음과 같습니다.

변수

get_train(epoch, batch_size, saving_interval)

  • epoch : 학습을 반복할 횟수
  • batch_size : 한 번에 모델이 학습하는 데이터 샘플의 개수
  • saving_interval : 저장 간격

함수 내용

def gan_train(epoch, batch_size, saving_interval):
## 데이터 전처리
    # 이미지 데이터 가져오기
    (X_train, _), (_, _) = mnist.load_data()
    
    # 흑백 변환 및 표준화
    X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32')
    
    # -1에서 1 사이의 값으로 변환
    X_train = (X_train - 127.5) / 127.5
    
## 실제 이미지에 대한 판별자 연결
    # 실제 이미지에 대해 참(1)이라는 레이블을 붙이기
    true = np.ones((batch_size, 1))
	
    # 불러올 이미지의 인덱스를 랜덤 추출
    idx = np.random.randint(0, X_train.shape[0], batch_size)
	
	# 랜덤 추출된 인덱스의 이미지 불러오기
    imgs = X_train[idx]
	
    # discriminator.train_on_batch() 함수를 써서 판별을 시작
    d_loss_real = discriminator.train_on_batch(imgs, true)
    
## 가상 이미지에 대한 판별자 연결
	# 가상 이미지에 대해 거짓(0)이라는 레이블을 붙이기
    fake = np.zeros((batch_size, 1))
    
    # 생성자의 입력으로 넣을 노이즈 이미지 생성
      # 0~1 사이의 값을 batch_size만큼 100열을 뽑기
    noise = np.random.normal(0, 1, (batch_size, 100))
    
    # 생성자에서 가상 이미지 생성
    gen_imgs = generator.predict(noise)
    
    # discriminator.train_on_batch() 함수를 써서 판별을 시작
    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)

 

 


2. GAN 모델


2.1. 전체 코드

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
import os

# 생성자 모델
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 실행 함수
def gan_train(epoch, batch_size, saving_interval):
	# 데이터 전처리
    (X_train, _), (_, _) = mnist.load_data()
    X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32')
    X_train = (X_train - 127.5) / 127.5

    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)

		 # saving_interval 마다 이미지 저장
        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
            
            if not os.path.exists("./gan_images/"):
                os.mkdir("./gan_images/")
            
            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_images/gan_mnist_%d.png" % i)

# 전체 모델 실행
gan_train(2001, 32, 200)

2.2. 실행 결과

GAN 모델을 이용하여 생성된 이미지는 다음과 같습니다. 학습을 거듭할수록 점점 숫자와 가까운 이미지를 생성하는 것을 확인할 수 있습니다.

  • Epoch: 0
  • Epoch: 200
  • Epoch: 400
  • Epoch: 600
  • Epoch: 800
  • Epoch: 1000
  • Epoch: 1200
  • Epoch: 1400
  • Epoch: 1600
  • Epoch: 1800
  • Epoch: 2000

 

 


Reference
- 해당 글은 "모두의 딥러닝" 19장을 기반으로 작성되었습니다.

  1. 조태호. 모두의 딥러닝 (개정3판). 길벗(2022)
profile
Electrical Engineering

0개의 댓글