diff --git a/03-vision/#04-cnn-cartes.py# b/03-vision/#04-cnn-cartes.py# new file mode 100644 index 0000000..f414338 --- /dev/null +++ b/03-vision/#04-cnn-cartes.py# @@ -0,0 +1,253 @@ +import os, time +import numpy as np +import matplotlib.pyplot as plt + +import tensorflow as tf +from tensorflow import keras + +############################################################################### +# 04-cnn-cartes.py +# @title: Vision par ordinateur - Visualisation des noyaux (carte, couche, filtre) de convolution +# @project: Mes scripts de ML +# @lang: fr +# @authors: Philippe Roy +# @copyright: Copyright (C) 2023 Philippe Roy +# @license: GNU GPL +############################################################################### + +### +# Installation : +# - pip3 install tensorflow +# - pip3 install keras +### + +### +# Commandes NumPy : +# - np.array : créer un tableau à partir d'une liste de listes +# - np.c_ : concatène les colonnes des tableaux +# - np.linspace : créer un tableau 1D de la valeur de début à la valeur de fin avec n valeurs +# - np.meshgrid : créer un tableau 2D avec l'ensemble des combinaisons allant des deux valeurs de début aux deux valeurs de fin +# - .reshape : reformater la tableau avec le nombre de lignes et le nombre de colonnes +### + +### +# Commandes Keras : +# - keras.models.Sequential() : créer un modèle où les couches de neurones sont reliées séquentiellement (modèle simple) +# - model.add : ajout d'une couche +# - keras.layers.Flatten : couche de formatage de mise à plat +# - keras.layers.Dense : couche de neurones +# - keras.layers.Conv2D : couche de convolution 2D (filtre) +# - keras.layers.MaxPool2D : couche d'agrégation des cartes (noyaux de convolution) type Pooling max +# - keras.layers.Dropout : couche de régularisation par extinction (Dropout) +# - keras.backend.clear_session() : reset de la session +# - model.compile : compilation du modèle +# - model.fit : entrainement du modèle +# - model.predict : prédiction du modèle +# - keras.utils.plot_model : créer le diagramme d'un modèle +### + +############################################################################### +# Initialisation +############################################################################### + +# Init du temps +t_debut = time.time() + +# Init des plots +fig = plt.figure(layout="constrained", figsize=(15, 5)) +fig.suptitle("Vision par ordinateur - Visualisation des noyaux de convolution") +subfigs = fig.subfigures(1, 3) + +############################################################################### +# Observations +############################################################################### + +# Observations d'apprentissage, de validation et de test +mnist = keras.datasets.mnist # Jeu de données MNIST (digit) +(X, y), (X_test, y_test) = mnist.load_data() +X_train, y_train = X[5000:]/255.0 , y[5000:] +X_valid, y_valid = X[:5000]/255.0 , y[:5000] + +X_train = X_train[..., np.newaxis] # Ajout du canal nuance de gris +X_valid = X_valid[..., np.newaxis] # Ajout du canal nuance de gris +X_test = X_test[..., np.newaxis] # Ajout du canal nuance de gris + +############################################################################### +# Phase d'apprentissage +############################################################################### + +n = 10 # Nombre d'itérations (valeur par défaut : 10 , hyperparamètre) +eta = 0.01 # Taux d'appentissage (valeur par défaut dans Keras : 0.01, hyperparamètre) +lot=32 # Taille de lot (valeur par défaut dans Keras: 32 , hyperparamètre) + +perte="sparse_categorical_crossentropy" # Type de perte (hyperparamètre) +#perte="mse" # Type de perte (hyperparamètre) + +keras.backend.clear_session() + +# Version 1 +# model = keras.models.Sequential() # Modèle de reseau de neurones +# conv1 = keras.layers.Conv2D(32, kernel_size=3, padding="same", activation="relu") # Couche de convolution avec 32 cartes +# conv2 = keras.layers.Conv2D(64, kernel_size=3, padding="same", activation="relu") # Couche de convolution avec 64 cartes +# model.add(conv1) +# model.add(conv2) +# model.add(keras.layers.MaxPool2D()) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Flatten()) # Couche de mise à plat des données +# model.add(keras.layers.Dropout(0.25)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(128, activation="relu")) # Couche dense : 128 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(10, activation="softmax")) # Couche de sortie : 1 node par classe soit 10 + +# Version 2 +# model = keras.models.Sequential() # Modèle de reseau de neurones +# model.add(keras.layers.Conv2D(64, kernel_size=7, padding="same", input_shape=[28,28,1], activation="relu")) # Couche de convolution avec 64 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Conv2D(128, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 128 cartes +# model.add(keras.layers.Conv2D(128, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 128 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Conv2D(256, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 256 cartes +# model.add(keras.layers.Conv2D(256, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 256 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Flatten()) # Couche de mise à plat des données +# model.add(keras.layers.Dense(16, activation="relu")) # Couche dense : 16 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(8, activation="relu")) # Couche dense : 8 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(2, activation="softmax")) # Couche de sortie : 1 node par classe soit 2 + +# Version 3 +# model = keras.models.Sequential() # Modèle de reseau de neurones +# model.add(keras.layers.Conv2D(4, kernel_size=7, padding="same", input_shape=[28,28,1], activation="relu")) # Couche de convolution avec 4 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Conv2D(8, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 8 cartes +# model.add(keras.layers.Conv2D(8, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 8 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Conv2D(16, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 16 cartes +# model.add(keras.layers.Conv2D(16, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 16 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Flatten()) # Couche de mise à plat des données +# model.add(keras.layers.Dense(16, activation="relu")) # Couche dense : 16 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(8, activation="relu")) # Couche dense : 8 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(2, activation="softmax")) # Couche de sortie : 1 node par classe soit 2 + +# Version 4 +# model = keras.models.Sequential() # Modèle de reseau de neurones +# model.add(keras.layers.Conv2D(16, kernel_size=28, padding="same", input_shape=[28,28,1], activation="relu")) # Couche de convolution avec 4 cartes +# model.add(keras.layers.Conv2D(16, kernel_size=28, padding="same", activation="relu")) # Couche de convolution avec 4 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Flatten()) # Couche de mise à plat des données +# model.add(keras.layers.Dense(16, activation="relu")) # Couche dense : 16 nodes +# model.add(keras.layers.Dense(2, activation="softmax")) # Couche de sortie : 1 node par classe soit 2 + +# model=keras.models.load_model("04-cnn-cartes-v4.h5") # Lire le modèle + entrainement +# print ("Lecture du modèle entrainé : 04-cnn-cartes-v4.h5") + +# Version 5 +model = keras.models.Sequential() # Modèle de reseau de neurones +convo1 = keras.layers.Conv2D(16, kernel_size=3, padding="same", input_shape=[28,28,1], activation="relu") # Couche de convolution avec 16 cartes +model.add(convo1) +convo2 = keras.layers.Conv2D(16, kernel_size=3, padding="same", activation="relu") # Couche de convolution avec 16 cartes +model.add(convo2) +maxpool = keras.layers.MaxPool2D(2) # Couche d'agrégation des cartes type Pooling max +model.add(maxpool) +model.add(keras.layers.Flatten()) # Couche de mise à plat des données +model.add(keras.layers.Dense(16, activation="relu")) # Couche dense : 16 nodes +model.add(keras.layers.Dense(10, activation="softmax")) # Couche de sortie : 1 node par classe soit 10 + +optimiseur=keras.optimizers.SGD(learning_rate= eta) +model.compile(loss=perte, optimizer=optimiseur, metrics=["accuracy"]) # Compilation du modèle + +apts = model.fit(X_train, y_train, epochs=n, batch_size=lot, validation_data=(X_valid, y_valid)) # Entrainement +# model.save("04-cnn-cartes-v5.h5") +# model=keras.models.load_model("04-cnn-cartes-v5.h5") # Lire le modèle + entrainement +# print ("Lecture du modèle entrainé : 04-cnn-cartes-v5.h5") + +############################################################################### +# Phase d'inférence +############################################################################### + +print ("\n") +print ("Test sur le jeu de test ("+str(X_test.shape[0])+" images).") +X_new = X_test +y_new = np.argmax(model.predict(X_new), axis=-1) # Prédictions +y_new_target= y_test # Cibles +eval=model.evaluate(X_new, y_new_target) +print ("Il y a "+str(int(np.round((1-eval[1])*X_new.shape[0]))) + " images non reconnues.\n") + +############################################################################### +# Résultats +############################################################################### + +# Récupérer les poids de couche de convolution +def convo_poids(layer_name): + _poids = model.get_layer(name=layer_name).get_weights()[0] + _poids = np.squeeze(_poids) + # _poids = _poids.reshape((_poids.shape[0], _poids.shape[1], _poids.shape[2]*_poids.shape[3])) + return _poids + +# Couche de convolution 1 +X_convo = X[..., np.newaxis]/255.0 +subfigs[0].suptitle("Couche de convolution 1 : 3x3 - 16 cartes") +convo1_axs = subfigs[0].subplots(8, 8) +convo1_poids = convo_poids("conv2d") +convo1_img = convo1(X_convo[:10]) +i=0 +for ligne in range (2): # Filtres (poids) + for colonne in range (8): + convo1_axs[ligne][colonne].set_axis_off() + convo1_axs[ligne][colonne].imshow(convo1_poids[:,:,i], cmap="binary", interpolation="nearest") + convo1_axs[ligne][colonne].set_title("Filtre "+str(i), fontsize=10) + i +=1 +for ligne in range (2, 8): # Résultats + convo1_axs[ligne][0].set_axis_off() + convo1_axs[ligne][0].imshow(X[ligne-2], cmap="binary", interpolation="nearest") + convo1_axs[ligne][0].set_title("Image "+str(ligne-2), fontsize=10)g + for colonne in range (1, 8): + convo1_axs[ligne][colonne].set_axis_off() + convo1_axs[ligne][colonne].imshow(convo1_img[ligne-2, :,:,colonne-1], cmap="binary", interpolation="nearest") + convo1_axs[ligne][colonne].set_title("Filtre "+str(colonne-1), fontsize=10) + +# Couche de convolution 2 +subfigs[1].suptitle("Couche de convolution 2 : 3x3 - 16 cartes") +convo2_axs = subfigs[1].subplots(8, 8) +convo2_poids = convo_poids("conv2d_1") +convo2_img = convo2(convo1_img) +i=0 +for ligne in range (2): # Filtres (poids) + for colonne in range (8): + convo2_axs[ligne][colonne].set_axis_off() + convo2_axs[ligne][colonne].imshow(convo2_poids[:,:,0, i], cmap="binary", interpolation="nearest") + convo2_axs[ligne][colonne].set_title("Filtre 0-"+str(i), fontsize=8) + i +=1 +for ligne in range (2, 8): # Résultats + convo2_axs[ligne][0].set_axis_off() + convo2_axs[ligne][0].imshow(X[ligne-2], cmap="binary", interpolation="nearest") + convo2_axs[ligne][0].set_title("Image "+str(ligne-2), fontsize=10) + for colonne in range (1, 8): + convo2_axs[ligne][colonne].set_axis_off() + convo2_axs[ligne][colonne].imshow(convo2_img[ligne-2, :,:,colonne-1], cmap="binary", interpolation="nearest") + convo2_axs[ligne][colonne].set_title("Filtre "+str(colonne-1), fontsize=10) + +# Couche d'agrégation MaxPooling +subfigs[2].suptitle("Couche d'agrégation MaxPool : 2 -> sortie 14x14") +maxpool_axs = subfigs[2].subplots(8, 8) # 1 couches de 16 cartes +maxpool_img = maxpool(convo2_img) +for ligne in range (2): # Filtres -> rien + for colonne in range (8): + maxpool_axs[ligne][colonne].set_axis_off() +for ligne in range (2, 8): # Résultats + maxpool_axs[ligne][0].set_axis_off() + maxpool_axs[ligne][0].imshow(X[ligne-2], cmap="binary", interpolation="nearest") + maxpool_axs[ligne][0].set_title("Image "+str(ligne-2), fontsize=10) + for colonne in range (1, 8): + maxpool_axs[ligne][colonne].set_axis_off() + maxpool_axs[ligne][colonne].imshow(maxpool_img[ligne-2, :,:,colonne-1], cmap="binary", interpolation="nearest") + maxpool_axs[ligne][colonne].set_title("Filtre "+str(colonne-1), fontsize=10) + +plt.show() + +# Performances +print ("\n") +print ("Temps total : "+str(time.time()-t_debut)) diff --git a/03-vision/.#04-cnn-cartes.py b/03-vision/.#04-cnn-cartes.py new file mode 120000 index 0000000..2c08a0d --- /dev/null +++ b/03-vision/.#04-cnn-cartes.py @@ -0,0 +1 @@ +phroy@debian.3622:1688640982 \ No newline at end of file diff --git a/03-vision/03-digit-cnn.py b/03-vision/03-digit-cnn.py index 4f8897d..4c9f92b 100644 --- a/03-vision/03-digit-cnn.py +++ b/03-vision/03-digit-cnn.py @@ -40,7 +40,7 @@ from tensorflow import keras # - keras.layers.Dense : couche de neurones # - keras.layers.Conv2D : couche de convolution 2D (filtre) # - keras.layers.MaxPool2D : couche d'agrégation des cartes (noyaux de convolution) type Pooling max -# - keras.layers.Dropout : couche de régularisation par abandon (Dropout) +# - keras.layers.Dropout : couche de régularisation par extinction (Dropout) # - keras.backend.clear_session() : reset de la session # - model.compile : compilation du modèle # - model.fit : entrainement du modèle diff --git a/03-vision/04-cnn-cartes-v4.h5 b/03-vision/04-cnn-cartes-v4.h5 new file mode 100644 index 0000000..d294ca4 Binary files /dev/null and b/03-vision/04-cnn-cartes-v4.h5 differ diff --git a/03-vision/04-cnn-cartes-v5.h5 b/03-vision/04-cnn-cartes-v5.h5 new file mode 100644 index 0000000..c980d51 Binary files /dev/null and b/03-vision/04-cnn-cartes-v5.h5 differ diff --git a/03-vision/04-cnn-cartes.py b/03-vision/04-cnn-cartes.py index 6d41d11..adabb15 100644 --- a/03-vision/04-cnn-cartes.py +++ b/03-vision/04-cnn-cartes.py @@ -19,8 +19,6 @@ from tensorflow import keras # Installation : # - pip3 install tensorflow # - pip3 install keras -# - pip3 install pydot -# - pip3 install graphviz ### ### @@ -40,7 +38,7 @@ from tensorflow import keras # - keras.layers.Dense : couche de neurones # - keras.layers.Conv2D : couche de convolution 2D (filtre) # - keras.layers.MaxPool2D : couche d'agrégation des cartes (noyaux de convolution) type Pooling max -# - keras.layers.Dropout : couche de régularisation par abandon (Dropout) +# - keras.layers.Dropout : couche de régularisation par extinction (Dropout) # - keras.backend.clear_session() : reset de la session # - model.compile : compilation du modèle # - model.fit : entrainement du modèle @@ -57,12 +55,8 @@ t_debut = time.time() # Init des plots fig = plt.figure(layout="constrained", figsize=(15, 5)) -fig.suptitle("Vision par ordinateur - Reconnaissance de digit par réseaux de neurones convolutifs") +fig.suptitle("Vision par ordinateur - Visualisation des noyaux de convolution") subfigs = fig.subfigures(1, 3) -model_ax = subfigs[0].subplots(1, 1) -apts_ax = subfigs[1].subplots(1, 1) -# conv_ax = subfigs[1].subplots(1, 1) -img_ax = subfigs[2].subplots(10, 15) ############################################################################### # Observations @@ -79,101 +73,178 @@ X_valid = X_valid[..., np.newaxis] # Ajout du canal nuance de gris X_test = X_test[..., np.newaxis] # Ajout du canal nuance de gris ############################################################################### -# Lire le modèle + entrainement +# Phase d'apprentissage ############################################################################### -model=keras.models.load_model("03-digit-cnn.h5") # Lire le modèle + entrainement +n = 10 # Nombre d'itérations (valeur par défaut : 10 , hyperparamètre) +eta = 0.01 # Taux d'appentissage (valeur par défaut dans Keras : 0.01, hyperparamètre) +lot=32 # Taille de lot (valeur par défaut dans Keras: 32 , hyperparamètre) + +perte="sparse_categorical_crossentropy" # Type de perte (hyperparamètre) +#perte="mse" # Type de perte (hyperparamètre) + +keras.backend.clear_session() + +# Version 1 +# model = keras.models.Sequential() # Modèle de reseau de neurones +# conv1 = keras.layers.Conv2D(32, kernel_size=3, padding="same", activation="relu") # Couche de convolution avec 32 cartes +# conv2 = keras.layers.Conv2D(64, kernel_size=3, padding="same", activation="relu") # Couche de convolution avec 64 cartes +# model.add(conv1) +# model.add(conv2) +# model.add(keras.layers.MaxPool2D()) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Flatten()) # Couche de mise à plat des données +# model.add(keras.layers.Dropout(0.25)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(128, activation="relu")) # Couche dense : 128 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(10, activation="softmax")) # Couche de sortie : 1 node par classe soit 10 + +# Version 2 +# model = keras.models.Sequential() # Modèle de reseau de neurones +# model.add(keras.layers.Conv2D(64, kernel_size=7, padding="same", input_shape=[28,28,1], activation="relu")) # Couche de convolution avec 64 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Conv2D(128, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 128 cartes +# model.add(keras.layers.Conv2D(128, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 128 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Conv2D(256, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 256 cartes +# model.add(keras.layers.Conv2D(256, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 256 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Flatten()) # Couche de mise à plat des données +# model.add(keras.layers.Dense(16, activation="relu")) # Couche dense : 16 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(8, activation="relu")) # Couche dense : 8 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(2, activation="softmax")) # Couche de sortie : 1 node par classe soit 2 + +# Version 3 +# model = keras.models.Sequential() # Modèle de reseau de neurones +# model.add(keras.layers.Conv2D(4, kernel_size=7, padding="same", input_shape=[28,28,1], activation="relu")) # Couche de convolution avec 4 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Conv2D(8, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 8 cartes +# model.add(keras.layers.Conv2D(8, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 8 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Conv2D(16, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 16 cartes +# model.add(keras.layers.Conv2D(16, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 16 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Flatten()) # Couche de mise à plat des données +# model.add(keras.layers.Dense(16, activation="relu")) # Couche dense : 16 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(8, activation="relu")) # Couche dense : 8 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation par abandon +# model.add(keras.layers.Dense(2, activation="softmax")) # Couche de sortie : 1 node par classe soit 2 + +# Version 4 +# model = keras.models.Sequential() # Modèle de reseau de neurones +# model.add(keras.layers.Conv2D(16, kernel_size=28, padding="same", input_shape=[28,28,1], activation="relu")) # Couche de convolution avec 4 cartes +# model.add(keras.layers.Conv2D(16, kernel_size=28, padding="same", activation="relu")) # Couche de convolution avec 4 cartes +# model.add(keras.layers.MaxPool2D(2)) # Couche d'agrégation des cartes type Pooling max +# model.add(keras.layers.Flatten()) # Couche de mise à plat des données +# model.add(keras.layers.Dense(16, activation="relu")) # Couche dense : 16 nodes +# model.add(keras.layers.Dense(2, activation="softmax")) # Couche de sortie : 1 node par classe soit 2 + +# model=keras.models.load_model("04-cnn-cartes-v4.h5") # Lire le modèle + entrainement +# print ("Lecture du modèle entrainé : 04-cnn-cartes-v4.h5") + +# Version 5 +model = keras.models.Sequential() # Modèle de reseau de neurones +convo1 = keras.layers.Conv2D(16, kernel_size=3, padding="same", input_shape=[28,28,1], activation="relu") # Couche de convolution avec 16 cartes +model.add(convo1) +convo2 = keras.layers.Conv2D(16, kernel_size=3, padding="same", activation="relu") # Couche de convolution avec 16 cartes +model.add(convo2) +maxpool = keras.layers.MaxPool2D(2) # Couche d'agrégation des cartes type Pooling max +model.add(maxpool) +model.add(keras.layers.Flatten()) # Couche de mise à plat des données +model.add(keras.layers.Dense(16, activation="relu")) # Couche dense : 16 nodes +model.add(keras.layers.Dense(10, activation="softmax")) # Couche de sortie : 1 node par classe soit 10 + +optimiseur=keras.optimizers.SGD(learning_rate= eta) +model.compile(loss=perte, optimizer=optimiseur, metrics=["accuracy"]) # Compilation du modèle + +apts = model.fit(X_train, y_train, epochs=n, batch_size=lot, validation_data=(X_valid, y_valid)) # Entrainement +# model.save("04-cnn-cartes-v5.h5") +# model=keras.models.load_model("04-cnn-cartes-v5.h5") # Lire le modèle + entrainement +# print ("Lecture du modèle entrainé : 04-cnn-cartes-v5.h5") ############################################################################### # Phase d'inférence ############################################################################### -# Inférence sur la totalité du jeu de test print ("\n") -print ("Test sur le jeu de test (10 000 images).") +print ("Test sur le jeu de test ("+str(X_test.shape[0])+" images).") X_new = X_test y_new = np.argmax(model.predict(X_new), axis=-1) # Prédictions y_new_target= y_test # Cibles eval=model.evaluate(X_new, y_new_target) -# print ("Il y a "+str(int(np.round((1-eval[1])*X_new.shape[0]))) + " images non reconnues.\n") +print ("Il y a "+str(int(np.round((1-eval[1])*X_new.shape[0]))) + " images non reconnues.\n") -# Division du jeu de test par classes -print ("\n") -print ("Test sur les jeux divisés par classe.") -_X_new_classes_lst=[] -_y_new_target_classes_lst=[] -for i in range (10): # Classe - _X_new_classe=[] - _y_new_target_classe=[] - for j in range (X_new.shape[0]): # Lecture de toutes les images - if y_new_target[j] == i: - _X_new_classe.append(X_new[j]) - _y_new_target_classe.append(y_new[j]) - _X_new_classes_lst.append(_X_new_classe) - _y_new_target_classes_lst.append(_y_new_target_classe) +############################################################################### +# Résultats +############################################################################### -# Remplissage du tableau à partir de la liste -X_new_classes=[] -y_new_target_classes=[] -for i in range (10): - X_new_classes.append(np.empty(shape=(len(_X_new_classes_lst[i]),28,28,1))) - y_new_target_classes.append(np.empty(shape=(len(_y_new_target_classes_lst[i]),))) - for j in range (len(_X_new_classes_lst[i])): - X_new_classes[i][[j]]=_X_new_classes_lst[i][j] - y_new_target_classes[i][[j]]=_y_new_target_classes_lst[i][j] +# Récupérer les poids de couche de convolution +def convo_poids(layer_name): + _poids = model.get_layer(name=layer_name).get_weights()[0] + _poids = np.squeeze(_poids) + # _poids = _poids.reshape((_poids.shape[0], _poids.shape[1], _poids.shape[2]*_poids.shape[3])) + return _poids -# Inférence sur les jeux par classe -y_new_classes=[] -for i in range (10): - y_new_classes.append(np.argmax(model.predict(X_new_classes[i]), axis=-1)) # Prédictions -somme=0 -print ("\n") -for i in range (10): - k=0 - for j in range (X_new_classes[i].shape[0]): - if y_new_classes[i][j] != i: - k +=1 - somme +=1 - print ("Dans la classe "+str(i)+", il y a "+str(k) + " images non reconnues sur "+ str(X_new_classes[i].shape[0])+".") +# Couche de convolution 1 +X_convo = X[..., np.newaxis]/255.0 +subfigs[0].suptitle("Couche de convolution 1 : 3x3 - 16 cartes") +convo1_axs = subfigs[0].subplots(8, 8) +convo1_poids = convo_poids("conv2d") +convo1_img = convo1(X_convo[:10]) +i=0 +for ligne in range (2): # Filtres (poids) + for colonne in range (8): + convo1_axs[ligne][colonne].set_axis_off() + convo1_axs[ligne][colonne].imshow(convo1_poids[:,:,i], cmap="binary", interpolation="nearest") + convo1_axs[ligne][colonne].set_title("Filtre "+str(i), fontsize=10) + i +=1 +for ligne in range (2, 8): # Résultats + convo1_axs[ligne][0].set_axis_off() + convo1_axs[ligne][0].imshow(X[ligne-2], cmap="binary", interpolation="nearest") + convo1_axs[ligne][0].set_title("Image "+str(ligne-2), fontsize=10) + for colonne in range (1, 8): + convo1_axs[ligne][colonne].set_axis_off() + convo1_axs[ligne][colonne].imshow(convo1_img[ligne-2, :,:,colonne-1], cmap="binary", interpolation="nearest") + convo1_axs[ligne][colonne].set_title("Filtre "+str(colonne-1), fontsize=10) -print ("\n") -print ("Au total, il y a "+str(somme) + " images non reconnues sur 10 000.") -print ("Soit une précision de "+str(1-(somme/10000))+".") +# Couche de convolution 2 +subfigs[1].suptitle("Couche de convolution 2 : 3x3 - 16 cartes") +convo2_axs = subfigs[1].subplots(8, 8) +convo2_poids = convo_poids("conv2d_1") +convo2_img = convo2(convo1_img) +i=0 +for ligne in range (2): # Filtres (poids) + for colonne in range (8): + convo2_axs[ligne][colonne].set_axis_off() + convo2_axs[ligne][colonne].imshow(convo2_poids[:,:,0, i], cmap="binary", interpolation="nearest") + convo2_axs[ligne][colonne].set_title("Filtre 0-"+str(i), fontsize=8) + i +=1 +for ligne in range (2, 8): # Résultats + convo2_axs[ligne][0].set_axis_off() + convo2_axs[ligne][0].imshow(X[ligne-2], cmap="binary", interpolation="nearest") + convo2_axs[ligne][0].set_title("Image "+str(ligne-2), fontsize=10) + for colonne in range (1, 8): + convo2_axs[ligne][colonne].set_axis_off() + convo2_axs[ligne][colonne].imshow(convo2_img[ligne-2, :,:,colonne-1], cmap="binary", interpolation="nearest") + convo2_axs[ligne][colonne].set_title("Filtre "+str(colonne-1), fontsize=10) -# ############################################################################### -# # Résultats -# ############################################################################### - -# Modèle -model_ax.set_title("Modèle") -keras.utils.plot_model(model, "model.png", show_shapes=True) -model_img=plt.imread("model.png") -model_ax.imshow(model_img) -model_ax.set_axis_off() -os.remove("model.png") # Supression du fichier temporaire - -# Courbes d'apprentissage -apts_ax.set_title("Courbes d'apprentissage") -apts_ax.plot(apts.epoch, apts.history['loss'], 'b-', label="Perte - entrainement") -apts_ax.plot(apts.epoch, apts.history['val_loss'], 'r-', label="Perte - validation") -apts_ax.plot(apts.epoch, apts.history['accuracy'], 'b:', label="Précision - entrainement") -apts_ax.plot(apts.epoch, apts.history['val_accuracy'], 'r:', label="Précision - validation") -apts_ax.set(ylim=(0, 1)) -apts_ax.set_xlabel("Époque") -apts_ax.legend() - -# Prédictions -for ligne in range (10): # Ligne - i_first=-1 - for colonne in range (15): # Colonne - for i in range (i_first+1, X_new.shape[0]): - img_ax[ligne][colonne].set_axis_off() - if y_new_target[i] == ligne and y_new[i]!=y_new_target[i]: - # if y_test[i] == 2: - img_ax[ligne][colonne].imshow(X_new[i], cmap="binary", interpolation="nearest") - img_ax[ligne][colonne].set_title(str(y_new[i]), fontsize=10, color="red") - i_first=i - break +# Couche d'agrégation MaxPooling +subfigs[2].suptitle("Couche d'agrégation MaxPool : 2 -> sortie 14x14") +maxpool_axs = subfigs[2].subplots(8, 8) # 1 couches de 16 cartes +maxpool_img = maxpool(convo2_img) +for ligne in range (2): # Filtres -> rien + for colonne in range (8): + maxpool_axs[ligne][colonne].set_axis_off() +for ligne in range (2, 8): # Résultats + maxpool_axs[ligne][0].set_axis_off() + maxpool_axs[ligne][0].imshow(X[ligne-2], cmap="binary", interpolation="nearest") + maxpool_axs[ligne][0].set_title("Image "+str(ligne-2), fontsize=10) + for colonne in range (1, 8): + maxpool_axs[ligne][colonne].set_axis_off() + maxpool_axs[ligne][colonne].imshow(maxpool_img[ligne-2, :,:,colonne-1], cmap="binary", interpolation="nearest") + maxpool_axs[ligne][colonne].set_title("Filtre "+str(colonne-1), fontsize=10) plt.show() diff --git a/03-vision/README.md b/03-vision/README.md index 810cad2..e7ac466 100644 --- a/03-vision/README.md +++ b/03-vision/README.md @@ -11,3 +11,12 @@ ### Reconnaissance de digits - Réseaux de neurones convolutifs (CNN) ![capture d'écran](img/03-digit-cnn.png) + +### Visualisation des noyeaux convolutifs (CNN) + +![capture d'écran](img/04-cnn-cartes.png) + +### Acquisition avec openCV + +![capture d'écran](img/05-opencv.png) + diff --git a/03-vision/img/04-cnn-cartes.png b/03-vision/img/04-cnn-cartes.png new file mode 100644 index 0000000..ec8fa54 Binary files /dev/null and b/03-vision/img/04-cnn-cartes.png differ diff --git a/03-vision/img/05-opencv.png b/03-vision/img/05-opencv.png new file mode 100644 index 0000000..dbd0e4f Binary files /dev/null and b/03-vision/img/05-opencv.png differ