본문 바로가기

Study/class note

딥러닝 / GAN

문제1. gan 으로 할 수 있는 것이 무엇이 있는가 ?

 

 미술작품속 인물 실물 재현

※ GAN 의 최신 활용 예 :  사진 한장으로 영상을 생성함 

 

 

https://techcrunch.com/2019/05/22/mona-lisa-frown-machine-learning-brings-old-paintings-and-photos-to-life/

 

※ GAN 의 최신 활용 예 :  눈 감은 사람 사진을 눈 뜬 사람 사진으로 

문제2. 적대적 신경망 GAN 의 원리는 어떻게 되는가 ? 

 

문제3. 코드로 구현하게 될 때의 큰 그림은 어떻게 되는가 ? 

분류모델은 진짜 데이터에 대한 이해가 필요함. 그래야 생성모델의 가짜 데이터를 판별해낼 수 있음.

 

문제4. MNIST 데이터를 입력했을 때의 GAN 의 큰 그림은 ?

노이즈 데이터를 넣었을때 가짜 0~9사이의 가짜 이미지가 나옴.

생성모델(generator)의 upsampling 기술 중요

-> 오차함수 관건 (신경망을 만들때 오차함수를 잘 정의해주는 것이 중요)

1) 교차엔트로피(분류)

2) MSE(회귀)

 

 

문제5. GAN 구현에 가장 중요한 목적함수의 식은 ? 

 

문제6. GAN 으로 구현 할 수 있는 다른 가능성은 ? 

=> Gan으로 새로운 이미지를 창조할 수 있음.

 

 

텐써 플로우 2.0 에서 Gan 구현하기:

https://www.tensorflow.org/tutorials/generative/dcgan?hl=ko 

 

 

 

ㅇGAN 코드 설명

 

- generator 코드

 

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이 주어집니다.
    # assert 를 쓴 이유는 assert 이후의 문장을 실행할 때 그 조건이 False 면 에러가 나게 하려고 assert 를 썼음


    model.add(layers.Conv2DTranspose(128, (5, 5), strides=(2, 2), padding='same', use_bias=False))
    assert model.output_shape == (None, 14, 14, 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, 28, 28, 64)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(32, (5, 5), strides=(1, 1), padding='same', use_bias=False))
    assert model.output_shape == (None, 28, 28, 32)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(1, (5, 5), strides=(1, 1), padding='same', use_bias=False, activation='tanh'))
    assert model.output_shape == (None, 28, 28, 1)

    return model

generator = make_generator_model()

noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)

plt.imshow(generated_image[0, :, :, 0], cmap='gray')

 

 

- discriminator 코드

0에 가까우면 위조, 1에 가까우면 진짜

 

 

 

 

- 전체 코드(colab에서 실행)

#1. 필요한 패키지 로드
import glob
import imageio  # gif 이미지를 파이썬에서 생성하기 위한 모듈
import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
from tensorflow.keras import layers
import time
import tensorflow as tf
from IPython import display

#2. mnist 필기체 데이터 : 훈련 데이터와 훈련 데이터의 라벨만 가져옴
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()  #필기체 데이터 로드

print(train_images.shape) #(60000, 28, 28)
print(train_labels.shape) #(60000,)

#3. 3차원 데이터를 4차원으로 변경하면서 정규화 작업 수행
#(60000,28,28) ---> (60000,28,28,1)
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5 # 이미지를 [-1, 1]로 정규화합니다.

BUFFER_SIZE = 60000
BATCH_SIZE = 256

#4. 데이터 배치를 만들고 섞습니다.
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)   # 60000개를 shuffle 한 다음에 batch를 256씩 주고 있음

