가장 쉬운 VAE (variational autoencoder) 예제 by 바죠

가장 쉬운 VAE (variational autoencoder) 예제

가장 쉬운 생성적 적대 모델, GAN : http://incredible.egloos.com/7473304
가장 쉬운 오토인코더: http://incredible.egloos.com/7473318
디노이징 오토인코더 : http://incredible.egloos.com/7479749
또 다른 생성적 모델로서 VAE. 

VAE 에는 아래의 세가지 구성 요소들이 있다. encoder,  decoder, and predictor
VAE ~ ( approximate inference in a latent Gaussian model )
[ approximate posterior and model likelihood are parameterized by neural networks ]

먼저 많은 데이터가 있다고 가정한다.
각 데이터가 축약된 형식으로 표현된 다음에 다시 원본으로 표현되는 과정을 생각해 볼 수 있다. 
특별하게도 중간 단계의 표현력이 부족하게 만들어 둔 경우를 생각한다. 
즉, 데이터 정보를 의도적으로 잃어 버리게 만들어 둔다.
따라서, 완전한 데이터의 복구는 불가능할 것이다.
왜냐하면, 원본이 가지고 있는 데이터를 표현하는 차원보다 중간 단계에서 낮은 차원으로 데이터가 강제적으로 표현되기 때문이다.
Convolution 을 수행하는 것은 입력과 출력의 비대칭 크기를 의미한다.

많은 데이터에 대해서 이러한 훈련을 반복하면, 결국, encoder, decoder는 데이터에서 나타나는 공통된 또는 함축적인 표현에 인공신경망은 집중할 수밖에 없을 것이다. 
결국, 인공신경망은 필수불가결한 어떤 암호화된 양들을 찾으려고 노력할 것이다. 
그것들에 집중할 수밖에 없을 것이다. 
그 표현이 얼마나 효율적일지는 잘 모르겠지만, 축약과 회복이라는 것에 집중하여 인공신경망은 훈련이 될 것이다.
데이터 스스로를 학습하는 것이다. 데이터의 축약된 정보를 도출해낸다. 
인공신경망을 훈련할 때, 두 가지 loss 함수들을 동시에 이용한다. 
원본 복구가 되도록 하는 항, 원본이 잠재 공간에서 분포가 되도록 해 주는 항, 이렇게 두 항이 동시에 일을 하도록 한다.

AE 방법은 데이터를 표현하는 축약된 양식을 정하는 방법이다.
축약된 표현식을 얻어내는 것이다. 굉장한 크기의 데이터도 2차원 latent space(잠재공간)에서는 단순히 숫자 두 개로 표현될 수 있다는 것이다.
물론, 이들 숫자 두 개가 얼마나 원본 데이터를 잘 표현하는지는 철저하게 따져보아야 할 것이다.
동시에 훈련된, encoder, decoder 신경망들을 사용하면 데이터의 특성을 굉장히 잘 표현할 수 있다는 가정을 도입하고 있다.

아주 훈련이 잘 된 decoder를 만들었다고 가정하면, latent space에서의 임의의 점을 이용하면 우리는 아주 그럴듯 한, 훈련에 사용된 것 같은 데이터를 만들어 낼 수 있다. 
이것은 유전 알고리듬에서 교차 또는 변이를 이용해서 새로운 개체를 만들어 내는 것과 비교가 가능한 것이다. 
인공신경망 훈련이 끝나면, 기본적으로, 새로운 데이터를 만들어 낼 수 있다.

Autoencoder의 latent space가 우리가 잘 아는 정규분포를 따른게 할 수 있다면, 이 분포는 소위, 평균과 표준편차만 구해낸다면 분포를 표현할 수 있습니다.

또한, latent space는 연속적이다. 이점을 활용하여 다수의 상태를 만들어 낼 수 있다. 2차원으로 latent space를 잡을 경우, VAE는 일종의 차원 축소 방식으로 볼 수 있고, 2차원 그림으로 데이터의 분포를 표시할 수 있다.
다시 말해서, VAE는 multidimensional scaling 방법을 대치할 수도 있다.

