[PYTHON] Implémenter le deep learning / VAE (Variational Autoencoder)

1.Tout d'abord

Cette fois, implémentons l'autoencodeur variationnel avec keras.

2. Autoencoder primitif

スクリーンショット 2020-04-01 15.45.33.png Considérez un Autoencoder primitif. Supposons que le poids W1 et la polarisation b1 soient appliqués à l'entrée x et mappés à la couche intermédiaire via la fonction d'activation f1, puis le poids W2 et la polarisation b2 sont appliqués et sortis via la fonction d'activation f2.

A ce moment, si f2 est ** fonction constante ** et que la fonction de perte est la somme de ** erreur carrée **, l'apprentissage se poursuit de sorte que la sortie y reproduise l'entrée x. W1 et b1 sont appelés quantités de caractéristiques qui représentent des données.

3. Implémentation d'Autoencoder

Tout d'abord, implémentons-le à partir d'un simple encodeur automatique. L'ensemble de données utilise MNIST. スクリーンショット 2020-03-21 19.07.58.png

Comme l'image d'entrée est MNIST, 28 * 28 = 784 dimensions, réduisez-la à 256 dimensions, 64 dimensions, 32 dimensions, puis restaurez-la à 64 dimensions, 256 dimensions, 784 dimensions. La fonction de perte est l'entropie croisée de la différence entre l'image de sortie et l'image d'entrée.

Parce que le nombre de dimensions est réduit en cours de route, le réseau neuronal apprend les poids pour tenter de ne laisser que les caractéristiques importantes. Après avoir appris, si vous mettez une image en entrée, presque la même image sera sortie de la sortie.

Maintenant, déplaçons le code ci-dessous.

from keras.layers import Input, Dense
from keras.models import Model
from keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt

#Lecture de l'ensemble de données
(x_train, _), (x_test, _) = mnist.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))

