Comment reconnaître une fusée sur une image avec fastai ?

Baptiste Florentin
France School of AI
10 min readFeb 25, 2019

Dans cet article je vais vous présenter comment coder votre propre intelligence artificielle pour reconnaître des fusées. Nous allons écrire notre code sur google colab, et nous utiliserons un framework python nommé fastai. Si vous ne connaissez pas ces deux outils, n’ayez pas peur, vous n’avez pas besoin de les connaître. Je ferais de mon mieux pour vous les expliquer.

En revanche si vous connaissez bien le framework fastai et que vous souhaitez simplement lire mon code, je vous invite à aller lire directement mon notebook en suivant ce lien :

Tout d’abord, Fastai est un framework. C’est à dire que c’est un ensemble d‘outils qui facilitent grandement la vie du programmer. Fastai améliore grandement l’accessibilité du deep learning. Vous n’aurez pas à tout coder vous-même, une bonne partie du code nécessaire pour faire de l’IA existe déjà, il ne vous reste plus qu’à l’utiliser.

Fastai est très intéressant pour notre problématique, en particulier car il permet de créer un dataset (ensemble de données, de photos dans notre cas) et permet d’entraîner un réseau de neurones en quelques lignes de code python.

Pour écrire cet article je me suis beaucoup inspiré du premier cours fast.ai, qui est gratuit et accessible pour n’importe qui ayant des bases de python et un niveau de maths de lycée. Vous pouvez le trouver au lien suivant.

Qu’est-ce que google colab ?

Google colab est un environnement de développement (IDE) qui est accessible en ligne depuis un navigateur. En claire c’est un site internet sur lequel vous pouvez écrire et exécuter du code. Il présente de nombreux avantages mais le principal est que google vous fournit gratuitement une carte graphique très puissante qui vous permet de gagner beaucoup de temps lors de l’exécution de votre code.

Je vous invite à aller sur le site suivant et de créer un nouveau notebook python 3 :

C’est bon vous êtes prêt à écrire du code. Un dernier détail qui n’est pas des moindres, il vous faut activer la carte graphique qui rend colab si intéressant.

Rien de compliqué, dans la barre des menus en haut de la page allez simplement dans “Exécution”, “Modifier le type d’exécution” puis changer l’option “CPU” par “GPU”.

C’est bon google vous a prêté gratuitement une carte graphique nous pouvons maintenant nous amuser.

Reconnaître une fusée.

Dans cet exemple nous allons créer un programme qui permet de reconnaître la fusée présente sur une photo. Le programme nous dira s’il s’agit d’une Ariane 5, d’un Soyouz, d’une Falcon 9, d’une Saturn V ou d’une New Shepard

L’avez vous reconnu ? Il s’agit d’une fusée Ariane 5

Création du dataset :

Le principe même du machine learning est de laisser notre programme apprendre “tout seul”, c’est à dire sans lui décrire nous-même ce qu’est une fusée. En conséquence nous devons fournir à notre programme un bon nombre d’exemples afin qu’il puisse efficacement discerner les Falcon 9 des Ariane 5 par exemple.

Ces exemples qui constitueront notre dataset nous allons les obtenir nous-même grâce à google image. Pour cela j’ai suivi l’article de Rémi Connesson que je vous invite à lire afin de créer 5 fichiers textes (nomFusee.txt). Ils devront contenir autour de 200 URLs pointant chacun vers une photo de ladite fusée.

Une fois vos 5 fichiers textes récupérés je vous invite à créer un répertoire github afin de pouvoir y accéder depuis google colab. Github est un service en ligne vous permettant d’enregistrer des fichiers en ligne.

Si vous ne savez pas comment faire je vous invite à vous renseigner en ligne il existe de nombreux tutoriels github, mais si vous souhaitez passer aux choses sérieuses plus rapidement vous pouvez simplement utiliser le répertoire que j’ai créé.

Importons donc nos fichiers :

! git clone https://github.com/Pibastte/rocket_dataset.git
# remplacez l'URL en gras par votre propre URL github