VAE :  reconstruction phase, regularization phase 두 가지를 모두 추구한다. 원본 복원을 위한 안전장치를 마련한다. 데이터들을 표현하는 더 좋은 방법을 추구하는 것이다.

입력/출력: image, video, text, audio등이 될 수 있다.
3D 결정구조의 경우, 
유한한 크기의 것으로 일관되게 만들어진 전자밀도, (원자위치로 부터 유도된 것),
결정파라미터들이 합쳐진 것들이 인풋이 될 수 있다.

KLD (Kullback Leibler divergence) 확률분포의 유사성을 체크하는 함수.
분포가 같을 때, 0
다를수록 더 큰 값을 가진다.

데이터를 통한 훈련이 이루어진 후를 가정하면 실질적인 데이터의 흐름:
latent variables (z_input)  →  generator 

응용: 다양한 분자구조들 학습하고 새로운 분자들을 만들어 낼 수 있다. 인물 사진들을 학습하고 새로운 인물을 만들어 낼 수 있다.


--------------------------------------------------------------------------------------------------------------------

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
from keras import backend as K
from keras.layers import Input, Dense, Lambda, Layer, Add, Multiply
from keras.models import Model, Sequential
from keras.datasets import mnist
original_dim = 784
intermediate_dim = 256
latent_dim = 2
batch_size = 100
epochs = 50
epsilon_std = 1.0

def nll(y_true, y_pred):
    """ Negative log likelihood (Bernoulli). """
    # keras.losses.binary_crossentropy gives the mean
    # over the last axis. we require the sum
    return K.sum(K.binary_crossentropy(y_true, y_pred), axis=-1)

class KLDivergenceLayer(Layer):
    """ Identity transform layer that adds KL divergence to the final model loss."""
    def __init__(self, *args, **kwargs):
        self.is_placeholder = True
        super(KLDivergenceLayer, self).__init__(*args, **kwargs)
    def call(self, inputs):
        mu, log_var = inputs
        kl_batch = - .5 * K.sum(1+log_var-K.square(mu)-K.exp(log_var), axis=-1)
        self.add_loss(K.mean(kl_batch), inputs=inputs)
        return inputs

decoder = Sequential([Dense(intermediate_dim, input_dim=latent_dim, activation='relu'),Dense(original_dim, activation='sigmoid')])

x = Input(shape=(original_dim,))
h = Dense(intermediate_dim, activation='relu')(x)
z_mu = Dense(latent_dim)(h)
z_log_var = Dense(latent_dim)(h)
z_mu, z_log_var = KLDivergenceLayer()([z_mu, z_log_var])
z_sigma = Lambda(lambda t: K.exp(.5*t))(z_log_var)

eps = Input(tensor=K.random_normal(stddev=epsilon_std,shape=(K.shape(x)[0], latent_dim)))
z_eps = Multiply()([z_sigma, eps])
z = Add()([z_mu, z_eps])
x_pred = decoder(z)
vae = Model(inputs=[x, eps], outputs=x_pred)
vae.compile(optimizer='rmsprop', loss=nll)

#         train the VAE on MNIST digits
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(-1, original_dim) / 255.
x_test = x_test.reshape(-1, original_dim) / 255.
vae.fit(x_train,x_train,shuffle=True,epochs=epochs,batch_size=batch_size,validation_data=(x_test, x_test))
encoder = Model(x, z_mu)

#         display a 2D plot of the digit classes in the latent space
z_test = encoder.predict(x_test, batch_size=batch_size)
plt.figure(figsize=(6, 6))
plt.scatter(z_test[:, 0], z_test[:, 1], c=y_test,alpha=.4, s=3**2, cmap='viridis')
plt.colorbar()
plt.show()

#          display a 2D manifold of the digits
n = 15              #       figure with 15x15 digits
digit_size = 28

