Développer des réseaux de neurones convolutifs (CNN) pour améliorer la vision par ordinateur

1. Avant de commencer

Dans cet atelier de programmation, vous allez apprendre à utiliser des réseaux CNN pour améliorer vos modèles de classification d'images.

Conditions préalables

Cet atelier de programmation s'appuie sur les travaux réalisés en deux versements précédents : Créer un modèle de vision par ordinateur, où nous présenterons une partie du code que vous utiliserez ici, ainsi que l'atelier de programmation Créer des convolutions et effectuer des pools.

Points abordés

  • Améliorer la vision et la précision de l'ordinateur grâce aux convolutions

Objectifs de l'atelier

  • Couches pour améliorer votre réseau de neurones

Prérequis

Vous trouverez le code pour le reste de l'atelier de programmation en cours d'exécution dans Colab.

TensorFlow et les bibliothèques que vous avez installées dans l'atelier de programmation précédent doivent également être installées.

2. Améliorer la précision de la vision par ordinateur grâce aux convolutions

Vous savez désormais comment reconnaître des images de mode à l'aide d'un réseau de neurones profond (DNN, Deep Neural Network) contenant trois couches : la couche d'entrée (en forme de données d'entrée), la couche de sortie (en forme de sortie souhaitée) et une couche cachée. Vous avez testé plusieurs paramètres qui ont une influence sur la précision finale, tels que les différentes tailles de calques cachés et le nombre d'époques d'entraînement.

Pour plus de commodité, ici tout le code. Exécutez-le et notez la précision du test imprimée à la fin.

import tensorflow as tf
mnist = tf.keras.datasets.fashion_mnist
(training_images, training_labels), (test_images, test_labels) = mnist.load_data()
training_images=training_images/255.0
test_images=test_images/255.0
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(training_images, training_labels, epochs=5)
test_loss, test_accuracy = model.evaluate(test_images, test_labels)
print ('Test loss: {}, Test accuracy: {}'.format(test_loss, test_accuracy*100))

La justesse est d'environ 89% pour l'entraînement et 87% pour la validation. Vous pouvez améliorer encore cet aspect en utilisant des convolutions qui affinent le contenu de l'image pour se concentrer sur des détails spécifiques et distincts.

Si vous avez effectué un traitement d'images à l'aide d'un filtre, les convolutions vous paraissent très familiers.

Pour résumer, vous prenez un tableau (généralement 3x3 ou 5x5) et le transmettez à l'image. En modifiant les pixels sous-jacents en fonction de la formule de cette matrice, vous pouvez effectuer des opérations telles que la détection d'arêtes. Par exemple, une connexion 3x3 est généralement définie pour la détection d'arêtes, où la cellule du milieu est 8, et tous ses voisins sont -1. Dans ce cas, pour chaque pixel, nous multiplions sa valeur par 8, puis soustrayez la valeur de chaque voisin. À chaque pixel, vous obtiendrez une nouvelle image dont les bords seront améliorés.

Cette approche est parfaite pour la vision par ordinateur, car elle permet à l'ordinateur de faire la différence entre un élément et un autre pour améliorer ses caractéristiques. Mieux encore, la quantité d'informations nécessaires est nettement inférieure, car vous n'avez besoin d'entraîner que les fonctionnalités mises en avant.

C'est le concept des réseaux de neurones convolutifs. Ajoutez des couches pour créer une convolution avant de disposer des couches denses. Les informations obtenues vers les couches denses deviendront alors plus précises et éventuellement plus précises.

3. Essayer le code

Exécutez le code suivant. Il s'agit du même réseau de neurones que précédemment, mais cette fois-ci, les couches convolutives ont été ajoutées en premier. Cette opération va prendre plus de temps, mais vérifiez son impact sur la précision:

