Generative Adversarial Network (GAN)
🗝 Keyword
📑 Concept
GAN(Generative Adversarial Networks)
-
GAN(Generative Adversarial Networks, 생성적 적대 신경망)
-
실제와 유사한 데이터를 만들어내는 생성 모델
-
like 위조지폐
GAN(Generative Adversarial Networks, 생성적 적대 신경망)
실제와 유사한 데이터를 만들어내는 생성 모델
like 위조지폐
생성자(Generator)
-
실제와 동일한 데이터를 생성하는 것이 목적
-
=> 비지도 학습
-
위조지폐범
-
G
판별자(Discriminator)
-
생성된 데이터가 진짜(
Real
)인지 아닌지(Fake
) 판단 -
=> 지도 학습
-
Fake / Real로 구분
-> 이진 분류 -
like 진품명품
-
탐정
-
D
- GAN
=>생성자
와판별자
가 서로 경쟁하며 성능을 개선하는 모델
GAN의 목적
-
'분포'를 만드는 모델을 학습하는 것
-
분포
=> 데이터의 형태, 분산 -
ex) 픽셀들의 분포에 따라 코, 눈이라는 것을 인식.
명암이나 사진의 전체적인 채도와는 큰 상관 X.
-> 분포를 만들어낸다는 것은, 단순히 결과값을 도출해내는 함수를 만드는 것을 넘어 '실제적인 형태' 를 갖춘 데이터를 만들어낸다는 것 -
학습을 잘 마친 GAN 모델에서
생성자
는 실제와 유사한 이미지를 생성
->
판별자
가 실제(Real)와 가짜(Fake)를 잘 구분해내지 못하게 됨
=> Accuracy 0.5
-> (이진 분류니까 확률이 1/2 이라서)
GAN의 학습 방법
⏪ Review
-
GAN
- 비유를 들어 설명
- 생성자(Generator)는 무엇을 입력받아 무엇을 출력?
- 판별자(Discriminator)는 무엇을 입력받아 무엇을 출력?
-
CycleGAN
- CycleGAN 이 기존
Pix2Pix
에 비해 갖는 장점은?
- 왜 A와 B가 순환되는 형태로 모델이 구성?
GAN
- 비유를 들어 설명
- 생성자(Generator)는 무엇을 입력받아 무엇을 출력?
- 판별자(Discriminator)는 무엇을 입력받아 무엇을 출력?
CycleGAN
- CycleGAN 이 기존
Pix2Pix
에 비해 갖는 장점은? - 왜 A와 B가 순환되는 형태로 모델이 구성?
💻 Code
Example - DCGAN(Deep Convolution GAN)
GAN을 통해 MNIST 손글씨 데이터 생성
# 필요한 라이브러리 import
from tensorflow.keras import layers
from IPython import display
import glob
import imageio
import os
import PIL
import time
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
# 데이터셋 불러오기
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()
# -> Test 데이터셋은 사용하지 않기 때문에 '_' 로 처리
# 이미지 정규화
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5 # 이미지를 [-1, 1]로 정규화
# 이미지를 [-1, 1]의 범위로 정규화하는 이유
# -> 활성화 함수로 tanh를 사용해서.
BUFFER_SIZE = 60000
BATCH_SIZE = 256
# 데이터 배치 생성 후 섞기
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
모델 구축
생성자
-
Random Noise 로부터 이미지를 생성하기 위해
Transpose Convolution
을 사용해 Upsampling 수행 -
첫 Dense 층은 Random Noise 를 입력받으며 원하는 이미지 사이즈인 28X28 이 나오도록
Conv2DTranspose
를 겹겹이 쌓음 -
은닉층 활성화 함수: ReLU 함수의 변형인
LeakyReLU
함수 사용 -
활성화 함수 이전에
배치 정규화(Batch Normalization)
적용
-
LeakyReLU 함수
- 음의 값이 0이 아니라 음수 값
- 리키렐루를 사용한 이유
-> 음의 값을 표현하기 위해
def make_generator_model():
"""
생성자 모델 구축 함수
"""
model = tf.keras.Sequential()
model.add(layers.Dense(7*7*256, use_bias=False, input_shape=(100,)))
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
model.add(layers.Reshape((7, 7, 256)))
assert model.output_shape == (None, 7, 7, 256) # 배치사이즈로 None이 주어짐
model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
assert model.output_shape == (None, 7, 7, 128)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
assert model.output_shape == (None, 14, 14, 64)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
assert model.output_shape == (None, 28, 28, 1)
return model
판별자
-
합성곱 신경망(Convolutional Neural Network, CNN)
기반의 이미지 분류기 -
은닉층 활성화 함수:
LeakyReLU
함수 -
드롭아웃(Dropout)
적용
def make_discriminator_model():
"""
판별자 모델 구축 함수
"""
model = tf.keras.Sequential()
model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=[28, 28, 1]))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
model.add(layers.Flatten())
model.add(layers.Dense(1))
return model
손실함수
- 생성자의 손실함수
- 전체가 1인 행렬과
fake_output
을 비교해서 구함
- 전체가 1인 행렬과
- 판별자의 손실함수
real_loss
와fake_loss
를 더한 값- -> Real, Fake 둘 다를 가지로 이진 분류하는 거라서
real_loss
와fake_loss
를 더함 real_loss
- 전체가 1인 행렬과
real_output
을 비교해서 구함
- 전체가 1인 행렬과
fake_loss
- 전체가 0인 행렬과
fake_output
을 비교해서 구함
- 전체가 0인 행렬과
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
def discriminator_loss(real_output, fake_output):
"""
판별자의 손실함수
"""
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss
return total_loss
def generator_loss(fake_output):
"""
생성자의 손실함수
"""
return cross_entropy(tf.ones_like(fake_output), fake_output)
# 옵티마이저
# -> 생성자, 판별자 모두 Adam 적용
generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
# 모델이 저장되는 Checkpoint 설정
checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,
discriminator_optimizer=discriminator_optimizer,
generator=generator,
discriminator=discriminator)
훈련 루프 지정
- 생성자가 입력으로 Random Noise 를 입력받음
- 입력받은 Random Noise 를 사용해 이미지 생성
- 판별자를 사용해 Train 데이터셋의 진짜 이미지와 생성자가 만들어낸 가짜 이미지를 분류
- 각 모델의 손실 계산, 경사 하강법과 역전파를 통해 가중치를 업데이트
EPOCHS = 50
noise_dim = 100
num_examples_to_generate = 16
seed = tf.random.normal([num_examples_to_generate, noise_dim])
@tf.function
def train_step(images):
"""
Iteration 마다 어떻게 각 모델이 갱신되는 지에 대한 함수
위에서 정의한 손실함수를 바탕으로
Iteration(=step) 마다 가중치 갱신
Args:
images: 훈련 데이터셋에 있는 실제 이미지
"""
noise = tf.random.normal([BATCH_SIZE, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
generated_images = generator(noise, training=True)
real_output = discriminator(images, training=True)
fake_output = discriminator(generated_images, training=True)
gen_loss = generator_loss(fake_output)
disc_loss = discriminator_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)
generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
def generate_and_save_images(model, epoch, test_input):
"""
모델이 이미지를 생성한 후 저장하는 함수
(중간 결과 확인하기 위해 생성된 이미지를 출력하고 저장하는 함수)
Args:
model: 이미지를 생성할 모델
epoch: 진행 중인 Epoch 숫자
test_input: model에 입력되는 데이터
"""
# training=False -> 모든 층이 추론(inference)모드로 진행
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='gray')
plt.axis('off')
plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
plt.show()
def train(dataset, epochs):
"""
학습 중 실행할 동작을 함수로 정의
Args:
dataset: (훈련) 데이터셋
epochs: 최종 학습 Epoch
"""
for epoch in range(epochs):
start = time.time()
for image_batch in dataset:
train_step(image_batch)
# 이미지를 생성한 뒤 저장(추후에 만들 GIF를 위함)
display.clear_output(wait=True)
generate_and_save_images(generator, epoch + 1, seed)
# 15 에포크가 지날 때마다 모델을 Checkpoint에 저장
if (epoch + 1) % 15 == 0:
checkpoint.save(file_prefix = checkpoint_prefix)
# Epoch 마다 소요 시간 출력
print(f'Time for epoch {epoch + 1} is {time.time()-start} sec')
# 마지막 에포크가 끝난 후 이미지 생성
display.clear_output(wait=True)
generate_and_save_images(generator, epochs, seed)
# 함수를 실행시켜 실제 훈련 진행
%%time
train(train_dataset, EPOCHS)
# 마지막 Checkpoint 복구
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))
# gif 생성
def display_image(epoch_no):
"""
특정 Epoch에 생성된 이미지를 불러오는 함수
Args:
epoch_no: 특정 Epoch에 해당하는 숫자
"""
return PIL.Image.open('image_at_epoch_{:04d}.png'.format(epoch_no))
display_image(EPOCHS)
# 훈련 중에 저장된 이미지를 이어붙여 gif 애니메이션 만들기
anim_file = 'dcgan.gif'
with imageio.get_writer(anim_file, mode='I') as writer:
filenames = glob.glob('image*.png')
filenames = sorted(filenames)
last = -1
for i,filename in enumerate(filenames):
frame = 2*(i**0.5)
if round(frame) > round(last):
last = frame
else:
continue
image = imageio.imread(filename)
writer.append_data(image)
image = imageio.imread(filename)
writer.append_data(image)
import IPython
if IPython.version_info > (6,2,0,''):
display.Image(filename=anim_file)
# 애니메이션 다운받기
# -> 코랩에서만 가능
try:
from google.colab import files
except ImportError:
pass
else:
files.download(anim_file)
Author And Source
이 문제에 관하여(Generative Adversarial Network (GAN)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@monzheld/Generative-Adversarial-Network-GAN저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)