[PYTHON] Implementieren Sie Deep Learning / VAE (Variational Autoencoder)

1. Zuallererst

Dieses Mal implementieren wir Variational Autoencoder mit Keras.

2. Primitiver Autoencoder

スクリーンショット 2020-04-01 15.45.33.png Betrachten Sie einen primitiven Autoencoder. Angenommen, das Gewicht W1 und die Vorspannung b1 werden an den Eingang x angelegt und über die Aktivierungsfunktion f1 auf die Zwischenschicht abgebildet, und dann werden das Gewicht W2 und die Vorspannung b2 angelegt und über die Aktivierungsfunktion f2 ausgegeben.

Zu diesem Zeitpunkt wird, wenn f2 eine ** konstante Funktion ** ist und die Verlustfunktion die Summe des ** quadratischen Fehlers ** ist, das Lernen fortgesetzt, so dass die Ausgabe y die Eingabe x reproduziert. W1 und b1 werden Merkmalsgrößen genannt, die Daten darstellen.

3. Implementierung des Autoencoders

Lassen Sie es uns zunächst mit einem einfachen Auto-Encoder implementieren. Der Datensatz verwendet MNIST. スクリーンショット 2020-03-21 19.07.58.png

Da das Eingabebild MNIST ist, 28 * 28 = 784 Dimensionen, beschränken Sie es auf 256 Dimensionen, 64 Dimensionen, 32 Dimensionen und stellen Sie es dann auf 64 Dimensionen, 256 Dimensionen, 784 Dimensionen wieder her. Die Verlustfunktion ist die Kreuzentropie der Differenz zwischen dem Ausgabebild und dem Eingabebild.

Da die Anzahl der Dimensionen auf dem Weg eingegrenzt wird, lernt das neuronale Netzwerk die Gewichte, um nur die wichtigen Merkmale zu belassen. Wenn Sie nach dem Lernen ein Bild in die Eingabe einfügen, wird fast dasselbe Bild von der Ausgabe ausgegeben.

Verschieben wir nun den folgenden Code.

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

#Datensatz lesen
(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:])))

#Modellbau
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()  

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

#Konvertieren Sie das Testbild mit dem Trainingsmodell
decoded_imgs = autoencoder.predict(x_test)

n = 10
plt.figure(figsize=(10, 2))
for i in range(n):
    #Testbild anzeigen
    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)

    #Konvertiertes Bild anzeigen
    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 Die obere Reihe ist das ursprüngliche Testbild, und die untere Reihe ist das Bild, das vom automatischen Encoder aus dem Testbild konvertiert wurde. Ich denke, dass das Testbild fast reproduziert werden kann.

Die Ausgabe wird übrigens durch das am stärksten eingegrenzte 32-dimensionale Z-Schichtsignal bestimmt. Mit anderen Worten kann gesagt werden, dass verschiedene Formen der Zahlen 0 bis 9 im 32-dimensionalen latenten Raum Z verteilt sind.

Wir können eigentlich keine 32 Dimensionen sehen, aber was ist, wenn wir die Anzahl der Dimensionen verringern und den latenten Raum z 2 Dimensionen machen? In zwei Dimensionen können Sie zeigen, wie die Zahlen 0-9 in einer Ebene verteilt sind.

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

Nun wollen wir sehen, was passiert, wenn der am meisten eingegrenzte Teil zweidimensional gemacht wird. Ändern Sie "encoding_dim = 32" im vorherigen Code in "encoding_dim = 2" und führen Sie ihn aus.

スクリーンショット 2020-03-19 23.16.56.png Wie erwartet ist es schwierig zu reproduzieren, wenn der latente Raum z zweidimensional ist. "0", "1", "7" werden reproduziert, aber der Rest wird mit anderen Zahlen gemischt und kann nicht gut reproduziert werden.

Mit anderen Worten, in einem engen latenten Raum von zwei Dimensionen können die Zahlen 0 bis 9 nicht sauber geteilt und verteilt werden, und es scheint, dass viele Zahlen gemischt und verteilt sind.

4. VAE-Modell

Wie können die Zahlen 0-9 im engen latenten Raum Z gut verteilt werden, ohne sich zu überlappen? Die typische Verteilung in der natürlichen Welt ist die Normalverteilung (Gaußsche Verteilung). Daher nehmen wir hier an, dass die Verteilung der Zahlen 0 bis 9 der Normalverteilung folgt, und betrachten dieses Modell.

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

Nachdem eine Zahl in die Eingabe eingegeben und auf 64 Dimensionen eingegrenzt wurde, überprüfen Sie die mittlere $ \ mu $ Varianz $ \ sigma $, um herauszufinden, zu welcher Normalverteilung die Zahl gehört. Zufällig abgetastete Werte aus dieser Verteilung werden in Z eingegeben, und die Decodergewichte werden gelernt, so dass es keinen Unterschied zwischen Eingabe und Ausgabe gibt. Auf diese Weise können die Zahlen von 0 bis 9 ohne Überlappung gut verteilt werden.

Es gibt jedoch ein Problem mit dieser Idee, und wenn ein zufälliges Stichprobenelement enthalten ist, ist eine Fehlerrückübertragung nicht möglich. Daher ist es ein solches Modell, das die Weitergabe von Fehlern ermöglicht, während diese Idee genutzt wird. スクリーンショット 2020-04-01 18.09.52.png

