CIFAR-10: apprentissage pour la reconnaissance d’images couleur avec les CNN
Le dataset CIFAR-10: initiation à la reconnaissance d’objets en couleur
Le
CIFAR-10 (Canadian Institute for Advanced Research) est l’un des jeux de données les plus utilisés pour l’apprentissage et l’évaluation des réseaux de neurones convolutionnels. Il contient 60 000 images couleur de 32×32 pixels,réparties en 10 classes distinctes (avion, automobile, oiseau, chat, cerf, chien, grenouille, cheval, bateau et camion). Chaque classe du dataset comporte 6 000 images, avec 50 000 pour l’entraînement et 10 000 pour le test.
Les images proviennent d’un sous-ensemble du gigantesque
Tiny Images Dataset et ont été soigneusement étiquetées pour servir de référence dans la recherche en vision par ordinateur.
Le dataset CIFAR-10 est particulièrement apprécié car il est à la fois simple (il renferme de petites images et un nombre limité de classes) et suffisamment varié pour tester la robustesse des modèles.
On peut obtenir CIFAR-10 directement depuis plusieurs sources :
Il existe également une version étendue appelée CIFAR-100, qui contient 100 classes au lieu de 10. Elle est plus riche et plus difficile, car chaque classe ne dispose que de 600 images, ce qui en fait un bon défi pour tester la capacité de généralisation des modèles.
Mise en pratique de la reconnaissance par CNN sur CIFAR-10
Puisque vous maîtrisez déjà le
pipeline d’un CNN à travers l’exemple de MNIST, je vous fournirai cette fois le code complet pour CIFAR-10 et j’expliquerai ensuite les points importants.
Je propose ce code:
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt
import numpy as np
# Charger le dataset CIFAR-10
(X_train, y_train), (X_test, y_test) = datasets.cifar10.load_data()
X_train, X_test = X_train / 255.0, X_test / 255.0 # Normalisation
y_train, y_test = y_train.flatten(), y_test.flatten()
# Liste des classes
class_names = ['airplane','car','bird','cat','deer',
'dog','frog','horse','ship','truck']
# Afficher 5 images avec labels avant l'entraînement
plt.figure(figsize=(10,2))
for i in range(5):
plt.subplot(1,5,i+1)
plt.imshow(X_train[i])
plt.title(class_names[y_train[i]])
plt.axis('off')
plt.show()
# Définir l'architecture du CNN
model = models.Sequential([
# Bloc 1
layers.Conv2D(32, (3,3), activation='relu', padding='same', input_shape=(32,32,3)),
layers.Conv2D(32, (3,3), activation='relu', padding='same'),
layers.MaxPooling2D((2,2)),
# Bloc 2
layers.Conv2D(64, (3,3), activation='relu', padding='same'),
layers.Conv2D(64, (3,3), activation='relu', padding='same'),
layers.MaxPooling2D((2,2)),
# Bloc 3
layers.Conv2D(64, (3,3), activation='relu', padding='same'),
layers.Conv2D(64, (3,3), activation='relu', padding='same'),
layers.MaxPooling2D((2,2)),
# Classification
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(10, activation='softmax')
])
# Compilation avec accuracy comme métrique
model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# Entraînement du modèle
history = model.fit(
X_train,
y_train,
epochs=5,
validation_data=(X_test, y_test)
)
# Évaluation du modèle
test_loss, test_acc = model.evaluate(X_test, y_test, verbose=2)
print(f"Test accuracy: {test_acc:.3f}")
# Prédictions sur 5 images du test
predictions = model.predict(X_test[:5])
predicted_labels = np.argmax(predictions, axis=1)
# Afficher 5 images avec labels réels et prédits
plt.figure(figsize=(10,2))
for i in range(5):
plt.subplot(1,5,i+1)
plt.imshow(X_test[i])
plt.title(
f"Vrai: {class_names[y_test[i]]}
nPrédit: {class_names[predicted_labels[i]]}"
)
plt.axis('off')
plt.show()
Passons l'explication du code:
Après avoir importé les modules nécessaire, on commence par charger le dataset CIFAR-10 et on procède à la normalisation:
(X_train, y_train), (X_test, y_test) = datasets.cifar10.load_data()
X_train, x_test = X_train / 255.0, X_test / 255.0
y_train, y_test = y_train.flatten(), y_test.flatten()
Dans ce code, on a chargé le dataset CIFAR-10 avec la fonction
datasets.cifar10.load_data(), qui renvoie deux ensembles: (X_train, y_train) pour l’entraînement et (X_test, y_test) pour le test.
Les images sont ensuite normalisées en divisant chaque pixel par 255.0 afin de ramener les valeurs dans l’intervalle [0,1], ce qui facilite l’apprentissage du réseau.
Enfin, les labels (y_train, y_test) sont aplatis avec la méthode
flatten() pour passer d’un format matriciel (par exemple (50000,1)) à un vecteur simple, ce qui est plus pratique pour l’entraînement et l’évaluation du modèle.
Avant de passer à l'élaboration de l'architecture de notre modèle, on affiche quelques images du dataset pour avoir une idée sur la nature des données manipulées:
# Afficher 5 images avec labels avant l'entraînement
plt.figure(figsize=(10,2))
for i in range(5):
plt.subplot(1,5,i+1)
plt.imshow(X_train[i])
plt.title(class_names[y_train[i]])
plt.axis('off')
plt.show()
Ce qui produit cette illustration:
Les images de CIFAR-10 sont de petite taille (32×32 pixels), ce qui les rend visuellement pixellisées. Cette résolution réduite est volontaire afin de simplifier le traitement et de permettre aux modèles de se concentrer sur l’apprentissage des caractéristiques essentielles plutôt que sur les détails fins.
Ensuite, nous avons construit notre modèle CNN:
# Définir l'architecture du CNN
model = models.Sequential([
# Bloc 1
layers.Conv2D(32, (3,3), activation='relu', padding='same', input_shape=(32,32,3)),
layers.Conv2D(32, (3,3), activation='relu', padding='same'),
layers.MaxPooling2D((2,2)),
# Bloc 2
layers.Conv2D(64, (3,3), activation='relu', padding='same'),
layers.Conv2D(64, (3,3), activation='relu', padding='same'),
layers.MaxPooling2D((2,2)),
# Bloc 3
layers.Conv2D(64, (3,3), activation='relu', padding='same'),
layers.Conv2D(64, (3,3), activation='relu', padding='same'),
layers.MaxPooling2D((2,2)),
# Classification
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(10, activation='softmax')
])
Ce modèle est construit en trois blocs convolutionnels successifs. Chaque bloc contient deux couches Conv2D avec activation ReLU et padding='same', suivies d’une couche MaxPooling2D qui réduit la taille des cartes de caractéristiques. Le nombre de filtres augmente progressivement (32 → 64 → 64), ce qui permet d’extraire des motifs de plus en plus complexes.
Dans l’argument input_shape=(32,32,3), le 3 correspond aux canaux de couleur (Rouge, Vert, Bleu). Chaque image de CIFAR-10 est donc représentée en RGB, ce qui permet au réseau de traiter non seulement la structure en deux dimensions (32×32 pixels), mais aussi l’information chromatique essentielle à la reconnaissance visuelle.
Après ces blocs, les cartes sont aplaties avec Flatten, puis passent dans une couche Dense de 128 neurones pour combiner les caractéristiques extraites. Enfin, une couche Dense à 10 neurones avec activation softmax réalise la classification finale sur les 10 classes de CIFAR-10.
L’option padding='same' dans une couche convolutionnelle permet de conserver la taille des images en sortie identique à celle en entrée. Concrètement, des pixels fictifs (dont la valeur est zéros) sont ajoutés autour de l’image afin que la convolution ne réduise pas les dimensions. Cela facilite la construction de réseaux plus profonds car les feature maps gardent une taille cohérente au fil des couches, et cela évite que les cartes deviennent trop petites trop rapidement.
Quant à la suite des instructions, je pense que vous en connaissez déjà l'utilité vu qu'on les a toujours invoqué dans nos pipeline de Deel Learning. En tout cas, l'évaluation du modèle produit ce résultat:
313/313 - 5s - 16ms/step - accuracy: 0.5166 - loss: 110.3507
Test accuracy: 0.517
Avec une accuracy de 0,517 sur CIFAR-10, notre modèle parvient à reconnaître correctement un peu plus de la moitié des images de test. Ce résultat montre qu’il dépasse largement le hasard (10% pour 10 classes), ce qui prouve que notre modèle CNN extrait déjà des caractéristiques pertinentes.
Cependant, le modèle reste limité face à la complexité des images couleur de CIFAR-10 qui comportent des variations de pose, de texture et de contexte beaucoup plus riches que MNIST. En pratique, ce score illustre bien la différence entre un dataset simple comme MNIST, où un CNN basique atteint facilement plus de 95% (99% dans notre cas) et CIFAR-10, qui nécessite des architectures plus profondes ou optimisées pour dépasser les 70%.
Des techniques avancées, comme la régularisation (Dropout, Batch Normalization...) pourraient améliorer significativement les performances d’un CNN sur CIFAR-10. Toutefois, nous ne les aborderons pas dans cet exemple. Mais je vous renvoie quand même à la
leçon qui explique comment les mettre en œuvre si vous voulez vous en servir.
Les prédictions du modèle réalisés sur 5 exemples correspondent à ceci:
On constate que le modèle parvient à identifier correctement certaines images, mais se trompe sur quelques autres. Cette situation est typique d’un CNN appliqué à CIFAR-10. En effet, les images étant petites et parfois visuellement proches entre classes (par exemple, un chat et un chien ou un camion et une voiture), le réseau peut confondre certains motifs.
Les erreurs du modèle traduisent les limites d’une architecture encore relativement simple, qui capte des caractéristiques générales mais manque de profondeur pour distinguer les détails plus subtils. Ce résultat est donc cohérent avec le niveau de performance global obtenu et illustre bien la nécessité d’architectures plus puissantes ou de techniques complémentaires pour améliorer la reconnaissance visuelle.