#5. 위조지폐범인 생성자 모델을 생성하는 함수
def make_generator_model():
    model = tf.keras.Sequential()  #Sequential을 선언해줘야 model층을 add 할 수 있음
    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))) # 7x7 사이즈의 256개의 습작 피처맵 생성
    assert model.output_shape == (None, 7, 7, 256) # 주목: 배치사이즈로 None이 주어짐
    # assert 이후의 문장을 실행했을 때 그 조건이 False라면 error  

    # 역합성곱을 진행하면서 이미지의 사이즈를 키움 : noise데이터에서 이미지를 생성함(cf. 합성곱은 이미지의 특징을 추출함)
    # https://m.blog.naver.com/mincheol9166/221740156045
    model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))  # Conv2DTranspose <- upsampling
    assert model.output_shape == (None, 7, 7, 128)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    # 역합성곱하면서 14x14의 이미지 64개, 숫자 이미지 생성
    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())

    # 역합성곱하면서 28x28의 제일 잘 나온 한 장의 이미지를 생성
    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

generator = make_generator_model()  # make_generator_model함수로 생성자 모델 generator 생성

noise = tf.random.normal([1, 100])  # 1x100의 noise데이터 생성
generated_image = generator(noise, training=False)  # noise데이터를 생성자 모델에 입력해서 위조 지폐 생성해서 generated_image 변수에 담음
                                                    
plt.imshow(generated_image[0, :, :, 0], cmap='gray')  # 위조지폐 시각화(이미지 확인용)

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]))   # 64개의 특징(피처맵)을 추출하고
    model.add(layers.LeakyReLU()) 
    model.add(layers.Dropout(0.3))

    model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))  # 128개의 피처맵을 추출하고 
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))

    model.add(layers.Flatten())  # 1차원으로 flatten시켜서
    model.add(layers.Dense(1))  # 0 ~ 1사이의 실수로 출력 - 0에 가까울수록 가짜이미지, 1에 가까울수록 진짜이미지

    return model

discriminator = make_discriminator_model()  # make_discriminator_model 함수로 경찰 모델 생성
decision = discriminator(generated_image)  # 경찰 모델에 위조 지폐범이 만든 이미지를 넣고 0 ~ 1사이의 값을 
print (decision)  # 출력

# 이 메서드는 크로스 엔트로피 손실함수(cross entropy loss)를 계산하기 위해 헬퍼 (helper) 함수를 반환합니다.
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)  # 1로 채워진 행렬과 real_output 이미지를 넣고
                                                                       # 진짜 이미지에 대한 오차를 real_loss에 담음
    fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)  # 가짜 이미지에 대한 오차값을 구해서 fake_loss에 담는데 오차함수에 입력값은 정답과 예측인데
                                                                        # 정답은 0으로 채워진 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)  # 진짜 같은 가짜를 생성해야하므로 1로 채워진 fake_output행렬과 똑같은 정답을 넣고 가짜 이미지에 대한 예측 행렬을 넣어서
                                                                  # 오차 출력

generator_optimizer = tf.keras.optimizers.Adam(1e-4)   # generator의 학습을 위한 옵티마이저 생성(위조지폐범)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)  # discriminator의 학습을 위한 옵티마이저 생성(경찰) 

checkpoint_dir = './training_checkpoints'  # 훈련 중간과 훈련 마지막에 가중치를 저장하기 위한 공간 지정
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")  # 가중치 생성 이름 앞글자를 ckpt로 지정
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,  # 훈련할 때 generator모델의 가중치 저장
                                 discriminator_optimizer=discriminator_optimizer,  # 훈련할 때 discriminator 가중치 저장
                                 generator=generator,  
                                 discriminator=discriminator)

EPOCHS = 50  # 에폭스
noise_dim = 100  # noise데이터 1x100
num_examples_to_generate = 16  # 훈련하는 과정에서 화면에 출력할 생성 필기체의 갯수

# 이 시드를 시간이 지나도 재활용하겠습니다. 
# (GIF 애니메이션에서 진전 내용을 시각화하는데 쉽기 때문입니다.) 
# 어느 자리에서든 똑같은 이미지의 gif를 확인하려고 시드값 설정
seed = tf.random.normal([num_examples_to_generate, noise_dim])