Wobei $ \ epsilon $ eine sehr kleine Zufallszahl ist. Multiplizieren Sie die Verteilung $ \ sigma $ mit diesem $ \ epsilon $. Diese Technik heißt ** Reparametrization Trick **. Wenn Sie diesen Teil als $ \ mu $ = z_mean, $ \ sigma $ = z_logvar, $ \ epsilon $ = epsilon, codieren

# 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. VAE-Verlustfunktion

Die Verlustfunktion $ L $ von VAE wird wie folgt ausgedrückt.   $L = E_{z \sim q(z|x)} log_{Pmodel}(x|z) - D_{KL}(q(z|x)||Pmodel(z)) $

Der erste Term ist der erwartete Wert der logarithmischen Wahrscheinlichkeit der Daten X für q (z | X), der angibt, ob die Ausgabe nahe an den Originaldaten liegt. Ersetzen Sie sie daher durch einen quadratischen Fehler. Der zweite Term wird als Calback Libra-Abstand bezeichnet ($ D_ {KL} $ steht für KL-Divergenz), und p (z) ist eine Normalverteilung

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

Der zweite Term wird ausgedrückt als kl_loss, $ \ sigma ^ 2 $ = z_logvar, $ \ mu $ = z_mean, und wenn er durch einen ungefähren Ausdruck ersetzt wird, wird die Verlustfunktion $ L $ (vae_loss) wie folgt.

#Verlustfunktion
# 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. Implementierung der gesamten VAE

Lassen Sie uns den gesamten VAE-Code einschließlich des vorherigen Codes schreiben und ausführen.

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

#Datensatz lesen
(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

#VAE Modellbau
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')

#Verlustfunktion
# 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))

#Testbild konvertieren
decoded_imgs = vae.predict(x_test)

#Anzeige des Testbildes und des konvertierten Bildes
n = 10
plt.figure(figsize=(10, 2))
for i in range(n):
    #Testbild anzeigen
    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)

    #Konvertiertes Bild anzeigen
    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 Der latente Raum z kann nun auch in nur zwei Dimensionen recht gut reproduziert werden. Viele Zahlen wie "0", "1", "2", "7", "9" können reproduziert werden. "4" und "5" sind immer noch nutzlos.

7. Stellen wir den latenten Raum z als Ebene dar

Da der latente Raum Z zweidimensional ist, kann er in einer Ebene dargestellt werden. Mal sehen, wie die Zahlen 0 bis 9 dort verteilt sind und wie das Bild dort verteilt ist. Fügen Sie dem Code für die gesamte VAE Folgendes hinzu und führen Sie ihn aus.

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)Von(4, 4)Ist in 30x30 unterteilt und geplottet
    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 Es scheint, dass "0", "1", "2", "6" und "7" verteilt sind, ohne sich mit anderen Zahlen zu überlappen.

スクリーンショット 2020-03-22 12.34.14.png Da es entlang der Normalverteilung verteilt ist, können Sie sehen, dass es sich kontinuierlich von einer Zahl zur anderen ändert.

Recommended Posts

Implementieren Sie Deep Learning / VAE (Variational Autoencoder)
Modell generiert von Variational Autoencoder (VAE)
Tiefes Lernen
Deep Learning Memorandum
Starten Sie Deep Learning
Python Deep Learning
Deep Learning × Python
Lesen und implementieren Sie Deep Residual Learning für die Bilderkennung
Implementieren Sie CVAE (Conditional Variational Autoencoder) im TensorFlow 2-System
Python: Deep Learning-Praxis
Deep Learning / Aktivierungsfunktionen
Deep Learning von Grund auf neu
Deep Learning 1 Übung des Deep Learning
Deep Learning / Cross Entropy
Erstes tiefes Lernen ~ Vorbereitung ~
Erstes tiefes Lernen ~ Lösung ~
[AI] Deep Metric Learning
Ich habe versucht, tief zu lernen
Variational Autoencoder Gründliche Erklärung
Python: Deep Learning Tuning
Deep Learning Großtechnologie
Künstliche Intelligenz, maschinelles Lernen, tiefes Lernen zu implementieren und zu verstehen
Deep Learning / Softmax-Funktion
Ich habe versucht, Perceptron Teil 1 [Deep Learning von Grund auf neu] zu implementieren.
Implementieren Sie LSTM AutoEncoder mit Keras
Deep Learning von Grund auf 1-3 Kapitel
Versuchen Sie es mit TensorFlow
Deep Learning Gaiden ~ GPU-Programmierung ~
<Kurs> Tiefes Lernen: Day2 CNN
Deep Learning Bilderkennung 1 Theorie
Deep Running 2 Tuning von Deep Learning
Deep Learning / LSTM Scratch Code
<Kurs> Tiefes Lernen: Tag 1 NN
Deep Kernel Learning mit Pyro
Versuchen Sie Deep Learning mit FPGA
Deep Learning für die Bildung von Verbindungen?
Einführung in Udacity Deep Learning Nanodegree
Themen> Deep Learning: Tag 3 RNN
Einführung in Deep Learning ~ Lernregeln ~
Tiefe Stärkung des Lernens 1 Einführung in die Stärkung des Lernens
Tiefes Lernen der Verstärkung 2 Implementierung des Lernens der Verstärkung
Generiere Pokemon mit Deep Learning
Einführung in Deep Learning ~ Backpropagation ~