#Construction de modèles
encoding_dim = 32
input_img = Input(shape=(784,))
x1 = Dense(256, activation='relu')(input_img)  
x2 = Dense(64, activation='relu')(x1)  
encoded = Dense(encoding_dim, activation='relu')(x2) 
x3 = Dense(64, activation='relu')(encoded)
x4 = Dense(256, activation='relu')(x3)  
decoded = Dense(784, activation='sigmoid')(x4) 
autoencoder = Model(input=input_img, output=decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.summary()  

#Apprentissage
autoencoder.fit(x_train, x_train,
                nb_epoch=50,    
                batch_size=256,
                shuffle=True,
                validation_data=(x_test, x_test))

#Convertir l'image de test avec le modèle de formation
decoded_imgs = autoencoder.predict(x_test)

n = 10
plt.figure(figsize=(10, 2))
for i in range(n):
    #Afficher l'image de test
    ax = plt.subplot(2, n, i+1)
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    #Afficher l'image convertie
    ax = plt.subplot(2, n, i+1+n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

スクリーンショット 2020-03-19 19.20.24.png La ligne supérieure est l'image de test d'origine et la ligne inférieure est l'image convertie à partir de l'image de test par l'encodeur automatique. Je pense que l'image de test peut être presque reproduite.

À propos, la sortie est déterminée par le signal de couche Z à 32 dimensions le plus réduit. En d'autres termes, on peut dire que diverses formes de nombres 0 à 9 sont réparties dans l'espace latent à 32 dimensions Z.

Nous ne pouvons pas réellement voir 32 dimensions, mais que se passe-t-il si nous abandonnons le nombre de dimensions et rendons l'espace latent z 2 dimensions? En deux dimensions, vous pouvez montrer comment les nombres 0-9 sont répartis sur un plan.

スクリーンショット 2020-03-21 19.12.04.png

Voyons ce qui se passe lorsque la partie la plus réduite est en deux dimensions. Remplacez ʻencoding_dim = 32 par ʻencoding_dim = 2 dans le code précédent et exécutez-le.

スクリーンショット 2020-03-19 23.16.56.png Comme prévu, il est difficile de se reproduire si l'espace latent z est bidimensionnel. "0", "1", "7" sont reproduits, mais le reste est mélangé avec d'autres nombres et ne peut pas être reproduit correctement.

En d'autres termes, dans un espace latent étroit de deux dimensions, les nombres de 0 à 9 ne peuvent pas être soigneusement divisés et distribués, et il semble que de nombreux nombres soient mélangés et distribués.

4. Modèle VAE

Comment bien répartir les nombres 0-9 dans l'espace latent étroit Z sans se chevaucher? La distribution typique dans le monde naturel est la distribution normale (distribution gaussienne), donc ici nous supposons que la distribution des nombres 0 à 9 suit la distribution normale, et considérons ce modèle.

スクリーンショット 2020-04-01 18.09.35.png

Une fois qu'un nombre entre en entrée et est réduit à 64 dimensions, regardez la variance moyenne $ \ mu $ $ \ sigma $ pour découvrir à quelle distribution normale le nombre appartient. Les valeurs échantillonnées aléatoirement de cette distribution sont placées dans Z, et les poids du décodeur sont appris de sorte qu'il n'y ait pas de différence entre l'entrée et la sortie. En faisant cela, il semble que les nombres de 0 à 9 peuvent être bien distribués sans se chevaucher.

Cependant, il y a un problème avec cette idée, et si un élément d'échantillonnage aléatoire est inclus, la propagation en retour d'erreur ne sera pas possible. Par conséquent, c'est un tel modèle qui permet la propagation de retour d'erreur tout en utilisant cette idée. スクリーンショット 2020-04-01 18.09.52.png

Où $ \ epsilon $ est un très petit nombre aléatoire. Multipliez la distribution $ \ sigma $ par ce $ \ epsilon $. Cette technique s'appelle ** Reparametrization Trick **. Si vous codez cette partie comme $ \ mu $ = z_mean, $ \ sigma $ = z_logvar, $ \ epsilon $ = epsilon,

# Reparametrization Trick
def sampling(args):
    z_mean, z_logvar = args
    batch = K.shape(z_mean)[0]
    dim = K.int_shape(z_mean)[1]
    epsilon = K.random_normal(shape=(batch, dim)) # ε
    return z_mean + K.exp(0.5 * z_logvar) * epsilon

5. Fonction de perte VAE

La fonction de perte $ L $ de VAE est exprimée comme suit.   $L = E_{z \sim q(z|x)} log_{Pmodel}(x|z) - D_{KL}(q(z|x)||Pmodel(z)) $

Le premier terme est la valeur attendue de la vraisemblance logarithmique des données X pour q (z | X), qui indique si la sortie est proche des données d'origine, remplacez-la par une erreur carrée. Le deuxième terme est appelé distance Calback Libra ($ D_ {KL} $ représente la divergence KL), et p (z) est une distribution normale.

$=\beta||y-x||^2 - D_{KL} (N(\mu, \sigma)|N(0,1))\ $

Le deuxième terme est exprimé par kl_loss, $ \ sigma ^ 2 $ = z_logvar, $ \ mu $ = z_mean, et lorsqu'il est remplacé par une expression approximative, la fonction de perte $ L $ (vae_loss) devient comme suit.

#Fonction de perte
# Kullback-Leibler Loss
kl_loss = 1 + z_logvar - K.square(z_mean) - K.exp(z_logvar)
kl_loss = K.sum(kl_loss, axis=-1)
kl_loss *= -0.5

# Reconstruction Loss
reconstruction_loss = mse(inputs, outputs)
reconstruction_loss *= original_dim

vae_loss = K.mean(reconstruction_loss + kl_loss)

6. Mise en œuvre de l'ensemble de la VAE

Écrivons et exécutons l'intégralité du code VAE, y compris le code précédent.

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from keras.layers import Lambda, Input, Dense
from keras.models import Model
from keras.datasets import mnist
from keras.losses import mse 
from keras import backend as K
import numpy as np
import matplotlib.pyplot as plt

#Lecture de l'ensemble de données
(x_train, y_train), (x_test, y_test) = mnist.load_data()
image_size = x_train.shape[1] # = 784
original_dim = image_size * image_size
x_train = np.reshape(x_train, [-1, original_dim])
x_test = np.reshape(x_test, [-1, original_dim])
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255

input_shape = (original_dim, )
latent_dim = 2

# Reparametrization Trick 
def sampling(args):
    z_mean, z_logvar = args
    batch = K.shape(z_mean)[0]
    dim = K.int_shape(z_mean)[1]
    epsilon = K.random_normal(shape=(batch, dim), seed = 5) # ε
    return z_mean + K.exp(0.5 * z_logvar) * epsilon

#Construction du modèle VAE
inputs = Input(shape=input_shape)
x1 = Dense(256, activation='relu')(inputs)  
x2 = Dense(64, activation='relu')(x1) 
z_mean = Dense(latent_dim)(x2)
z_logvar = Dense(latent_dim)(x2)
z = Lambda(sampling, output_shape=(latent_dim,))([z_mean, z_logvar])
encoder = Model(inputs, [z_mean, z_logvar, z], name='encoder')
encoder.summary()

latent_inputs = Input(shape=(latent_dim,))
x3 = Dense(64, activation='relu')(latent_inputs)  
x4 = Dense(256, activation='relu')(x3)  
outputs = Dense(original_dim, activation='sigmoid')(x4)
decoder = Model(latent_inputs, outputs, name='decoder')
decoder.summary()

z_output = encoder(inputs)[2]
outputs = decoder(z_output)
vae = Model(inputs, outputs, name='variational_autoencoder')

#Fonction de perte
# Kullback-Leibler Loss
kl_loss = 1 + z_logvar - K.square(z_mean) - K.exp(z_logvar)
kl_loss = K.sum(kl_loss, axis=-1)
kl_loss *= -0.5
# Reconstruction Loss
reconstruction_loss = mse(inputs, outputs)
reconstruction_loss *= original_dim

vae_loss = K.mean(reconstruction_loss + kl_loss)
vae.add_loss(vae_loss)
vae.compile(optimizer='adam')
vae.fit(x_train,
                epochs=50,
                batch_size=256,
                validation_data=(x_test, None))

#Convertir l'image de test
decoded_imgs = vae.predict(x_test)

#Affichage de l'image de test et de l'image convertie
n = 10
plt.figure(figsize=(10, 2))
for i in range(n):
    #Afficher l'image de test
    ax = plt.subplot(2, n, i+1)
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    #Afficher l'image convertie
    ax = plt.subplot(2, n, i+1+n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

スクリーンショット 2020-03-22 12.20.19.png L'espace latent z peut maintenant être assez bien reproduit même en deux dimensions seulement. De nombreux nombres tels que "0", "1", "2", "7" et "9" peuvent être reproduits. "4" et "5" sont toujours inutiles.

7. Représentons l'espace latent z comme un plan

L'espace latent Z étant bidimensionnel, il peut être représenté sur un plan. Voyons comment les nombres 0 à 9 y sont répartis et quelle image y est distribuée. Ajoutez ce qui suit au code de l'ensemble de VAE et exécutez-le.

import matplotlib.cm as cm
def plot_results(encoder,
                 decoder,
                 x_test,
                 y_test,
                 batch_size=128,
                 model_name="vae_mnist"):
    z_mean, _, _ = encoder.predict(x_test,
                                   batch_size=128)
    plt.figure(figsize=(12, 10))
    cmap=cm.tab10
    plt.scatter(z_mean[:, 0], z_mean[:, 1], c=cmap(y_test))
    m = cm.ScalarMappable(cmap=cmap)
    m.set_array(y_test)
    plt.colorbar(m)
    plt.xlabel("z[0]")
    plt.ylabel("z[1]")
    plt.show()
    
    # (-4, -4)De(4, 4)Est divisé en 30x30 et tracé
    n = 30  # 50>30
    digit_size = 28
    figure = np.zeros((digit_size * n, digit_size * n))
    grid_x = np.linspace(-4, 4, n)
    grid_y = np.linspace(-4, 4, n)[::-1]
    for i, yi in enumerate(grid_y):
        for j, xi in enumerate(grid_x):
            z_sample = np.array([[xi, yi]])
            x_decoded = decoder.predict(z_sample)
            digit = x_decoded[0].reshape(digit_size, digit_size)
            figure[i * digit_size: (i + 1) * digit_size,
                   j * digit_size: (j + 1) * digit_size] = digit
    plt.figure(figsize=(10, 10))
    start_range = digit_size // 2
    end_range = n * digit_size + start_range + 1
    pixel_range = np.arange(start_range, end_range, digit_size)
    sample_range_x = np.round(grid_x, 1)
    sample_range_y = np.round(grid_y, 1)
    plt.xticks(pixel_range, sample_range_x)
    plt.yticks(pixel_range, sample_range_y)
    plt.xlabel("z[0]")
    plt.ylabel("z[1]")
    plt.axis('off')
    plt.imshow(figure, cmap='Greys_r')
    #plt.savefig(filename)
    plt.show()
    
plot_results(encoder,
                 decoder,
                 x_test,
                 y_test,
                 batch_size=128,
                 model_name="vae_mlp")

スクリーンショット 2020-03-22 12.32.15.png Il semble que "0", "1", "2", "6" et "7" sont distribués sans chevauchement avec d'autres nombres.

スクリーンショット 2020-03-22 12.34.14.png Puisqu'il est distribué le long de la distribution normale, vous pouvez voir qu'il change continuellement d'un nombre à un autre.

Recommended Posts

Implémenter le deep learning / VAE (Variational Autoencoder)
Modèle généré par Variational Autoencoder (VAE)
L'apprentissage en profondeur
Mémorandum d'apprentissage profond
Commencer l'apprentissage en profondeur
Apprentissage en profondeur Python
Apprentissage profond × Python
Lire et mettre en œuvre l'apprentissage résiduel profond pour la reconnaissance d'image
Implémenter le codeur automatique variationnel conditionnel (CVAE) dans le système TensorFlow 2
Python: pratique du Deep Learning
Fonctions d'apprentissage en profondeur / d'activation
Apprentissage profond à partir de zéro
Deep learning 1 Pratique du deep learning
Apprentissage profond / entropie croisée
Premier apprentissage profond ~ Préparation ~
Première solution d'apprentissage en profondeur ~
[AI] Apprentissage métrique profond
J'ai essayé le deep learning
Autoencoder variationnel Explication approfondie
Python: réglage du Deep Learning
Technologie d'apprentissage en profondeur à grande échelle
Intelligence artificielle, machine learning, deep learning pour mettre en œuvre et comprendre
Fonction d'apprentissage profond / softmax
J'ai essayé d'implémenter Perceptron Part 1 [Deep Learning from scratch]
Implémenter LSTM AutoEncoder avec Keras
Apprentissage profond à partir de zéro 1 à 3 chapitres
Essayez l'apprentissage en profondeur avec TensorFlow
Deep Learning Gaiden ~ Programmation GPU ~
<Cours> Apprentissage en profondeur: Day2 CNN
Reconnaissance d'image par apprentissage profond 1 théorie
Deep running 2 Réglage de l'apprentissage profond
Apprentissage profond / code de travail LSTM
<Cours> Apprentissage en profondeur: Jour 1 NN
Apprentissage profond du noyau avec Pyro
Essayez le Deep Learning avec FPGA
Apprentissage profond pour la formation composée?
Présentation d'Udacity Deep Learning Nanodegree
Sujets> Deep Learning: Day3 RNN
Introduction au Deep Learning ~ Règles d'apprentissage ~
Apprentissage par renforcement profond 1 Introduction au renforcement de l'apprentissage
Apprentissage par renforcement profond 2 Mise en œuvre de l'apprentissage par renforcement
Générez des Pokémon avec Deep Learning
Introduction au Deep Learning ~ Rétropropagation ~