7 분 소요

내용 출처 : Aiffel

1. 생성 모델링

1) 판별 모델(분류, 회귀 등)과의 차이

  • 판별 모델 : 분류, 회귀
  • 생성 모델 : 학습 데이터셋에 기반하여 새로운 데이터셋을 생성
    • ✨최근 예시 : AWS의 DeepComposer
      • GAN(생성적 적대 신경망) = Generator(생성자) vs Discriminator(판별자) 으로 학습진행
    • Pix2Pix(그림→사진 변경 AI) '01'

2) 모델1_CycleGAN

  • 한 이미지와 다른 이미지를 번갈아 가며 Cyclic하게 변환
    • paired data가 필요X → annotation 비용 X
    • 📌 주요 용어 : paired data(쌍으로 이루어진 데이터 셋), annotation(라벨링) '02'

      '03'

3) 모델2_Neural Style Transfer

  • 스타일 변환

    '04'

    • ✨한 개의 Base Image로 5가지 스타일 변환을 적용시킨 모습
    • Base Image의 Content + Style Image의 Style을 합
    • 📌 주요 용어 : Content(내용), Style(스타일)

4) 코드 구현을 통한 GAN이해하기

'05'

  • 📌 Conv2DTranspose(생성) ↔ Conv2D(판별)

✨ 사전 준비

import os
import glob
import time

import PIL
import imageio
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
from IPython import display
import matplotlib.pyplot as plt
%matplotlib inline

print("tensorflow", tf.__version__)
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_x, _), (test_x, _) = fashion_mnist.load_data()

train_x = (train_x - 127.5)/127.5
train_x = train_x.reshape(train_x.shape[0], 28, 28, 1).astype('float32')
plt.imshow(train_x[0].reshape(28, 28), cmap='gray')
plt.colorbar()
plt.show()
BUFFER_SIZE = 60000
BATCH_SIZE = 256
train_dataset = tf.data.Dataset.from_tensor_slices(train_x).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)