# `tf.function`이 어떻게 사용되는지 주목해 주세요.
# 이 데코레이터는 함수를 "컴파일"합니다.

# 텐서플로우는 원래 구글에서 만든 함수만을 사용할 수 밖에 없음. 그런데 사용자 함수를 텐서플로우에서 사용되게 하고 싶다면 아래의 데코레이션(@tf.function)을 사용
# 그 다음에 함수생성 코드를 작성하면 됨
# 텐서플로우 2.0에 새로 생긴 기능

@tf.function
def train_step(images):  # generator와 discriminator를 훈련시키는 함수
    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)  # 노이즈를 생성자 모델에 넣고 결과를 generated_images에 담음

      real_output = discriminator(images, training=True) # 진짜 이미지를 경찰 모델에 넣어서 real_output을 출력
      fake_output = discriminator(generated_images, training=True) # 가짜 이미지를 경찰 모델에 넣어서 fake_output에 담음

      gen_loss = generator_loss(fake_output)  # 가짜 이미지에 대한 오차를 gen_loss에 넣고
      disc_loss = discriminator_loss(real_output, fake_output) # 진짜 이미지와 가짜 이미지에 대한 토탈 오차를 disc loss에 넣음

    # 역전파를 빠르게 하기 위해서 생성자의 기울기를 순전파할 때 구해놓음
    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):  # 여러개의 이미지를 묶어 gif 이미지 생성하는 함수
  # `training`이 False로 맞춰진 것을 주목하세요.
  # 이렇게 하면 (배치정규화를 포함하여) 모든 층들이 추론 모드로 실행됩니다. 
  predictions = model(test_input, training=False)

  fig = plt.figure(figsize=(4,4))  # 16개의 이미지가 표시될 수 있도록 자리를 잡고

  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):  # 훈련 데이터셋을 에폭스만큼 학습 시키며 훈련 도중 생성되는 이미지들을 저장하게 하는 함수
  for epoch in range(epochs):  # 에폭스만큼 loop문 도는데
    start = time.time()  # 시작할 때 시작시간을 start에 저장해두고

    for image_batch in dataset:  # 훈련데이터를 하나씩 불러와서
      train_step(image_batch)   # 생성자와 판별자를 훈련시키는 함수에 넣어서 훈련시킴

    # GIF를 위한 이미지를 바로 생성합니다.
    display.clear_output(wait=True)  # 코드out창에 결과를 지우고
    generate_and_save_images(generator,  # 생성자 모델을 generate_and_save_images 함수에 넣어서
                             epoch + 1,
                             seed)  # seed 값에 해당하는 이미지를 하나 표시하고 저장

    # 15 에포크가 지날 때마다 모델을 저장합니다.
    if (epoch + 1) % 15 == 0:
      checkpoint.save(file_prefix = checkpoint_prefix)
    
    # print (' 에포크 {} 에서 걸린 시간은 {} 초 입니다'.format(epoch +1, time.time()-start))
    print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))

  # 마지막 에포크가 끝난 후 생성합니다.
  display.clear_output(wait=True)  # 화면을 지우고
  generate_and_save_images(generator,  # 마지막 에폭스가 끝났을 때 이미지 표시하고 저장
                           epochs,
                           seed)
  
  

train(train_dataset, EPOCHS)   # train 함수 실행

 

 

문제215. (진짜 마지막 문제) 위의 생성자가 숫자 7만 생성할 수 있도록 하시오.

힌트 : np.where 사용

#2. mnist 필기체 데이터 : 훈련 데이터와 훈련 데이터의 라벨만 가져옴
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()  #필기체 데이터 로드

print(train_images.shape) #(60000, 28, 28)
print(train_labels.shape) #(60000,)

##########특정이미지만 뽑기##############
idx = np.where(train_labels == 7)

train_images = train_images[idx]
train_labels = train_labels[idx]
#########################################

=> 이미지 불러오는 부분에서 7만 불러오기

gan_수업내용.ipynb
0.05MB

반응형