Colab으로 하루에 하나씩 딥러닝

생성모델_3.GAN 본문

딥러닝_개념

생성모델_3.GAN

Elleik 2023. 2. 7. 23:50
728x90

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

  • 판별자를 먼저 학습시킨 후 생성자를 학습시키는 과정을 반복하여 서로 적대적인 경쟁자로 인식하여 모두 발전함
  • 생성자는 진짜 이미지에 완벽하게 가까운 이미지를 만들고, 판별자는 실제와 구분할 수 없음
  • 판별자와 생성자의 파라미터를 번갈아 가며 업데이트하여 학습시킴

GAN 구성요소

1. 생성자(Generator)

  • 판별자가 구별할 수 없을 만큼 진짜와 같은 모조 이미지를 노이즈 데이터를 사용하여 만듦
  • 경찰과 위조지폐범 사이에서 '위조지폐범' 역할

2. 판별자(Discriminator)

  • 주어진 입력 이미지가 진짜 이미지인지 가짜 이미지인지 구별
  • 경찰과 위조지폐범 사이에서 '경찰' 역할

GAN 실습

### 라이브러리 호출

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import os 
from tensorflow.keras.layers import Reshape,Dense,Dropout,LeakyReLU,Conv2DTranspose,Conv2D,Flatten,BatchNormalization
from tensorflow.keras import Model, Sequential
from tensorflow.keras.datasets import mnist
import time
from IPython import display

### MNIST 데이터셋 내려받기 및 전처리

(x_train, y_train), (x_test, y_test) = mnist.load_data()  # load_data()를 사용하여 MNIST 데이터셋을 내려받음
train_images = x_train.reshape(x_train.shape[0],28,28,1).astype('float32')
train_images = (train_images -127.5) / 127.5
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(60000).batch(256)  # 벡터 값들을 섞고, 이미지 256개의 배치 단위로 데이터를 메모리로 불러옴

데이터셋 내려받기 완료


### 생성자 네트워크 생성

def create_generator():
    generator=Sequential()
    generator.add(Dense(7*7*256, input_dim=100))  # 설명 1
    generator.add(LeakyReLU(0.2))
    
    
    generator.add(Reshape((7, 7, 256)))
    
    generator.add(Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=True))  # 설명 2
    generator.add(BatchNormalization()) # 이미지에서 노이즈를 줄이는 정규화 목적으로 사용 
    generator.add(LeakyReLU(0.2)) # 활성화 함수로 리키렐루 사용
    generator.add(Dropout(0.3))
       
    generator.add(Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=True))
    generator.add(BatchNormalization())
    generator.add(LeakyReLU(0.2))
    
    generator.add(Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=True, activation='tanh'))
    
    return generator
g=create_generator()
g.summary()

생성자 모델 요약

상세 설명

  • 설명 1
    • 7*7*256: 출력 뉴런(노드)의 수
    • input_dim: 입력 뉴런(노드)의 수
  • 설명 2(Conv2DTranspose는 합성곱 연삭의 역인 '전치 합성곱')
    • 128: 서로 다른 종류의 필터를 몇 개 활용할지 나타내는 것으로, 출력 모양의 깊이를 결정
    • (5,5): 커널 크기를 의미
    • strides=(1,1): 연산을 수행할 때 윈도우가 한 번에 얼마나 움직일지 의미
    • padding='same': 입력과 출력 이미지의 크기를 동일하게 맞춰줌
    • use_bias=True: 바이어스 벡터 사용

### 판별자 네트워크 생성

def create_discriminator():
    discriminator=Sequential()
    discriminator.add(Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[28, 28, 1]))  # Conv2DTranspose의 역을 계산하려고 Conv2D 사용
    discriminator.add(LeakyReLU())  # 활성화 함수는 생성자와 같은 리키렐루 사용 
    discriminator.add(Dropout(0.3)) # 훈련 데이터셋의 30%를 제외하고 훈련

    discriminator.add(Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
    discriminator.add(LeakyReLU())
    discriminator.add(Dropout(0.3))
    
    discriminator.add(Flatten())
    discriminator.add(Dense(1))
    
    return discriminator
d =create_discriminator()
d.summary()

판별자 모델 요약


### 손실 함수 정의

loss_function = tf.keras.losses.BinaryCrossentropy(from_logits=True)  # 설명 1

def D_loss(real_output, fake_output): # 판별자에 대한 오차(loss) 정의
    real_loss = loss_function(tf.ones_like(real_output), real_output) # 설명 2
    fake_loss = loss_function(tf.zeros_like(fake_output), fake_output)   # 설명 3
    final_loss = real_loss + fake_loss
    return final_loss
  
def G_loss(fake_output):  # 생성자에 대한 오차(loss) 정의
    return loss_function(tf.ones_like(fake_output), fake_output)

상세 설명

  • 설명 1
    • 모델이 확률 값을 출력하므로 BinaryCrossentropy를 사용
    • True로 설정되면 logits를 직접 사용
  • 설명 2
    • 모든 원소값이 1인 텐서를 생성 
  • 설명 3
    • 모든 원소 값이 0인 텐서를 생성 

### 옵티마이저 정의

G_optimizer = tf.keras.optimizers.Adam()
D_optimizer = tf.keras.optimizers.Adam()

### 파라미터의 초깃값 설정

noise_dim = 100
num_of_generated_examples = 16
BATCH_SIZE = 1

seed = tf.random.normal([num_of_generated_examples, noise_dim]) # 정규 분포의 난수를 생성한 수 seed에 저장

### 모델 훈련을 위한 파이프라인 정의

generator = create_generator()
discriminator = create_discriminator()

@tf.function
def train_step(images):
    noise = tf.random.normal([BATCH_SIZE, noise_dim])

    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape: # 설명 1
        generated_images = generator(noise, training=True)

        real_output = discriminator(images, training=True)  # 실제 이미지를 사용하여 판별자 훈련 루 판별 값 반환
        fake_output = discriminator(generated_images, training=True)  # 모조 이미지를 사용하여 판별자 훈련 후 판별 값 반환
        gen_loss = G_loss(fake_output)  # 생성자에 대한 오차 계산
        disc_loss = D_loss(real_output, fake_output)  # 판별자에 대한 오차 계산

        gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables) # 생성자의 오차 계산
        gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables) # 판별자의 오차 계산

        G_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)) # 생성자의 기울기 계산
        D_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables)) # 판별자의 기울기 계산

상세 설명

  • 설명 1
    • GradientTape를 사용해서 주어진 입력에 대한 연산의 미분 값을 자동으로 계산

### 생성된 이미지 출력 함수 

def generate_images(model, epoch, test_input):
  predictions = model(test_input, training=False)
  fig = plt.figure(figsize=(4,4))

  for i in range(predictions.shape[0]):
      plt.subplot(4, 4, i+1)
      plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='rainbow')

### 모델 훈련 함수

def train_GAN(dataset, epochs):
    for epoch in range(epochs):
        start = time.time()
        for image_batch in dataset:
            train_step(image_batch)
        if epoch % 10 == 0:
            generate_images(generator,epoch + 1,seed)
        print ('에포크 {} 은/는 {} 초'.format(epoch + 1, time.time()-start))
    generate_images(generator,epochs,seed)

### 모델 훈련 및 생성 이미지 출력

train_GAN(train_dataset, 30)

모델 훈련시키는 과정
GAN 훈련 출력 결과

 

참고: 서지영, 『딥러닝 텐서플로 교과서』, 길벗(2022)