Encodeur automatique dans TensorFlow avec exemple

Qu’est-ce que l’autoencodeur dans le Deep Learning ?

An Encodeur automatique est un outil permettant d'apprendre efficacement le codage de données de manière non supervisée. Il s'agit d'un type de réseau neuronal artificiel qui vous aide à apprendre la représentation d'ensembles de données pour la réduction de dimensionnalité en entraînant le réseau neuronal à ignorer le bruit du signal. C'est un excellent outil pour recréer une entrée.

En termes simples, la machine prend, disons, une image et peut produire une image étroitement liée. L’entrée dans ce type de réseau neuronal n’est pas étiquetée, ce qui signifie que le réseau est capable d’apprendre sans supervision. Plus précisément, l'entrée est codée par le réseau pour se concentrer uniquement sur la fonctionnalité la plus critique. C'est l'une des raisons pour lesquelles l'auto-encodeur est populaire pour la réduction de dimensionnalité. En outre, les auto-encodeurs peuvent être utilisés pour produire modèles d'apprentissage génératifs. Par exemple, le réseau neuronal peut être entraîné avec un ensemble de visages, puis produire de nouveaux visages.

Comment fonctionne l'encodeur automatique TensorFlow ?

Le but d'un auto-encodeur est de produire une approximation de l'entrée en se concentrant uniquement sur les caractéristiques essentielles. Vous vous demandez peut-être pourquoi ne pas simplement apprendre à copier et coller l'entrée pour produire la sortie. En fait, un auto-encodeur est un ensemble de contraintes qui obligent le réseau à apprendre de nouvelles façons de représenter les données, différentes de la simple copie de la sortie.

Un auto-encodeur typique est défini avec une entrée, une représentation interne et une sortie (une approximation de l'entrée). L'apprentissage s'effectue dans les couches attachées à la représentation interne. En fait, il existe deux blocs principaux de couches qui ressemblent à un réseau neuronal traditionnel. La légère différence est que la couche contenant la sortie doit être égale à l’entrée. Dans l'image ci-dessous, l'entrée d'origine va dans le premier bloc appelé le codeur. Cette représentation interne compresse (réduit) la taille de l’entrée. Dans le deuxième bloc a lieu la reconstruction de l'entrée. C'est la phase de décodage.

Fonctionnement de l'auto-encodeur
Fonctionnement de l'auto-encodeur

Fonctionnement de l'auto-encodeur

Le modèle mettra à jour les poids en minimisant la fonction de perte. Le modèle est pénalisé si le résultat de la reconstruction est différent de l'entrée.

Concrètement, imaginez une image d'une taille de 50×50 (soit 250 pixels) et un réseau de neurones avec une seule couche cachée composée d'une centaine de neurones. L'apprentissage se fait sur une carte de caractéristiques deux fois plus petite que l'entrée. Cela signifie que le réseau doit trouver un moyen de reconstruire 250 pixels avec seulement un vecteur de neurones égal à 100.

Exemple d'encodeur automatique empilé

Dans ce didacticiel Autoencoder, vous apprendrez à utiliser un autoencodeur empilé. L'architecture est similaire à un réseau de neurones traditionnel. L'entrée va vers une couche cachée afin d'être compressée, ou de réduire sa taille, puis atteint les couches de reconstruction. L'objectif est de produire une image de sortie aussi proche que l'originale. Le modèle doit apprendre un moyen d’accomplir sa tâche sous un ensemble de contraintes, c’est-à-dire avec une dimension inférieure.

De nos jours, les auto-encodeurs dans L'apprentissage en profondeur sont principalement utilisés pour débruiter une image. Imaginez une image avec des rayures ; un humain est toujours capable de reconnaître le contenu. L'idée de l'auto-encodeur de débruitage est d'ajouter du bruit à l'image pour forcer le réseau à apprendre le modèle derrière les données.

L’autre famille utile d’Autoencoder Deep Learning est l’autoencodeur variationnel. Ce type de réseau peut générer de nouvelles images. Imaginez que vous formez un réseau à l'image d'un homme ; un tel réseau peut produire de nouveaux visages.

Comment créer un encodeur automatique avec TensorFlow

Dans ce didacticiel, vous apprendrez à créer un encodeur automatique empilé pour reconstruire une image.

Vous utiliserez le Ensemble de données CIFAR-10 qui contient 60000 32 images couleur 32 × 50000. L'ensemble de données Autoencoder est déjà réparti entre 10000 images pour la formation et pour les tests. Il existe jusqu'à dix classes :

  • Avion
  • Automobile
  • Oiseau
  • Cathy
  • Cerf
  • Chien
  • grenouille
  • Cheval
  • Expédier
  • Camion

Vous devez télécharger les images dans cette URL https://www.cs.toronto.edu/~kriz/cifar.html et les décompresser. Le dossier for-10-batches-py contient cinq lots de données contenant chacun 10000 images dans un ordre aléatoire.

Avant de créer et d'entraîner votre modèle, vous devez appliquer un certain traitement des données. Vous procéderez comme suit :

  1. Importez les données
  2. Convertir les données au format noir et blanc
  3. Ajouter tous les lots
  4. Construire l'ensemble de données de formation
  5. Construire un visualiseur d'images

Prétraitement d'image

Étape 1) Importez les données

