Vous êtes sur la page 1sur 11

Vision appliquée pour la Robotique

Majeure ROBIA/ module IA Vision


2023/2024

TP
Classification d'images par
Deeplearning.

Prise en main de Tensorflow

Partie 1
Question 1

1.1 Expliquez les points communs et différences entre Keras, TensorFlow, PyTorch et
YOLO (Darknet)
Ces technologies sont toutes open source, et ont des objectifs différents. Là où Keras, TensorFlow et
PyTorch permettent de développer des applications d’apprentissage automatique et des réseaux de
neurones plus ou moins profonds pour de nombreux types de tâches comme de la génération de texte, de
la détection d’objets, de la classification d’images… YOLO est plus spécifiquement utilisé pour la détection
d’objets en temps réel. Darknet propose cependant d’autres types de tâches.

Toutes ces technologies n’ont cependant pas le même niveau d’abstraction :

• Keras : Keras est une interface d'apprentissage en Python de haut niveau qui peut être utilisée avec
TensorFlow, Microsoft Cognitive Toolkit ou encore Theano. Elle offre une syntaxe simple et intuitive
pour créer des modèles d'apprentissage automatique.

• TensorFlow : C’est une bibliothèque d'apprentissage automatique complète qui offre un contrôle
total sur les modèles, allant des modèles classiques aux réseaux de neurones profonds.

• PyTorch : C’est une bibliothèque d'apprentissage automatique, à la manière de TensorFlow, qui elle
est basée sur des graphes dynamiques, ce qui facilite le débogage et le prototypage rapide.

• YOLO (Darknet) : C’est un framework spécialisé dans la détection d’objets en temps réel, qui est
reconnu pour sa rapidité et son efficacité dans ce domaine.

1.2 On explicitera en particulier, ce qui est « statique » vs « dynamique »


• Keras : Keras peut être à la fois statique et dynamique en fonction de ce que l’on place derrière
(TensorFlow va être statique, là où TensorFlow 2.0 peut être dynamique)

• TensorFlow : Comme dit précédemment, Tf est statique de base, mais offre la possibilité d’être
dynamique avec sa version 2.0.

• PyTorch : PyTorch est dynamique, permettant ainsi de pouvoir modifier et construire les graphes à
la volée.

• YOLO (Darknet) : Le modèle est statique, ne pouvant pas être modifié à la volée durant l’exécution
de celui-ci.

1.3 Le langage support utilisé


• Keras : Python