#       linearly spaced coordinates on the unit square were transformed
#       through the inverse CDF (ppf) of the Gaussian to produce values
#       of the latent variables z, since the prior of the latent space is Gaussian
u_grid = np.dstack(np.meshgrid(np.linspace(0.05, 0.95, n),np.linspace(0.05, 0.95, n)))
z_grid = norm.ppf(u_grid)
x_decoded = decoder.predict(z_grid.reshape(n*n, 2))
x_decoded = x_decoded.reshape(n, n, digit_size, digit_size)
plt.figure(figsize=(10, 10))
plt.imshow(np.block(list(map(list, x_decoded))), cmap='gray')
plt.show()
참고:


binary-crossentropy를 사용하나요?
--------------------------------------------------------------------------------------------------------------------
#     example of calculating the KL divergence (relative entropy) with scipy
from scipy.special import rel_entr
#     define distributions
p = [0.10, 0.40, 0.50]
q = [0.80, 0.15, 0.05]
#     calculate (P || Q)
kl_pq = rel_entr(p, q)
print('KL(P || Q): %.3f nats' % sum(kl_pq))
#     calculate (Q || P)
kl_qp = rel_entr(q, p)
print('KL(Q || P): %.3f nats' % sum(kl_qp))
#     example of calculating the js divergence between two mass functions
from math import log2
from math import sqrt
from numpy import asarray
#     calculate the KL divergence
def kl_divergence(p, q):
        return sum(p[i] * log2(p[i]/q[i]) for i in range(len(p)))
#     calculate the JS divergence
def js_divergence(p, q):
        m = 0.5 * (p + q)
        return 0.5 * kl_divergence(p, m) + 0.5 * kl_divergence(q, m)
#     define distributions
p = asarray([0.10, 0.40, 0.50])
q = asarray([0.80, 0.15, 0.05])
#      calculate JS(P || Q)
js_pq = js_divergence(p, q)
print('JS(P || Q) divergence: %.3f bits' % js_pq)
print('JS(P || Q) distance: %.3f' % sqrt(js_pq))
#      calculate JS(Q || P)
js_qp = js_divergence(q, p)
print('JS(Q || P) divergence: %.3f bits' % js_qp)
print('JS(Q || P) distance: %.3f' % sqrt(js_qp))
KL(P || Q): 1.336 nats
KL(Q || P): 1.401 nats
JS(P || Q) divergence: 0.420 bits
JS(P || Q) distance: 0.648
JS(Q || P) divergence: 0.420 bits
JS(Q || P) distance: 0.648

참고:

--------------------------------------------------------------------------------------------------------------------
a = np.array([[4,3, 5, 7],
                    [1, 12,11, 9],
                    [2, 15, 1,14]])
np.sort(a,axis=0)
array([[ 1, 3, 1, 7],
          [2, 12, 5, 9],
          [4, 15, 11, 14]])

np.sort(a)   # axis=-1 or axis=1 ,default
array([[ 3, 4, 5, 7],
[ 1, 9, 11, 12],
[ 1, 2, 14, 15]])

--------------------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------------------
동그라미들은 무작위 숫자가 연관된 연산이다.  마름모 꼴들은 고정된 연산을 의미한다.
--------------------------------------------------------------------------------------------------------------------
latent space에서 정의되는 격자점들은 각각 하나의 데이터로 해독될 수 있다. 
이것은 데이터 생성에 해당한다.
--------------------------------------------------------------------------------------------------------------------
https://towardsdatascience.com/the-variational-autoencoder-as-a-two-player-game-part-i-4c3737f0987b
https://towardsdatascience.com/the-variational-autoencoder-as-a-two-player-game-part-ii-b80d48512f46
https://towardsdatascience.com/the-variational-autoencoder-as-a-two-player-game-part-iii-d8d56c301600
code space == latent space
--------------------------------------------------------------------------------------------------------------------
좌측이 AE, 우측이 VAE 를 활용한 경우이다. VAE는 Kullback-Leibler divergence(KLD) term, 정규화 항을, 손실함수에 추가적으로, 가지고 있다. 두 가지 확률 분포(q, p)가 정확히 같으면 KLD 항은 0이 된다. 따라서, 손실함수 최소화 과정에서 두 확률 분포가 최대한 같아지는 것을 기대할 수 있다.
이것은 z1, z2, latent space에서의 확률 분포가 전반적으로 가우시안 형태의 분포가 되도록 한다. 이렇게 가우시안 분포가 되면 보다 더 일반적인 샘플링이 가능하다. 암호화된 데이터의 분포가 가우시안 형태가 되도록 한다.
모두 다 가우시안 형식이다.

