Encodeur automatique dans TensorFlow avec exemple
⚡ Résumé intelligent
Les auto-encodeurs compressent une entrée en une représentation interne plus petite, puis la reconstruisent, en apprenant les caractéristiques les plus importantes sans étiquettes. Ce tutoriel illustre l'empilement de quatre couches denses dans TensorFlow et la reconstruction d'images de chevaux du CIFAR-10.
Qu’est-ce que l’autoencodeur dans le Deep Learning ?
An Encodeur automatique est un outil permettant d'apprendre efficacement le codage des données dans un sans surveillance manière. C'est un type de réseau neuronal artificiel Cela permet d'apprendre la représentation des 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, par exemple, une image et peut produire une image très similaire. L'entrée de 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 encodée par le réseau pour se concentrer uniquement sur la caractéristique la plus importante. C'est l'une des raisons pour lesquelles les auto-encodeurs sont populaires pour la réduction de dimensionnalité. De plus, les auto-encodeurs peuvent être utilisés pour créer des modèles d'apprentissage génératif. Par exemple, le réseau neuronal peut être entraîné avec un ensemble de visages, puis générer 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 par une entrée, une représentation interne et une sortie (une approximation de l'entrée). L'apprentissage se déroule dans les couches associées à la représentation interne. En réalité, il comporte deux blocs principaux de couches, semblables à ceux d'un réseau de neurones traditionnel. La différence réside dans le fait que la couche de sortie doit être identique à l'entrée. Dans le schéma ci-dessous, l'entrée originale est traitée par le premier bloc, appelé encodeur. Cette représentation interne compresse (réduit) la taille de l'entrée. Le second bloc effectue la reconstruction de l'entrée : c'est la phase de décodage.

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 de 50×50 pixels (soit 2 500 pixels) et un réseau de neurones à une seule couche cachée composée de cent neurones. L’apprentissage s’effectue sur une carte de caractéristiques vingt-cinq fois plus petite que l’image d’entrée. Autrement dit, le réseau doit reconstruire les 2 500 pixels avec seulement 100 neurones.
Exemple d'encodeur automatique empilé
Dans ce tutoriel sur les auto-encodeurs, vous apprendrez à utiliser un auto-encodeur empilé. Son architecture est similaire à celle d'un réseau de neurones classique. L'entrée est traitée par une couche cachée afin d'être compressée (ou de réduire sa taille), puis transmise aux couches de reconstruction. L'objectif est de produire une image de sortie aussi proche que possible de l'originale. Le modèle doit apprendre à réaliser cette tâche en respectant un ensemble de contraintes, c'est-à-dire avec une dimension réduite.
De nos jours, les auto-encodeurs en apprentissage profond sont principalement utilisés pour le débruitage d'images. Imaginez une image rayée : un humain peut encore en reconnaître le contenu. Le principe du débruitage par auto-encodeur est d'ajouter du bruit à l'image pour contraindre le réseau à apprendre les motifs sous-jacents aux données.
L'autre famille utile d'auto-encodeurs L'apprentissage en profondeur Il s'agit d'un auto-encodeur variationnel. Ce type de réseau peut générer de nouvelles images. Imaginez que vous entraîniez un réseau avec l'image d'un homme ; un tel réseau peut produire de nouveaux visages.
Le tableau ci-dessous place l'autoencodeur empilé construit dans ce tutoriel à côté des autres familles que vous êtes susceptible de rencontrer, afin que la contrainte ajoutée par chacune soit facile à comparer.
| Type d'autoencodeur | Contrainte ajoutée | Utilisation typique |
|---|---|---|
| Incomplet / empilé | Couche de codage plus étroite que l'entrée | Réduction de dimensionnalité, caractéristiques extracproduction |
| Débruitage | Bruit ajouté à l'entrée, cible propre | Nettoyage d'images et de signaux |
| Clairsemé | Pénalité sur le nombre d'unités actives | Fonctionnalités interprétables et basées sur les parties |
| variationnel | La couche de codage apprend une distribution de probabilité | Générer de nouveaux échantillons |
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 jeu de données CIFAR-10, qui contient 60 000 images couleur de 32 × 32 pixels. Le jeu de données de l’auto-encodeur est déjà divisé en 50 000 images pour l’entraînement et 10 000 pour les tests. Il comporte jusqu’à dix classes.
- Avion
- Automobile
- Oiseau
- Cathy
- Cerf
- Chien
- grenouille
- Cheval
- Expédier
- Camion
Vous devez télécharger les images depuis page de données CIFAR-10 Décompressez ensuite l'archive. Le dossier cifar-10-batches-py contient cinq lots de données, chacun composé de 10 000 images présentées 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 :
- Importez les données
- Convertir les données au format noir et blanc
- Ajouter tous les lots
- Construire l'ensemble de données de formation
- Construire un visualiseur d'images
Note de version : Le code de ce tutoriel est destiné à TensorFlow 1.x. Sur TensorFlow 2, le module `tf.contrib` n'existe plus ; les espaces réservés, les sessions et l'itérateur initialisable sont désormais gérés par `tf.compat.v1`. Appeler `tf.compat.v1.disable_v2_behavior()` après l'importation est la méthode la plus rapide pour exécuter le code sans modification.
Prétraitement d'image
Étape 1) Importez les données
D'après le site officiel, vous pouvez importer les données avec le code suivant. Le code de l'autoencodeur chargera les données dans un dictionnaire contenant les données et leurs étiquettes. Veuillez noter qu'il s'agit d'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 que l'ensemble de données est chargé, vous pouvez écrire une boucle pour ajouter les données en mémoire. Si vous vérifiez attentivement, le fichier décompressé contenant les données s'appelle data_batch_ et son numéro est compris entre 1 et 5. Vous pouvez parcourir ce fichier et l'ajouter à data.
Une fois cette étape terminée, les données de couleur sont converties en niveaux de gris. Comme vous pouvez le constater, les données ont une dimension de 50 000 et 1 024. Les 32 × 32 pixels sont ainsi aplatis à 1 024.
# 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)
À noter: Remplacez './cifar-10-batches-py/data_batch_' par l'emplacement réel de votre fichier. Par exemple, pour un 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 accélérer et simplifier l'entraînement, vous entraînerez un modèle uniquement sur les images de chevaux. Les chevaux sont étiquetés 7. Comme indiqué dans la documentation du jeu de données CIFAR-10, chaque classe contient 5 000 images. Vous pouvez visualiser la structure des données pour confirmer la présence de 5 000 images et 1 024 colonnes, comme illustré dans l'exemple d'auto-encodeur TensorFlow ci-dessous.
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.
Pour imprimer facilement des images, utilisez la fonction `imshow()` de la bibliothèque Matplotlib. Notez qu'il est nécessaire de convertir les données de 1024 à 32 × 32 pixels (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 : l'entrée
- Forme : liste, la dimension de l'image
- Cmap : choisissez la palette de couleurs. Par défaut, le 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")
L'appel renvoie la vignette en niveaux de gris ci-dessous et confirme que la redimensionnement de 1024 valeurs vers une image 32×32 est correct.
Définir l'estimateur d'ensemble de données
Parfait, maintenant que l'ensemble de données est prêt, vous pouvez commencer à utiliser TensorFlow. Avant de construire le modèle, utilisez l'estimateur de jeu de données 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é de forme [None, n_entrées]. La première dimension est définie sur None car le nombre d'images fournies au réseau est égal à la taille du lot, qui est déterminée uniquement lors de l'exécution. Pour plus de détails, veuillez consulter le tutoriel sur régression linéaire avec TensorFlow.
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 avez défini la taille du lot à 1 car vous souhaitez n'utiliser qu'une seule image pour l'ensemble de données. Vous pouvez visualiser la dimension des données avec `print(sess.run(features).shape)`. Elle est égale à (1, 1024). La valeur 1 indique qu'une seule image de 1024 valeurs est utilisée à chaque itération. Si la taille du lot est définie à deux, deux images seront traitées. Ne modifiez pas la taille du lot, sous peine de générer une erreur, car la fonction `plot_image()` ne peut traiter qu'une seule image à la fois.
## 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)
Le pipeline renvoie le même cavalier qui a été tracé directement à partir de la matrice NumPy, ce qui prouve que l'espace réservé, l'itérateur et la taille du lot sont correctement connectés.
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 encodeur comprend une couche cachée supérieure de 300 neurones et une couche centrale de 150 neurones. Le bloc décodeur est symétrique à l'encodeur. Le réseau est illustré dans le schéma ci-dessous. Notez que vous pouvez modifier les valeurs des couches cachée et centrale.
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 :
- Définir les paramètres
- Définir les calques
- Définir l'architecture
- Définir l'optimisation
- Exécuter le modèle
- É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 recréer l’ensemble de données. Vous allez construire un auto-encodeur à quatre couches. Vous utiliserez l’initialisation de Xavier, une technique permettant de définir les poids initiaux en fonction de la variance des entrées et des sorties. Enfin, vous utiliserez la fonction d’activation ELU. Vous régulariserez la fonction de perte avec un régulariseur 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, importez la fonction partielle. 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'encodeur comporte deux couches : la première contient 300 neurones et la seconde 150. 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. Ces valeurs sont stockées dans les variables 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 de Xavier est appelée avec l'objet `xavier_initializer` de l'estimateur `contrib`. Dans ce 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)
Note de version : La fonction tf.contrib a été supprimée dans TensorFlow 2. Ses remplaçants directs sont tf.keras.initializers.GlorotUniform() pour xavier_initializer() et tf.keras.regularizers.l2() pour l2_regularizer().
Étape 2) Définir les couches
Tous les paramètres des couches denses ont été définis ; vous pouvez tout regrouper dans la variable `dense_layer` à l’aide de l’objet `partial`. `dense_layer` utilise ensuite l’activation ELU, l’initialisation Xavier et la régularisation L2 à chaque appel.
## 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 examinez le schéma de l'architecture, vous constaterez que le réseau empile trois couches et une couche de sortie. Le code ci-dessous connecte les couches appropriées. Par exemple, la première couche calcule le produit scalaire entre la matrice de caractéristiques d'entrée et la matrice contenant les 300 poids. Une fois le produit scalaire calculé, la sortie est soumise à la fonction d'activation ELU. Cette sortie devient l'entrée de la couche suivante ; c'est pourquoi elle est utilisée pour calculer `hidden_2`, et ainsi de suite. La multiplication matricielle est identique pour chaque couche, car la même fonction d'activation est utilisée. Notez que la dernière couche, la couche de sortie, n'applique pas de fonction d'activation. Cela 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 (EQM) comme fonction de perte. Si vous vous souvenez du tutoriel sur la régression linéaire, vous savez que l'EQM est calculée à partir de la différence entre la sortie prédite et l'étiquette réelle. Ici, l'étiquette est la caractéristique, car le modèle tente de reconstruire l'entrée. Par conséquent, vous souhaitez calculer la moyenne de la somme des carrés des différences entre la sortie prédite et l'entrée. Avec TensorFlow, vous pouvez coder la fonction de perte comme suit :
loss = tf.reduce_mean(tf.square(outputs - features))
Ensuite, il faut optimiser la fonction de perte. On utilise l'optimiseur Adam pour calculer les gradients. L'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 traiter 150 images à chaque fois et que vous savez que l'ensemble de données contient 5000 images, le nombre d'itérations est égal à 5000 divisé par 150. Python Vous pouvez exécuter les codes suivants et vous assurer que le résultat est 33 :
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, il ne reste plus qu'à entraîner le modèle. L'entraînement se fait sur 100 époques. Autrement dit, le modèle verra les images 100 fois tout en optimisant les poids.
Vous connaissez déjà les codes permettant d'entraîner un modèle avec TensorFlow. La légère différence consiste à acheminer les données avant l'entraînement. 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 entraîné, il est temps de l'évaluer. Vous devez importer l'ensemble de test depuis le 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'])
À noter: Pour une solution plus permanente, un verrou à surfaçage ou un loquet monté en surface peut être fixé à la porte et au cadre à l'aide de vis. Lorsqu'il est actionné, le verrou glisse dans un support de réception sur le mur ou le cadre, maintenant la porte coulissante escamotable fermement fermée. C'est l'une des options sans serrure les plus sécurisées disponibles et elle peut être installée en moins de minutes avec des outils de base. Windows machine, le code devient test_data = unpickle(r”E:\cifar-10-batches-py\test_batch”)
Vous pouvez essayer d'imprimer l'image 13, qui représente un cheval.
plot_image(test_x[13], shape=[32, 32], cmap = "Greys_r")
Le graphique confirme que le lot de test a été chargé correctement et que l'image 13 appartient à la classe sur laquelle le modèle a été entraîné.
Pour évaluer le modèle, vous utiliserez la valeur des pixels de cette image et vérifierez si l'encodeur peut la reconstruire après l'avoir réduite à 1024 pixels. Notez que vous définissez une fonction pour évaluer le modèle sur différentes images. Le modèle devrait être plus performant sur les images de chevaux.
La fonction prend deux arguments :
- df : Importer les données de test
- numéro_image : indique l’image à importer
La fonction est divisée en trois parties :
- Remodeler l'image à la bonne dimension, c'est-à-dire 1, 1024
- Nourrir le modèle avec l'image invisible, encoder/décoder l'image
- 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 examiner 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)
La figure place l'image de test originale à gauche et la sortie de l'autoencodeur à droite, ce qui permet de constater facilement la perte de détails fins après la compression de 1024 à 150.