Selon le site officiel, vous pouvez télécharger les données avec le code suivant. Le code Autoencoder chargera les données dans un dictionnaire avec le données et la étiquette. Notez que le code est une fonction.

import numpy as np
import tensorflow as tf
import pickle
def unpickle(file):
    import pickle
    with open(file, 'rb') as fo:
        dict = pickle.load(fo, encoding='latin1')
    return dict

Étape 2) Convertissez les données au format noir et blanc

Pour plus de simplicité, vous convertirez les données en niveaux de gris. C'est-à-dire avec une seule dimension contre trois pour l'image en couleurs. La plupart des réseaux neuronaux fonctionnent uniquement avec une entrée à une dimension.

def grayscale(im):
    return im.reshape(im.shape[0], 3, 32, 32).mean(1).reshape(im.shape[0], -1)

Étape 3) Ajouter tous les lots

Maintenant que les deux fonctions sont créées et l'ensemble de données chargé, vous pouvez écrire une boucle pour ajouter les données en mémoire. Si vous vérifiez attentivement, le fichier de décompression contenant les données est nommé data_batch_ avec un nombre de 1 à 5. Vous pouvez parcourir les fichiers et l'ajouter aux données.

Une fois cette étape terminée, vous convertissez les données de couleurs au format d'échelle de gris. Comme vous pouvez le voir, la forme des données est 50000 1024 et 32 32. Les 2014* pixels sont désormais aplatis à .

# Load the data into memory
data, labels = [], []
## Loop over the b
for i in range(1, 6):
    filename = './cifar-10-batches-py/data_batch_' + str(i)
    open_data = unpickle(filename)
    if len(data) > 0:
        data = np.vstack((data, open_data['data']))
        labels = np.hstack((labels, open_data['labels']))
    else:
        data = open_data['data']
        labels = open_data['labels']

data = grayscale(data)
x = np.matrix(data)
y = np.array(labels)
print(x.shape)
(50000, 1024)

Remarque : remplacez « ./cifar-10-batches-py/data_batch_ » par l'emplacement réel de votre fichier. Par exemple pour Windows machine, le chemin pourrait être filename = 'E:\cifar-10-batches-py\data_batch_' + str(i)

Étape 4) Construire l'ensemble de données de formation

Pour rendre l'entraînement plus rapide et plus facile, vous entraînerez un modèle sur les images du cheval uniquement. Les chevaux constituent la septième classe dans les données de l'étiquette. Comme mentionné dans la documentation de l'ensemble de données CIFAR-10, chaque classe contient 5000 5.000 images. Vous pouvez imprimer la forme des données pour confirmer qu'il y a 1024 images avec colonnes, comme indiqué ci-dessous. TensorFlow Exemple d’étape d’encodeur automatique.