--------------------------------------------------------------------------------------------------------------------

GAN, VAE 모두 새로운 해를 생성할 수 있는 생성적 모델이다. 
좋은 representation을 찾고, 새로운 해를 제시할 수 있는 것은, 마치, 유전 알고리듬에서 변이, 교차를 통해서 새로운 해를 얻어내는 것에 비유된다.

--------------------------------------------------------------------------------------------------------------------
Wassersein GAN with gradient penalty
Our proposed method performs better than standard WGAN and enables stable training of a wide variety of GAN architectures with almost no hyperparameter tuning,including 101-layer ResNets and language models with continuous generators.We also achieve high quality generations on CIFAR-10 and LSUN bedrooms.

--------------------------------------------------------------------------------------------------------------------
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

class Sampling(layers.Layer):
    """Uses (z_mean, z_log_var) to sample z, the vector encoding a digit."""
    def call(self, inputs):
        z_mean, z_log_var = inputs
        batch = tf.shape(z_mean)[0]
        dim = tf.shape(z_mean)[1]
        epsilon = tf.keras.backend.random_normal(shape=(batch, dim))
        return z_mean + tf.exp(0.5 * z_log_var) * epsilon

intermediate_dim = 50
intermediate2_dim = 25
intermediate3_dim = 12
latent_dim = 2

x = keras.Input(shape=(original_dim,))
h = tf.keras.layers.Dense(intermediate_dim, activation='relu')(x)
h = tf.keras.layers.Dense(intermediate2_dim, activation='relu')(x)
h = tf.keras.layers.Dense(intermediate3_dim, activation='relu')(x)
z_mean = layers.Dense(latent_dim, name="z_mean")(x)
z_log_var = layers.Dense(latent_dim, name="z_log_var")(x)
z = Sampling()([z_mean, z_log_var])
encoder = keras.Model(x, [z_mean, z_log_var, z], name="encoder")
encoder.summary()


latent_inputs = keras.Input(shape=(latent_dim,))
h = tf.keras.layers.Dense(intermediate_dim3, activation='relu')(latent_inputs)
h = tf.keras.layers.Dense(intermediate_dim2, activation='relu')(latent_inputs)
h = tf.keras.layers.Dense(intermediate_dim, activation='relu')(latent_inputs)
decoder_outputs =tf.keras.layers.Dense(original_dim, activation='relu')(h)
decoder = keras.Model(latent_inputs, decoder_outputs, name="decoder")
decoder.summary()
class VAE(keras.Model):
    def __init__(self, encoder, decoder, **kwargs):
        super(VAE, self).__init__(**kwargs)
        self.encoder = encoder
        self.decoder = decoder
        self.total_loss_tracker = keras.metrics.Mean(name="total_loss")
        self.reconstruction_loss_tracker = keras.metrics.Mean(
            name="reconstruction_loss"
        )
        self.kl_loss_tracker = keras.metrics.Mean(name="kl_loss")
    @property
    def metrics(self):
        return [
            self.total_loss_tracker,
            self.reconstruction_loss_tracker,
            self.kl_loss_tracker,
        ]
    def train_step(self, data):
        with tf.GradientTape() as tape:
            z_mean, z_log_var, z = self.encoder(data)
            reconstruction = self.decoder(z)
            reconstruction_loss = tf.reduce_mean(
                tf.reduce_sum(
                    keras.losses.binary_crossentropy(data, reconstruction), axis=(1, 2)
                )
            )
            kl_loss = -0.5 * (1 + z_log_var - tf.square(z_mean) - tf.exp(z_log_var))
            kl_loss = tf.reduce_mean(tf.reduce_sum(kl_loss, axis=1))
            total_loss = reconstruction_loss + kl_loss
        grads = tape.gradient(total_loss, self.trainable_weights)
        self.optimizer.apply_gradients(zip(grads, self.trainable_weights))
        self.total_loss_tracker.update_state(total_loss)
        self.reconstruction_loss_tracker.update_state(reconstruction_loss)
        self.kl_loss_tracker.update_state(kl_loss)
        return {
            "loss": self.total_loss_tracker.result(),
            "reconstruction_loss": self.reconstruction_loss_tracker.result(),
            "kl_loss": self.kl_loss_tracker.result(),
        }
