확률분포를 이용하는 모델인 VAE(Variational Auto Encoder)를 알아보자
try:
%tensorflow_version 2.x"
except Exception:
pass
import tensorflow as tf
from tensorflow.keras import layers,models,datasets,losses
from tensorflow.keras import backend as K
import numpy as np
import matplotlib.pyplot as plt
VAE모델 만들기에 앞서 패키지들을 선언을 한다.
새롭게 추가된 패키지에는 백엔드(backend) 패키지가 있는데 케라스내에서 텐서플로우 패키지를 사용하는 것입니다.
(x_train, _), (x_test,_) = datasets.mnist.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape(-1,784)
x_test = x_test.reshape(-1,784)
현재 VAE 실습에서 하는 모델은 순환 신경망이기 때문에 2차원으로 바꾸고 정규화 후 진행을 해야한다.
input_shape = (784,)
latent = 2 # 차원 축소의 크기
#인코더
encoder_input = tf.keras.Input(input_shape)
encoding = layers.Dense(latent, activation='relu')(encoder_input)
#평균 분산
mean = layers.Dense(latent)(encoding)
log_var = layers.Dense(latent)(encoding)
인코더를 작성해보자.
latent는 차원축소 될 크기를 의미한다.
인코딩에서 유닛을 latent로 지정함으로서 2차원까지 축소가 된다.
VAE는 확률 분포를 이용하기 때문에 평균과 분산을 생성해야한다.
# 평균 + log(분산) * epsilon(0 ~ 1)
def sampling(data) :
mean,log_var = data
epsilon = K.random_normal(mean=0. ,stddev=1. ,shape=(K.shape(log_var)[0],latent))
return mean + K.exp(log_var)* epsilon
sampled = layers.Lambda(sampling,output_shape=(latent,))([mean,log_var])
VAE에서 가우시안 정규분포식을 이용하여 확률분포를 알아낸다.
가우시안 식은 “평균 + 분산 * 앱실론(0 ~ 1)” 이다.
그래서 평균과 분산을 람다(Lambda)로 묶고 가우시안 정규분포식의 샘플 값을 얻는 과정이다.
decoder_input = tf.keras.Input((latent,)) #bs (2,)
decoding = layers.Dense(784,activation='sigmoid')(decoder_input) #bs (None,784)
decoder = models.Model(decoder_input,decoding) #모델을 생성
decoded = decoder(sampled) #샘플링한 모델 입력 사용
이제는 디코더를 만들어보자
기존에 AE에서는 이미지의 크기를 입력을 받았지만 VAE에서는 차원축소 크기를 입력으로 한다.
이미지의 원래크기인 784로 원복을 시키면 된다.
디코더 모델을 생성한 후 아까 만들었던 가우시안 샘플 데이터를 이용하여 입력데이터로 사용한다.
vae = models.Model(encoder_input, decoded)
VAE 생성 모델이다.
인코더 모델 입력을 디코더 모델을 출력으로 모델을 생성했다.
bc_loss = losses.binary_crossentropy(encoder_input,decoded)
bc_loss *= 784
KL_loss = K.mean(1 + log_var - K.square(mean) -K.exp(log_var)) * -0.0005
vae_loss = K.mean(bc_loss + KL_loss)
vae.add_loss(vae_loss)
VAE에는 엘보(ELBO)라는 로스함수를 사용하게 되고 구현이 되어있지 않기에 직접 만들어줘야 한다.
ELBO의 식은 크로스엔트로피와 KL(Kullback Leibler divergence)을 더한 다음 평균을 낸 Loss다.
KL의 식은 -0.5 * (1 + log_z_var - z_mean^2 - z_var)이지만 -0.5는 조정이 가능하다.
로스 식을 만들었으면 add_loss 명령어를 통해서 추가를 해줘야한다.
vae.compile(optimizer='adam',loss=None)
VAE의 컴파일이다.
로스함수는는 이전에 만들었기 때문에 None 값으로 설정한다.
vae.fit(x_train,None,shuffle=True,
epochs=10,validation_data=(x_test, None))
학습을 시켜보자
AE는 비지도학습이므로 y의 값은 없어도 되므로 None값이다.
100번 정도 돌렸을 때 나오는 결과물입니다.
많이 선명하지 않지만 모양이 잡히는 것을 알 수가 있다.
작성자 김강빈 kkb08190819@gmail.com / 이원재 ondslee0808@gmail.com