import tensorflow as tf
print(tf.__version__)
mnist = tf.keras.datasets.fashion_mnist
(training_images, training_labels), (test_images, test_labels) = mnist.load_data()
training_images=training_images.reshape(60000, 28, 28, 1)
training_images=training_images / 255.0
test_images = test_images.reshape(10000, 28, 28, 1)
test_images=test_images / 255.0
model = tf.keras.models.Sequential([
  tf.keras.layers.Conv2D(64, (3, 3), activation='relu', input_shape=(28, 28, 1)),
  tf.keras.layers.MaxPooling2D(2, 2),
  tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
  tf.keras.layers.MaxPooling2D(2,2),
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.summary()
model.fit(training_images, training_labels, epochs=5)
test_loss, test_accuracy = model.evaluate(test_images, test_labels)
print ('Test loss: {}, Test accuracy: {}'.format(test_loss, test_accuracy*100))

Il a probablement grimpé jusqu'à 93% des données d'entraînement et 91% des données de validation.

À présent, essayez de l'exécuter pendant plus d'époques (en compte environ 20) et d'explorer les résultats. Les résultats de l'entraînement peuvent sembler très bons, mais ils peuvent en fait baisser en raison d'un phénomène appelé surapprentissage.

Le surapprentissage se produit lorsque le réseau apprend les données de l'ensemble d'entraînement trop bien, de sorte qu'il est spécialisé dans la reconnaissance de ces données uniquement, et qu'il est donc moins efficace de voir les autres données dans des situations plus générales. Par exemple, si vous n'avez entraîné que des talons, le réseau peut être très efficace pour identifier les talons, mais les baskets peuvent prêter à confusion.

Consultez à nouveau le code et découvrez comment les convolutions ont été créées étape par étape.

4. Collecter les données

La première étape consiste à collecter les données.

Vous remarquerez qu'une modification est apportée ici et que les données d'entraînement doivent être remodelées. Cela s'explique par le fait que la première convolution attend un Tensor unique contenant tous les éléments. Ainsi, au lieu de 60 000 éléments 28 x 28 x 1 dans une liste, vous disposez d'une seule liste 4D de 60 000 x 28 x 18 x 1, identique pour les images de test. Si vous ne le faites pas, vous obtiendrez un message d'erreur lors de l'entraînement, car les convolutions ne reconnaissent pas la forme.

import tensorflow as tf
mnist = tf.keras.datasets.fashion_mnist
(training_images, training_labels), (test_images, test_labels) = mnist.load_data()
training_images=training_images.reshape(60000, 28, 28, 1)
training_images = training_images/255.0
test_images = test_images.reshape(10000, 28, 28, 1)
test_images = test_images/255.0

5. Définir le modèle

Définissez ensuite votre modèle. Au lieu d'ajouter la couche d'entrée en haut, vous allez ajouter une couche convolutive. Les paramètres sont les suivants :

  • Nombre de convolutions que vous souhaitez générer. Une valeur comme 32 est un bon point de départ.
  • Taille de la matrice convolutive, dans ce cas une grille de 3x3.
  • Fonction d'activation à utiliser. Dans ce cas, utilisez relu.
  • Dans la première couche, la forme des données d'entrée.

Vous allez suivre la convolution avec une couche de pooling maximale, conçue pour compresser l'image tout en conservant le contenu des caractéristiques mises en évidence par la convolution. En spécifiant (2,2) pour le pooling maximal, l'effet consiste à réduire la taille de l'image d'un facteur de 4. Il crée un tableau de pixels 2x2 et sélectionne la plus grande valeur de pixel, passant à 4 pixels en 1. Ce calcul est répété sur l'image, ce qui permet de réduire de moitié le nombre de pixels horizontaux et de réduire de moitié le nombre de pixels verticaux.

Vous pouvez appeler model.summary() pour afficher la taille et la forme du réseau. Notez qu'après chaque couche de pool maximal, la taille de l'image est réduite comme suit:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_2 (Conv2D)            (None, 26, 26, 64)        640       
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 13, 13, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 11, 11, 64)        36928     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
flatten_2 (Flatten)          (None, 1600)              0         
_________________________________________________________________
dense_4 (Dense)              (None, 128)               204928    
_________________________________________________________________
dense_5 (Dense)              (None, 10)                1290      
=================================================================

Voici le code complet du réseau de neurones convolutif:

model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
tf.keras.layers.MaxPooling2D(2, 2),
#Add another convolution
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2, 2),
#Now flatten the output. After this you'll just have the same DNN structure as the non convolutional version
tf.keras.layers.Flatten(),
#The same 128 dense layers, and 10 output layers as in the pre-convolution example:
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])