vae = VAE(encoder, decoder)
vae.summary()
vae.compile(optimizer=keras.optimizers.Adam())

# train the VAE on MNIST digits
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(-1, original_dim) / 255.
x_test = x_test.reshape(-1, original_dim) / 255.

vae.fit(x_train, x_train, shuffle=True, epochs=epochs, batch_size=batch_size, validation_data=(x_test, x_test))



# display a 2D plot of the digit classes in the latent space
z_test = encoder.predict(x_test, batch_size=batch_size)
plt.figure(figsize=(6, 6))
plt.scatter(z_test[:, 0], z_test[:, 1], c=y_test, alpha=.4, s=3**2, cmap='viridis')
plt.colorbar()
plt.show()

# display a 2D manifold of the digits
n = 15  # figure with 15x15 digits
digit_size = 28

# linearly spaced coordinates on the unit square were transformed
# through the inverse CDF (ppf) of the Gaussian to produce values
# of the latent variables z, since the prior of the latent space
# is Gaussian
u_grid = np.dstack(np.meshgrid(np.linspace(0.05, 0.95, n),
                               np.linspace(0.05, 0.95, n)))
z_grid = norm.ppf(u_grid)
x_decoded = decoder.predict(z_grid.reshape(n*n, 2))
x_decoded = x_decoded.reshape(n, n, digit_size, digit_size)

plt.figure(figsize=(10, 10))
plt.imshow(np.block(list(map(list, x_decoded))), cmap='gray')
plt.show()




핑백

덧글

  • 바죠 2020/01/25 11:13 # 답글

    The classifier used the reduced representation built by the VAE as features to detect cancer types.

    https://medium.com/swlh/clustering-and-visualizing-cancer-types-using-variational-autoencoders-vaes-4522ae2dfab7
  • 바죠 2020/02/28 17:04 # 답글

    https://www.quora.com/What-are-the-pros-and-cons-of-Generative-Adversarial-Networks-vs-Variational-Autoencoders?top_ans=26547608


  • 바죠 2020/08/30 11:22 # 답글

    https://towardsdatascience.com/generating-images-with-autoencoders-77fd3a8dd368
  • 바죠 2020/09/16 10:16 # 답글

    image, text, audio
    등이 인풋이 될 수 있다.
    3D 결정구조의 경우, 유한한 크기의 것으로 일관되게 만들어진 전자밀도, (원자위치로 부터 유도된 것), 결정파라미터들이 인풋이 될 수 있다.
  • 바죠 2020/09/16 10:55 # 답글

    Symmetric mean absolute percentage error
    https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error

  • 바죠 2020/09/16 10:59 # 답글

    https://en.wikipedia.org/wiki/U-Net

  • 바죠 2020/09/16 11:26 # 답글

    https://pubs.rsc.org/en/content/articlelanding/2019/sc/c8sc05372c#!divAbstract

  • 바죠 2021/04/03 08:57 # 삭제 답글

    VAE learns parameters in the probability distribution representing the data.
댓글 입력 영역

최근 포토로그



MathJax