GAN in Keras

1. 데이터 준비하기

1.1. 다운받은 데이터를 내 PC에서 구글 드라이브에 업로드

In [0]:
from google.colab import files 

uploaded = files.upload()

for fn in uploaded.keys():
  print('User uploaded file "{name}" with length {length} bytes'.format(
      name=fn, length=len(uploaded[fn]))) 
Upload widget is only available when the cell has been executed in the current browser session. Please rerun this cell to enable.
Saving data_3000.zip to data_3000.zip
User uploaded file "data_3000.zip" with length 3309996 bytes

1.2. 구글 드라이브와 Colab 연동

In [2]:
from google.colab import drive
drive.mount('/gdrive')
Go to this URL in a browser: https://accounts.google.com/o/oauth2/auth?client_id=947318989803-6bn6qk8qdgf4n4g3pfee6491hc0brc4i.apps.googleusercontent.com&redirect_uri=urn%3aietf%3awg%3aoauth%3a2.0%3aoob&response_type=code&scope=email%20https%3a%2f%2fwww.googleapis.com%2fauth%2fdocs.test%20https%3a%2f%2fwww.googleapis.com%2fauth%2fdrive%20https%3a%2f%2fwww.googleapis.com%2fauth%2fdrive.photos.readonly%20https%3a%2f%2fwww.googleapis.com%2fauth%2fpeopleapi.readonly

Enter your authorization code:
··········
Mounted at /gdrive

1.3. 구글 드라이브에 업로드한 데이터 압축 풀기

In [0]:
!mkdir -p ./data_3000
!unzip data_3000.zip -d data_3000

2. 라이브러리 읽어들이기

In [0]:
%tensorflow_version 2.x
## load modules
import cv2 as cv
import matplotlib.pyplot as plt
import os, time  
import numpy as np 
from keras.preprocessing.image import load_img
from keras.preprocessing.image import img_to_array
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.layers import Input, Dense, Reshape, Flatten, Dropout
from tensorflow.keras import layers
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.models import Sequential, load_model, Model
from sklearn.model_selection import train_test_split
from tensorflow.keras.utils import plot_model
print(tf.__version__)
2.1.0

3. 데이터 전처리

In [0]:
# 압축해제된 데이터 경로를 찾아 복사해서 붙여넣어주세요
src = './data_3000/'
img_size = 56       # 이미지 사이즈
channels = 1
noise_dim = 100
#이미지 읽기
def img_read(src,file):
    img = cv.imread(src+file,cv.COLOR_BGR2GRAY)
    return img
def get_data():
    #src 경로에 있는 파일 명을 저장합니다. 
    files = os.listdir(src)
    X = []  

    # 경로와 파일명을 입력으로 넣어 확인하고 
    # 데이터를 255로 나눠서 0~1사이로 정규화 하여 X 리스트에 넣습니다. 

    for file in files:
      
        X.append((img_read(src,file)-127.5)/127.5) 
    

    # Train set(80%), Test set(20%)으로 나누기 
    X_train, X_test = train_test_split(X, test_size=0.2, random_state=1,shuffle=True)
        
    # (x, 56, 56, 1) 차원으로 맞춰줌 
    X_train = np.expand_dims(X_train, axis=3)
    X_test = np.expand_dims(X_test, axis=3)


    return X_train, X_test

# 데이터 셋 불러옴 (이미지만 필요해서 y 라벨 필요 없음)
X_train, X_test = get_data()
print("X_train.shape = {}".format(X_train.shape))
print("X_test.shape = {}".format(X_test.shape))
X_train.shape = (2400, 56, 56, 1)
X_test.shape = (600, 56, 56, 1)
In [0]:
# images 확인용
fig = plt.figure(figsize=(20,10))
nplot = 5
for i in range(1,nplot):
    ax = fig.add_subplot(1,nplot,i)
    ax.imshow(X_train[i, :, :, 0],cmap = plt.cm.bone)
plt.show()

4. 모델링 및 학습

Generator network 구성

In [0]:
# ---------------------
#  Generator 모델 구성 (input : noise / output : image)
# ---------------------    
 
def build_generator():
    model = Sequential()
   
    model.add(layers.Dense(256, use_bias=False, input_shape=(100,)))
    model.add(layers.LeakyReLU(alpha=0.2))
    model.add(layers.BatchNormalization(momentum=0.8))
    model.add(layers.Dense(512))
    model.add(layers.LeakyReLU(alpha=0.2))
    model.add(layers.BatchNormalization(momentum=0.8))
    model.add(layers.Dense(1024))
    model.add(layers.LeakyReLU(alpha=0.2))
    model.add(layers.BatchNormalization(momentum=0.8))
    model.add(layers.Dense(np.prod(56*56), activation='tanh'))
    model.add(layers.Reshape((56,56,1)))
    # noise 텐서 생성, model에 noise 넣으면 이미지 나옴
    noise = Input(shape=(100,))
    img = model(noise)
    model.summary()
    return Model(noise,img) 