(1) Generator

  • 코드 구현
      def make_generator_model():
          model = tf.keras.Sequential()
    	
          # First: Dense layer
          # units은 7x7x256, 편향은 사용하지 않음, 입력 값의 크기는 100차원 벡터
          model.add(tf.keras.layers.Dense(7*7*256, use_bias=False, input_shape=(100,)))
          model.add(tf.keras.layers.BatchNormalization()) # 배치 정규화 사용
          model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용
    	
          # Second: Reshape layer
          model.add(layers.Reshape((7, 7, 256)))
    	
          # Third: Conv2DTranspose layer
          # 커널 크기는 5, stride는 1, 패딩은 사용, 편향은 사용하지 않음
          model.add(tf.keras.layers.Conv2DTranspose(128, kernel_size=5, use_bias=False, strides=1, padding='same'))
          model.add(tf.keras.layers.BatchNormalization()) # 배치 정규화 사용
          model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용
    	
          # Fourth: Conv2DTranspose layer
          # 커널 크기는 5, stride는 2, 패딩은 사용, 편향은 사용하지 않음
          model.add(tf.keras.layers.Conv2DTranspose(128, kernel_size=5, use_bias=False, strides=2, padding='same'))
          model.add(tf.keras.layers.BatchNormalization()) # 배치 정규화 사용
          model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용
    	
          # Fifth: Conv2DTranspose layer
          model.add(tf.keras.layers.Conv2DTranspose(1, kernel_size=(5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
    	
          return model
    
      generator = make_generator_model()
      generator.summary()
    


  • 확인
      noise = tf.random.normal([1, 100])
      generated_image = generator(noise, training=False)
      plt.imshow(generated_image[0, :, :, 0], cmap='gray')
      plt.colorbar()
      plt.show()
    

(2) discriminator

  • 정의
      def make_discriminator_model():
          model = tf.keras.Sequential()
    	
          # First: Conv2D Layer
          # 필터의 크기는 5이며 64개 사용, stride는 2, 패딩은 사용, 입력 값의 크기는 준비된 흑백 사진
          model.add(tf.keras.layers.Conv2D(64, kernel_size=5, strides=2, padding='same', input_shape=(28, 28, 1)))
          model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용
          model.add(tf.keras.layers.Dropout(0.3)) # Dropout은 0.3을 사용
    	
          # Second: Conv2D Layer
          # 필터의 크기는 5이며 128개 사용, stride는 2, 패딩은 사용
          model.add(tf.keras.layers.Conv2D(128, kernel_size=5, strides=2, padding='same'))
          model.add(tf.keras.layers.LeakyReLU()) # LeakyReLU 활성화 함수 사용
          model.add(tf.keras.layers.Dropout(0.3)) # Dropout은 0.3을 사용
    	  
          # Third: Flatten Layer
          model.add(layers.Flatten())
    	  
          # Fourth: Dense Layer
          model.add(layers.Dense(1))
    	
          return model
    
      discriminator = make_discriminator_model()
      discriminator.summary()
    


  • 확인
      decision = discriminator(generated_image, training=False)
      decision
    

(3) Loss function

  • Cross Entropy : 두 값이 얼마나 큰 차이가 나는지를 정량적으로 계산
    • Real Image 라벨 : 1 / Fake Image 라벨 : 0로 가정 시
      • 생성자의 학습 목표 : Fake Image에 대해 판별값이 1에 가까워지는 것
      • 판별자의 학습 목표 : Real Image 판별값이 1, Fake Image 판별값이 0에 가까워지는 것
  • 코드 구현
      cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
    
    • generator_loss는 fake_output가 1에 가까워지는 것이 목표
        def generator_loss(fake_output):
        return cross_entropy(tf.ones_like(fake_output), fake_output)
      
    • discriminator_loss는 real_output은 1, fake_output은 0에 가까워지는 것이 목표
        def discriminator_loss(real_output, fake_output):
        # 손실함수는 cross entropy를 사용, ones_like를 활용
        real_loss = cross_entropy(tf.ones_like(real_output), real_output)
        # 손실함수는 cross entropy를 사용, zeros_like를 활용
        fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
        total_loss = real_loss + fake_loss # real_loss와 fake_loss를 더한 값
        return total_loss
      

(4) Accuracy

  • 목표 : 1이 아닌 둘다 0.5에 가까워지는 것(1이면 판별자가 매우 쉽게 판별해 내고 있다는 뜻)
  • 코드 구현
      def discriminator_accuracy(real_output, fake_output):
          real_accuracy = tf.reduce_mean(tf.cast(tf.math.greater_equal(real_output, tf.constant([0.5])), tf.float32))
          fake_accuracy = tf.reduce_mean(tf.cast(tf.math.less(fake_output, tf.constant([0.5])), tf.float32))
          return real_accuracy, fake_accuracy
    

(5) Optimizer : Adam

  • 코드 구현(learning rate는 0.0001)
      generator_optimizer = tf.keras.optimizers.Adam(1e-4)
      discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
    

(6) 학습 설계

  • 샘플 생성
    noise_dim = 100
    num_examples_to_generate = 16
    seed = tf.random.normal([num_examples_to_generate, noise_dim])
    seed.shape
    


  • 훈련 과정 설계(tf.function 데코 레이터 활용) : 1 step
      @tf.function
      def train_step(images):  #(1) 입력데이터
          noise = tf.random.normal([BATCH_SIZE, noise_dim])  #(2) 생성자 입력 노이즈
          with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:  #(3) tf.GradientTape() 오픈
              generated_images = generator(noise, training=True)  #(4) generated_images 생성
    
              #(5) discriminator 판별
              real_output = discriminator(images, training=True)
              fake_output = discriminator(generated_images, training=True)
    
              #(6) loss 계산
              gen_loss = generator_loss(fake_output)
              disc_loss = discriminator_loss(real_output, fake_output)
    
              #(7) accuracy 계산
              real_accuracy, fake_accuracy = discriminator_accuracy(real_output, fake_output)
    
          #(8) gradient 계산
          gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
          gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
    
          #(9) 모델 학습
          generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
          discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
    	
          return gen_loss, disc_loss, real_accuracy, fake_accuracy  #(10) 리턴값
    


  • 학습 현황 확인
    • 샘플 생성 후 확인
        def generate_and_save_images(model, epoch, it, sample_seeds):
            predictions = model(sample_seeds, 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], cmap='gray')
                plt.axis('off')
            plt.savefig('sample_epoch_{:04d}_iter_{:03d}.png'.format(epoch, it))
            plt.show()
      
    • loss와 accuracy 그래프 시각화
        from matplotlib.pylab import rcParams
        rcParams['figure.figsize'] = 15, 6    # matlab 차트의 기본 크기를 15,6으로 지정해 줍니다.
      		
        def draw_train_history(history, epoch):
            # summarize history for loss  
            plt.subplot(211)  
            plt.plot(history['gen_loss'])  
            plt.plot(history['disc_loss'])  
            plt.title('model loss')  
            plt.ylabel('loss')  
            plt.xlabel('batch iters')  
            plt.legend(['gen_loss', 'disc_loss'], loc='upper left')  
      		
            # summarize history for accuracy  
            plt.subplot(212)  
            plt.plot(history['fake_accuracy'])  
            plt.plot(history['real_accuracy'])  
            plt.title('discriminator accuracy')  
            plt.ylabel('accuracy')  
            plt.xlabel('batch iters')  
            plt.legend(['fake_accuracy', 'real_accuracy'], loc='upper left')  
      		
            # training_history 디렉토리에 epoch별로 그래프를 이미지 파일로 저장합니다.
            plt.savefig('train_history_{:04d}.png'.format(epoch))
            plt.show()
      
    • 체크포인트 정의
        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)
      

(7) 학습

  • 학습 구현
      def train(dataset, epochs, save_every):
          start = time.time()
          history = {'gen_loss':[], 'disc_loss':[], 'real_accuracy':[], 'fake_accuracy':[]}
    
          for epoch in range(epochs):
              epoch_start = time.time()
              for it, image_batch in enumerate(dataset):
                  gen_loss, disc_loss, real_accuracy, fake_accuracy = train_step(image_batch)
                  history['gen_loss'].append(gen_loss)
                  history['disc_loss'].append(disc_loss)
                  history['real_accuracy'].append(real_accuracy)
                  history['fake_accuracy'].append(fake_accuracy)
    
                  if it % 50 == 0:
                      display.clear_output(wait=True)
                      generate_and_save_images(generator, epoch+1, it+1, seed)
                      print('Epoch {} | iter {}'.format(epoch+1, it+1))
                      print('Time for epoch {} : {} sec'.format(epoch+1, int(time.time()-epoch_start)))
    
              if (epoch + 1) % save_every == 0:
                  checkpoint.save(file_prefix=checkpoint_prefix)
    
              display.clear_output(wait=True)
              generate_and_save_images(generator, epochs, it, seed)
              print('Time for training : {} sec'.format(int(time.time()-start)))
    	
    
              draw_train_history(history, epoch)
    
      save_every = 5
      EPOCHS = 100
    
      # 사용가능한 GPU 디바이스 확인
      tf.config.list_physical_devices("GPU")
    
      %%time
      train(seed, EPOCHS, save_every)
    
      # 학습과정의 loss, accuracy 그래프 이미지 파일이 ~/training_history 경로에 생성되고 있으니
      # 진행 과정을 수시로 확인해 보시길 권합니다.
    

(8) 학습과정 시각화

anim_file = 'fashion_mnist_dcgan.gif'
with imageio.get_writer(anim_file, mode='I') as writer:
    filenames = glob.glob('generated_samples/sample*.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)
# fashion/fashion_mnist_dcgan.gif

✨ 주요 성능 개선 방식


  • 참고링크 : https://github.com/soumith/ganhacks
    1. Normalize the inputs : normalize the images between -1 and 1 ☑️
    2. A modified loss function : Flip labels when training generator: real = fake, fake = real ☑️
    3. Use a spherical Z : Sample from a gaussian distribution ☑️
    4. BatchNorm : Construct different mini-batches for real and fake ☑️
    5. Avoid Sparse Gradients: ReLU, MaxPool ☑️
    6. Use Soft and Noisy Labels : if it is real, then replace the label with a random number between 0.7 and 1.2, and if it is a fake sample, replace it with 0.0 and 0.3
    7. DCGAN / Hybrid Models ☑️
    8. Use stability tricks from RL : All stability tricks that work for deep deterministic policy gradients
    9. Use the ADAM Optimizer ☑️
    10. Track failures early ☑️
    11. Dont balance loss via statistics (unless you have a good reason to) ☑️
    12. If you have labels, use them
    13. Add noise to inputs, decay over time
    14. notsure Train discriminator more (sometimes)
    15. notsure Batch Discrimination
    16. Discrete variables in Conditional GANs : Use an Embedding layer, Add as additional channels to images
    17. Use Dropouts in G in both train and test phase : Provide noise in the form of dropout (50%).

✨ 관련 프로젝트 링크


댓글남기기