Nous avons à présent accès à nos fichiers textes depuis colab. Vous pouvez utiliser la commande ! ls {chemin}pour visualiser l’emplacement de vos fichiers. Par exemple :

!ls#puis!ls rocket_dataset    
#cette commande devrait vous afficher les fichiers .txt si vous avez utilisé mon répertoire github

Nous pouvons à présent télécharger nos images.

# ici nous ne faisons qu'importer notre framework fastai et quelques # autres librairies que nous utiliserons par la suiteimport osimport numpy as np
from fastai import *
from fastai.vision import *

Création de quelques variables

p = "rocket_dataset"  # Or the name of your github repositorypathAriane = p + "/ariane5.txt"
pathFalcon = p + "/falcon9.txt"
pathNewShepard = p + "/new_shepard.txt"
pathSaturn = p + "/saturnV.txt"
pathSoyouz = p + "/soyouz.txt"

Ensuite nous créons 5 répertoires dans lesquels nous téléchargeons les images de chaque modèle de fusée.

os.makedirs(p + "/Ariane5", exist_ok=True)
os.makedirs(p + "/Falcon9", exist_ok=True)
os.makedirs(p + "/New_shepard", exist_ok=True)
os.makedirs(p + "/SaturnV", exist_ok=True)
os.makedirs(p + "/Soyouz", exist_ok=True)
download_images(urls=pathAriane, dest=p+"/Ariane5")
download_images(urls=pathFalcon, dest=p+"/Falcon9")
download_images(urls=pathNewShepard, dest=p+"/New_shepard")
download_images(urls=pathSaturn, dest=p+"/SaturnV")
download_images(urls=pathSoyouz, dest=p+"/Soyouz")

Nous pouvons ensuite vérifier que nos images ont les bonnes caractéristiques pour l’entraînement.

classes = ['/Ariane5', '/Falcon9', '/New_shepard', '/SaturnV', '/Soyouz']for c in classes:
print(c)
path_to_class_folder = p + c
verify_images(path_to_class_folder, delete=True, img_format=f'{c} %d')

Et finalement il ne nous reste plus qu’à supprimer nos fichiers textes qui nous sont à présent inutile.

!rm rocket_dataset/ariane5.txt rocket_dataset/falcon9.txt
!rm rocket_dataset/new_shepard.txt
!rm rocket_dataset/saturnV.txt
!rm rocket_dataset/soyouz.txt

Organiser nos fichiers :

Pour que fastai comprenne la structure de notre dataset nous avons besoin de placer chaque image dans des répertoires spécifiques comme ci-dessous :

Nos photos se trouvent dans les dossiers portant le nom des fusées

Quelques explications :

Tout d’abord notre dataset est divisé en deux. Une partie qui est le training-set qui est représenté par le répertoire /trainpour training. L’autre partie est le validation-set, il est représenté par le répertoire /valid.

La partie entrainement est utilisée comme son nom l’indique pour entraîner le réseau de neurone afin qu’il devienne meilleur à différencier les fusées.

La partie validation est quant à elle utilisé en dehors de l’entrainement afin de tester si notre programme classifie correctement des images qu’il n’a jamais vu. En effet nous ne voulons pas que notre programme fasse uniquement un effort de mémorisation des images présentent dans le trainingset. Nous voulons qu’en présence de n’importe quelle image d’une Ariane 5 par exemple, il classifie celle-ci en tant que Ariane 5. Un réseau de neurones qui classifie bien les images du train mais qui est mauvais avec les images du valid est en surapprentissage (overfitting en anglais). Nous voulons éviter ce genre de cas, c’est pourquoi nous définissons un validationset.

En général le trainingset est composé de 70 à 80 % des images du dataset et le validation set est composé du reste des images.

Nous pouvons à présent créer cette hiérarchie de dossiers avec le code suivant :

Ici nous créons les répertoires train et valid.

!mkdir rocket_dataset/train
!mkdir rocket_dataset/valid