horse_i = np.where(y == 7)[0]
horse_x = x[horse_i]
print(np.shape(horse_x)) 
(5000, 1024)

Étape 5) Construire un visualiseur d'images

Enfin, vous construisez une fonction pour tracer les images. Vous aurez besoin de cette fonction pour imprimer l'image reconstruite à partir de l'auto-encodeur.

Un moyen simple d'imprimer des images consiste à utiliser l'objet imshow de la bibliothèque matplotlib. Notez que vous devez convertir la forme des données de 1024 en 32*32 (c'est-à-dire le format d'une image).

# To plot pretty figures
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
def plot_image(image, shape=[32, 32], cmap = "Greys_r"):
    plt.imshow(image.reshape(shape), cmap=cmap,interpolation="nearest")
    plt.axis("off")   

La fonction prend 3 arguments :

  • Image(s): l'entrée
  • Forme: liste, la dimension de l'image
  • cmap:choisissez la carte des couleurs. Par défaut, gris

Vous pouvez essayer de tracer la première image de l'ensemble de données. Vous devriez voir un homme à cheval.

plot_image(horse_x[1], shape=[32, 32], cmap = "Greys_r")

Construire un visualiseur d'images

Définir l'estimateur d'ensemble de données

Très bien, maintenant que l'ensemble de données est prêt à être utilisé, vous pouvez commencer à utiliser Tensorflow. Avant de construire le modèle, utilisons l'estimateur Dataset de Tensorflow pour alimenter le réseau.

Vous construirez un ensemble de données avec l'estimateur TensorFlow. Pour vous rafraîchir l'esprit, vous devez utiliser :

  • from_tensor_slices
  • répéter
  • lot

Le code complet pour créer l’ensemble de données est :

dataset = tf.data.Dataset.from_tensor_slices(x).repeat().batch(batch_size)

Notez que x est un espace réservé avec la forme suivante :

  • [Aucun,n_inputs] : défini sur Aucun car le nombre d'images transmises au réseau est égal à la taille du lot.

pour plus de détails, veuillez vous référer au tutoriel sur régression linéaire.

Après cela, vous devez créer l'itérateur. Sans cette ligne de code, aucune donnée ne transitera par le pipeline.

iter = dataset.make_initializable_iterator() # create the iteratorfeatures = iter.get_next()

Maintenant que le pipeline est prêt, vous pouvez vérifier si la première image est la même qu'avant (c'est-à-dire un homme sur un cheval).

