[PYTHON] Implémenter LSTM AutoEncoder avec Keras

1.Tout d'abord

Cet article est comme une suite de Implémentation de Keras LSTM Feed Forward avec numpy. Implémentez LSTM AutoEncoder avec Keras et essayez la classification binaire à partir des fonctionnalités obtenues. Les données génèrent deux ondes de péché avec des fréquences différentes et les identifient.

2. Environnement

3. Création de données

3.1 Aperçu

L'article suivant est utilisé comme référence pour la génération de données. Je vous remercie.

Cette fois, nous avons légèrement modifié la méthode de génération de données pour la classification binaire. Préparez deux ondes de péché avec des fréquences différentes et laissez-les être identifiées.

3.2 Code

C'est le code pour générer les données utilisées cette fois.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def make_data(random_factor, number_of_cycles, \
                timesteps, sampling_num_pair):
    """
    sampling_num_pair : 2 elements of tupple
Nombre à échantillonner en un cycle
        Ex. (20,80)
    """
    def _load_data(data, n_prev = 100):  
        """
        data should be pd.DataFrame()
        """
        docX, docY = [], []
        for i in range(len(data)-n_prev):
            docX.append(data.iloc[i:i+n_prev].as_matrix())
        alsX = np.array(docX)

        return alsX

    def _train_test_split(df, test_size=0.1, n_prev = 100):  
        """
        This just splits data to training and testing parts
        """
        ntrn = round(len(df) * (1 - test_size))
        ntrn = int(ntrn)
        X_train = _load_data(df.iloc[0:ntrn], n_prev)
        X_test = _load_data(df.iloc[ntrn:], n_prev)

        return X_train, X_test

    np.random.seed(0)

    sampling_num1, sampling_num2 = sampling_num_pair

    df1 = pd.DataFrame(np.arange(sampling_num1 * number_of_cycles + 1), columns=["t"])
    df1["sin_t"] = df1.t.apply(lambda x: np.sin(x * (2 * np.pi / sampling_num1)+ np.random.uniform(-1.0, +1.0) * random_factor))

    df2 = pd.DataFrame(np.arange(sampling_num2 * number_of_cycles + 1), columns=["t"])
    df2["sin_t2"] = df2.t.apply(lambda x: np.sin(x * (2 * np.pi / sampling_num2)+ np.random.uniform(-1.0, +1.0) * random_factor))

    X_train1, X_test1 = _train_test_split(df1[["sin_t"]], n_prev=timesteps) 
    X_train2, X_test2 = _train_test_split(df2[["sin_t2"]], n_prev=timesteps) 

    # concatenate X and make y
    X_train = np.r_[X_train1, X_train2]
    y_train = np.r_[np.tile(0, X_train1.shape[0]), np.tile(1, X_train2.shape[0])]

    X_test = np.r_[X_test1, X_test2]
    y_test = np.r_[np.tile(0, X_test1.shape[0]), np.tile(1, X_test2.shape[0])]

    return X_train, y_train, X_test, y_test

#Coefficient aléatoire
random_factor = 0.05
#Nombre de cycles à générer
number_of_cycles = 200
#Combien de points doivent être échantillonnés en un cycle?
sampling_num_pair=(20,80)
#La longueur de la fenêtre. Cela devient la longueur d'une série.
timesteps = 100

X_train, y_train, X_test, y_test = make_data(random_factor, number_of_cycles, timesteps, sampling_num_pair)
print("X_train.shape : ", X_train.shape) #X_train.shape :  (17802, 100, 1)
print("y_train.shape : ", y_train.shape) #y_train.shape :  (17802,)
print("X_test.shape : ", X_test.shape) #X_test.shape :  (1800, 100, 1)
print("y_test.shape : ", y_test.shape) #y_test.shape :  (1800,)

3.3 Tracé

Tracons l'onde de péché générée

# make_fonction de données df1,Extrayez df2 et tracez-le.
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(df1["sin_t"][0:80], label="class0 sampling_num 20", color="red")
ax.plot(df2["sin_t2"][0:80], label="class1 sampling_num 80", color="blue")
ax.legend(loc="upper right")
fig.savefig("./sin_plot.png ")
plt.close("all")