Ensuite nous déplaçons avec os.rename() 70% des images vers le train et 30% vers le valid sachant que chaque image est déposé dans le répertoire associé au nom de la fusée présente sur la photo.

np.random.seed(42)
for c in classes:
os.makedirs(p + "/valid" + c)
os.makedirs(p + "/train" + c)
for f in os.listdir(p + c):
if(np.random.random() < 0.3):
os.rename(p+c+"/"+f, p+"/valid"+c+"/"+f)
else:
os.rename(p+c+"/"+f, p+"/train"+c+"/"+f)

Pour finir nous pouvons supprimer nos anciens répertoires

!ls rocket_dataset/
!rm -r rocket_dataset/Ariane5/
!rm -r rocket_dataset/Falcon9/
!rm -r rocket_dataset/New_shepard/
!rm -r rocket_dataset/SaturnV/
!rm -r rocket_dataset/Soyouz/

Nous pouvons à présent créer notre dataset à l’aide de la fonction fastai ImageDataBunch.from_folder() comme ci-dessous :

path = "rocket_dataset"tfms = get_transforms(do_flip=False)   #data augmentation
data = ImageDataBunch.from_folder(path, ds_tfms=tfms, size=256)

Voici quelques caractéristiques de notre dataset :

data.classes, data.c, len(data.train_ds), len(data.valid_ds)