Vous définissez la taille du lot sur 1 car vous souhaitez uniquement alimenter l'ensemble de données avec une seule image. Vous pouvez voir la dimension des données avec print(sess.run(features).shape). Il est égal à (1, 1024). 1 signifie qu'une seule image avec 1024 est alimentée chacune. Si la taille du lot est définie sur deux, alors deux images passeront par le pipeline. (Ne modifiez pas la taille du lot. Sinon, cela générera une erreur. Une seule image à la fois peut accéder à la fonction plot_image().

## Parameters
n_inputs = 32 * 32
BATCH_SIZE = 1
batch_size = tf.placeholder(tf.int64)

# using a placeholder
x = tf.placeholder(tf.float32, shape=[None,n_inputs])
## Dataset
dataset = tf.data.Dataset.from_tensor_slices(x).repeat().batch(batch_size)
iter = dataset.make_initializable_iterator() # create the iterator
features = iter.get_next()

## Print the image
with tf.Session() as sess:
    # feed the placeholder with data
    sess.run(iter.initializer, feed_dict={x: horse_x,
                                         batch_size: BATCH_SIZE}) 
    print(sess.run(features).shape) 
    plot_image(sess.run(features), shape=[32, 32], cmap = "Greys_r")
(1, 1024)

Définir l'estimateur d'ensemble de données

Construire le réseau

Il est temps de construire le réseau. Vous entraînerez un auto-encodeur empilé, c'est-à-dire un réseau avec plusieurs couches cachées.

Votre réseau aura une couche d'entrée avec 1024 points, soit 32×32, la forme de l'image.

Le bloc codeur aura une couche supérieure cachée avec 300 neurones, une couche centrale avec 150 neurones. Le bloc décodeur est symétrique au codeur. Vous pouvez visualiser le réseau dans l'image ci-dessous. Notez que vous pouvez modifier les valeurs des calques masqués et centraux.

Construire le réseau
Construire le réseau pour Autoencoder

La construction d'un encodeur automatique est très similaire à tout autre modèle d'apprentissage en profondeur.

Vous construirez le modèle en suivant ces étapes :

  1. Définir les paramètres
  2. Définir les calques
  3. Définir l'architecture
  4. Définir l'optimisation
  5. Exécuter le modèle
  6. Évaluer le modèle

Dans la section précédente, vous avez appris à créer un pipeline pour alimenter le modèle. Il n'est donc pas nécessaire de créer à nouveau l'ensemble de données. Vous construirez un auto-encodeur à quatre couches. Vous utilisez l'initialisation Xavier. Il s'agit d'une technique permettant de définir les poids initiaux égaux à la variance de l'entrée et de la sortie. Enfin, vous utilisez la fonction d'activation d'elu. Vous régularisez la fonction de perte avec le régularisateur L2.

Étape 1) Définir les paramètres

La première étape consiste à définir le nombre de neurones dans chaque couche, le taux d'apprentissage et l'hyperparamètre du régulariseur.

Avant cela, vous importez partiellement la fonction. C'est une meilleure méthode pour définir les paramètres des couches denses. Le code ci-dessous définit les valeurs de l'architecture de l'auto-encodeur. Comme indiqué précédemment, l'auto-encodeur comporte deux couches, avec 300 neurones dans les premières couches et 150 dans les secondes. Leurs valeurs sont stockées dans n_hidden_1 et n_hidden_2.

Vous devez définir le taux d'apprentissage et l'hyperparamètre L2. Les valeurs sont stockées dans learning_rate et l2_reg

from functools import partial

## Encoder
n_hidden_1 = 300
n_hidden_2 = 150  # codings

## Decoder
n_hidden_3 = n_hidden_1
n_outputs = n_inputs

learning_rate = 0.01
l2_reg = 0.0001

La technique d'initialisation Xavier est appelée avec l'objet xavier_initializer de la contrib de l'estimateur. Dans le même estimateur, vous pouvez ajouter le régulariseur avec l2_regularizer

## Define the Xavier initialization
xav_init =  tf.contrib.layers.xavier_initializer()
## Define the L2 regularizer
l2_regularizer = tf.contrib.layers.l2_regularizer(l2_reg)

Étape 2) Définir les calques

Tous les paramètres des couches denses ont été définis ; vous pouvez tout emballer dans la variable dense_layer en utilisant l'objet partial. dense_layer qui utilise l'activation ELU, l'initialisation Xavier et la régularisation L2.

## Create the dense layer
dense_layer = partial(tf.layers.dense,
                         activation=tf.nn.elu,
                         kernel_initializer=xav_init,
                         kernel_regularizer=l2_regularizer)

Étape 3) Définir l'architecture

Si vous regardez l’image de l’architecture, vous remarquez que le réseau empile trois couches avec une couche de sortie. Dans le code ci-dessous, vous connectez les couches appropriées. Par exemple, la première couche calcule le produit scalaire entre les caractéristiques matricielles d'entrée et les matrices contenant les 300 poids. Une fois le produit scalaire calculé, la sortie est envoyée à la fonction d'activation Elu. La sortie devient l'entrée de la couche suivante, c'est pourquoi vous l'utilisez pour calculer Hidden_2 et ainsi de suite. Les multiplications des matrices sont les mêmes pour chaque couche car vous utilisez la même fonction d'activation. Notez que la dernière couche, les sorties, n'applique pas de fonction d'activation. C'est logique car il s'agit de l'entrée reconstruite