6. Compiler et entraîner le modèle

Compilez le modèle, appelez la méthode d'adéquation pour effectuer l'entraînement, et évaluez la perte et la justesse de l'ensemble de test.

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(training_images, training_labels, epochs=5)
test_loss, test_acc = model.evaluate(test_images, test_labels)
print ('Test loss: {}, Test accuracy: {}'.format(test_loss, test_acc*100))

7. Visualiser les convolutions et les pools

Ce code montre les convolutions sous forme graphique. print (test_labels[:100]) affiche les 100 premières étiquettes de l'ensemble de test, et vous pouvez constater que les mêmes valeurs dans les index 0, 23 et 28 sont identiques (9). Toutes les chaussures. Observez le résultat de l'exécution de la convolution sur chacun d'eux et, vous commencerez à constater l'émergence de caractéristiques communes. Désormais, lorsque le DNN s'entraîne à ces données, il utilise beaucoup moins d'informations et cherche peut-être un point commun entre les chaussures en fonction de cette convolution et de la combinaison du pool.

print(test_labels[:100])
[9 2 1 1 6 1 4 6 5 7 4 5 7 3 4 1 2 4 8 0 2 5 7 9 1 4 6 0 9 3 8 8 3 3 8 0 7
 5 7 9 6 1 3 7 6 7 2 1 2 2 4 4 5 8 2 2 8 4 8 0 7 7 8 5 1 1 2 3 9 8 7 0 2 6
 2 3 1 2 8 4 1 8 5 9 5 0 3 2 0 6 5 3 6 7 1 8 0 1 4 2]

Vous pouvez maintenant sélectionner certaines des images correspondantes pour ces libellés et afficher leur apparence à travers les convolutions. Dans le code suivant, FIRST_IMAGE, SECOND_IMAGE et THIRD_IMAGE sont donc tous les index de la valeur 9, une cheville.

import matplotlib.pyplot as plt
f, axarr = plt.subplots(3,4)
FIRST_IMAGE=0
SECOND_IMAGE=23
THIRD_IMAGE=28
CONVOLUTION_NUMBER = 6
from tensorflow.keras import models
layer_outputs = [layer.output for layer in model.layers]
activation_model = tf.keras.models.Model(inputs = model.input, outputs = layer_outputs)
for x in range(0,4):
  f1 = activation_model.predict(test_images[FIRST_IMAGE].reshape(1, 28, 28, 1))[x]
  axarr[0,x].imshow(f1[0, : , :, CONVOLUTION_NUMBER], cmap='inferno')
  axarr[0,x].grid(False)
  f2 = activation_model.predict(test_images[SECOND_IMAGE].reshape(1, 28, 28, 1))[x]
  axarr[1,x].imshow(f2[0, : , :, CONVOLUTION_NUMBER], cmap='inferno')
  axarr[1,x].grid(False)
  f3 = activation_model.predict(test_images[THIRD_IMAGE].reshape(1, 28, 28, 1))[x]
  axarr[2,x].imshow(f3[0, : , :, CONVOLUTION_NUMBER], cmap='inferno')
  axarr[2,x].grid(False)

Vous devriez observer l'exemple suivant, où la convolution s'appuie sur l'essence de la semelle de la chaussure, et qui est efficace comme caractéristique commune à toutes les chaussures.

6c9109bcc640a1ec.png

8. Exercices

Exercice 1

Essayez de modifier les convolutions. Modifiez le nombre de convolutions de 32 à 16 ou 64. Quelles sont les conséquences sur la précision et la durée de l'entraînement ?

Exercice 2

Supprimez la convolution finale. Quelles sont les conséquences sur la précision ou la durée de l'entraînement ?

Exercice 3

Ajoutez d'autres convolutions. Quelles sont les conséquences ?

Exercice 4

Supprimez toutes les convolutions, sauf la première. Quelles sont les conséquences ? Faites un essai.

9. Félicitations

Vous avez créé votre premier CNN ! Pour savoir comment améliorer vos modèles de vision par ordinateur, consultez Utiliser des réseaux de neurones convolutifs (CNN) avec des images complexes.