• TensorFlow : Python, C, certains langages sans rétrocompatibilité (C++, Go, Java, Swift, Javascript),
certains packages faits par des tiers (C#, Haskell, R, Julia, Scala, Rust, Ocaml, Crystal)

• PyTorch : Python

• YOLO (Darknet) : C
1.4 Comment est décrit le réseau de neurones (donner des exemples)
• Keras : Créer un réseau de neurones avec Keras est très simple, il suffit de créer nos différentes
couches, et de créer notre model à partir de ces couches. L’ensemble se fait dans le code de
manière normale.

• TensorFlow : Le plus facile reste d’utiliser Keras, mais si l’on veut n’utiliser que Tf, c’est tout de
même possible. Il faut alors définir les tenseurs pour les entrées et les poids, spécifier les
différentes opérations de calcul pour chacune des couches de notre réseau, définir notre loss
function et un optimiseur pour l’entrainement. Tout se fait dans le code.

• PyTorch : La création de réseau de neurones est un peu différente, même si elle garde le même
principe. On va créer notre model à partir de la class NeuralNetwork dans laquelle on va placer nos
différents paramètres. Ainsi, dans __init__, on définit nos couches, et dans forward(), on spécifie
comment les données se propagent dans le réseau. Il suffit d’appeler le modèle créé comme une
fonction avec nos données en paramètres.
• YOLO (Darknet) : Avec YOLO, on a un fichier .cfg qui spécifie l’architecture du réseau et donc qu’il
faut définir, contenant les couches, les filtres, les nombres de boîtes englobantes prédites. On utilise
aussi soit des poids pré-entrainés à télécharger, soit il faut les entrainer sur notre ensemble de
données. Après cela, la détection d’objets se fait à partir de la configuration définie.

Question 2
Prise en main de l’exemple suivant :

https://www.tensorflow.org/tutorials/keras/classification

On commence par regarder et tester le code fournit sous google Collab


2.1 Donner la définition d’un neurone au sens informatique
Un neurone est une sorte de nœud effectuant des opérations mathématiques en fonction de valeurs
d’entrée afin de donner une valeur de sortie. Celui-ci prend en compte les entrées, les poids des différentes
entrées, et applique une certaine fonction (suivant l’objectif du-dit neurone) à ce résultat pour retourner
des valeurs plus ou moins complexes.

2.2 En quoi consiste la notion de couche dans les réseaux de neurones


Une couche est un groupement de neurones organisés de manière à effectuer des calculs sur les données
d’entrées. Ces couches sont placées de manière séquentielle. Chaque neurone dans une couche est
connecté aux neurones de la couche précédente, ou aux données d’entrées pour la première couche, ainsi
qu’aux neurones de la couche suivante. C’est la notion de poids des entrées qui va définir pour chaque
neurone ce qui lui sera plus ou moins utile.

2.4 Quelles sont les différentes couches généralement associées dans les réseaux de
neurones (faire d’abord une réponse rapide que vous compléterez jusqu’au dernier TP
au fur et à mesure …)
En général, on retrouvera :

• La couche d’entrée : Chaque neurone récupère une caractéristique ou une dimension des données
fournies à l’entrée. Il n’y a pas de calculs effectués, cette couche ne sert qu’à transmettre les
données aux couches suivantes

• La ou les couches cachées : Celles-ci sont responsables de la récupération des caractéristiques et


des motifs complexes dans les données. Chaque neurone applique une transformation non linéaire
aux données reçues (c’est souvent une fonction d’activation).

• La couche de sortie : C’est la couche qui sera responsable de fournir le résultat, celle-ci peut être
assez différente en fonction du résultat voulu. Pour une classification multi-classe comme dans
l’exemple, on a normalement un neurone par classe.

2.5 Expliquer en détail le réseau sous-jacent de l’exemple


Le réseau de neurones comporte une première couche d’entrée, qui va s’occuper de reformater les données
reçues avec un layer Flatten, en passant d’un tableau bidimensionnel de 28 cases par 28 cases, à un tableau
de 28 * 28 cases, soit 784 cases.
Les données sont ensuite fournies à un second layer de 128 nodes qui va s’occuper de passer toutes les
valeurs négatives à 0, pour éviter les mauvaises valeurs, avec la fonction ReLU. C’est un layer Dense, donc
les neurones seront reliés à tous les neurones précédents.
La dernière couche a 10 neurones de sortie. Cela signifie que pour chaque exemple d'entrée, cette couche
produira un vecteur de 10 valeurs en sortie. On utilise ce modèle pour la classification avec 10 classes
différentes, donc chaque valeur dans ce vecteur va représenter la probabilité que l'entrée appartienne à
l'une des 10 classes. La classe avec la probabilité la plus élevée sera alors la prédiction du modèle.
2.6 Est ce habituel ? Peut-on faire plus simple ?
C’est en effet habituel pour des tâches de classification d’images simples. On pourrait faire encore plus
simple en supprimant le layer Flatten et en plaçant directement dans le premier layer Dense avec une
input_shape de 28*28, en ayant au préalable mis nos données sous le format vectoriel 1D.

2.7 Quel type de modifications simplistes peut être faite sur le réseau et en
particulier sur les hypers paramètres.
On peut modifier certains éléments comme le nombre d’epochs ou le type d’optimizer, ou même le nombre
de neurones de notre première couche Dense, afin de faire varier certains éléments.

2.8 Relancer l’apprentissage en changeant ses paramètres, mettre en place un


tableau montrant l’influence de ses paramètres.
On utilise scikitlearn et sa GridSearchCV pour tester les différences.

https://deeplylearning.fr/cours-theoriques-deep-learning/reglages-des-hyper-parametres/

On peut ainsi lister plusieurs types de configuration en modifiant les hypers paramètres et trouver ainsi la
configuration la mieux adaptée et la plus performante.

2.9 Quelle est la fonction d’activation associée au modèle, tester avec d’autres
fonctions d’activation. Quelles sont les performances obtenues.
La fonction d’activation reliée au model est ‘ReLU’.

• ReLU : loss : 0.3619 - accuracy : 0.8746 - 385ms/epoch

• Sigmoid : loss : 0.3224 - accuracy : 0.8808 - 508ms/epoch

• Softplus : loss : 0.3289 - accuracy : 0.8868 - 574ms/epoch


Question 3d
On veut tester 10 images que vous créerez vous même ne partant d’image couleur trouvés sur internet et
en les convertissant pour les mettre au format reconnu par « ..._model.predict ».

3.1 Quelle est ce format ? On passera par un fichier bmp en 28x28 puis on rajoutera
au dossier sous google collab et on convertira directement pour l’usage dans predict
(quel est le code associé sous google collab)
Le format est un tableau rempli d’images sous la forme de tableau de 28 cases par 28 cases. On récupère
donc 10 images, que l’on convertit en BMP 28*28.

3.2 Déposer ces images sur le e-campus sous un format bmp 28X28 et déposer aussi
l’original trouvé sur internet.
Est ce fait ?

Oui

3.3 Tester vos images, on affichera l’image et le résultat sous google collab sous la
forme suivante :

Copier ici le résultat et le code spécifique que vous avez écrit

import os
from PIL import Image, ImageOps
import numpy as np

# Dossier contenant les images BMP


dossier_images = "./images_bmp"
# Liste pour stocker les images
images = []

# Parcourir le dossier et charger les images


for nom_fichier in os.listdir(dossier_images):
chemin_fichier = os.path.join(dossier_images, nom_fichier)
# Vérifier si le fichier est un fichier BMP
if nom_fichier.endswith(".bmp"):
# Charger l'image
image = Image.open(chemin_fichier).convert("L")
image_inversee = ImageOps.invert(image)
# Convertir l'image en tableau NumPy et normaliser les valeurs des
pixels
image_array = np.array(image_inversee)
# Ajouter l'image au tableau
images.append(image_array)

# Convertir la liste d'images en un tableau NumPy


images = np.array(images)

images = images / 255

# Vérifier la forme du tableau d'images


print("Forme du tableau d'images chargées :", images.shape)
predictions = probability_model.predict(images)
def plot_image_bis(i, predictions_array , img):
img = img[i]
plt.grid(False)
plt.xticks([])
plt.yticks([])

plt.imshow(img, cmap=plt.cm.binary)

predicted_label = np.argmax(predictions_array)
color = 'blue'

plt.xlabel("{} {:2.0f}%".format(class_names[predicted_label],
100*np.max(predictions_array)),
color=color)

def plot_value_array_bis(i, predictions_array):


plt.grid(False)
plt.xticks(range(10))
plt.yticks([])
thisplot = plt.bar(range(10), predictions_array, color="#777777")
plt.ylim([0, 1])
predicted_label = np.argmax(predictions_array)

thisplot[predicted_label].set_color('blue')

num_rows = 5
num_cols = 2
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
plt.subplot(num_rows, 2*num_cols, 2*i+1)
plot_image_bis(i, predictions[i], images)
plt.subplot(num_rows, 2*num_cols, 2*i+2)
plot_value_array_bis(i, predictions[i])
plt.tight_layout()
plt.show()
Si rien ne marche essayer de comprendre pourquoi et trouver une solution pour valider votre processus en
partant d’une image du dataset.
Question 4
(Faire d’abord une réponse rapide que vous compléterez jusqu’au dernier TP au fur et à mesure ….)

4.1 Avez-vous des idées pour améliorer la structure du réseau ?


J’en ai une.

4.2 Lesquels ?
On pourrait modifier notre model initial, en mettant des couches de convolution au lieu de commencer par
un Flatten. On pourrait ainsi se baser sur une architecture LeNet pour l’adapter à notre situation, et en
remplaçant par exemple sigmoid par nos ReLU. En fonction de nos performances, on peut repasser sur du
sigmoid ou non.

4.3 Tester pour voir si c’est mieux et conclure ?


On décide d’abord de tester avec ReLU en nous basant sur un LeNet (10 classes à détecter, et une classe
d’entrée de 28*28). Dès le début, on constate une nette amélioration des performances par rapport aux
essais de début du TP.
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(6, (5, 5), padding='same', activation='relu', in-
put_shape=(28, 28, 1)),
tf.keras.layers.AveragePooling2D((2, 2), strides=(2, 2)),
tf.keras.layers.Conv2D(16, (5, 5), activation='relu'),
tf.keras.layers.AveragePooling2D((2, 2), strides=(2, 2)),
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(120, activation='relu'),
tf.keras.layers.Dense(84, activation='relu'),
tf.keras.layers.Dense(10)
])

loss: 0.2896 - accuracy: 0.9009 - 3s/epoch - 8ms/step


On constate au niveau des images que celui-ci est en effet plus performant. On va essayer de remplacer relu
par sigmoid étant donné que nous avions vu plus haut dans le TP qu’elle était plus performante.
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(6, (5, 5), padding='same', activation='sigmoid',
input_shape=(28, 28, 1)),
tf.keras.layers.AveragePooling2D((2, 2), strides=(2, 2)),
tf.keras.layers.Conv2D(16, (5, 5), activation='sigmoid'),
tf.keras.layers.AveragePooling2D((2, 2), strides=(2, 2)),
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(120, activation='sigmoid'),
tf.keras.layers.Dense(84, activation='sigmoid'),
tf.keras.layers.Dense(10)
])
4s - loss: 0.3555 - accuracy: 0.8641 - 4s/epoch - 12ms/step
On constate donc avec sigmoid de moins bonnes performances, il faut bien se tourner vers du ReLU.
Les couches de convolution permettent donc d’avoir de vraies performances sur la classification.

Vous aimerez peut-être aussi