👩‍💻LEARN : ML&Data/Book Study

[모두의 딥러닝] #5-19. GAN

쟈니유 2023. 2. 26. 23:18
728x90

제네러티브 모델 쉽지 않네

 

 


#5-19. GAN 

 

[GAN(Generative Adversial Networks)]

생성자(Generator) : 진짜에 가까워보이는 이미지를 생성 

판별자(Discriminator) : 생성자가 만든 이미지를 진짜인지 아닌지 판별. 판별자의 정확도가 0.5에 달하면 생성자의 학습은 종료된다. 

 

기존의 GAN은 불안정한 모델으로 GAN에 컨볼루션 신경망을 적용한 DCGAN을 사용한다. 

 

1. 생성자 

  • 먼저 랜덤한 픽셀값으로 채워진 가짜 이미지를 생성한다.
  • 판별자의 판별 결과에 따라 지속적으로 업데이트 하여 원하는 이미지를 만들어 간다. 

 

DCGAN에서 생성자를 다룰 시 컨볼루션신경망 사용에 주의해야 할 점 

  • 옵티마이저를 사용하는 최적화 과정과 컴파일과정이 없다
    • model.compile(~ optimizer='adam')  이거 안쓴다! 
  • 풀링 과정이 없다
    • model.add(MaxPooling2D(pool_size=(n,n))) 이거 없다! 
  • 패딩 과정을 포함한다. 커널을 이동하며 컨볼루션 층을 만들때 처음 이미지보다 크기가 줄어들기 때문에, 처음 이미지에 패딩을 더하고 컨볼루션작업을 수행하면 처음 이미지와 크기가 동일해짐
    • model.add(Conv2D(n, kernel_size = m, padding='same')) 패딩 옵션으로 입/출력 크기가 다를 경우 같게 만들어줌 
  • 배치정규화를 수행한다
    • model.add(BatchNormalization()) 
  • 생성자의 활성화 함수로는 ReLU(정확하겐 LeakyReLU)를 사용한다 
    • model.add(Dense(~~~, activation = LeakyReLU(0.2))) 
LeakyReLU는 ReLU에서 x가 음수이면 바로 0이 되어 소실되는 단점을 보안하기 위해 음수일 경우 파라미터를 곱하게 함 
  • 생성자의 마지막 활성화 함수로는 tanh 함수를 사용한다 
    • activation='tanh' 
    • 출력되는 값을 -1 ~ 1 사이로 맞춰줌 

 

2. 판별자 

  • 생성자에서 넘어온 이미지의 진위여부(1,0)를 판별 → 오차함수로는 binary_crossentropy, 최적화 함수로는 adam 사용 
    • model.compile(loss = 'binary_crossentropy', optimizer = 'adam')
  • 컨볼루션 신경망은 이미지 판별 최적화 함수로 컨볼루션 신경망에 사용한 것 사용 → 드롭아웃 + 배치정규화, 패딩 
    • model.add(Conv2D(64, kernel_size = 5, strides = 2, input_shape = (28,28,1), padding="same"))
      • 노드의 수는 64개, 커널의 사이즈는 5*5임을 확인할 수 있음 
      • input_shape = 28 *28 이며 색상은 흑백 
      • strides 는 커널 윈도를 움직이는 칸 수를 의미하며 2와 같이 1 이상 여러칸 움직이게 될 경우 가로 세로 크기가 더 줄어들어 새로운 특징을 뽑아주는 효과가 생김 like dropout, pooling 
  • 진위만 판별할 뿐 스스로 학습해선 안되며, 얻은 가중치는 생성자로 넘겨야함 → 가중치 저장 학습 기능을 off 
    • model.trainable = False 

 

3. 적대적 신경망 실행 

생성자에서 나온 출력을 판별자에 넣어 진위여부를 판별하게 만드는 것 

생성자(Generator) -- G(input) --> 판별자(Discriminator) -> D(G(input)) = 1?0?  vs D(x) = 0 

 

