IV. Perception avec Keras
tensorflow et keras sont deux modules Python qui permettent de construire des réseaux de neurones apprenants. Nous allons les utiliser pour entraîner un réseau de neurones à reconnaître des chiffres écrits manuellement au feutre avec différentes calligraphies, ce que l'on appelle aussi classifier.
Prérequis
- BAC+2 et +
- Bonne compréhension de Python et numpy
Diapositives
0. Installation
📥 Le code source à télécharger se trouve à cet emplacement.
Une fois téléchargé, vous devez installer les packages Python requis en tapant la commande suivante (depuis le dossier ros4pro_perception
) :
pip3 install -r requirements.txt
1. Documentation
Pour anlyser et compléter les codes fournis, vous aurez besoin de consulter plusieurs documentations.
De manière générale, vous aurez besoin de numpy. Si vous n'êtes pas familier avec ce module, vous pouvez consulter:
Pour la partie détection des faces des cubes et pré-processing, nous utiliserons scikit-image
:
Enfin, pour la partie reconnaissance, nous utilsons le module keras
inclus dans le module tensorflow
depuis sa version 2. Un point d'entrée sur l'API Séquentielle de keras peut être consulté sur cette page :
2. Partie apprentissage
2.0 Travail préliminaire avec les notebooks Jupyter 📒
En tapant la commande jupyter notebook
depuis le dossier ros4pro_perception
vous pouvez charger les deux notebooks à trous pour la prise en main du machine learning avec tensorflow et keras :
notebook/TP1_MNIST_dense.ipynb
: utiliser ce notebook pour l'acquisition des bases sur le machine learning, la banque d'images MNIST utilisée pour l'entraînement des réseaux, et la construction d'un réseau de neurones dense, son entraînement et son exploitation, conduisant à un taux de reconnaissance des images MNIST voisin de 98 %.notebook/TP2_MNIST_convol.ipynb
: utiliser ensuite ce notebook pour la construction d'un réseau convolutif, son entraînement avec les images MNIST et son exploitation, conduisant à un taux de reconnaissance voisin de 99 %.
Une fois familiarisé avec les principes de construction des réseaux denses et convolutifs, vous pouvez utiliser les programmes Python du répertoire src/
.
2.1 Chargement des images MNIST
Ouvrir le fichier src/learning.py
, prendre connaissance du code, puis lancer le programme.
Avant d'appuyer sur entrée, assurez-vous que vous savez répondre aux questions suivantes :
Que contiennent les variables
x_train
ety_train
?Pourquoi la fonction
load_data
renvoie-t-elle également les donnéesx_test
ety_test
?Quelles sont les formes respectives de
x_train
ety_train
?
2.2 Prévisualisation des données brutes
Appuyer sur entrée pour continuer, et observer les images :
Quelles sont les valeurs des pixels blancs et noirs ?
Observer les données et leurs labels. Toutes les images sont elles simples à classifier correctement ? Ferriez vous des erreurs en les classifiants ?
2.3 Préparation des données
Fermer la fenêtre et appuyer à nouveau sur entrée :
Quelles sont les formes de
x_train
ety_train
maintenant ?Pourquoi ces changements ?
2.4 Prévisualisation des données préparées
Appuyer à nouveau sur entrée et observer les images :
Quelles sont les valeurs des pixels blanc et noirs maintenant ?
Regarder la fonction
prepare_input
: quelle transformation des images est effectuée ?
2.5 Le modèle du réseau convolutif
Arrêter le script. Dans le fichier source learning.py
modifier la fonction build_model
pour implémenter un réseau convolutif semblable à celui implémenté dans le notebook TP2_MNIST_convol.ipynb
.
Relancer le script et faire défiler jusqu'à la partie 2.5 (vous pouvez modifier SHOW_SAMPLES
pour ne pas afficher toutes les fenêtres...) :
- vérifier les informations des couches sur le résumé du modèle...
2.6 La fonction de coût et l'optimiseur
Arrêter le script et vérifier :
- la fonction de coût et l'optimiseur utilisés dans l'appel à
modele.compile(...)
2.7 Entraînement
Observer la fonction
train_model
: vérifier la présence et le paramétrage de la gestion de l'over-fit.Relancer le code jusqu'au déclenchement de la partie 2.7 : vous devriez voir les itérations d'entraînement se succéder et s'arrêter sur un événement early stopping.
2.8 Poids appris
Appuyer sur entrée pour visualiser les noyaux convolutifs appris par le réseau de neurones :
Observant les noyaux de la première couche : arrivez vous à distinguer le genre de features qui seront extraites par chacun ?
Pouvez vous en faire de même pour la deuxième couche ?
2.9 Activations
Appuyer sur entrée, puis entrer un indice (un entier de valeur inférieure a 12000 (pourquoi 1200 ?)) :
Après la première couche de convolution, les features extraites correspondent-elles à celles que vous imaginiez ?
Après la première couche de pooling, les features présentes auparavant sont-elles conservées ?
Après la deuxième couche de pooling, diriez vous que de l'information spatiale est toujours présente ? Autrement dit, les activations ressemblent elles toujours à des images ?
2.10 Entraînement final
Arrêter le script. Jusqu'à présent, nous avons travaillé sur l'ensemble des images montrant des chiffres de '0' à '9', mais pour la suite nous n'aurons besoin que des images de '1' et de '2' :
Changer la valeur de la variable
CLASSES
pour ne garder que les classes qui nous intéressent.Changer
SHOW_SAMPLES
,SHOW_WEIGHTS
etSHOW_ACTIV
si vous désirez moins d'affichage graphique...Entraîner le réseau avec le nouveau jeu de données réduites, puis le sauvegarder en donnant le nom d'un répertoire où sauvegarder les fichiers du réseau entraîné.
Vous pouvez passer maintenant à la Partie Vision qui permettra, une fois achevée, d'observer les inférences du réseau avec les images des cubes correctement traitées...
3. Partie Vision
Le but de la partie Vision est de traiter les images fournies par la caméra du robot :
pour trouver les contours des cubes :
et extraire des images compatibles MNIST :
qui seront envoyées au réseau de neurone pour classification en '1' ou '2'...
3.1 Présentation des données
Ouvrir le fichier src/detection.py
et lancer le script. Une des images exemple issue de la caméra du robot apparaît :
Observer les valeurs de pixels ? Quelles sont les valeurs de pixels blancs et noirs ?
De manière générale, la face des cubes est-elle semblable aux images MNIST ?
3.2 Binarisation de l'image
Appuyer sur entrée pour afficher l'image binarisée :
- Pouvez vous penser à un algorithme permettant d'arriver à un résultat similaire ?
Dans le code, observer la fonction binarize
:
- À quoi sert la fonction
threshold_otsu
? (voir au besoin la documentationscikit-image
).
En commentant successivement les lignes les utilisant, observer l'impact de chacune des fonctions suivantes :
closing
clear_border
convex_hull_object
Pourquoi faut-il éviter d'avoir des cubes qui touchent les bords de l'image ?
3.3 Recherche des contours des cubes
Appuyer sur entrée pour faire défiler quelques images dont les contours ont été détectés.
Observer la fonction get_box_contours
:
- À quoi sert la fonction
label
? - À quoi sert le paramètre
area
? - À quoi sert la fonction numpy
argsort
utilisée à la fin pour le ré-arragement des contours ? Pourquoi cette opération est elle importante ?
3.4 Extraction des vignettes
Appuyer sur entrée pour faire défiler quelques images dont les vignettes ont été extraites.
Observer la fonction get_sprites
: Qu'est ce qu'une "transformation projective" ?
3.5 Préparation des images
Pendant la phase d'apprentissage, nous avons étudié la préparation qui était faite des images.
Les vignettes présentées au réseau de neurones doivent aussi être traitées pour avoir les mêmes caractéristiques que les images d'entrainement MNIST :
- Remplir la fonction
preprocess_sprites
pour effectuer ce traitement...
Une fois fait, exécuter le script jusqu'à la fin et conclure sur l'allure des images traitées.
Vous pouvez maintenant ouvrir le fichier main.py
pour tester l'intégration de la détection et de la reconnaissance par réseau apprenant...
4. Intégration
Il est maintenant temps d'intégrer les deux parties du pipeline pour l'utilisation finale. Ouvrez le fichier main.py
à la racine du projet.
Pour que les deux parties du pipeline s'adaptent correctement, vous avez complété la fonction preprocess_sprites
pour mettre les vignettes renvoyées par la partie détection dans un format compatible avec celui des images MNIST.
Exécuter maintenant le programme main.py
: donner le chemin d'un dossier qui contient les fichiers du réseau entraîné et vous devriez commencer à obtenir la reconnaissance des chiffres '1' et '2' dans les images fournies.
Il faudra certainement refaire plusieurs fois l'entraînement du réseau en jouant sur plusieurs paramètres avant d'obtenir un réseau entraîné qui fonctionne correctement :
la valeur de la graine
SEED
peut conduire à un état initial des poids du réseau qui donne un entraînement meilleur ou pas...augmenter/diminuer
BATCH_SIZE
peut modifier les temps de calcul et la qualité du réseau entraîné...augmenter/diminuer le paramètre
patience
du callbackEarlyStopping
...enfin, tous les paramètres qui définissent les couches de convolution et de spooling du réseau convolutif sont autant de possibilités d'améliorer ou pas les performances du réseau entraîné....
À vous de jouer pour obtenir un réseau entraîné classifiant le mieux possible les chiffres '1' et '2' dans les images fournies par la caméra du robot...
Pour confirmer la qualité de votre réseau entraîné vous pouvez enregistrer vos propres fichiers PNG avec les images faites avec la caméra du robot en utilisant le service ROS /get_image
. Aidez-vous des idications du paragraphe 2.4. Récupérer les images de la caméra en Python dans la section Manipulation/Poppy Ergo Jr : vous pouvez ajouter une instruction cv2.imwrite(<file_name>, image)
pour écrire vos propres fichiers PNG dans le répertoire data/ergo_cubes/perso
et modifier en conséquence la variable img_dir
du fichier main.py
.
Lancer le programme et observer les performances de votre réseau opérant sur vos propres images.