## Make the mat mul
hidden_1 = dense_layer(features, n_hidden_1)
hidden_2 = dense_layer(hidden_1, n_hidden_2)
hidden_3 = dense_layer(hidden_2, n_hidden_3)
outputs = dense_layer(hidden_3, n_outputs, activation=None)

Étape 4) Définir l'optimisation

La dernière étape consiste à construire l'optimiseur. Vous utilisez l’erreur quadratique moyenne comme fonction de perte. Si vous vous souvenez du didacticiel sur la régression linéaire, vous savez que le MSE est calculé avec la différence entre la sortie prédite et l'étiquette réelle. Ici, l'étiquette est la fonctionnalité car le modèle tente de reconstruire l'entrée. Par conséquent, vous voulez la moyenne de la somme des différences du carré entre la sortie et l’entrée prédites. Avec TensorFlow, vous pouvez coder la fonction de perte comme suit :

loss = tf.reduce_mean(tf.square(outputs - features))

Ensuite, vous devez optimiser la fonction de perte. Vous utilisez l'optimiseur Adam pour calculer les dégradés. La fonction objectif est de minimiser la perte.

## Optimize
loss = tf.reduce_mean(tf.square(outputs - features))
optimizer = tf.train.AdamOptimizer(learning_rate)
train  = optimizer.minimize(loss)

Un réglage supplémentaire avant d'entraîner le modèle. Vous souhaitez utiliser une taille de lot de 150, c'est-à-dire alimenter le pipeline avec 150 images à chaque itération. Vous devez calculer le nombre d'itérations manuellement. C'est trivial à faire :

Si vous souhaitez transmettre 150 images à chaque fois et que vous savez qu'il y a 5000 33 images dans l'ensemble de données, le nombre d'itérations est égal à . En python, vous pouvez exécuter les codes suivants et vous assurer que la sortie est  :

BATCH_SIZE = 150
### Number of batches :  length dataset / batch size
n_batches = horse_x.shape[0] // BATCH_SIZE
print(n_batches)
33

Étape 5) Exécuter le modèle

Enfin et surtout, entraînez le modèle. Vous entraînez le modèle avec 100 époques. Autrement dit, le modèle verra 100 fois les images avec des poids optimisés.

Vous connaissez déjà les codes pour entraîner un modèle dans Tensorflow. La légère différence est de canaliser les données avant d'exécuter la formation. De cette façon, le modèle s'entraîne plus rapidement.

Vous souhaitez imprimer la perte après dix époques pour voir si le modèle apprend quelque chose (c'est-à-dire que la perte diminue). La formation dure 2 à 5 minutes, selon le matériel de votre machine.

## Set params
n_epochs = 100

## Call Saver to save the model and re-use it later during evaluation
saver = tf.train.Saver()

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    # initialise iterator with train data
    sess.run(iter.initializer, feed_dict={x: horse_x,
                                          batch_size: BATCH_SIZE})
    print('Training...')
    print(sess.run(features).shape) 
    for epoch in range(n_epochs):       
        for iteration in range(n_batches):
            sess.run(train)
        if epoch % 10 == 0:
            loss_train = loss.eval()   # not shown
            print("\r{}".format(epoch), "Train MSE:", loss_train) 
        #saver.save(sess, "./my_model_all_layers.ckpt") 
    save_path = saver.save(sess, "./model.ckpt")    
    print("Model saved in path: %s" % save_path)  
Training...
(150, 1024)
0 Train MSE: 2934.455
10 Train MSE: 1672.676
20 Train MSE: 1514.709
30 Train MSE: 1404.3118
40 Train MSE: 1425.058
50 Train MSE: 1479.0631
60 Train MSE: 1609.5259
70 Train MSE: 1482.3223
80 Train MSE: 1445.7035
90 Train MSE: 1453.8597
Model saved in path: ./model.ckpt

Étape 6) Évaluer le modèle