generator 모델 생성과 컴파일

In [0]:
# Optimizer
optimizer = Adam(0.0002, 0.5)

# generator 모델 생성과 컴파일(loss함수와 optimizer 설정)
generator = build_generator()
generator.compile(loss='binary_crossentropy', optimizer=optimizer)

# 노이즈 만들어서 generator에 넣은 후 나오는 이미지 출력 (확인용)
noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)
plt.imshow(generated_image[0, :, :, 0], cmap='gray')

# plot_model(generator, show_shapes=True)
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 256)               25600     
_________________________________________________________________
leaky_re_lu (LeakyReLU)      (None, 256)               0         
_________________________________________________________________
batch_normalization (BatchNo (None, 256)               1024      
_________________________________________________________________
dense_1 (Dense)              (None, 512)               131584    
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU)    (None, 512)               0         
_________________________________________________________________
batch_normalization_1 (Batch (None, 512)               2048      
_________________________________________________________________
dense_2 (Dense)              (None, 1024)              525312    
_________________________________________________________________
leaky_re_lu_2 (LeakyReLU)    (None, 1024)              0         
_________________________________________________________________
batch_normalization_2 (Batch (None, 1024)              4096      
_________________________________________________________________
dense_3 (Dense)              (None, 3136)              3214400   
_________________________________________________________________
reshape (Reshape)            (None, 56, 56, 1)         0         
=================================================================
Total params: 3,904,064
Trainable params: 3,900,480
Non-trainable params: 3,584
_________________________________________________________________
Out[0]:
<matplotlib.image.AxesImage at 0x7f88fb968198>

Discriminator network 구성

In [0]:
# ---------------------
#  Discriminator 모델 구성 (input : image / output : 판별값(0에서 1사이의 숫자))
# ---------------------   
def build_discriminator():
    model = tf.keras.Sequential()
    img_shape = (img_size, img_size, channels)
    model.add(layers.Flatten(input_shape=img_shape))
    model.add(layers.Dense(512))
    model.add(layers.LeakyReLU(alpha=0.2))
    
    model.add(layers.Dense(256))
    model.add(layers.LeakyReLU(alpha=0.2))
    model.add(layers.Dense(1, activation='sigmoid'))
    
    model.summary()
    # 이미지 들어갈 텐서 생성, model에 넣으면 판별값 나옴
    img = Input(shape=img_shape)
    validity = model(img)
        
    return Model(img, validity) 

discriminator 모델 생성과 컴파일

In [0]:
# discriminator 모델 생성과 컴파일(loss함수와 optimizer 설정, accaracy 측정)
discriminator = build_discriminator()
discriminator.compile(loss = 'binary_crossentropy',optimizer = optimizer, metrics   = ['accuracy'])

# image를 discriminator에 넣었을 때 판별값 나옴 (예시. 확인용)
decision = discriminator(generated_image)
print (decision)
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
flatten (Flatten)            (None, 3136)              0         
_________________________________________________________________
dense_4 (Dense)              (None, 512)               1606144   
_________________________________________________________________
leaky_re_lu_3 (LeakyReLU)    (None, 512)               0         
_________________________________________________________________
dense_5 (Dense)              (None, 256)               131328    
_________________________________________________________________
leaky_re_lu_4 (LeakyReLU)    (None, 256)               0         
_________________________________________________________________
dense_6 (Dense)              (None, 1)                 257       
=================================================================
Total params: 1,737,729
Trainable params: 1,737,729
Non-trainable params: 0
_________________________________________________________________
tf.Tensor([[0.54282683]], shape=(1, 1), dtype=float32)

Combined Model 구성

  • noise -> generator -> discriminator
In [0]:
# Combined Model
# 랜덤으로 만든 이미지로부터 학습해서 새로운 이미지를 만들어내는 generator의 데이터를 discriminator가 분류. 

z = layers.Input(shape=(100,), name="noise_input")
img = generator(z)

# 모델을 합쳐서 학습하기 때문에 발란스 때문에 discriminator는 학습을 꺼둠. 우리는 generator만 학습
discriminator.trainable = False

# discriminator에 이미지를 입력으로 넣어서 진짜이미지인지 가짜이미지인지 판별
valid = discriminator(img)

# generator와 discriminator 모델 합침. (노이즈가 인풋으로 들어가서 판별결과가 아웃풋으로 나오게)
# discriminator를 속이도록 generator를 학습
combined = Model(z, valid)
combined.compile(loss='binary_crossentropy', optimizer=optimizer)
combined.summary()
Model: "model_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
noise_input (InputLayer)     [(None, 100)]             0         
_________________________________________________________________
model (Model)                (None, 56, 56, 1)         3904064   
_________________________________________________________________
model_1 (Model)              (None, 1)                 1737729   
=================================================================
Total params: 5,641,793
Trainable params: 3,900,480
Non-trainable params: 1,741,313
_________________________________________________________________

Training 함수

In [0]:
def train(epochs, batch_size=128, sample_interval=50):
              
        # 정답으로 사용 할 매트릭스. valid는 1, fake는 0
        valid = np.ones((batch_size, 1))
        fake = np.zeros((batch_size, 1))
        history = []
        for epoch in range(epochs):
            
            # ---------------------
            #  Train Discriminator
            # ---------------------
            
            # batch_size만큼 이미지와 라벨을 랜덤으로 뽑음
            idx = np.random.randint(0, X_train.shape[0], batch_size)
            imgs = X_train[idx]
            
            # Sample noise 생성(batch_size만큼)
            noise = np.random.normal(0, 1, (batch_size, 100))
            
            # noise를 generator에 넣어서 fake image 이미지 생성
            gen_imgs = generator.predict(noise)
            
            # discriminator를 학습함. 진짜 이미지는 1이 나오게, 가짜 이미지는 0이 나오게
            # discriminator가 이미지를 판별한 값과 valid와 fake가 
            # 각각 같이 들어가서 binary_crossentropy으로 계산되어 업데이트함.
            d_loss_real = discriminator.train_on_batch(imgs, valid)
            d_loss_fake = discriminator.train_on_batch(gen_imgs, fake)
            
            # real을 넣었을 때와 fake를 넣었을 때의 discriminator의 loss값과 accracy값의 평균을 구함.
            d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
            
            # ---------------------
            #  Train Generator
            # ---------------------
            
            # noise 생성
            noise = np.random.normal(0, 1, (batch_size, noise_dim))
            
            # noise가 들어가서 discriminator가 real image라고 판단하도록 generator를 학습
            g_loss = combined.train_on_batch(noise, valid)

            history.append({"D":d_loss[0],"G":g_loss})

            # sample_interval(1000) epoch 마다 loss와 accuracy와 이미지 출력
            if epoch % sample_interval == 0:
                print("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, d_loss[0], 100*d_loss[1], g_loss))
                sample_images(epoch)
        return(history)
In [0]:
 # 이미지 출력
def sample_images(epoch):
        r, c = 4, 4
        noise = np.random.normal(0, 1, (r * c, noise_dim))
        gen_imgs = generator.predict(noise)

        # Rescale images 0 - 1
        gen_imgs = 0.5 * gen_imgs + 0.5

        fig, axs = plt.subplots(r, c)
        cnt = 0
        for i in range(r):
            for j in range(c):
                axs[i,j].imshow(gen_imgs[cnt, :,:,0], cmap=plt.cm.bone)
                axs[i,j].axis('off')
                cnt += 1
#             fig.savefig("images/%d.png" % epoch)
#         plt.close()
In [0]:
# GAN 실행
history=train(epochs=10001, batch_size=32, sample_interval=1000)
0 [D loss: 0.656755, acc.: 40.62%] [G loss: 0.488745]
1000 [D loss: 0.627596, acc.: 62.50%] [G loss: 1.057999]
2000 [D loss: 0.502540, acc.: 78.12%] [G loss: 1.343930]
3000 [D loss: 0.384818, acc.: 85.94%] [G loss: 1.980562]
4000 [D loss: 0.289700, acc.: 87.50%] [G loss: 3.078918]
5000 [D loss: 0.230944, acc.: 87.50%] [G loss: 3.543353]
6000 [D loss: 0.107951, acc.: 96.88%] [G loss: 2.956030]
7000 [D loss: 0.147433, acc.: 93.75%] [G loss: 3.710814]
8000 [D loss: 0.178354, acc.: 93.75%] [G loss: 3.991983]
9000 [D loss: 0.169445, acc.: 93.75%] [G loss: 3.515020]
10000 [D loss: 0.227300, acc.: 87.50%] [G loss: 4.393452]