sin_plot.png

Vous pouvez voir que class0 a quatre fois la fréquence de class1.

4. construction du modèle

4.1 Aperçu

J'ai fait référence au Blog Keras pour créer le modèle. Il y avait quelque chose comme un indice dans le chapitre de l'autoencodeur séquence-à-séquence, donc je l'ai ajouté le cas échéant.

L'article original est Apprentissage non supervisé des représentations vidéo à l'aide de LSTM. Cet article propose du conditionnel (passer l'entrée dans l'ordre inverse au moment du décodage) et inconditionnel (ne rien transmettre), mais cette fois nous implémentons le conditionnel.

4.2 Code

Définissez le modèle de LSTM AutoEncoder. Ensuite, entraînez et reconstruisez le X_train original.

from keras.layers import Input, LSTM, RepeatVector, concatenate, Dense
from keras.models import Model

input_dim = 1
latent_dim = 10

# encode
inputs = Input(shape=(timesteps, input_dim))
encoded = LSTM(latent_dim, activation="tanh", recurrent_activation="sigmoid", return_sequences=False)(inputs)

#decode
hidden = RepeatVector(timesteps)(encoded)
reverse_input = Input(shape=(timesteps, input_dim))
hidden_revinput = concatenate([hidden, reverse_input])
decoded = LSTM(latent_dim, activation="tanh", recurrent_activation="sigmoid", return_sequences=True)(hidden_revinput)
decoded = Dense(latent_dim, activation="relu")(decoded)
decoded = Dense(input_dim, activation="tanh")(decoded)

# train
LSTM_AE = Model([inputs, reverse_input], decoded)
LSTM_AE.compile(optimizer='rmsprop', loss='mse')
X_train_rev = X_train[:,::-1,:]
LSTM_AE.fit([X_train, X_train_rev], X_train, epochs=30, batch_size=500, shuffle=True, validation_data=([X_train, X_train_rev], X_train))

X_hat = LSTM_AE.predict([X_train, X_train_rev])

Ce code ajoute une couche de connexion complète lors du décodage, mais cela ne signifie rien de particulier. Keras est incroyable! Vous pouvez ajouter des couches si facilement! C'est le résultat de jouer avec.

Ensuite, class0 et class1 sont divisés en classes pour voir comment ils sont reconfigurés par AutoEncoder.

def split_X(X, y):
    y_inv = np.abs(y - 1.)
    X_0 = X[y_inv.astype(bool),:,:]
    X_1 = X[y.astype(bool),:,:]
    return X_0, X_1

X_train_0, X_train_1 = split_X(X_train, y_train)
X_hat_0, X_hat_1 = split_X(X_hat, y_train)

print("X_train_0.shape : ", X_train_0.shape) #X_train_0.shape :  (3501, 100, 1)
print("X_train_1.shape : ", X_train_1.shape) #X_train_1.shape :  (14301, 100, 1)
print("X_hat_0.shape : ", X_hat_0.shape) #X_hat_0.shape :  (3501, 100, 1)
print("X_hat_1.shape : ", X_hat_1.shape) #X_hat_1.shape :  (14301, 100, 1)

4.3 Tracé

Tracez et vérifiez ce qui a été reconstruit par AutoEncoder.

#Reconstruit X_Voyons à quoi ressemble le train
def plot_save(start_index, X_hat, X_train, X_class):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    for i in np.arange(start_index,start_index+5):
        #Placer 5 chacun.
        ax.plot(X_hat[i,:,0], label="X hat", color="red")
        ax.plot(X_train[i,:,0], label="X train", color="blue")
    savename = "./AE_reconst_30ep_start_" + str(start_index) + "_cls" + str(X_class) + ".png "
    fig.savefig(savename)
    plt.close("all")

start_list = np.arange(0,len(X_train_0), 1000)
for start_index in start_list:
    plot_save(start_index, X_hat_0, X_train_0, X_class=0)

start_list = np.arange(0,len(X_train_1), 1000)
for start_index in start_list:
    plot_save(start_index, X_hat_1, X_train_1, X_class=1)

