diff --git a/03-vision/02-digit-simple.py b/03-vision/02-digit-simple.py index 4da16b7..c2af354 100644 --- a/03-vision/02-digit-simple.py +++ b/03-vision/02-digit-simple.py @@ -75,7 +75,7 @@ X_valid, y_valid = X[:5000]/255.0 , y[:5000] # Phase d'apprentissage ############################################################################### -n = 50 # Nombre d'itérations (valeur par défaut : 50 , hyperparamètre) +n = 200 # Nombre d'itérations (valeur par défaut : 50 , 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) @@ -83,8 +83,8 @@ perte="sparse_categorical_crossentropy" # Type de perte (hyperparamètre) #perte="mse" # Type de perte (hyperparamètre) keras.backend.clear_session() -model = keras.models.Sequential() # Modèle de reseau de neurones -model.add(keras.layers.Flatten(input_shape=[28, 28])) # Couche d'entrée : mise à plat des données d'entrée -> 1 node / pixel soit 784 (28x28) +model = keras.models.Sequential() # Modèle de réseau de neurones +model.add(keras.layers.Flatten(input_shape=[28, 28])) # Couche de mise à plat des données -> 1 node / pixel soit 784 (28x28) model.add(keras.layers.Dense(300, activation="relu")) # Couche 1 : 300 nodes model.add(keras.layers.Dense(100, activation="relu")) # Couche 2 : 100 nodes -> ajout model.add(keras.layers.Dense(10, activation="softmax")) # Couche de sortie : 1 node par classe soit 10 diff --git a/03-vision/03-digit-cnn.py b/03-vision/03-digit-cnn.py index 20c1add..1d74809 100644 --- a/03-vision/03-digit-cnn.py +++ b/03-vision/03-digit-cnn.py @@ -38,6 +38,9 @@ from tensorflow import keras # - 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 type Dropout # - keras.backend.clear_session() : reset de la session # - model.compile : compilation du modèle # - model.fit : entrainement du modèle @@ -49,57 +52,71 @@ from tensorflow import keras # Initialisation ############################################################################### -# Init de Tensorflow + Keras - -# tf.__version__ -# keras.__version__ -# tf.config.list_physical_devices('GPU') - # Init du temps t_debut = time.time() # Init des plots fig = plt.figure(layout="constrained", figsize=(15, 5)) -fig.suptitle("Réseaux de neurones avec Keras - Classificateur d'images") +fig.suptitle("Vision par ordinateur - Reconnaissance de digit par réseaux de neurones convolutifs") subfigs = fig.subfigures(1, 3) model_ax = subfigs[0].subplots(1, 1) apts_ax = subfigs[1].subplots(1, 1) -img_ax = subfigs[2].subplots(4, 8) +img_ax = subfigs[2].subplots(10, 15) ############################################################################### # Observations ############################################################################### # Observations d'apprentissage, de validation et de test -vetement = keras.datasets.fashion_mnist # Jeu de données Fashion MNIST -(X, y), (X_test, y_test) = vetement.load_data() +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] -classes = ["Tshirt", "Pantalon", "Pull", "Robe", "Manteau", "Sandale", "Chemise", "Basket", "Sac", "Bottine"] + +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 = 30 # Nombre d'itérations (valeur par défaut : 30 , hyperparamètre) +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() -# np.random.seed(42) -# tf.random.set_seed(42) - model = keras.models.Sequential() # Modèle de reseau de neurones -model.add(keras.layers.Flatten(input_shape=[28, 28])) # Couche d'entrée : mise à plat des données d'entrée -> 1 node / pixel soit 784 (28x28) -model.add(keras.layers.Dense(300, activation="relu")) # Couche 1 : 300 nodes -# model.add(keras.layers.Dense(300, activation="relu")) # Couche 2 : 300 nodes -> passage de 100 à 300 -# model.add(keras.layers.Dense(300, activation="relu")) # Couche 3 : 300 nodes -> ajout -model.add(keras.layers.Dense(100, activation="relu")) # Couche 4 : 100 nodes -> ajout + +# Version 2 +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(128, activation="relu")) # Couche dense : 128 nodes +model.add(keras.layers.Dropout(0.5)) # Couche de régularisation type dropout +model.add(keras.layers.Dense(64, activation="relu")) # Couche dense : 64 nodes +model.add(keras.layers.Dropout(0.5)) # Couche de régularisation type dropout model.add(keras.layers.Dense(10, activation="softmax")) # Couche de sortie : 1 node par classe soit 10 -# model.compile(loss="sparse_categorical_crossentropy", optimizer="sgd", metrics=["accuracy"]) +# Version 1 +# model.add(keras.layers.Conv2D(32, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 32 cartes +# model.add(keras.layers.Conv2D(64, kernel_size=3, padding="same", activation="relu")) # Couche de convolution avec 64 cartes +# 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 type dropout +# model.add(keras.layers.Dense(128, activation="relu")) # Couche dense : 128 nodes +# model.add(keras.layers.Dropout(0.5)) # Couche de régularisation type dropout +# 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 @@ -109,25 +126,61 @@ apts = model.fit(X_train, y_train, epochs=n, batch_size=lot, validation_data=(X_ # Phase d'inférence ############################################################################### -# FIXME : prendre 8 images aléatoirement -# X_new=[] -# y_new=[] -# for i in range(8): -# idx = np.random.randint(X_test.shape[0]) # Index aléatoire -# X_new.append(X_test[idx:idx+1]/255.0) -# y_new.append(y_test[idx:idx+1]) - -idx = np.random.randint(X_test.shape[0]-32) # Index aléatoire -print ("\n") -print ("Test sur les images de "+ str(idx) + " à "+ str(idx+32) + " sur un jeu de 10 000 images.") -X_new = X_test[idx:idx+32] -y_new = np.argmax(model.predict(X_new), axis=-1) -y_new_test= y_test[idx:idx+32] +# Inférence sur la totalité du jeu de test print ("\n") +print ("Test sur le jeu de test (10 000 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 -############################################################################### +# 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) + +# 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] + +# 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])+".") + +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))+".") + +# ############################################################################### +# # Résultats +# ############################################################################### # Modèle model_ax.set_title("Modèle") @@ -148,16 +201,20 @@ apts_ax.set_xlabel("Époque") apts_ax.legend() # Prédictions -for i in range (8): - for j in range (4): - img_ax[j][i].imshow(X_new[i*2+j], cmap="binary", interpolation="nearest") - img_ax[j][i].set_axis_off() - if y_new[i*2+j] == y_new_test[i*2+j]: - img_ax[j][i].set_title(classes[y_new[i*2+j]], fontsize=10) - else: - img_ax[j][i].set_title(classes[y_new[i*2+j]], fontsize=10, color="red") +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 plt.show() # Performances +print ("\n") print ("Temps total : "+str(time.time()-t_debut)) diff --git a/03-vision/img/03-digit-cnn.png b/03-vision/img/03-digit-cnn.png new file mode 100644 index 0000000..2329426 Binary files /dev/null and b/03-vision/img/03-digit-cnn.png differ