Maintenant que votre modèle est formé, il est temps de l'évaluer. Vous devez importer le test sert à partir du fichier /cifar-10-batches-py/.

test_data = unpickle('./cifar-10-batches-py/test_batch')
test_x = grayscale(test_data['data'])
#test_labels = np.array(test_data['labels'])

REMARQUE: Pour un Windows machine, le code devient test_data = unpickle(r”E:\cifar-10-batches-py\test_batch”)

Vous pouvez essayer d'imprimer les images 13, qui est un cheval

plot_image(test_x[13], shape=[32, 32], cmap = "Greys_r")

Évaluer le modèle

Pour évaluer le modèle, vous utiliserez la valeur en pixels de cette image et verrez si l'encodeur peut reconstruire la même image après avoir réduit de 1024  pixels. Notez que vous définissez une fonction pour évaluer le modèle sur différentes images. Le modèle ne devrait fonctionner mieux que sur les chevaux.

La fonction prend deux arguments :

  • df: Importer les données de test
  • numéro_image: indique quelle image importer

La fonction est divisée en trois parties :

  1. Remodeler l'image à la bonne dimension, c'est-à-dire 1, 1024
  2. Nourrir le modèle avec l'image invisible, encoder/décoder l'image
  3. Imprimer l'image réelle et reconstruite
def reconstruct_image(df, image_number = 1):
    ## Part 1: Reshape the image to the correct dimension i.e 1, 1024
    x_test = df[image_number]
    x_test_1 = x_test.reshape((1, 32*32))
    
    ## Part 2: Feed the model with the unseen image, encode/decode the image
    with tf.Session() as sess:     
        sess.run(tf.global_variables_initializer()) 
        sess.run(iter.initializer, feed_dict={x: x_test_1,
                                      batch_size: 1})
    ## Part 3:  Print the real and reconstructed image
      # Restore variables from disk.
        saver.restore(sess, "./model.ckpt")  
        print("Model restored.")
      # Reconstruct image
        outputs_val = outputs.eval()
        print(outputs_val.shape)
        fig = plt.figure()
      # Plot real
        ax1 = fig.add_subplot(121)
        plot_image(x_test_1, shape=[32, 32], cmap = "Greys_r")
      # Plot estimated
        ax2 = fig.add_subplot(122)
        plot_image(outputs_val, shape=[32, 32], cmap = "Greys_r")
        plt.tight_layout()
        fig = plt.gcf()

Maintenant que la fonction d'évaluation est définie, vous pouvez jeter un œil à l'image reconstruite numéro treize

reconstruct_image(df =test_x, image_number = 13)
INFO:tensorflow:Restoring parameters from ./model.ckpt
Model restored.
(1, 1024)

Évaluer le modèle

Résumé

  • L'objectif principal d'un encodeur automatique est de compresser les données d'entrée, puis de les décompresser en une sortie qui ressemble beaucoup aux données d'origine.
  • L'architecture d'un auto-encodeur symétrique avec une couche pivot nommée couche centrale.
  • Vous pouvez créer l'encodeur automatique en utilisant :
  • Partiel: pour créer les calques denses avec le réglage typique :

      	tf.layers.dense,                         
      	activation=tf.nn.elu,                         
      	kernel_initializer=xav_init,                         
      	kernel_regularizer=l2_regularizer

    dense_layer(): pour faire la multiplication matricielle

  • Vous pouvez définir la fonction de perte et l'optimisation avec :
  • loss = tf.reduce_mean(tf.square(outputs - features))
    optimizer = tf.train.AdamOptimizer(learning_rate)
    train  = optimizer.minimize(loss)
    
  • Exécutez pour la dernière fois une session pour entraîner le modèle.