class0 AE_reconst_30ep_start_0_cls0.png

class1 AE_reconst_30ep_start_6000_cls1.png

Le bleu correspond aux données d'origine et le rouge aux données reconstruites. Ce sont ceux que j'ai choisis parmi un certain nombre de parcelles. Tout était comme ça.

Au fait, il semble que cela n'ait pas encore convergé, donc je pense que ce sera une meilleure approximation si vous augmentez l'époque, mais j'ai décidé que cela suffisait en termes de spécifications de la machine. En passant, cela a pris environ 15 minutes à 30 époques. (On me dira probablement de faire de mon mieux)

5. Extraction des fonctionnalités acquises

5.1 Aperçu

C'est la même chose que Article précédent. Extraire la quantité de caractéristiques obtenue par l'encodeur du modèle

5.2 Code

Définissez la fonction requise pour extraire la quantité d'objets.


def split_params(W, U, b):
    Wi = W[:,0:latent_dim]
    Wf = W[:,latent_dim:2*latent_dim]
    Wc = W[:,2*latent_dim:3*latent_dim]
    Wo = W[:,3*latent_dim:]

    print("Wi : ",Wi.shape)
    print("Wf : ",Wf.shape)
    print("Wc : ",Wc.shape)
    print("Wo : ",Wo.shape)

    Ui = U[:,0:latent_dim]
    Uf = U[:,latent_dim:2*latent_dim]
    Uc = U[:,2*latent_dim:3*latent_dim]
    Uo = U[:,3*latent_dim:]

    print("Ui : ",Ui.shape)
    print("Uf : ",Uf.shape)
    print("Uc : ",Uc.shape)
    print("Uo : ",Uo.shape)

    bi = b[0:latent_dim]
    bf = b[latent_dim:2*latent_dim]
    bc = b[2*latent_dim:3*latent_dim]
    bo = b[3*latent_dim:]
    print("bi : ",bi.shape)
    print("bf : ",bf.shape)
    print("bc : ",bc.shape)
    print("bo : ",bo.shape)

    return (Wi, Wf, Wc, Wo), (Ui, Uf, Uc, Uo), (bi, bf, bc, bo)


def calc_ht(params):
    x, latent_dim, W_, U_, b_ = params
    Wi, Wf, Wc, Wo = W_
    Ui, Uf, Uc, Uo = U_
    bi, bf, bc, bo = b_ 
    n = x.shape[0]

    ht_1 = np.zeros(n*latent_dim).reshape(n,latent_dim) #h_{t-1}Veux dire.
    Ct_1 = np.zeros(n*latent_dim).reshape(n,latent_dim) #C_{t-1}Veux dire.

    ht_list = []

    for t in np.arange(x.shape[1]):
        xt = np.array(x[:,t,:])
        it = sigmoid(np.dot(xt, Wi) + np.dot(ht_1, Ui) + bi)
        Ct_tilda = np.tanh(np.dot(xt, Wc) + np.dot(ht_1, Uc) + bc)
        ft = sigmoid(np.dot(xt, Wf) + np.dot(ht_1, Uf) + bf)
        Ct = it * Ct_tilda + ft * Ct_1
        ot = sigmoid( np.dot(xt, Wo) + np.dot(ht_1, Uo) + bo)
        ht = ot * np.tanh(Ct)
        ht_list.append(ht)
        ht_1 = ht
        Ct_1 = Ct

    ht = np.array(ht)
    return ht
    
def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))
        

En fait extraire.

W, U, b = LSTM_AE.layers[1].get_weights()
Ws, Us, bs = split_params(W, U, b)

params = [X_train, latent_dim, Ws, Us, bs]
ht_train = calc_ht(params)

params = [X_test, latent_dim, Ws, Us, bs]
ht_test = calc_ht(params)

LSTM_AE.layers [1] est la couche d'encodage LSTM. Le poids en est extrait.

5.3 Tracé

ht_train est une matrice de (17802, 10), qui est une quantité de caractéristiques à 10 dimensions. Étant donné que la dimension d'origine est de 100 dimensions définies avec timesteps = 100 '' ', elle est compressée de 100 dimensions à 10 dimensions. Tracons cette caractéristique en 10 dimensions pour le moment.