OUTPUT :(['Ariane5', ‘Falcon9', ‘New_Shepard', ‘SaturnV’, ‘Soyou'], 5, 563, 247)

Nous avons donc 5 catégories ou modèles de fusées qui sont répertorié dans un trainingset contenant 563 images et un validationset contenant 247 images.

Pour obtenir de meilleurs résultats il est important de normaliser nos images.

data.normalize(imagenet_stats)

Visualisons maintenant une partie de notre dataset !

data.show_batch(rows=3, figsize=(7,6))

Nous pouvons à présent créer notre réseau de neurone qui est plus précisément un réseau de convolutions. Je ne vais pas rentrer dans les détails mais dites-vous que c’est une grosse fonction qui prend en entrée une image et qui renvoie en sortie la fusée que le réseau pense avoir détecté. Cette fonction nous devons l’approximer et le processus pour y parvenir s’appelle l’entrainement.

Schéma simplifié de Resnet 34

Pour être plus performant et surtout pour entraîner notre réseau pendant un temps raisonnable nous allons utiliser le réseau de convolution resnet34. C’est un réseau qui a déjà été entrainé pour faire de la reconnaissance d’images. Ce réseau est très performant mais de base il ne reconnait malheureusement pas les falcon 9 et les Saturn V.

Les classes resnet, sont les choses que resnet 34 peut reconnaitre. Ce sont par exemples des chiens, des voitures, etc… mais nous voulons reconnaître des fusées !

Modifications de resnet 34, seul la partie verte sera entrainée.

La petite astuce est de garder les connaissances du réseau resnet34 et de s’en servir pour reconnaître nos fusées. Ce que nous faisons là s’appelle du fine tuning. Pour les personnes familières avec le deep learning nous allons enlever le réseau de neurones MLP qui se trouve à la fin de resnet34 pour le remplacer par le nôtre que nous pourrons alors entraîner pour reconnaître nos fameuses fusées.

Pour créer ce réseau il nous suffit d’exécuter le code suivant :

learn = create_cnn(data, models.resnet34, metrics=error_rate)

Ensuite nous pouvons entraîner notre model afin qu’il devienne enfin capable de reconnaître une fusée et l’enregistrer (cette étape peut prendre un certain temps, 5 à 10 minutes) :

learn.fit_one_cycle(10)
learn.save("stage-1")
# ici nous sauvegardons notre réseau pour pouvoir le réutiliser
résultats de l’entrainement

Suivant votre dataset vous obtiendrez un taux d’erreur final plus ou moins important. Pour ma part j’obtiens un taux final de 8,4%. Ce taux est plutôt bon, il signifie que notre réseau se trompe sur seulement 8,4% des images du validationset. En revanche il serait pratique de pouvoir afficher les images ayant été mal classifiée afin de mieux comprendre la provenance de ces erreurs.

Pour ce faire il suffit de taper ces deux commandes :

interp = ClassificationInterpretation.from_learner(learn)interp.plot_top_losses(9, figsize=(15,11))
Nous pouvons voir en orange les parties sur lesquels le réseau s’est concentré.

Et nous pouvons afficher la matrice de confusion qui nous permet de facilement déterminer les fusées qui ont tendance à être mal classifiées.

interp.plot_confusion_matrix(figsize=(6,6), dpi=60)

Nous pouvons ainsi voir que notre réseau à bien classifier 69 images d’Ariane 5 mais a confondu 2 Arianes avec des New Shepard.

Notre programme fonctionne déjà bien mais nous pouvons faire mieux. En effet, jusque là notre entrainement ne ce faisait que sur la dernière partie de Resnet 34 (le réseau de neuronne MLP) mais nous pouvons améliorer notre précision en entrainant tout le réseau pendant quelques cycles.

Pour débloquer l’entrainement sur tout le réseau il nous faut entrer la commande suivante :

learn.unfreeze()

Nous pouvons à présent entraîner de nouveau notre modèle pour diminuer notre taux d’erreur.

learn.fit_one_cycle(1)
Notre taux d’erreur a augmenté, ce n’est pas bon.

Comme vous avez pu le remarquer notre taux d’erreur a bizarrement augmenter, nous sommes en plein surapprentissage. Ceci est dû à un paramètre appelé le learning rate (taux d’apprentissage). Ce learning rate est un nombre, généralement entre 0 et 1, qui est cruciale pour la stabilité de notre entrainement.

Revenons en arrière en utilisant la commande :

learn.load('stage-1')

Puis débloquons le réseau entier à nouveau.

learn.unfreeze()

Comment déterminer le learning rate ?

Il suffit d’utiliser deux commandes que voici :

learn.lr_find()learn.recorder.plot()

Vous devriez alors obtenir un graphe comme ci-dessus. Il vous suffit alors de relever la valeur du learning rate correspondant au point rouge (valeur affichée au dessus du graphique) et de lancer l’entrainement en spécifiant le learning rate maximal comme étant slice(lr, 10*lr) avec lr le learning rate que vous avez pu relever. Cette commande permet de moduler le learning rate entre ces deux valeurs au cours de l’entrainement.

learn.fit_one_cycle(7, max_lr=slice(3.02e-05, 3.02e-4))
learn.save(‘after_unfreeze’)

Nous avons cette fois ci réussi à baisser notre taux d’erreur de plus de 1%, c’est déjà ça de gagné.

Nous pouvons afficher de nouveau notre matrice de confusion et les images qui ont été mal classifié afin d’observer les améliorations.

interp = ClassificationInterpretation.from_learner(learn)
interp.plot_top_losses(9, figsize=(15,11))

Puis,

interp.plot_confusion_matrix(figsize=(6,6), dpi=60)

Nous avons une belle diagonale ce qui nous indique que l’entrainement était efficace.

Conclusion

Vous savez maintenant comment créer un programme qui classifie des fusées. Tout ce code est très facilement transférable vers d’autres datasets. Je vous encourage donc fortement à créer le vôtre et à essayer d’appliquer les techniques abordées dans cet article.

Nous n’avons pas pu obtenir un meilleur taux d’erreur car le dataset contenait des images ambigüe comme des fusées loins dans le ciel. Je pense également qu’en recadrant les images autour des fusées il serait possible d’augmenter la fiabilité de ce programme.

Néamoins, un taux d’erreur de 7% est déjà très bien compte tenu de la difficulté du problème. Il y a 10 voir même 5 ans, une telle fiabilité aurait été extraordinaire. Fastai est donc un merveilleux outil pour faire de l’intelligence artificielle et notament pour faire de la reconnaissance d’images.

Vous pouvez suivre la leçons n°2 de fastai pour avoir des détails sur comment déployer votre réseau de neurones sur un site web..

--

--