이를 GAN 모델로 만들면 아래와 같다 (원래 실습코드에서 한번에 하려고 했는데 적대적 신경망이 함수도 있고 여튼 뭐가 많아서 여기에서 한번 짚고 가야할 것 같음 -_-)

 

[실행할 수 있도록 GAN model 생성]

#gan model로 연결

ginput = Input(shape=(100,))  #랜덤한 100개의 벡터를 케라스 input 함수에 넣어 생성자에 입력할 초기 ginput을 만들음 
dis_output = discriminator(generator(ginput)) #생성자에 ginput을 넣어주고, 그 결과 값을 판별자에 넣어줌. 판별(0,1)은 dis_outputa에 저장 
gan = Model(ginput, dis_output) #GAN 이라는 새로운 모델 생성 (생성자가 만든 값과 판별자의 판별값 사용) 
gan.compile(loss='binary_crossentropy', optimizer='adam') #GAN 모델 컴파일 
 

[실행 함수 생성] 

#신경망 실행 함수

def gan_train(epoch,batch_size, saving_interval):  #batch_size : 한번에 몇개이 실제/가상 이미지를 판별자에 넣을 지. 


#Data 불러오기

  (X_train, _), (_,_) = mnist.load_data()  #테스트 말고 학습 이미지만 사용할 것이기 때문에 X_train만 호출 
  X_train = X_train.reshape(X_train.shape[0], 28,28,1).astype('float32')

  X_train = (X_train - 127.5) / 127.5       #생성자에서 tanh 통해 -1 ~ 1 사이 값으로 보냈기 때문에 여기에서도 이에 맞게 전처리해줌 

  true = np.ones((batch_size, 1))          #모두 참(1)이라는 배열 만들기 
  fake = np.zeros((batch_size, 1))        #모두 거짓(0)이라는 배열 만들기 


  for i in range(epoch):
    #1. 판별자를 위해 실제 이미지와 생성한 거짓 이미지 간의 오차 확인하기 
     idx = np.random.randint(0, X_train.shape[0], batch_size)    #랜덤하게 실제이미지 하나 불러오기 
     imgs = X_train[idx]
     d_loss_real = discriminator.train_on_batch(imgs, true)        #train_on_batch(실제이미지, 모두참이라는 레이블) 로 판별하기. 

     noise = np.random.normal(0,1,(batch_size, 100))  #생성자에 집어넣을 랜덤 이미지 생성. batch_size만큼 100열을 뽑으란 의미 
     gen_imgs = generator.predict(noise) #랜덤 이미지를 생성자에 넣기 
     d_loss_fake = discriminator.train_on_batch(gen_imgs, fake) #train_on_batch(생성이미지, 모두거짓 레이블) 로 판별넣기  
    
     d_loss = 0.5 * np.add(d_loss_real, d_loss_fake) #판별자의 오차 (실제이미지의 오차와 생성이미지의 오차의 평균) 

    #2. 생성자만의 오차 만들기 
     g_loss = gan.train_on_batch(noise,true) #gan 모델을 이용해 이미지 생성하고 무조건 참이라고 레이블 집어넣고 판별자에 넣기. 

     print('epoch: %d' % i, 'd_loss: %.4f' % d_loss, 'g_loss:%.4f' % g_loss)


    #중간 과정을이미지로 저장하는 부분 (위의 for문 안에 들어가야 함) 


if i % saving_interval == 0:
noise = np.random.normal(0,1,(25,100))
gen_imgs = generator.predict(noise)

gen_imgs = 0.5 * gen_imgs + 0.5

fig, axs = plt.subplots(5,5)
count = 0
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_mnist_%d.png" % i)


# 2000번 반복, 배치크기는 32, 200번 마다 결과 저장

gan_train(2001, 32, 200)

 

4. 실습 코드 _생성자모델, 판별자 모델에만 한정 (뒤에는 위의 코드 그대로임)

 

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

#생성자 모델
#주의점1. 처음 Dense에서 Upsampling(2배 만드는 애) 고려해서 초기 이미지 세팅해야함. 

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