Introduction et initialisation¶
Objectifs et approche¶
L'objectif de ce TP est de développer un réseau de neuronnes convolutifs pour détecter des obstacles à partir d'un dataset d'images.
Nous étudierons l’influence de différents hyperparamètres, comme le nombre de couches convolutionnelles, la taille des filtres et le taux d’apprentissage, sur la performance du modèle.
Notre analyse inclura principalement la visualisation des courbes d’apprentissage (accuracy et loss), ainsi que la matrice de confusion pour mesurer la qualité des prédictions.
Configurations initiales¶
Nous commençons par importer les librairies utiles.
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.regularizers import l2
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau
import matplotlib.pyplot as plt
import numpy as np
import math
from sklearn.metrics import confusion_matrix, classification_report
import itertools
Nous récupérons ensuite le dataset, chargeons et on préparons les données.
# Charger et préparer les données
path='dataset'
train_dir = f'{path}/train'
val_dir = f'{path}/test'
datagen = ImageDataGenerator(rescale=1./255)
train_generator = datagen.flow_from_directory(
train_dir, target_size=(64, 64),
batch_size=32,
class_mode='categorical',
shuffle=False)
val_generator = datagen.flow_from_directory(
val_dir, target_size=(64, 64),
batch_size=32,
class_mode='categorical',
shuffle=False)
# Définir le modèle CNN
model = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(64, 64, 3)),
MaxPooling2D(pool_size=(2, 2)),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D(pool_size=(2, 2)),
Flatten(),
Dense(128, activation='relu'),
Dense(10, activation='softmax')])
Found 3132 images belonging to 10 classes. Found 432 images belonging to 10 classes.
/Users/alaaeddineahriz/.pyenv/versions/3.10.16/lib/python3.10/site-packages/keras/src/layers/convolutional/base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
Analyse du dataset¶
Nous examinons maintenant le jeu de données. Nous commençons par sélectionner aléatoirement 10 images que nous affichons accompagnées de leurs étiquettes. Cette visualisation permet d’observer la diversité des exemples et d’identifier d’éventuelles similitudes visuelles entre les catégories.
# Afficher des exemples d'images d'entraînement
def show_batch_samples(generator, title=None):
plt.figure(figsize=(15, 8))
# Récupérer un batch d'images
images, labels = next(generator)
# Convertir les labels one-hot en indices de classe
labels = np.argmax(labels, axis=1)
# Afficher 10 images (une par classe si possible)
for i in range(min(10, len(images))):
plt.subplot(2, 5, i+1)
plt.imshow(images[i])
plt.title(f"Classe: {list(generator.class_indices.keys())[labels[i]]}")
plt.axis('off')
if title:
plt.suptitle(title)
plt.tight_layout()
plt.show()
show_batch_samples(train_generator, "Exemples d'images d'entraînement")
Par la suite, nous représentons graphiquement la distribution des classes pour les ensembles d’entraînement et de validation. Cette répartition des données injectées au modèle facilite l’interprétation des résultats. Elle permettrait notamment d'expliquer d’éventuels écarts aux résultats attendus pour la matrice de confusion.
# Aficher la distribution des classes
def plot_class_distribution(generator, title="Distribution des classes"):
# Obtenir les nombres d'images par classe
class_counts = [0] * len(generator.class_indices)
for i in range(len(generator.classes)):
class_counts[generator.classes[i]] += 1
# Récupérer les noms des classes
class_names = list(generator.class_indices.keys())
# Création du graphique
plt.figure(figsize=(12, 6))
plt.bar(class_names, class_counts)
plt.xlabel('Classes')
plt.ylabel('Nombre d\'images')
plt.title(title)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# Afficher les statistiques
print("Nombre total d'images:", sum(class_counts))
print("Répartition par classe:")
for i, (name, count) in enumerate(zip(class_names, class_counts)):
print(f" Classe {i} ({name}): {count} images ({count/sum(class_counts)*100:.1f}%)")
# Afficher la distribution des classes d'entraînement et de validation
plot_class_distribution(train_generator, "Distribution des classes - Données d'entraînement")
plot_class_distribution(val_generator, "Distribution des classes - Données de validation")
Nombre total d'images: 3132 Répartition par classe: Classe 0 (chair): 383 images (12.2%) Classe 1 (door): 191 images (6.1%) Classe 2 (fence): 167 images (5.3%) Classe 3 (garbage_bin): 158 images (5.0%) Classe 4 (obstacle): 332 images (10.6%) Classe 5 (plant): 115 images (3.7%) Classe 6 (pothole): 676 images (21.6%) Classe 7 (stairs): 444 images (14.2%) Classe 8 (table): 152 images (4.9%) Classe 9 (vehicle): 514 images (16.4%)
Nombre total d'images: 432 Répartition par classe: Classe 0 (chair): 24 images (5.6%) Classe 1 (door): 51 images (11.8%) Classe 2 (fence): 12 images (2.8%) Classe 3 (garbage_bin): 17 images (3.9%) Classe 4 (obstacle): 91 images (21.1%) Classe 5 (plant): 24 images (5.6%) Classe 6 (pothole): 30 images (6.9%) Classe 7 (stairs): 60 images (13.9%) Classe 8 (table): 33 images (7.6%) Classe 9 (vehicle): 90 images (20.8%)
L’analyse de la distribution des classes dans le dataset révèle un déséquilibre marqué entre les classes dans les ensembles d’entraînement et de validation. Dans le jeu d’entraînement, certaines classes telles que "pothole" (21,6%), "vehicle" (16,4%) et "stairs" (14,2%) sont surreprésentées, tandis que des classes comme "plant" (3,7%), "fence" (5,3%) ou "garbage_bin (5,0%) figurent en minorité. Le jeu de validation présente une répartition similaire, bien que quelques écarts subsistent (par exemple, "door" passe de 6,1% en entraînement à 11,8% en validation).
Ces disparités peuvent influencer la matrice de confusion : le modèle est susceptible d’obtenir de meilleures performances sur les classes les mieux représentées, tandis que les classes rares pourraient être confondues, entraînant des précisions et des rappels faibles. La variation relative entre les distributions d’entraînement et de validation pourrait également contribuer à des erreurs de classification spécifiques.
Fonctions utiles¶
On définit ici les fonctions utiles à l'analyse des résultats des modèles, notamment :
- l'évolution de l'accuracy et de la loss en fonction des epochs pour l'entraînement et la validation
- la matrice de confusion
# Visualiser les courbes d'apprentissage (accuracy et loss)
def plot_learning_curves(history, title="Courbes d'apprentissage"):
# Récupérer l'historique d'entraînement
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
# Créer les graphiques
plt.figure(figsize=(12, 5))
# Graphique d'accuracy
plt.subplot(1, 2, 1)
plt.plot(epochs, acc, 'b-', label='Accuracy entraînement')
plt.plot(epochs, val_acc, 'r-', label='Accuracy validation')
plt.title('Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
# Graphique de loss
plt.subplot(1, 2, 2)
plt.plot(epochs, loss, 'b-', label='Loss entraînement')
plt.plot(epochs, val_loss, 'r-', label='Loss validation')
plt.title('Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.suptitle(title)
plt.tight_layout()
plt.show()
# Visualiser les courbes d'apprentissage pour une approche spécifique
def plot_training_curves(history, title=""):
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
# Accuracy
ax1.plot(history.history['accuracy'])
ax1.plot(history.history['val_accuracy'])
ax1.set_title(f'Accuracy - {title}')
ax1.set_ylabel('Accuracy')
ax1.set_xlabel('Epoch')
ax1.legend(['Train', 'Validation'], loc='lower right')
# Loss
ax2.plot(history.history['loss'])
ax2.plot(history.history['val_loss'])
ax2.set_title(f'Loss - {title}')
ax2.set_ylabel('Loss')
ax2.set_xlabel('Epoch')
ax2.legend(['Train', 'Validation'], loc='upper right')
plt.tight_layout()
plt.show()
# Afficher le learning rate si disponible
if 'lr' in history.history:
plt.figure(figsize=(10, 5))
plt.plot(history.history['lr'])
plt.title(f'Learning Rate - {title}')
plt.ylabel('Learning Rate')
plt.xlabel('Epoch')
plt.yscale('log')
plt.grid(True)
plt.show()
#Afficher la matrice de confusion
def plot_confusion_matrix(cm, classes, title='Matrice de confusion', cmap=plt.cm.Blues):
plt.figure(figsize=(10, 8))
plt.imshow(cm, interpolation='nearest', cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes, rotation=45)
plt.yticks(tick_marks, classes)
# Afficher les valeurs dans les cellules
thresh = cm.max() / 2.
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
plt.text(j, i, format(cm[i, j], 'd'),
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black")
plt.tight_layout()
plt.ylabel('Classe réelle')
plt.xlabel('Classe prédite')
plt.show()
def evaluate_model(model, generator, title="Évaluation du modèle"):
# Réinitialiser le générateur
generator.reset()
# Prédire les classes
Y_pred = model.predict(generator)
y_pred = np.argmax(Y_pred, axis=1)
# Classes réelles
y_true = generator.classes
# Noms des classes
class_names = list(generator.class_indices.keys())
# Calculer et afficher la matrice de confusion
cm = confusion_matrix(y_true, y_pred)
plot_confusion_matrix(cm, class_names, title=f"Matrice de confusion - {title}")
# Afficher le rapport de classification
print(f"Rapport de classification - {title}")
print(classification_report(y_true, y_pred, target_names=class_names))
# Visualiser les résultats pour tous les learning rates
def plot_learning_rate_comparison(results):
learning_rates = list(results.keys())
accuracies = [results[lr]['val_accuracy'] for lr in learning_rates]
losses = [results[lr]['val_loss'] for lr in learning_rates]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
# Graphique d'accuracy
ax1.semilogx(learning_rates, accuracies, 'o-')
ax1.set_title('Validation Accuracy vs Learning Rate')
ax1.set_xlabel('Learning Rate (log scale)')
ax1.set_ylabel('Validation Accuracy')
ax1.grid(True)
# Graphique de loss
ax2.semilogx(learning_rates, losses, 'o-')
ax2.set_title('Validation Loss vs Learning Rate')
ax2.set_xlabel('Learning Rate (log scale)')
ax2.set_ylabel('Validation Loss')
ax2.grid(True)
plt.tight_layout()
plt.show()
# Trouver le meilleur learning rate
best_lr = min(results.items(), key=lambda x: x[1]['val_loss'])[0]
best_accuracy = results[best_lr]['val_accuracy']
best_loss = results[best_lr]['val_loss']
print(f"\nMeilleur learning rate: {best_lr}")
print(f"Meilleure accuracy: {best_accuracy:.4f}")
print(f"Meilleure loss: {best_loss:.4f}")
return best_lr
Modèle CNN de base¶
Nous commmençons ici par compiler et entraîner le modèle de base tel que donné dans le sujet. Ensuite, nous l'évaluons en affichant l'évolution de l'accuracy et de la loss pour les sets d'entraînement et de validation. Cette étape initiale nous permettra d'établir une référence (benchmark) pour comparer les performances des modèles plus complexes que nous développerons par la suite.
Définition du modèle¶
# Définition du modèle CNN de base
def create_base_model():
model = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(64, 64, 3)),
MaxPooling2D(pool_size=(2, 2)),
Conv2D(64, (3, 3), activation='relu', padding='same'),
MaxPooling2D(pool_size=(2, 2)),
Flatten(),
Dense(128, activation='relu'),
Dense(10, activation='softmax')
])
# Compilation du modèle
model.compile(
optimizer='adam',
loss='categorical_crossentropy', # Corrigé pour classification multiclasse
metrics=['accuracy']
)
return model
# Création du modèle de base
base_model = create_base_model()
# Résumé du modèle
base_model.summary()
Model: "sequential_1"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d_2 (Conv2D) │ (None, 62, 62, 32) │ 896 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 31, 31, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_3 (Conv2D) │ (None, 31, 31, 64) │ 18,496 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_3 (MaxPooling2D) │ (None, 15, 15, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten_1 (Flatten) │ (None, 14400) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_2 (Dense) │ (None, 128) │ 1,843,328 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_3 (Dense) │ (None, 10) │ 1,290 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 1,864,010 (7.11 MB)
Trainable params: 1,864,010 (7.11 MB)
Non-trainable params: 0 (0.00 B)
# Entraîner le modèle
base_history = base_model.fit(
train_generator,
validation_data=val_generator,
epochs=30,
verbose=1
)
# Évaluer le modèle
loss, accuracy = base_model.evaluate(val_generator)
print(f'Loss: {loss:.4f}, Accuracy: {accuracy:.4f}')
Epoch 1/30
/Users/alaaeddineahriz/.pyenv/versions/3.10.16/lib/python3.10/site-packages/keras/src/trainers/data_adapters/py_dataset_adapter.py:121: UserWarning: Your `PyDataset` class should call `super().__init__(**kwargs)` in its constructor. `**kwargs` can include `workers`, `use_multiprocessing`, `max_queue_size`. Do not pass these arguments to `fit()`, as they will be ignored. self._warn_if_super_not_called()
98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 49ms/step - accuracy: 0.1203 - loss: 2.5208 - val_accuracy: 0.0694 - val_loss: 2.2605 Epoch 2/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 45ms/step - accuracy: 0.2950 - loss: 2.2403 - val_accuracy: 0.2083 - val_loss: 2.2033 Epoch 3/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 43ms/step - accuracy: 0.2751 - loss: 2.1920 - val_accuracy: 0.2199 - val_loss: 2.1398 Epoch 4/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 44ms/step - accuracy: 0.2630 - loss: 2.1683 - val_accuracy: 0.2384 - val_loss: 2.0760 Epoch 5/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 44ms/step - accuracy: 0.3144 - loss: 1.9357 - val_accuracy: 0.2245 - val_loss: 2.0235 Epoch 6/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 44ms/step - accuracy: 0.3893 - loss: 1.8013 - val_accuracy: 0.2685 - val_loss: 1.9126 Epoch 7/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 44ms/step - accuracy: 0.4410 - loss: 1.6239 - val_accuracy: 0.1852 - val_loss: 2.0894 Epoch 8/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 46ms/step - accuracy: 0.4769 - loss: 1.5949 - val_accuracy: 0.3287 - val_loss: 1.9095 Epoch 9/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 45ms/step - accuracy: 0.4698 - loss: 1.4651 - val_accuracy: 0.4977 - val_loss: 1.4930 Epoch 10/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 46ms/step - accuracy: 0.5185 - loss: 1.3446 - val_accuracy: 0.5787 - val_loss: 1.3516 Epoch 11/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 45ms/step - accuracy: 0.6499 - loss: 1.0240 - val_accuracy: 0.5417 - val_loss: 1.3316 Epoch 12/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 46ms/step - accuracy: 0.7092 - loss: 0.8443 - val_accuracy: 0.6227 - val_loss: 1.1701 Epoch 13/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 46ms/step - accuracy: 0.8052 - loss: 0.5975 - val_accuracy: 0.6134 - val_loss: 1.2105 Epoch 14/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 48ms/step - accuracy: 0.8376 - loss: 0.4759 - val_accuracy: 0.6273 - val_loss: 1.2674 Epoch 15/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 47ms/step - accuracy: 0.8529 - loss: 0.4199 - val_accuracy: 0.6088 - val_loss: 1.2443 Epoch 16/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 45ms/step - accuracy: 0.9271 - loss: 0.2729 - val_accuracy: 0.6574 - val_loss: 1.0781 Epoch 17/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 45ms/step - accuracy: 0.9533 - loss: 0.1743 - val_accuracy: 0.6713 - val_loss: 1.2189 Epoch 18/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 46ms/step - accuracy: 0.9673 - loss: 0.1282 - val_accuracy: 0.6806 - val_loss: 1.2874 Epoch 19/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 45ms/step - accuracy: 0.9917 - loss: 0.0571 - val_accuracy: 0.6528 - val_loss: 1.6690 Epoch 20/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 45ms/step - accuracy: 0.9849 - loss: 0.0675 - val_accuracy: 0.6690 - val_loss: 1.4887 Epoch 21/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 46ms/step - accuracy: 0.9985 - loss: 0.0204 - val_accuracy: 0.6505 - val_loss: 1.6301 Epoch 22/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 4s 46ms/step - accuracy: 0.9972 - loss: 0.0190 - val_accuracy: 0.6736 - val_loss: 1.6749 Epoch 23/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 51ms/step - accuracy: 0.9998 - loss: 0.0089 - val_accuracy: 0.6736 - val_loss: 1.6519 Epoch 24/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 51ms/step - accuracy: 1.0000 - loss: 0.0074 - val_accuracy: 0.6898 - val_loss: 1.6545 Epoch 25/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 6s 60ms/step - accuracy: 1.0000 - loss: 0.0038 - val_accuracy: 0.6806 - val_loss: 1.8003 Epoch 26/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 6s 56ms/step - accuracy: 1.0000 - loss: 0.0030 - val_accuracy: 0.6736 - val_loss: 1.8212 Epoch 27/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 6s 57ms/step - accuracy: 1.0000 - loss: 0.0020 - val_accuracy: 0.6829 - val_loss: 1.8082 Epoch 28/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 5s 54ms/step - accuracy: 1.0000 - loss: 0.0018 - val_accuracy: 0.6782 - val_loss: 1.8018 Epoch 29/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 6s 62ms/step - accuracy: 1.0000 - loss: 0.0017 - val_accuracy: 0.6852 - val_loss: 1.8815 Epoch 30/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 8s 83ms/step - accuracy: 1.0000 - loss: 0.0015 - val_accuracy: 0.6782 - val_loss: 1.9056 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 39ms/step - accuracy: 0.6828 - loss: 1.8055 Loss: 1.9056, Accuracy: 0.6782
Résultats¶
# Visualiser les courbes d'apprentissage du modèle de base
plot_learning_curves(base_history, "Courbes d'apprentissage - Modèle de base")
En analysant les courbes d’apprentissage, nous constatons que l’accuracy de la validation diverge rapidement de celle de l’entraînement, indiquant un surapprentissage précoce. De même, la loss de validation s’écarte notablement de celle mesurée pendant l’entraînement.
# Évaluer le modèle de base avec la matrice de confusion et le rapport de classification
evaluate_model(base_model, val_generator, "Modèle de base")
14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 35ms/step
Rapport de classification - Modèle de base
precision recall f1-score support
chair 0.33 0.75 0.46 24
door 0.79 0.75 0.77 51
fence 0.62 0.42 0.50 12
garbage_bin 0.60 0.71 0.65 17
obstacle 0.86 0.54 0.66 91
plant 0.95 0.79 0.86 24
pothole 0.63 0.90 0.74 30
stairs 0.61 0.75 0.67 60
table 0.68 0.70 0.69 33
vehicle 0.77 0.63 0.70 90
accuracy 0.68 432
macro avg 0.68 0.69 0.67 432
weighted avg 0.73 0.68 0.69 432
La matrice de confusion présente déjà une diagonale bien définie, traduisant des résultats satisfaisants. Certaines classes, notamment « vehicle », « obstacle », « stairs », « door » et « pothole », sont nettement mieux reconnues que les autres, ce qui s’explique principalement par la distribution déséquilibrée du dataset (d'entraînement et de validation) où ces classes sont surreprésentées
Une analyse par classe, effectuée sur le rapport de classification, révèle elle aussi des performances contrastées :
- La classe « pothole » affiche le meilleur recall (0,90) : 90 % des nids-de-poule sont correctement identifiés, bien que la précision soit moyenne (0,63) en raison d’un nombre important de faux positifs.
- La classe « plant » présente quant à elle la meilleure précision (0,95) avec un recall acceptable (0,79).
- La classe « obstacle » montre en revanche une bonne précision (0,6) mais un recall médiocre (0,54), indiquant que le modèle omet fréquemment cette classe.
- Enfin, la classe « chair » a la précision la plus faible (0,33). Elle est donc souvent confondue avec d'autres classes.
Globalement, l’accuracy atteint 0,68, signifiant que 68 % des prédictions sont correctes. La moyenne pondérée du f1-score (0,69) dépasse légèrement la moyenne macro (0,67) ce qui souligne, une fois encore, l’influence du déséquilibre des classes sur la précision du modèle.
Nous allons donc tester différentes optimisations de notre modèle afin d'avoir de meilleurs résultats.
Expérimentation - Nombre de couches convolutives¶
À cette étape, nous cherchons à évaluer l’impact du nombre de couches convolutives sur la performance de notre réseau de neuronnes. La fonction create_model_with_layers(num_conv_layers) construit un modèle contenant n couches convolutives (n allant de 1 à 5). Plus précisement :
- Nous commençons par une couche Conv2D (32 filtres, noyau 3×3) suivie d’un max pooling.
- Au moyen d'une boucle on ajoute ensuite, pour chaque couche supplémentaire, une paire Conv2D/MaxPooling2D, doublant le nombre de filtres à chaque fois (limité à 256).
- Nous terminons le réseau par deux couches d'activation. Une couche fully connected de 128 neurones et une couche de sortie softmax, adaptée à la classification multiclasse (10 dans notre cas).
Pour différentes configurations (1 à 5 couches), nous entraînons chaque modèle sur 15 époques, puis comparons leurs performances en affichant un graphique de l’accuracy de validation. Enfin nous comparons les courbes d’apprentissage du meilleur modèle par rapport à celles du modèle de base (benchmark).
Définition du modèle à n couches convolutives¶
def create_model_with_layers(num_conv_layers):
model = Sequential()
# Première couche
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(64, 64, 3)))
model.add(MaxPooling2D(pool_size=(2, 2)))
# Ajouter des couches convolutives
filters = 64
for _ in range(1, num_conv_layers):
model.add(Conv2D(filters, (3, 3), padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
filters = min(filters * 2, 256) # Augmenter le nombre de filtres, max 256
# Couches fully connected
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(10, activation='softmax'))
# Compiler le modèle
model.compile(
optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy']
)
return model
# Tester différents nombres de couches
layer_results = {}
for num_layers in [1,2,3]:
print(f"\nEntraînement du modèle avec {num_layers} couches convolutives")
# Créer et entraîner le modèle
model = create_model_with_layers(num_layers)
history = model.fit(
train_generator,
validation_data=val_generator,
epochs=15,
#callbacks=[early_stopping],
verbose=1
)
# Évaluer le modèle
val_loss, val_acc = model.evaluate(val_generator)
# Stocker les résultats
layer_results[num_layers] = {
'model': model,
'history': history,
'val_accuracy': val_acc,
'val_loss': val_loss
}
print(f"Modèle avec {num_layers} couches: Loss = {val_loss:.4f}, Accuracy = {val_acc:.4f}")
Entraînement du modèle avec 1 couches convolutives Epoch 1/15
/Users/alaaeddineahriz/.pyenv/versions/3.10.16/lib/python3.10/site-packages/keras/src/layers/convolutional/base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 89ms/step - accuracy: 0.1499 - loss: 5.3390 - val_accuracy: 0.0556 - val_loss: 2.2643 Epoch 2/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 94ms/step - accuracy: 0.1014 - loss: 2.2568 - val_accuracy: 0.0903 - val_loss: 2.2268 Epoch 3/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 92ms/step - accuracy: 0.2729 - loss: 2.1177 - val_accuracy: 0.0625 - val_loss: 2.2049 Epoch 4/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 107ms/step - accuracy: 0.1922 - loss: 2.1547 - val_accuracy: 0.1343 - val_loss: 2.4176 Epoch 5/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 110ms/step - accuracy: 0.3255 - loss: 1.9668 - val_accuracy: 0.1134 - val_loss: 2.1940 Epoch 6/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 122ms/step - accuracy: 0.2535 - loss: 2.0272 - val_accuracy: 0.2616 - val_loss: 2.0733 Epoch 7/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 102ms/step - accuracy: 0.3826 - loss: 1.7720 - val_accuracy: 0.2685 - val_loss: 2.0102 Epoch 8/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 97ms/step - accuracy: 0.4001 - loss: 1.7705 - val_accuracy: 0.2338 - val_loss: 1.9280 Epoch 9/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 98ms/step - accuracy: 0.4020 - loss: 1.7362 - val_accuracy: 0.2500 - val_loss: 2.1009 Epoch 10/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 94ms/step - accuracy: 0.4268 - loss: 1.7197 - val_accuracy: 0.3426 - val_loss: 1.8467 Epoch 11/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 89ms/step - accuracy: 0.5075 - loss: 1.4405 - val_accuracy: 0.2731 - val_loss: 1.8622 Epoch 12/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 92ms/step - accuracy: 0.4990 - loss: 1.4343 - val_accuracy: 0.3264 - val_loss: 1.7648 Epoch 13/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 102ms/step - accuracy: 0.5188 - loss: 1.3581 - val_accuracy: 0.3819 - val_loss: 1.6997 Epoch 14/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 100ms/step - accuracy: 0.6114 - loss: 1.1927 - val_accuracy: 0.4120 - val_loss: 1.7432 Epoch 15/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 92ms/step - accuracy: 0.5732 - loss: 1.2456 - val_accuracy: 0.4282 - val_loss: 1.6047 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 52ms/step - accuracy: 0.3977 - loss: 1.6934 Modèle avec 1 couches: Loss = 1.6047, Accuracy = 0.4282 Entraînement du modèle avec 2 couches convolutives Epoch 1/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 99ms/step - accuracy: 0.0941 - loss: 2.5512 - val_accuracy: 0.0694 - val_loss: 2.2739 Epoch 2/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 100ms/step - accuracy: 0.2541 - loss: 2.2013 - val_accuracy: 0.0694 - val_loss: 2.4715 Epoch 3/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 111ms/step - accuracy: 0.2539 - loss: 2.1075 - val_accuracy: 0.1968 - val_loss: 2.1632 Epoch 4/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 126ms/step - accuracy: 0.2872 - loss: 2.0559 - val_accuracy: 0.2292 - val_loss: 2.0910 Epoch 5/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 110ms/step - accuracy: 0.2660 - loss: 2.0183 - val_accuracy: 0.2361 - val_loss: 2.1477 Epoch 6/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 111ms/step - accuracy: 0.3168 - loss: 1.9616 - val_accuracy: 0.1551 - val_loss: 2.1047 Epoch 7/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 98ms/step - accuracy: 0.4168 - loss: 1.7562 - val_accuracy: 0.4074 - val_loss: 1.7689 Epoch 8/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 103ms/step - accuracy: 0.4605 - loss: 1.6193 - val_accuracy: 0.3958 - val_loss: 1.7715 Epoch 9/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 104ms/step - accuracy: 0.4889 - loss: 1.5139 - val_accuracy: 0.3796 - val_loss: 1.7838 Epoch 10/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 102ms/step - accuracy: 0.5664 - loss: 1.2352 - val_accuracy: 0.4537 - val_loss: 1.6314 Epoch 11/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 96ms/step - accuracy: 0.6470 - loss: 1.0530 - val_accuracy: 0.5694 - val_loss: 1.3123 Epoch 12/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 101ms/step - accuracy: 0.7540 - loss: 0.7563 - val_accuracy: 0.5972 - val_loss: 1.2626 Epoch 13/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 98ms/step - accuracy: 0.7983 - loss: 0.6553 - val_accuracy: 0.5880 - val_loss: 1.4577 Epoch 14/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 90ms/step - accuracy: 0.7598 - loss: 0.6916 - val_accuracy: 0.6389 - val_loss: 1.2038 Epoch 15/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 8s 86ms/step - accuracy: 0.8756 - loss: 0.4034 - val_accuracy: 0.6713 - val_loss: 1.1510 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 36ms/step - accuracy: 0.6420 - loss: 1.1646 Modèle avec 2 couches: Loss = 1.1510, Accuracy = 0.6713 Entraînement du modèle avec 3 couches convolutives Epoch 1/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 100ms/step - accuracy: 0.1498 - loss: 2.3759 - val_accuracy: 0.0694 - val_loss: 2.3318 Epoch 2/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 99ms/step - accuracy: 0.2221 - loss: 2.2024 - val_accuracy: 0.0764 - val_loss: 2.2646 Epoch 3/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 99ms/step - accuracy: 0.2239 - loss: 2.2322 - val_accuracy: 0.0718 - val_loss: 2.2728 Epoch 4/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 102ms/step - accuracy: 0.2295 - loss: 2.1535 - val_accuracy: 0.1759 - val_loss: 2.2270 Epoch 5/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 98ms/step - accuracy: 0.3162 - loss: 2.1433 - val_accuracy: 0.1782 - val_loss: 2.1681 Epoch 6/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 101ms/step - accuracy: 0.3530 - loss: 2.0046 - val_accuracy: 0.2569 - val_loss: 2.1137 Epoch 7/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 109ms/step - accuracy: 0.2689 - loss: 2.0517 - val_accuracy: 0.1759 - val_loss: 2.2289 Epoch 8/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 127ms/step - accuracy: 0.3786 - loss: 1.8130 - val_accuracy: 0.2037 - val_loss: 2.0636 Epoch 9/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 113ms/step - accuracy: 0.2662 - loss: 1.9937 - val_accuracy: 0.0764 - val_loss: 2.5577 Epoch 10/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 115ms/step - accuracy: 0.3870 - loss: 1.9486 - val_accuracy: 0.3889 - val_loss: 1.7742 Epoch 11/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 113ms/step - accuracy: 0.4092 - loss: 1.7308 - val_accuracy: 0.3588 - val_loss: 1.8263 Epoch 12/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 118ms/step - accuracy: 0.4617 - loss: 1.6095 - val_accuracy: 0.3981 - val_loss: 1.7080 Epoch 13/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 119ms/step - accuracy: 0.5019 - loss: 1.4629 - val_accuracy: 0.4421 - val_loss: 1.6233 Epoch 14/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 99ms/step - accuracy: 0.5167 - loss: 1.3942 - val_accuracy: 0.4468 - val_loss: 1.6256 Epoch 15/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 103ms/step - accuracy: 0.5836 - loss: 1.2169 - val_accuracy: 0.5463 - val_loss: 1.3340 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 64ms/step - accuracy: 0.5655 - loss: 1.3264 Modèle avec 3 couches: Loss = 1.3340, Accuracy = 0.5463 Entraînement du modèle avec 4 couches convolutives Epoch 1/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 143ms/step - accuracy: 0.1601 - loss: 2.2876 - val_accuracy: 0.0694 - val_loss: 2.2665 Epoch 2/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.2086 - loss: 2.1968 - val_accuracy: 0.2083 - val_loss: 2.2664 Epoch 3/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 137ms/step - accuracy: 0.1406 - loss: 2.1899 - val_accuracy: 0.0694 - val_loss: 2.2549 Epoch 4/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 137ms/step - accuracy: 0.2144 - loss: 2.1575 - val_accuracy: 0.0556 - val_loss: 2.4432 Epoch 5/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 139ms/step - accuracy: 0.2293 - loss: 2.2342 - val_accuracy: 0.1389 - val_loss: 2.2664 Epoch 6/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 134ms/step - accuracy: 0.1312 - loss: 2.2109 - val_accuracy: 0.0764 - val_loss: 2.2572 Epoch 7/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 138ms/step - accuracy: 0.2474 - loss: 2.1652 - val_accuracy: 0.0764 - val_loss: 2.2522 Epoch 8/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 133ms/step - accuracy: 0.2079 - loss: 2.1788 - val_accuracy: 0.0694 - val_loss: 2.2970 Epoch 9/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 140ms/step - accuracy: 0.2385 - loss: 2.1730 - val_accuracy: 0.0856 - val_loss: 2.2076 Epoch 10/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 152ms/step - accuracy: 0.2596 - loss: 2.1278 - val_accuracy: 0.1250 - val_loss: 2.1706 Epoch 11/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 149ms/step - accuracy: 0.2300 - loss: 2.0461 - val_accuracy: 0.0903 - val_loss: 2.2771 Epoch 12/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 142ms/step - accuracy: 0.3231 - loss: 1.9933 - val_accuracy: 0.2384 - val_loss: 2.0646 Epoch 13/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 143ms/step - accuracy: 0.3619 - loss: 1.9711 - val_accuracy: 0.1389 - val_loss: 2.1544 Epoch 14/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 141ms/step - accuracy: 0.2874 - loss: 2.0588 - val_accuracy: 0.2037 - val_loss: 2.0917 Epoch 15/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 145ms/step - accuracy: 0.4207 - loss: 1.7468 - val_accuracy: 0.2431 - val_loss: 2.0432 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 69ms/step - accuracy: 0.2556 - loss: 2.1511 Modèle avec 4 couches: Loss = 2.0432, Accuracy = 0.2431 Entraînement du modèle avec 5 couches convolutives Epoch 1/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 18s 172ms/step - accuracy: 0.1215 - loss: 2.3137 - val_accuracy: 0.0694 - val_loss: 2.2748 Epoch 2/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 166ms/step - accuracy: 0.0709 - loss: 2.1397 - val_accuracy: 0.0694 - val_loss: 2.2639 Epoch 3/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 165ms/step - accuracy: 0.3502 - loss: 2.1968 - val_accuracy: 0.0694 - val_loss: 2.2392 Epoch 4/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 157ms/step - accuracy: 0.2529 - loss: 2.1269 - val_accuracy: 0.0694 - val_loss: 2.2407 Epoch 5/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 159ms/step - accuracy: 0.2320 - loss: 2.1797 - val_accuracy: 0.1389 - val_loss: 2.2187 Epoch 6/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 17s 169ms/step - accuracy: 0.2938 - loss: 2.0810 - val_accuracy: 0.0694 - val_loss: 2.2392 Epoch 7/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 17s 172ms/step - accuracy: 0.1742 - loss: 2.1437 - val_accuracy: 0.1343 - val_loss: 2.2083 Epoch 8/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 17s 177ms/step - accuracy: 0.2891 - loss: 2.1009 - val_accuracy: 0.1481 - val_loss: 2.1829 Epoch 9/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 141ms/step - accuracy: 0.2664 - loss: 2.0813 - val_accuracy: 0.1343 - val_loss: 2.1788 Epoch 10/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 143ms/step - accuracy: 0.2733 - loss: 2.0630 - val_accuracy: 0.1620 - val_loss: 2.1810 Epoch 11/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 140ms/step - accuracy: 0.3456 - loss: 1.9401 - val_accuracy: 0.0833 - val_loss: 2.2288 Epoch 12/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 142ms/step - accuracy: 0.3284 - loss: 1.9999 - val_accuracy: 0.2245 - val_loss: 2.0804 Epoch 13/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 138ms/step - accuracy: 0.3322 - loss: 1.9792 - val_accuracy: 0.2685 - val_loss: 2.0731 Epoch 14/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 129ms/step - accuracy: 0.3343 - loss: 1.8784 - val_accuracy: 0.3032 - val_loss: 1.9331 Epoch 15/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 122ms/step - accuracy: 0.3578 - loss: 1.8037 - val_accuracy: 0.2037 - val_loss: 2.0253 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 41ms/step - accuracy: 0.2281 - loss: 2.1056 Modèle avec 5 couches: Loss = 2.0253, Accuracy = 0.2037
Résultats pour le modèle à n couches convolutives¶
# Afficher un graphique comparatif
plt.figure(figsize=(10, 6))
layers = list(layer_results.keys())
accuracies = [results['val_accuracy'] for results in layer_results.values()]
plt.bar(layers, accuracies)
plt.xlabel('Nombre de couches convolutives')
plt.ylabel('Accuracy de validation')
plt.title('Impact du nombre de couches convolutives sur la performance')
plt.xticks(layers)
plt.ylim(0, 1)
for i, v in enumerate(accuracies):
plt.text(i + 1, v + 0.02, f"{v:.4f}", ha='center')
plt.tight_layout()
plt.show()
# Visualiser les courbes d'apprentissage du meilleur modèle
best_layer_count = max(layer_results, key=lambda k: layer_results[k]['val_accuracy'])
print(f"Le meilleur modèle a {best_layer_count} couches convolutives")
plot_learning_curves(layer_results[best_layer_count]['history'],
f"Courbes d'apprentissage - Modèle avec {best_layer_count} couches")
# Visualiser les courbes d'apprentissage du modèle de base
# plot_learning_curves(base_history, "Courbes d'apprentissage - Modèle de base")
# Évaluation du meilleur modèle
best_cnn_model = layer_results[best_layer_count]['model']
evaluate_model(best_cnn_model, val_generator, f"Modèle avec {best_layer_count} couches")
best_cnn_model.summary()
best_cnn_model.save("best_cnn_model.h5")
Le meilleur modèle a 2 couches convolutives
14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 38ms/step
Rapport de classification - Modèle avec 2 couches
precision recall f1-score support
chair 0.39 0.58 0.47 24
door 0.66 0.76 0.71 51
fence 0.67 0.33 0.44 12
garbage_bin 0.71 0.71 0.71 17
obstacle 0.75 0.53 0.62 91
plant 0.84 0.67 0.74 24
pothole 0.63 0.87 0.73 30
stairs 0.69 0.70 0.69 60
table 0.82 0.42 0.56 33
vehicle 0.67 0.83 0.74 90
accuracy 0.67 432
macro avg 0.68 0.64 0.64 432
weighted avg 0.69 0.67 0.67 432
Model: "sequential_3"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d_5 (Conv2D) │ (None, 62, 62, 32) │ 896 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_5 (MaxPooling2D) │ (None, 31, 31, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_6 (Conv2D) │ (None, 31, 31, 64) │ 18,496 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_6 (MaxPooling2D) │ (None, 15, 15, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten_3 (Flatten) │ (None, 14400) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_6 (Dense) │ (None, 128) │ 1,843,328 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_7 (Dense) │ (None, 10) │ 1,290 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 5,592,032 (21.33 MB)
Trainable params: 1,864,010 (7.11 MB)
Non-trainable params: 0 (0.00 B)
Optimizer params: 3,728,022 (14.22 MB)
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Sur la graph de comparaison des valeurs d'accuracy pour les différents modèles, on remarque que l'ajout de trois couches convolutives améliore légèrement les performances (accuracy) du modèle. Contre-intuitivement, l’ajout d’une quatrième couche entraîne une diminution drastique des performances, celles-ci étant divisées par cinq.
En analysant les courbes de performance (accuracy et loss) du modèle à trois couches convolutives, nous observons des résultats nettement supérieurs par rapport à ceux du modèle initial : l’évolution des courbes de validation (accuracy et loss) suit bien celle des courbes d’entraînement. Nous pouvons donc espérer, avec davantage d'optimisations et d'apprentissage (plus d'époques), converger vers des valeurs de performance à la validation proches de celles obtenues à l'entraînement.
Enfin, la matrice de confusion présente une diagonale légèrement mieux définie. Toutefois, certaines classes, qui n’étaient pas faussement prédites auparavant, le deviennent désormais, comme c’est le cas pour « vehicle » et « stairs ». Cet écart pourrait être dû au fait que les deux modèles n’aient pas été entraînés sur le même nombre d’époques (20 contre 15). Malgré cela, nous retenons cette optimisation et expérimentons avec davantage d’hyperparamètres (taille des noyaux prochainement).
À terme, nous réentraînerons le modèle optimisé et le modèle de base sur un même nombre d’époques afin de les comparer de manière équitable.
Expérimentation - Taille des kernels¶
À partir des résultats de l’expérimentation précédente, nous avons sélectionné le modèle optimal doté de 3 couches convolutives. Nous allons maintenant déterminer la taille de filtres la plus adaptée.
Cette étape vise à équilibrer la capacité d’extraction des caractéristiques et la complexité du modèle, en évaluant l’impact de différentes tailles de filtres sur la performance globale.
Définition du modèle pour différentes tailles des filtres¶
def create_model_with_kernel_size(kernel_size):
model = Sequential([
Conv2D(32, kernel_size, activation='relu', input_shape=(64, 64, 3)),
MaxPooling2D(pool_size=(2, 2)),
Conv2D(64, kernel_size, padding='same', activation='relu'),
MaxPooling2D(pool_size=(2, 2)),
Flatten(),
Dense(128, activation='relu'),
Dense(10, activation='softmax')
])
# Compiler le modèle
model.compile(
optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy']
)
return model
# Tester différentes tailles de kernel
kernel_results = {}
for kernel_size in [(3, 3), (5, 5), (7, 7)]:
print(f"\nEntraînement du modèle avec kernel de taille {kernel_size}")
# Créer et entraîner le modèle
model = create_model_with_kernel_size(kernel_size)
history = model.fit(
train_generator,
validation_data=val_generator,
epochs=15,
verbose=1
)
# Évaluer le modèle
val_loss, val_acc = model.evaluate(val_generator)
# Stocker les résultats
kernel_results[str(kernel_size)] = {
'model': model,
'history': history,
'val_accuracy': val_acc,
'val_loss': val_loss
}
print(f"Modèle avec kernel {kernel_size}: Loss = {val_loss:.4f}, Accuracy = {val_acc:.4f}")
Entraînement du modèle avec kernel de taille (3, 3)
/Users/alaaeddineahriz/.pyenv/versions/3.10.16/lib/python3.10/site-packages/keras/src/layers/convolutional/base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
Epoch 1/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 92ms/step - accuracy: 0.1295 - loss: 2.8854 - val_accuracy: 0.0880 - val_loss: 2.2491 Epoch 2/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 8s 85ms/step - accuracy: 0.1485 - loss: 2.2266 - val_accuracy: 0.2083 - val_loss: 2.2367 Epoch 3/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 8s 85ms/step - accuracy: 0.1354 - loss: 2.2722 - val_accuracy: 0.2222 - val_loss: 2.1132 Epoch 4/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 90ms/step - accuracy: 0.2670 - loss: 2.0985 - val_accuracy: 0.1829 - val_loss: 2.1108 Epoch 5/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 87ms/step - accuracy: 0.3333 - loss: 1.9164 - val_accuracy: 0.2407 - val_loss: 2.0075 Epoch 6/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 9s 88ms/step - accuracy: 0.3337 - loss: 1.8296 - val_accuracy: 0.2269 - val_loss: 1.9670 Epoch 7/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 8s 85ms/step - accuracy: 0.3541 - loss: 1.7418 - val_accuracy: 0.3287 - val_loss: 1.8034 Epoch 8/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 102ms/step - accuracy: 0.4850 - loss: 1.4601 - val_accuracy: 0.3704 - val_loss: 1.7494 Epoch 9/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 112ms/step - accuracy: 0.5613 - loss: 1.3494 - val_accuracy: 0.4977 - val_loss: 1.4983 Epoch 10/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 109ms/step - accuracy: 0.5947 - loss: 1.1855 - val_accuracy: 0.4884 - val_loss: 1.5114 Epoch 11/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 106ms/step - accuracy: 0.6775 - loss: 0.9581 - val_accuracy: 0.5718 - val_loss: 1.3642 Epoch 12/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 106ms/step - accuracy: 0.7056 - loss: 0.8845 - val_accuracy: 0.5718 - val_loss: 1.3076 Epoch 13/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 105ms/step - accuracy: 0.7255 - loss: 0.8172 - val_accuracy: 0.6042 - val_loss: 1.2654 Epoch 14/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 106ms/step - accuracy: 0.8054 - loss: 0.5902 - val_accuracy: 0.6343 - val_loss: 1.1929 Epoch 15/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 10s 104ms/step - accuracy: 0.8340 - loss: 0.5108 - val_accuracy: 0.6505 - val_loss: 1.1308 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 57ms/step - accuracy: 0.5810 - loss: 1.3219 Modèle avec kernel (3, 3): Loss = 1.1308, Accuracy = 0.6505 Entraînement du modèle avec kernel de taille (5, 5) Epoch 1/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 153ms/step - accuracy: 0.1083 - loss: 2.3685 - val_accuracy: 0.0694 - val_loss: 2.2727 Epoch 2/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 160ms/step - accuracy: 0.1390 - loss: 2.2749 - val_accuracy: 0.0694 - val_loss: 2.3044 Epoch 3/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 149ms/step - accuracy: 0.2600 - loss: 2.1548 - val_accuracy: 0.2083 - val_loss: 2.2328 Epoch 4/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 148ms/step - accuracy: 0.2113 - loss: 2.2087 - val_accuracy: 0.0741 - val_loss: 2.2534 Epoch 5/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 149ms/step - accuracy: 0.2418 - loss: 2.2458 - val_accuracy: 0.0718 - val_loss: 2.3265 Epoch 6/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 143ms/step - accuracy: 0.2413 - loss: 2.2437 - val_accuracy: 0.0972 - val_loss: 2.2187 Epoch 7/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 154ms/step - accuracy: 0.2340 - loss: 2.0878 - val_accuracy: 0.1019 - val_loss: 2.3139 Epoch 8/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 159ms/step - accuracy: 0.2432 - loss: 2.1708 - val_accuracy: 0.1829 - val_loss: 2.1323 Epoch 9/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 161ms/step - accuracy: 0.2577 - loss: 2.1066 - val_accuracy: 0.2477 - val_loss: 2.1221 Epoch 10/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 166ms/step - accuracy: 0.3718 - loss: 1.7821 - val_accuracy: 0.2963 - val_loss: 1.9548 Epoch 11/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 155ms/step - accuracy: 0.4369 - loss: 1.6948 - val_accuracy: 0.4144 - val_loss: 1.7439 Epoch 12/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 158ms/step - accuracy: 0.4460 - loss: 1.5325 - val_accuracy: 0.3657 - val_loss: 1.8167 Epoch 13/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 160ms/step - accuracy: 0.4957 - loss: 1.4877 - val_accuracy: 0.5231 - val_loss: 1.5338 Epoch 14/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 158ms/step - accuracy: 0.5784 - loss: 1.2266 - val_accuracy: 0.5023 - val_loss: 1.5285 Epoch 15/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 157ms/step - accuracy: 0.6089 - loss: 1.1273 - val_accuracy: 0.5347 - val_loss: 1.3703 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 67ms/step - accuracy: 0.5467 - loss: 1.3546 Modèle avec kernel (5, 5): Loss = 1.3703, Accuracy = 0.5347 Entraînement du modèle avec kernel de taille (7, 7) Epoch 1/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 26s 251ms/step - accuracy: 0.0911 - loss: 2.4139 - val_accuracy: 0.2130 - val_loss: 2.2822 Epoch 2/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 24s 241ms/step - accuracy: 0.1045 - loss: 2.3952 - val_accuracy: 0.2500 - val_loss: 2.1901 Epoch 3/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 28s 282ms/step - accuracy: 0.1692 - loss: 2.2498 - val_accuracy: 0.0972 - val_loss: 2.2333 Epoch 4/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 27s 271ms/step - accuracy: 0.3157 - loss: 2.1060 - val_accuracy: 0.1620 - val_loss: 2.1730 Epoch 5/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 26s 261ms/step - accuracy: 0.2648 - loss: 2.1396 - val_accuracy: 0.1505 - val_loss: 2.1963 Epoch 6/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 25s 256ms/step - accuracy: 0.1769 - loss: 2.1377 - val_accuracy: 0.1551 - val_loss: 2.1755 Epoch 7/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 24s 243ms/step - accuracy: 0.2669 - loss: 2.1124 - val_accuracy: 0.1481 - val_loss: 2.1465 Epoch 8/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 23s 237ms/step - accuracy: 0.2961 - loss: 2.0015 - val_accuracy: 0.1759 - val_loss: 2.1604 Epoch 9/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 23s 236ms/step - accuracy: 0.3378 - loss: 1.9252 - val_accuracy: 0.2384 - val_loss: 2.0825 Epoch 10/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 23s 235ms/step - accuracy: 0.3096 - loss: 1.9549 - val_accuracy: 0.2384 - val_loss: 2.0561 Epoch 11/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 21s 216ms/step - accuracy: 0.3251 - loss: 1.8843 - val_accuracy: 0.2546 - val_loss: 1.9610 Epoch 12/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 23s 232ms/step - accuracy: 0.3663 - loss: 1.7756 - val_accuracy: 0.2083 - val_loss: 2.0938 Epoch 13/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 23s 236ms/step - accuracy: 0.4344 - loss: 1.6228 - val_accuracy: 0.3426 - val_loss: 1.9144 Epoch 14/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 23s 230ms/step - accuracy: 0.3962 - loss: 1.6566 - val_accuracy: 0.2986 - val_loss: 1.9383 Epoch 15/15 98/98 ━━━━━━━━━━━━━━━━━━━━ 22s 228ms/step - accuracy: 0.5060 - loss: 1.4001 - val_accuracy: 0.3843 - val_loss: 1.8554 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 72ms/step - accuracy: 0.2855 - loss: 2.1278 Modèle avec kernel (7, 7): Loss = 1.8554, Accuracy = 0.3843
Résultats pour différentes tailles de noyaux¶
# Afficher un graphique comparatif
plt.figure(figsize=(10, 6))
kernels = list(kernel_results.keys())
accuracies = [results['val_accuracy'] for results in kernel_results.values()]
plt.bar(kernels, accuracies)
plt.xlabel('Taille des kernels')
plt.ylabel('Accuracy de validation')
plt.title('Impact de la taille des kernels sur la performance')
plt.ylim(0, 1)
for i, v in enumerate(accuracies):
plt.text(i, v + 0.02, f"{v:.4f}", ha='center')
plt.tight_layout()
plt.show()
# Visualiser les courbes d'apprentissage du meilleur modèle
best_kernel_size = max(kernel_results, key=lambda k: kernel_results[k]['val_accuracy'])
print(f"Le meilleur modèle a des kernels de taille {best_kernel_size}")
plot_learning_curves(kernel_results[best_kernel_size]['history'],
f"Courbes d'apprentissage - Modèle avec kernels {best_kernel_size}")
# Évaluation du meilleur modèle
best_kernel_model = kernel_results[best_kernel_size]['model']
evaluate_model(best_kernel_model, val_generator, f"Modèle avec {best_kernel_size} couches")
# Résumé du modèle
best_kernel_model.summary()
best_kernel_model.save("best_kernel_model.h5")
Le meilleur modèle a des kernels de taille (3, 3)
14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 54ms/step
Rapport de classification - Modèle avec (3, 3) couches
precision recall f1-score support
chair 0.47 0.33 0.39 24
door 0.64 0.67 0.65 51
fence 0.56 0.42 0.48 12
garbage_bin 0.48 0.82 0.61 17
obstacle 0.78 0.49 0.60 91
plant 0.74 0.71 0.72 24
pothole 0.59 0.77 0.67 30
stairs 0.65 0.75 0.70 60
table 0.64 0.70 0.67 33
vehicle 0.68 0.74 0.71 90
accuracy 0.65 432
macro avg 0.62 0.64 0.62 432
weighted avg 0.66 0.65 0.65 432
Model: "sequential_7"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d_19 (Conv2D) │ (None, 62, 62, 32) │ 896 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_19 (MaxPooling2D) │ (None, 31, 31, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_20 (Conv2D) │ (None, 31, 31, 64) │ 18,496 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_20 (MaxPooling2D) │ (None, 15, 15, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten_7 (Flatten) │ (None, 14400) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_14 (Dense) │ (None, 128) │ 1,843,328 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_15 (Dense) │ (None, 10) │ 1,290 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 5,592,032 (21.33 MB)
Trainable params: 1,864,010 (7.11 MB)
Non-trainable params: 0 (0.00 B)
Optimizer params: 3,728,022 (14.22 MB)
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Le noyau de taille (3,3) offre des performances nettement supérieures à celles des noyaux de tailles (5,5) et (7,7), atteignant jusqu’à deux fois plus de précision. Les courbes de performance obtenues sont sensiblement similaires à celles issues de l’optimisation du nombre de couches convolutives, ce qui est cohérent puisque les hyper-paramètres (taille du noyau et nombre de couches) restent identiques.
La diagonale de la matrice de confusion est cependant légèrement moins bien définie. On observe une inversion inhabituelle des mauvaises prédictions entre les classes « stairs » et « vehicle ». Alors qu’auparavant le modèle confondait les escaliers avec les véhicules, il tend maintenant à classer les véhicules comme des escaliers. Étrange !
Expérimentation - Learning Rate¶
Nous étudions ici l’impact du taux d’apprentissage sur les performances du modèle en conservant la meilleure architecture identifiée jusqu’à présent. Le même modèle est entraîné et comparé pour différents taux d’apprentissage, en maintenant ce paramètre fixe durant l’ensemble de l’apprentissage. Il serait pertinent d’examiner ultérieurement les résultats obtenus avec un taux d’apprentissage dynamique.
Définition du modèle pour différents learning rates statiques¶
def create_model_lr(learning_rate):
model = Sequential([
Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(64, 64, 3)),
MaxPooling2D(pool_size=(2, 2)),
Conv2D(64, (3, 3), padding='same', activation='relu'),
MaxPooling2D(pool_size=(2, 2)),
Flatten(),
Dense(128, activation='relu'),
Dense(10, activation='softmax')
])
# Compiler avec le learning rate spécifié
optimizer = Adam(learning_rate=learning_rate)
model.compile(
optimizer=optimizer,
loss='categorical_crossentropy',
metrics=['accuracy']
)
return model
# Définir une fonction pour obtenir un early stopping callback
def get_early_stopping():
return EarlyStopping(
monitor='val_loss',
patience=5,
restore_best_weights=True,
verbose=1
)
# Tester différents learning rates (approche statique)
def test_learning_rates():
learning_rates = [0.0001, 0.0005, 0.001, 0.005, 0.01]
results = {}
for lr in learning_rates:
print(f"\n===== Test avec learning rate = {lr} =====")
model = create_model_lr(learning_rate=lr)
# Early stopping pour éviter l'overfitting
early_stopping = get_early_stopping()
# Entraîner le modèle
history = model.fit(
train_generator,
validation_data=val_generator,
epochs=20, # Nombre d'époques maximal
callbacks=[early_stopping],
verbose=1
)
# Évaluer le modèle
val_loss, val_acc = model.evaluate(val_generator)
# Stocker les résultats
results[lr] = {
'model': model,
'history': history,
'val_accuracy': val_acc,
'val_loss': val_loss,
'epochs_trained': len(history.history['loss'])
}
print(f"Learning rate {lr}: Loss = {val_loss:.4f}, Accuracy = {val_acc:.4f}, Epochs: {len(history.history['loss'])}")
return results
# Exécuter les tests
lr_results = test_learning_rates()
# Loop through each learning rate result and save the corresponding model
for lr, res in lr_results.items():
filename = f"model_lr_{lr}.h5"
res['model'].save(filename)
print(f"Model with learning rate {lr} saved as {filename}.")
===== Test avec learning rate = 0.0001 =====
/Users/alaaeddineahriz/.pyenv/versions/3.10.16/lib/python3.10/site-packages/keras/src/layers/convolutional/base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
Epoch 1/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 17s 151ms/step - accuracy: 0.1704 - loss: 3.3230 - val_accuracy: 0.1620 - val_loss: 2.7824 Epoch 2/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 150ms/step - accuracy: 0.4374 - loss: 1.6767 - val_accuracy: 0.2037 - val_loss: 3.4913 Epoch 3/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 143ms/step - accuracy: 0.6269 - loss: 1.1975 - val_accuracy: 0.2523 - val_loss: 3.5326 Epoch 4/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 141ms/step - accuracy: 0.6938 - loss: 0.9259 - val_accuracy: 0.3356 - val_loss: 2.4516 Epoch 5/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 136ms/step - accuracy: 0.8343 - loss: 0.5627 - val_accuracy: 0.4537 - val_loss: 1.9238 Epoch 6/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.8662 - loss: 0.4460 - val_accuracy: 0.5208 - val_loss: 1.5259 Epoch 7/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.9640 - loss: 0.1966 - val_accuracy: 0.5000 - val_loss: 1.5864 Epoch 8/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 133ms/step - accuracy: 0.9703 - loss: 0.1535 - val_accuracy: 0.5787 - val_loss: 1.4588 Epoch 9/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.9974 - loss: 0.0644 - val_accuracy: 0.5602 - val_loss: 1.5156 Epoch 10/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 11s 107ms/step - accuracy: 1.0000 - loss: 0.0419 - val_accuracy: 0.5718 - val_loss: 1.4630 Epoch 11/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 127ms/step - accuracy: 1.0000 - loss: 0.0237 - val_accuracy: 0.6065 - val_loss: 1.4172 Epoch 12/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 130ms/step - accuracy: 1.0000 - loss: 0.0197 - val_accuracy: 0.6227 - val_loss: 1.4499 Epoch 13/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 126ms/step - accuracy: 1.0000 - loss: 0.0126 - val_accuracy: 0.6157 - val_loss: 1.4965 Epoch 14/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 121ms/step - accuracy: 1.0000 - loss: 0.0107 - val_accuracy: 0.6088 - val_loss: 1.5076 Epoch 15/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 127ms/step - accuracy: 1.0000 - loss: 0.0092 - val_accuracy: 0.6065 - val_loss: 1.4499 Epoch 16/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 128ms/step - accuracy: 1.0000 - loss: 0.0077 - val_accuracy: 0.6157 - val_loss: 1.5004 Epoch 16: early stopping Restoring model weights from the end of the best epoch: 11. 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 53ms/step - accuracy: 0.5733 - loss: 1.4518 Learning rate 0.0001: Loss = 1.4172, Accuracy = 0.6065, Epochs: 16 ===== Test avec learning rate = 0.0005 ===== Epoch 1/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 128ms/step - accuracy: 0.1134 - loss: 6.4255 - val_accuracy: 0.1181 - val_loss: 9.4316 Epoch 2/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 124ms/step - accuracy: 0.3027 - loss: 2.0765 - val_accuracy: 0.1273 - val_loss: 11.4659 Epoch 3/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 125ms/step - accuracy: 0.3884 - loss: 1.8369 - val_accuracy: 0.1620 - val_loss: 11.6740 Epoch 4/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 127ms/step - accuracy: 0.4696 - loss: 1.5878 - val_accuracy: 0.2153 - val_loss: 5.6966 Epoch 5/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 130ms/step - accuracy: 0.5516 - loss: 1.3455 - val_accuracy: 0.3148 - val_loss: 2.6742 Epoch 6/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 129ms/step - accuracy: 0.6494 - loss: 0.9692 - val_accuracy: 0.4167 - val_loss: 1.8378 Epoch 7/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 120ms/step - accuracy: 0.7389 - loss: 0.7430 - val_accuracy: 0.4491 - val_loss: 1.9165 Epoch 8/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 131ms/step - accuracy: 0.8398 - loss: 0.4609 - val_accuracy: 0.5069 - val_loss: 1.7431 Epoch 9/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.9139 - loss: 0.2490 - val_accuracy: 0.5417 - val_loss: 1.8147 Epoch 10/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 139ms/step - accuracy: 0.9693 - loss: 0.1229 - val_accuracy: 0.5787 - val_loss: 1.6374 Epoch 11/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 20s 134ms/step - accuracy: 0.9888 - loss: 0.0567 - val_accuracy: 0.5556 - val_loss: 2.0349 Epoch 12/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 139ms/step - accuracy: 0.9922 - loss: 0.0379 - val_accuracy: 0.5741 - val_loss: 1.7004 Epoch 13/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 142ms/step - accuracy: 0.9954 - loss: 0.0199 - val_accuracy: 0.5579 - val_loss: 1.8122 Epoch 14/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 136ms/step - accuracy: 0.9970 - loss: 0.0151 - val_accuracy: 0.5532 - val_loss: 1.8933 Epoch 15/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 137ms/step - accuracy: 0.9998 - loss: 0.0072 - val_accuracy: 0.5509 - val_loss: 1.9329 Epoch 15: early stopping Restoring model weights from the end of the best epoch: 10. 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 67ms/step - accuracy: 0.5806 - loss: 1.5877 Learning rate 0.0005: Loss = 1.6374, Accuracy = 0.5787, Epochs: 15 ===== Test avec learning rate = 0.001 ===== Epoch 1/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 138ms/step - accuracy: 0.1156 - loss: 10.9948 - val_accuracy: 0.2014 - val_loss: 6.5068 Epoch 2/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 134ms/step - accuracy: 0.2777 - loss: 2.0430 - val_accuracy: 0.1389 - val_loss: 16.5722 Epoch 3/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.3464 - loss: 1.8431 - val_accuracy: 0.1829 - val_loss: 15.6984 Epoch 4/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 134ms/step - accuracy: 0.4587 - loss: 1.5847 - val_accuracy: 0.2407 - val_loss: 7.0959 Epoch 5/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 134ms/step - accuracy: 0.5158 - loss: 1.4822 - val_accuracy: 0.2407 - val_loss: 3.6278 Epoch 6/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 140ms/step - accuracy: 0.5900 - loss: 1.2306 - val_accuracy: 0.3032 - val_loss: 2.4170 Epoch 7/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 134ms/step - accuracy: 0.5912 - loss: 1.1531 - val_accuracy: 0.1875 - val_loss: 3.6372 Epoch 8/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 12s 126ms/step - accuracy: 0.6593 - loss: 0.9371 - val_accuracy: 0.3356 - val_loss: 2.4382 Epoch 9/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 131ms/step - accuracy: 0.7741 - loss: 0.6766 - val_accuracy: 0.4444 - val_loss: 1.8359 Epoch 10/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 137ms/step - accuracy: 0.8294 - loss: 0.4678 - val_accuracy: 0.5069 - val_loss: 1.8505 Epoch 11/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 139ms/step - accuracy: 0.8620 - loss: 0.3643 - val_accuracy: 0.4560 - val_loss: 2.0720 Epoch 12/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 138ms/step - accuracy: 0.9369 - loss: 0.1982 - val_accuracy: 0.4838 - val_loss: 2.7952 Epoch 13/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.9562 - loss: 0.1297 - val_accuracy: 0.4861 - val_loss: 2.6397 Epoch 14/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 132ms/step - accuracy: 0.9779 - loss: 0.0712 - val_accuracy: 0.5532 - val_loss: 2.3878 Epoch 14: early stopping Restoring model weights from the end of the best epoch: 9. 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 63ms/step - accuracy: 0.4451 - loss: 1.7743 Learning rate 0.001: Loss = 1.8359, Accuracy = 0.4444, Epochs: 14 ===== Test avec learning rate = 0.005 ===== Epoch 1/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 147ms/step - accuracy: 0.1460 - loss: 43.6402 - val_accuracy: 0.1227 - val_loss: 3.5458 Epoch 2/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 131ms/step - accuracy: 0.1712 - loss: 2.2769 - val_accuracy: 0.1273 - val_loss: 7.6646 Epoch 3/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.2649 - loss: 2.2302 - val_accuracy: 0.1088 - val_loss: 4.7536 Epoch 4/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 136ms/step - accuracy: 0.2449 - loss: 2.1543 - val_accuracy: 0.0810 - val_loss: 2.6122 Epoch 5/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 140ms/step - accuracy: 0.1708 - loss: 2.1814 - val_accuracy: 0.0741 - val_loss: 2.2628 Epoch 6/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 137ms/step - accuracy: 0.1695 - loss: 2.1722 - val_accuracy: 0.0694 - val_loss: 2.2657 Epoch 7/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.2178 - loss: 2.1839 - val_accuracy: 0.0694 - val_loss: 2.2693 Epoch 8/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 136ms/step - accuracy: 0.2239 - loss: 2.1460 - val_accuracy: 0.0694 - val_loss: 2.2697 Epoch 9/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.2413 - loss: 2.1332 - val_accuracy: 0.0718 - val_loss: 2.2725 Epoch 10/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 133ms/step - accuracy: 0.1970 - loss: 2.1340 - val_accuracy: 0.0694 - val_loss: 2.2741 Epoch 10: early stopping Restoring model weights from the end of the best epoch: 5. 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 62ms/step - accuracy: 0.0538 - loss: 2.3350 Learning rate 0.005: Loss = 2.2628, Accuracy = 0.0741, Epochs: 10 ===== Test avec learning rate = 0.01 ===== Epoch 1/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 135ms/step - accuracy: 0.1496 - loss: 78.3532 - val_accuracy: 0.0694 - val_loss: 2.2682 Epoch 2/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 140ms/step - accuracy: 0.1330 - loss: 2.2308 - val_accuracy: 0.0856 - val_loss: 2.2430 Epoch 3/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 140ms/step - accuracy: 0.2035 - loss: 2.1699 - val_accuracy: 0.0741 - val_loss: 2.2482 Epoch 4/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 142ms/step - accuracy: 0.2424 - loss: 2.1507 - val_accuracy: 0.0718 - val_loss: 2.2562 Epoch 5/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 137ms/step - accuracy: 0.2883 - loss: 2.1112 - val_accuracy: 0.0694 - val_loss: 2.2664 Epoch 6/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 135ms/step - accuracy: 0.2442 - loss: 2.1126 - val_accuracy: 0.0694 - val_loss: 2.2749 Epoch 7/20 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 131ms/step - accuracy: 0.2026 - loss: 2.1062 - val_accuracy: 0.0718 - val_loss: 2.2774 Epoch 7: early stopping Restoring model weights from the end of the best epoch: 2. 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 66ms/step - accuracy: 0.0594 - loss: 2.3115
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Learning rate 0.01: Loss = 2.2430, Accuracy = 0.0856, Epochs: 7
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Model with learning rate 0.0001 saved as model_lr_0.0001.h5. Model with learning rate 0.0005 saved as model_lr_0.0005.h5.
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Model with learning rate 0.001 saved as model_lr_0.001.h5.
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Model with learning rate 0.005 saved as model_lr_0.005.h5. Model with learning rate 0.01 saved as model_lr_0.01.h5.
Résultats pour les différents learning rates statiques¶
# Afficher un graphique comparatif
plt.figure(figsize=(10, 6))
lrs = list(lr_results.keys())
accuracies = [results['val_accuracy'] for results in lr_results.values()]
plt.bar([str(lr) for lr in lrs], accuracies)
plt.xlabel('Learning Rate')
plt.ylabel('Accuracy de validation')
plt.title('Impact du learning rate sur la performance')
plt.ylim(0, 1)
for i, v in enumerate(accuracies):
plt.text(i, v + 0.02, f"{v:.4f}", ha='center')
plt.tight_layout()
plt.show()
# Visualiser l'évolution de l'accuracy et de la loss de validatoin en fonction du learning rate
plot_learning_rate_comparison(lr_results)
# Visualiser les courbes d'apprentissage du meilleur modèle
best_lr = max(lr_results, key=lambda k: lr_results[k]['val_accuracy'])
print(f"Le meilleur modèle a un learning rate de {best_lr}")
plot_learning_curves(lr_results[best_lr]['history'],
f"Courbes d'apprentissage - Modèle avec learning rate {best_lr}")
# Évaluation du meilleur modèle
best_lr_model = lr_results[best_lr]['model']
evaluate_model(best_lr_model, val_generator, f"Modèle avec {best_lr} couches")
# Résumé et sauvegarde du modèle
best_lr_model.summary()
best_lr_model.save("best_lr_model.h5")
Meilleur learning rate: 0.0001 Meilleure accuracy: 0.6065 Meilleure loss: 1.4172 Le meilleur modèle a un learning rate de 0.0001
14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 63ms/step
Rapport de classification - Modèle avec 0.0001 couches
precision recall f1-score support
chair 0.28 0.54 0.37 24
door 0.74 0.55 0.63 51
fence 0.50 0.42 0.45 12
garbage_bin 0.58 0.65 0.61 17
obstacle 0.77 0.55 0.64 91
plant 0.76 0.79 0.78 24
pothole 0.53 0.57 0.55 30
stairs 0.49 0.65 0.56 60
table 0.70 0.58 0.63 33
vehicle 0.67 0.68 0.67 90
accuracy 0.61 432
macro avg 0.60 0.60 0.59 432
weighted avg 0.64 0.61 0.61 432
Model: "sequential_10"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d_25 (Conv2D) │ (None, 64, 64, 32) │ 896 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization │ (None, 64, 64, 32) │ 128 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_25 (MaxPooling2D) │ (None, 32, 32, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_26 (Conv2D) │ (None, 32, 32, 64) │ 18,496 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization_1 │ (None, 32, 32, 64) │ 256 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_26 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten_10 (Flatten) │ (None, 16384) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_20 (Dense) │ (None, 128) │ 2,097,280 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_21 (Dense) │ (None, 10) │ 1,290 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 6,354,656 (24.24 MB)
Trainable params: 2,118,154 (8.08 MB)
Non-trainable params: 192 (768.00 B)
Optimizer params: 4,236,310 (16.16 MB)
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Nous remarquons ici que le learning rate influence directement les performances du modèle. Pour des valeurs faibles, comprises entre $10^{-3}$ et $10^{-4}$, l’accuracy se stabilise autour de 58 %. En revanche, en augmentant le taux d’apprentissage à $5 \times 10^{-3}$, la précision chute drastiquement (divisée par plus de 5) et la loss s’envole. Ce constat indique qu’un learning rate trop élevé perturbe le processus d’optimisation, empêchant le modèle de converger correctement et menant à une divergence des poids.
Pour un learning rate de $5 \times 10^{-4}$, la matrice de confusion est nettement améliorée, avec une diagonale mieux définie et une reconnaissance globale plus satisfaisante des classes. Cependant, certaines classes restent problématiques (valeurs non négligeables écartés de la diagonale). Le rapport de classification révèle que la classe « chair » présente une faible précision (0,24) malgré un recall élevé (0,67), signe d’une confusion marquée avec d’autres classes. De même, la classe « stairs » affiche une précision de 0,45 et un recall de 0,63, tandis que la classe « obstacle » se distingue par une précision de 0,69 et un recall de 0,52. La classe « vehicle », bien que mieux reconnue avec une précision de 0,79 et un recall de 0,62, ne parvient pas à compenser les difficultés observées pour les autres catégories.
Ainsi, bien que le learning rate de $5 \times 10^{-4}$ améliore globalement la performance du modèle en termes de clarté de la matrice de confusion, il subsiste des axes d’amélioration pour optimiser la reconnaissance des classes les plus problématiques.
Enfin, étant donné que les performances obtenues pour les trois premiers learning rates testés sont suffisamment proches, nous optons pour le plus élevé ($10^{-3}$). Ce choix permettra de gagner en temps de calcul. Exceptionnellement, et merci Kaggle GPUs, nous relançons un entraînement sur un nombre d’époques supérieur en utilisant la meilleure architecture retenue jusqu’ici et le learning rate ayant fourni les meilleurs résultats.
Modèle final¶
# Entraîner un modèle final avec le meilleur learning rate et plus d'époques
best_lr = 0.0001
def train_final_model(best_lr):
print(f"\n===== Entraînement du modèle final avec learning rate = {best_lr} =====")
# Créer le modèle avec le meilleur learning rate
final_model = create_model_lr(learning_rate=best_lr)
# Early stopping
# early_stopping = get_early_stopping()
# Entraîner le modèle
final_history = final_model.fit(
train_generator,
validation_data=val_generator,
epochs=30, # Plus d'époques pour le modèle final
# callbacks=[early_stopping],
verbose=1
)
# Évaluer le modèle
val_loss, val_acc = final_model.evaluate(val_generator)
print(f"Modèle final: Loss = {val_loss:.4f}, Accuracy = {val_acc:.4f}")
return final_model, final_history
# Entraîner le modèle final avec le meilleur learning rate
final_model, final_history = train_final_model(best_lr)
===== Entraînement du modèle final avec learning rate = 0.0001 =====
/Users/alaaeddineahriz/.pyenv/versions/3.10.16/lib/python3.10/site-packages/keras/src/layers/convolutional/base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
Epoch 1/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 135ms/step - accuracy: 0.1943 - loss: 3.1343 - val_accuracy: 0.2755 - val_loss: 2.1893 Epoch 2/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 18s 182ms/step - accuracy: 0.4916 - loss: 1.4550 - val_accuracy: 0.2361 - val_loss: 2.6681 Epoch 3/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 159ms/step - accuracy: 0.6360 - loss: 1.1078 - val_accuracy: 0.2616 - val_loss: 2.5709 Epoch 4/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 160ms/step - accuracy: 0.7308 - loss: 0.8291 - val_accuracy: 0.3542 - val_loss: 2.0962 Epoch 5/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 158ms/step - accuracy: 0.8397 - loss: 0.5081 - val_accuracy: 0.4537 - val_loss: 1.7081 Epoch 6/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 159ms/step - accuracy: 0.9254 - loss: 0.3189 - val_accuracy: 0.4769 - val_loss: 1.5919 Epoch 7/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 18s 186ms/step - accuracy: 0.9655 - loss: 0.1984 - val_accuracy: 0.5301 - val_loss: 1.4882 Epoch 8/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 166ms/step - accuracy: 0.9915 - loss: 0.0968 - val_accuracy: 0.5301 - val_loss: 1.4596 Epoch 9/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 161ms/step - accuracy: 0.9991 - loss: 0.0496 - val_accuracy: 0.5463 - val_loss: 1.4659 Epoch 10/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 158ms/step - accuracy: 0.9982 - loss: 0.0367 - val_accuracy: 0.5556 - val_loss: 1.4896 Epoch 11/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 158ms/step - accuracy: 0.9995 - loss: 0.0245 - val_accuracy: 0.5671 - val_loss: 1.4452 Epoch 12/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 14s 139ms/step - accuracy: 1.0000 - loss: 0.0181 - val_accuracy: 0.5648 - val_loss: 1.5006 Epoch 13/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 153ms/step - accuracy: 1.0000 - loss: 0.0140 - val_accuracy: 0.5671 - val_loss: 1.4611 Epoch 14/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 164ms/step - accuracy: 1.0000 - loss: 0.0112 - val_accuracy: 0.5625 - val_loss: 1.5361 Epoch 15/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 167ms/step - accuracy: 1.0000 - loss: 0.0105 - val_accuracy: 0.5718 - val_loss: 1.5250 Epoch 16/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 17s 169ms/step - accuracy: 1.0000 - loss: 0.0084 - val_accuracy: 0.5694 - val_loss: 1.5453 Epoch 17/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 17s 173ms/step - accuracy: 1.0000 - loss: 0.0071 - val_accuracy: 0.5787 - val_loss: 1.5312 Epoch 18/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 161ms/step - accuracy: 1.0000 - loss: 0.0062 - val_accuracy: 0.5741 - val_loss: 1.5238 Epoch 19/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 160ms/step - accuracy: 1.0000 - loss: 0.0056 - val_accuracy: 0.5741 - val_loss: 1.5890 Epoch 20/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 155ms/step - accuracy: 1.0000 - loss: 0.0048 - val_accuracy: 0.5741 - val_loss: 1.5478 Epoch 21/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 156ms/step - accuracy: 1.0000 - loss: 0.0043 - val_accuracy: 0.5648 - val_loss: 1.5676 Epoch 22/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 153ms/step - accuracy: 1.0000 - loss: 0.0042 - val_accuracy: 0.5949 - val_loss: 1.5157 Epoch 23/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 149ms/step - accuracy: 1.0000 - loss: 0.0034 - val_accuracy: 0.5787 - val_loss: 1.5830 Epoch 24/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 13s 131ms/step - accuracy: 1.0000 - loss: 0.0033 - val_accuracy: 0.5694 - val_loss: 1.5954 Epoch 25/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 167ms/step - accuracy: 1.0000 - loss: 0.0029 - val_accuracy: 0.5787 - val_loss: 1.5812 Epoch 26/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 164ms/step - accuracy: 1.0000 - loss: 0.0026 - val_accuracy: 0.5741 - val_loss: 1.5884 Epoch 27/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 16s 161ms/step - accuracy: 1.0000 - loss: 0.0023 - val_accuracy: 0.5903 - val_loss: 1.5799 Epoch 28/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 153ms/step - accuracy: 1.0000 - loss: 0.0022 - val_accuracy: 0.5741 - val_loss: 1.6256 Epoch 29/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 156ms/step - accuracy: 1.0000 - loss: 0.0021 - val_accuracy: 0.5833 - val_loss: 1.6339 Epoch 30/30 98/98 ━━━━━━━━━━━━━━━━━━━━ 15s 156ms/step - accuracy: 1.0000 - loss: 0.0018 - val_accuracy: 0.5856 - val_loss: 1.5907 14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 82ms/step - accuracy: 0.5715 - loss: 1.6253 Modèle final: Loss = 1.5907, Accuracy = 0.5856
# Visualiser les courbes d'apprentissage du meilleur modèle
plot_learning_curves(final_history,
f"Courbes d'apprentissage du meilleur modèle")
# Évaluation du meilleur modèle
evaluate_model(final_model, val_generator, f"Évaluation du meilleur modèle")
# Résumé et sauvegarde du modèle
final_model.summary()
final_model.save("final_model.h5")
14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 81ms/step
Rapport de classification - Évaluation du meilleur modèle
precision recall f1-score support
chair 0.30 0.67 0.42 24
door 0.76 0.55 0.64 51
fence 0.50 0.58 0.54 12
garbage_bin 0.86 0.71 0.77 17
obstacle 0.74 0.46 0.57 91
plant 0.72 0.75 0.73 24
pothole 0.55 0.57 0.56 30
stairs 0.45 0.63 0.53 60
table 0.81 0.39 0.53 33
vehicle 0.61 0.69 0.65 90
accuracy 0.59 432
macro avg 0.63 0.60 0.59 432
weighted avg 0.64 0.59 0.59 432
Model: "sequential_15"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d_35 (Conv2D) │ (None, 64, 64, 32) │ 896 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization_10 │ (None, 64, 64, 32) │ 128 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_35 (MaxPooling2D) │ (None, 32, 32, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_36 (Conv2D) │ (None, 32, 32, 64) │ 18,496 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization_11 │ (None, 32, 32, 64) │ 256 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_36 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten_15 (Flatten) │ (None, 16384) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_30 (Dense) │ (None, 128) │ 2,097,280 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_31 (Dense) │ (None, 10) │ 1,290 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 6,354,656 (24.24 MB)
Trainable params: 2,118,154 (8.08 MB)
Non-trainable params: 192 (768.00 B)
Optimizer params: 4,236,310 (16.16 MB)
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
Après avoir entraîné notre modèle avec les meilleurs paramètres identifiés jusqu’à présent (3 couches convolutives, noyaux de taille (3,3) et learning rate à 0.0005), nous analysons les résultats obtenus.
Les courbes d’accuracy et de loss révèlent un écart marqué entre l’entraînement et la validation. L’accuracy sur l’ensemble d’entraînement atteint presque 100 % alors que celle de validation se stabilise autour de 58–62 %. Parallèlement, la loss d’entraînement chute presque à zéro tandis que la loss de validation demeure élevée (entre 1.5 et 1.7). Ce comportement est caractéristique d’un surapprentissage. Ce comportement suggère un surapprentissage : le modèle mémorise presque parfaitement les données d’entraînement mais généralise moins bien aux données de validation. Pour y remédier, nous pourrons mettre en place des mécanismes de normalisation par batch ou en ajouter des couches de dropout.
Par ailleurs, nous pouvons intégrer un mécanisme d’early stopping afin de limiter le nombre d’époques dès lors que la performance du modèle cesse de s'améliorer. Nous y gagnerions de précieuses minutes (voire heures) de calculs non nécessaires.
En analysant les performances par classe, nous notons des disparités importantes. La matrice de confusion et le rapport de classification montrent une bonne reconnaissance des classes telles que « vehicle » (recall de 69 %) et « garbage_bin » (recall de 71 %), tandis que des classes comme « table » (39 % de recall) et « obstacle » (46 % de recall) sont moins bien détectées. La classe « chair » présente une précision particulièrement faible (0.30), indiquant de fréquentes erreurs de prédiction. Par ailleurs, le modèle confond régulièrement certaines classes, « obstacle » étant parfois interprété à tort comme « vehicle » (17 cas) ou « stairs » (16 cas), et « door » est souvent confondu avec « stairs » (8 cas).
Le rapport de classification met aussi en lumière un déséquilibre marqué dans la répartition des données tel que remarqué au début. Les classes majoritaires, telles que « obstacle » (91 échantillons) et « vehicle » (90), dominent l’ensemble de données, alors que les classes minoritaires, comme « fence » (12 échantillons) et « garbage_bin » (17 échantillons), sont sous-représentées. Ce déséquilibre contribue aux performances inégales et accentue les difficultés de généralisation de notre modèle.
Enfin, l’architecture est très complexe. Le résumé du modèle indique que notre couche dense comportant plus de 2 millions de paramètres. Ce grand nombre de paramètre pourrait facilier la mémorisation des données d’entraînement, renforcer le surapprentissage et expliquer nos résultats.
En somme, l’écart significatif entre les performances (loss et accuracy) d’entraînement et de validation, les disparités de reconnaissance entre classes, les confusions récurrentes, le déséquilibre des données et la complexité (peut être exessive) du modèle expliquent l'accuracy globale limitée (environ 59 %). Des ajustements, tant sur le plan de la régularisation que sur celui de la conception du modèle (architecture) et du rééquilibrage des classes, peuvent s'avérer utiles pour améliorer les capacités de généralisation. Nous explorerons dans la suite certaines de ses optimisations.
Analyse visuelles des erreurs de prédiction¶
def analyze_errors(model, generator):
# Réinitialiser le générateur
generator.reset()
# Collecter toutes les images et prédictions
all_images = []
all_true_labels = []
batch_count = 0
max_batches = len(generator)
for i in range(max_batches):
batch_images, batch_labels = next(generator)
if i == 0: # Garder le premier batch pour l'analyse
all_images = batch_images
all_true_labels = batch_labels
batch_count += 1
# Prédictions du modèle
predictions = model.predict(all_images)
predicted_classes = np.argmax(predictions, axis=1)
true_classes = np.argmax(all_true_labels, axis=1)
# Identifier les erreurs
errors = np.where(predicted_classes != true_classes)[0]
# Afficher quelques exemples d'erreurs
plt.figure(figsize=(15, 10))
n_errors = min(10, len(errors))
class_names = list(generator.class_indices.keys())
for i in range(n_errors):
idx = errors[i]
plt.subplot(2, 5, i+1)
plt.imshow(all_images[idx])
plt.title(f"Réel: {class_names[true_classes[idx]]}\nPrédit: {class_names[predicted_classes[idx]]}")
plt.axis('off')
plt.tight_layout()
plt.suptitle('Analyse des erreurs de classification')
plt.subplots_adjust(top=0.9)
plt.show()
# Afficher des statistiques sur les erreurs
print(f"Nombre total d'images analysées: {len(all_images)}")
print(f"Nombre d'erreurs: {len(errors)} ({len(errors)/len(all_images)*100:.2f}%)")
# Analyser les erreurs du modèle optimisé
analyze_errors(final_model, val_generator)
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 220ms/step
Nombre total d'images analysées: 32 Nombre d'erreurs: 11 (34.38%)
Exploration de nouvelles optimisations¶
Dans cette section, le TP étant terminé, nous explorons des optimisations supplémentaires afin d’affiner davantage les performances du modèle. Nous conservons l’utilisation de filtres de taille (3, 3) et ajoutons une troisième couche convolutive pour enrichir l’extraction des caractéristiques.
Pour essayer de limiter le surapprentissage, nous ajoutons des couches de dropout après chaque couche convolutive. Cela devrait, théoriquement, améliorer la capacité de généralisation du modèle en atténuant le risque d’apprentissage de patterns spécifiques.
Par ailleurs, nous réduisons davantage le learning rate et optons pour un learning rate dynamique. Cette approche, en adaptant le taux d’apprentissage au cours de la formation, devrait permettre d’équilibrer la vitesse de convergence et la stabilité de l’entraînement.
Voyons voir ce que ça donne.
def new_opt_model():
model = Sequential([
# Premier bloc convolutional - augmenter les filtres
Conv2D(64, (3, 3), activation='relu', padding='same', input_shape=(64, 64, 3)),
Conv2D(64, (3, 3), activation='relu', padding='same'), # Couche supplémentaire
MaxPooling2D(pool_size=(2, 2)),
Dropout(0.25),
# Deuxième bloc convolutional - plus profond
Conv2D(128, (3, 3), padding='same', activation='relu'),
Conv2D(128, (3, 3), padding='same', activation='relu'), # Couche supplémentaire
MaxPooling2D(pool_size=(2, 2)),
Dropout(0.3),
# Troisième bloc convolutional - réactivé et amélioré
Conv2D(256, (3, 3), padding='same', activation='relu'),
MaxPooling2D(pool_size=(2, 2)),
Dropout(0.4),
# Partie fully connected - plus de capacité
Flatten(),
Dense(128, activation='relu', kernel_regularizer=l2(0.001)), # Plus grand, avec régularisation
Dropout(0.5),
Dense(10, activation='softmax')
])
# Compiler avec un learning rate encore plus faible
optimizer = Adam(learning_rate=0.00005) # Réduit de 0.0001 à 0.00005
model.compile(
optimizer=optimizer,
loss='categorical_crossentropy',
metrics=['accuracy']
)
return model
def train_new_optimized_model():
print("\n===== Entraînement du modèle optimisé v2 =====")
# Créer le modèle optimisé
opt_model = new_opt_model()
# Ajouter des callbacks
early_stopping = EarlyStopping(
monitor='val_accuracy', # Surveiller l'accuracy au lieu de la loss
patience=10,
restore_best_weights=True,
verbose=1
)
reduce_lr = ReduceLROnPlateau(
monitor='val_loss',
factor=0.2,
patience=5,
min_lr=1e-6,
verbose=1
)
# Entraîner le modèle avec plus d'époques
opt_history = opt_model.fit(
train_generator,
validation_data=val_generator,
epochs=50, # Plus d'époques avec early stopping
callbacks=[early_stopping, reduce_lr],
verbose=1
)
# Évaluer le modèle
val_loss, val_acc = opt_model.evaluate(val_generator)
print(f"Modèle optimisé v2: Loss = {val_loss:.4f}, Accuracy = {val_acc:.4f}")
return opt_model, opt_history
opt_mode, opt_history = train_new_optimized_model()
===== Entraînement du modèle optimisé v2 ===== Epoch 1/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 32s 318ms/step - accuracy: 0.1426 - loss: 2.5540 - val_accuracy: 0.0694 - val_loss: 2.5137 - learning_rate: 5.0000e-05 Epoch 2/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 34s 342ms/step - accuracy: 0.2861 - loss: 2.3918 - val_accuracy: 0.0694 - val_loss: 2.4881 - learning_rate: 5.0000e-05 Epoch 3/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 38s 388ms/step - accuracy: 0.1850 - loss: 2.4100 - val_accuracy: 0.0694 - val_loss: 2.4675 - learning_rate: 5.0000e-05 Epoch 4/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 52s 534ms/step - accuracy: 0.1520 - loss: 2.4290 - val_accuracy: 0.0694 - val_loss: 2.4443 - learning_rate: 5.0000e-05 Epoch 5/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 49s 499ms/step - accuracy: 0.2617 - loss: 2.3113 - val_accuracy: 0.0694 - val_loss: 2.4343 - learning_rate: 5.0000e-05 Epoch 6/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 47s 481ms/step - accuracy: 0.2344 - loss: 2.3572 - val_accuracy: 0.0694 - val_loss: 2.4126 - learning_rate: 5.0000e-05 Epoch 7/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 49s 497ms/step - accuracy: 0.1872 - loss: 2.3277 - val_accuracy: 0.0694 - val_loss: 2.4102 - learning_rate: 5.0000e-05 Epoch 8/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 47s 484ms/step - accuracy: 0.1541 - loss: 2.3293 - val_accuracy: 0.0694 - val_loss: 2.3898 - learning_rate: 5.0000e-05 Epoch 9/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 47s 477ms/step - accuracy: 0.1569 - loss: 2.3706 - val_accuracy: 0.0764 - val_loss: 2.3677 - learning_rate: 5.0000e-05 Epoch 10/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 47s 484ms/step - accuracy: 0.2014 - loss: 2.3155 - val_accuracy: 0.1620 - val_loss: 2.3286 - learning_rate: 5.0000e-05 Epoch 11/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 54s 548ms/step - accuracy: 0.2037 - loss: 2.2800 - val_accuracy: 0.1204 - val_loss: 2.3085 - learning_rate: 5.0000e-05 Epoch 12/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 52s 530ms/step - accuracy: 0.2428 - loss: 2.2430 - val_accuracy: 0.2106 - val_loss: 2.2718 - learning_rate: 5.0000e-05 Epoch 13/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 48s 489ms/step - accuracy: 0.3278 - loss: 2.1337 - val_accuracy: 0.2454 - val_loss: 2.2229 - learning_rate: 5.0000e-05 Epoch 14/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 47s 482ms/step - accuracy: 0.2801 - loss: 2.1902 - val_accuracy: 0.2824 - val_loss: 2.1313 - learning_rate: 5.0000e-05 Epoch 15/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 48s 487ms/step - accuracy: 0.3417 - loss: 2.0611 - val_accuracy: 0.2847 - val_loss: 2.1536 - learning_rate: 5.0000e-05 Epoch 16/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 49s 497ms/step - accuracy: 0.3127 - loss: 2.1324 - val_accuracy: 0.3009 - val_loss: 2.0838 - learning_rate: 5.0000e-05 Epoch 17/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 48s 484ms/step - accuracy: 0.3808 - loss: 2.0263 - val_accuracy: 0.2778 - val_loss: 2.1019 - learning_rate: 5.0000e-05 Epoch 18/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 48s 492ms/step - accuracy: 0.4089 - loss: 1.9003 - val_accuracy: 0.3519 - val_loss: 1.9982 - learning_rate: 5.0000e-05 Epoch 19/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 48s 484ms/step - accuracy: 0.3658 - loss: 1.9032 - val_accuracy: 0.3403 - val_loss: 1.9938 - learning_rate: 5.0000e-05 Epoch 20/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 47s 480ms/step - accuracy: 0.3991 - loss: 1.8861 - val_accuracy: 0.3194 - val_loss: 2.0116 - learning_rate: 5.0000e-05 Epoch 21/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 45s 461ms/step - accuracy: 0.3934 - loss: 1.8641 - val_accuracy: 0.3449 - val_loss: 1.9293 - learning_rate: 5.0000e-05 Epoch 22/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 39s 397ms/step - accuracy: 0.4547 - loss: 1.6887 - val_accuracy: 0.3519 - val_loss: 1.9183 - learning_rate: 5.0000e-05 Epoch 23/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 34s 346ms/step - accuracy: 0.4815 - loss: 1.6974 - val_accuracy: 0.3819 - val_loss: 1.8652 - learning_rate: 5.0000e-05 Epoch 24/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 34s 351ms/step - accuracy: 0.3727 - loss: 1.8802 - val_accuracy: 0.3796 - val_loss: 1.8210 - learning_rate: 5.0000e-05 Epoch 25/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 36s 371ms/step - accuracy: 0.4787 - loss: 1.6747 - val_accuracy: 0.4306 - val_loss: 1.7884 - learning_rate: 5.0000e-05 Epoch 26/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 38s 390ms/step - accuracy: 0.4585 - loss: 1.6344 - val_accuracy: 0.4236 - val_loss: 1.8159 - learning_rate: 5.0000e-05 Epoch 27/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 42s 432ms/step - accuracy: 0.4290 - loss: 1.7392 - val_accuracy: 0.4144 - val_loss: 1.7877 - learning_rate: 5.0000e-05 Epoch 28/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 41s 419ms/step - accuracy: 0.5001 - loss: 1.5843 - val_accuracy: 0.4745 - val_loss: 1.7297 - learning_rate: 5.0000e-05 Epoch 29/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 41s 414ms/step - accuracy: 0.4938 - loss: 1.5556 - val_accuracy: 0.4306 - val_loss: 1.8162 - learning_rate: 5.0000e-05 Epoch 30/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 45s 464ms/step - accuracy: 0.5208 - loss: 1.5316 - val_accuracy: 0.4468 - val_loss: 1.7182 - learning_rate: 5.0000e-05 Epoch 31/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 48s 488ms/step - accuracy: 0.4898 - loss: 1.5817 - val_accuracy: 0.4329 - val_loss: 1.7394 - learning_rate: 5.0000e-05 Epoch 32/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 41s 420ms/step - accuracy: 0.5072 - loss: 1.5546 - val_accuracy: 0.4630 - val_loss: 1.6574 - learning_rate: 5.0000e-05 Epoch 33/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 40s 410ms/step - accuracy: 0.4957 - loss: 1.5579 - val_accuracy: 0.4699 - val_loss: 1.6134 - learning_rate: 5.0000e-05 Epoch 34/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 39s 400ms/step - accuracy: 0.4938 - loss: 1.5345 - val_accuracy: 0.4931 - val_loss: 1.5748 - learning_rate: 5.0000e-05 Epoch 35/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 39s 402ms/step - accuracy: 0.5268 - loss: 1.4692 - val_accuracy: 0.4421 - val_loss: 1.6724 - learning_rate: 5.0000e-05 Epoch 36/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 41s 419ms/step - accuracy: 0.5273 - loss: 1.4641 - val_accuracy: 0.4769 - val_loss: 1.5848 - learning_rate: 5.0000e-05 Epoch 37/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 42s 427ms/step - accuracy: 0.5333 - loss: 1.4451 - val_accuracy: 0.4815 - val_loss: 1.6668 - learning_rate: 5.0000e-05 Epoch 38/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 41s 415ms/step - accuracy: 0.5726 - loss: 1.3345 - val_accuracy: 0.4630 - val_loss: 1.6782 - learning_rate: 5.0000e-05 Epoch 39/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 41s 414ms/step - accuracy: 0.5282 - loss: 1.4255 - val_accuracy: 0.5417 - val_loss: 1.4884 - learning_rate: 5.0000e-05 Epoch 40/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 40s 406ms/step - accuracy: 0.5977 - loss: 1.2698 - val_accuracy: 0.5162 - val_loss: 1.5401 - learning_rate: 5.0000e-05 Epoch 41/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 36s 368ms/step - accuracy: 0.5525 - loss: 1.3557 - val_accuracy: 0.5347 - val_loss: 1.4945 - learning_rate: 5.0000e-05 Epoch 42/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 35s 356ms/step - accuracy: 0.5420 - loss: 1.3965 - val_accuracy: 0.5324 - val_loss: 1.4536 - learning_rate: 5.0000e-05 Epoch 43/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 42s 434ms/step - accuracy: 0.6016 - loss: 1.2759 - val_accuracy: 0.5602 - val_loss: 1.4164 - learning_rate: 5.0000e-05 Epoch 44/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 42s 426ms/step - accuracy: 0.5966 - loss: 1.2579 - val_accuracy: 0.5347 - val_loss: 1.5394 - learning_rate: 5.0000e-05 Epoch 45/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 38s 390ms/step - accuracy: 0.6115 - loss: 1.2357 - val_accuracy: 0.5417 - val_loss: 1.4022 - learning_rate: 5.0000e-05 Epoch 46/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 39s 396ms/step - accuracy: 0.5710 - loss: 1.3355 - val_accuracy: 0.5648 - val_loss: 1.4249 - learning_rate: 5.0000e-05 Epoch 47/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 40s 411ms/step - accuracy: 0.6152 - loss: 1.2065 - val_accuracy: 0.5162 - val_loss: 1.4956 - learning_rate: 5.0000e-05 Epoch 48/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 41s 422ms/step - accuracy: 0.6089 - loss: 1.2337 - val_accuracy: 0.5486 - val_loss: 1.4316 - learning_rate: 5.0000e-05 Epoch 49/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 41s 414ms/step - accuracy: 0.6328 - loss: 1.1425 - val_accuracy: 0.5556 - val_loss: 1.3632 - learning_rate: 5.0000e-05 Epoch 50/50 98/98 ━━━━━━━━━━━━━━━━━━━━ 41s 419ms/step - accuracy: 0.6262 - loss: 1.1563 - val_accuracy: 0.5023 - val_loss: 1.5762 - learning_rate: 5.0000e-05 Restoring model weights from the end of the best epoch: 46. 14/14 ━━━━━━━━━━━━━━━━━━━━ 2s 106ms/step - accuracy: 0.5125 - loss: 1.5014 Modèle optimisé v2: Loss = 1.4249, Accuracy = 0.5648
plot_learning_curves(opt_history, "Courbes d'apprentissage - Modèle optimisé par ajout de dropout")
evaluate_model(opt_mode, val_generator, f"Évaluation du modèle optimisé")
14/14 ━━━━━━━━━━━━━━━━━━━━ 1s 80ms/step
Rapport de classification - Évaluation du modèle optimisé
precision recall f1-score support
chair 0.23 0.58 0.33 24
door 0.64 0.31 0.42 51
fence 0.36 0.33 0.35 12
garbage_bin 0.48 0.88 0.62 17
obstacle 0.66 0.49 0.57 91
plant 0.56 0.58 0.57 24
pothole 0.52 0.87 0.65 30
stairs 0.56 0.48 0.52 60
table 0.77 0.52 0.62 33
vehicle 0.74 0.71 0.72 90
accuracy 0.56 432
macro avg 0.55 0.58 0.54 432
weighted avg 0.61 0.56 0.57 432
Après avoir optimisé notre modèle par l'ajout de dropout, nous observons des améliorations significatives dans le comportement d'apprentissage et les performances générales. Les courbes d'apprentissage montrent une progression plus équilibrée entre les données d'entraînement et de validation. L'accuracy d'entraînement atteint environ 57% en fin d'apprentissage, tandis que l'accuracy de validation se stabilise autour de 52-56%. Cet écart réduit entre les deux courbes, comparé aux modèles précédents (sans mécanismes de régularisation), indique que le dropout a effectivement permis de limiter le surapprentissage. De même, les courbes de loss suivent une trajectoire descendante cohérente, avec la loss de validation restant légèrement supérieure à celle d'entraînement, ce qui est attendu et souhaitable.
La matrice de confusion révèle des performances variables selon les classes. Certaines catégories sont particulièrement bien reconnues :
- "vehicle" présente la meilleure performance avec 64 prédictions correctes sur 90 échantillons (71% de recall)
- "pothole" affiche également un bon recall (87%)
- "garbage_bin" est bien identifié (88% de recall)
En revanche, d'autres classes montrent des difficultés persistantes :
- "fence" n'est correctement identifiée que dans 4 cas sur 12 (33%)
- "door" présente un recall faible (31%)
- "chair" est fréquemment confondue avec d'autres classes, notamment "obstacle" (13 cas)
Le rapport de classification confirme ces observations avec une accuracy globale de 56%. Les F1-scores varient considérablement : excellent pour "vehicle" (0.72), satisfaisant pour "pothole" (0.65) et faible pour "fence" (0.35) et "chair" (0.33). Ces écarts s'expliquent en partie par le déséquilibre des données : "obstacle" et "vehicle" dominent avec respectivement 91 et 90 échantillons, tandis que "fence" ne compte que 12 échantillons.
Les confusions les plus fréquentes concernent des objets partageant des caractéristiques visuelles similaires :
- "obstacle" est parfois confondu avec "chair" (13 erreurs)
- "door" est confondue avec "chair" (9 cas) et "stairs" (7 cas)
- "stairs" et "vehicle" présentent des confusions réciproques
Bien que le modèle atteigne une accuracy de 56%, les moyennes macro (0.54) et pondérée (0.57) des F1-scores suggèrent une performance modérée mais équilibrée. L'ajout de dropout a manifestement amélioré la capacité de généralisation, comme en témoigne la convergence des courbes d'apprentissage.
Pour améliorer davantage les performances, nous pourrions envisager :
- L'augmentation des données pour les classes minoritaires comme vu en TIP (projet CV&ML du S3)
- L'ajustement des poids selon l'inverse de la fréquence des classes
- L'exploration d'architectures plus adaptées ou le transfert learning
- La mise en place d'une stratégie d'ensemble combinant plusieurs modèles
Ces optimisations permettraient potentiellement de renforcer la robustesse du modèle face aux classes moins représentées et d'améliorer sa capacité de discrimination entre classes visuellement similaires.