class0 hidden_two_class0_2.png

class1 hidden_two_class1_4.png

Je ne suis pas sûr. Puisqu'il s'agit d'un signal périodique, il est tracé de sorte que tous les mouvements du montant de la caractéristique dans un cycle soient affichés. Le comportement général est similaire, mais il semble qu'il puisse être différent si vous regardez chacun d'eux. (Parce que c'est difficile, je ne vais pas le regarder en détail)

6. Identifier par les caractéristiques acquises

Classifions class0 et class1 en fonction des fonctionnalités acquises par cet AutoEncoder LSTM. Pour l'instant, mettons de côté le fait que ce n'est pas iid, et essayons la régression logistique.

from sklearn.linear_model import LogisticRegression 
from sklearn.metrics import accuracy_score
model = LogisticRegression(n_jobs=-1)
model.fit(ht_train, y_train)
y_hat_test = model.predict(ht_test)
accuracy_score(y_hat_test, y_test)
# 1.0

La précision des données de test est désormais de 1,0. Je suis inquiet parce que c'est trop cher, mais c'était un signal avec une forme d'onde très différente, alors est-ce quelque chose comme ça?

7. À la fin

«J'ai construit un modèle pour la première fois avec Keras, mais je crains que ce que je fais réellement à l'intérieur soit trop complexe et corresponde à ce que je veux faire. En ne regardant que les résultats, c'est comme prévu, alors j'aimerais croire que c'est fait. (S'il vous plaît dites-moi) ――Comme pour l'article original, comment les membres de la communauté d'apprentissage automatique comprennent-ils et mettent-ils en œuvre le modèle qui y est proposé parce qu'il n'est pas écrit dans des formules mathématiques? L'idée de graphe informatique est-elle répandue et véhiculée par des diagrammes? Ou est-ce un problème si le modèle ne peut pas être imaginé simplement en lisant l'article et qu'il n'y a pas de reproductibilité? Je ne comprends pas vraiment. ――Parce que Keras est un rappeur de tendorflow, c'est naturel, mais les humains comme moi qui n'ont jamais touché tensolflow rencontrent des difficultés, il vaut peut-être mieux toucher tensorflow pour le moment.

Recommended Posts

Implémenter LSTM AutoEncoder avec Keras
Implémenter Keras LSTM feed forward avec numpy
Implémenter le codeur automatique variationnel conditionnel (CVAE) dans le système TensorFlow 2
Ecrire DCGAN avec Keras
Implémentation hard-swish avec Keras
Mettre en œuvre des recommandations en Python
Implémenter XENO avec python
Implémenter sum en Python
LSTM multivarié avec Keras
Implémenter Traceroute dans Python 3
Implémenter BEGAN (Boundary Equilibrium Generative Adversarial Networks) avec Keras
Visualiser le modèle Keras avec Python 3.5
Implémenter la fonction de suivi dans Django
Implémenter la fonction de minuterie dans pygame
Implémenter le transfert de style avec Pytorch
Mettre en œuvre une fermeture récursive dans Go
Implémenter Naive Bayes dans Python 3.3
Implémenter UnionFind (équivalent) en 10 lignes
Implémenter d'anciens chiffrements en python
Implémenter Redis Mutex en Python
Implémenter l'extension en Python
Mettre en œuvre un RPC rapide en Python
Implémenter l'algorithme de Dijkstra en python
Implémenter le bot de discussion Slack en Python
Implémenter le processus gaussien dans Pyro
Mettre en œuvre l'apprentissage de l'empilement en Python [Kaggle]
Mettre en œuvre un test piloté par table en Java
Implémenter la fonction power.prop.test de R en python
Implémenter le deep learning / VAE (Variational Autoencoder)
Implémenter un paramètre de date dans Tkinter
Implémenter le modèle Singleton en Python
Solution pour ValueError dans Keras imdb.load_data
Débutant RNN (LSTM) | Essayer avec Keras
Implémentez rapidement l'API REST en Python
Implémentation simple de l'analyse de régression avec Keras