Bonjour ! Dans la leçon précédente, nous avons disséqué l'anatomie d'un réseau de neurones convolutif (CNN), en comprenant comment les couches de convolution, de pooling et entièrement connectées travaillent de concert. Vous avez appris la théorie derrière l'extraction de caractéristiques et la classification.
Aujourd'hui, nous passons de la théorie à la pratique. L'objectif de cette leçon est de vous guider pas à pas pour construire et entraîner votre tout premier CNN de base pour la classification d'images. Nous allons utiliser le célèbre jeu de données MNIST, qui contient des images de chiffres écrits à la main, et nous mettrons en œuvre l'architecture que nous avons étudiée en utilisant PyTorch. C'est le moment où tous les concepts que nous avons vus, de la programmation orientée objet en Python aux principes de l'entraînement des réseaux de neurones, se rejoignent pour créer un modèle fonctionnel.
Regardons une dernière fois le schéma d'un CNN typique pour la reconnaissance de chiffres. C'est exactement ce que nous allons construire aujourd'hui.
1. Préparation des données avec torchvision
La première étape de tout projet d'apprentissage automatique est de préparer les données. Heureusement, PyTorch, via sa bibliothèque torchvision, nous facilite grandement la tâche pour les jeux de données standards comme MNIST.
Le processus se déroule en plusieurs étapes clés :
- Télécharger les données :
torchvisionpeut télécharger automatiquement le jeu de données MNIST. - Transformer les données : Les images originales sont dans un format appelé PIL Image. Nous devons les convertir en tenseurs, qui sont la structure de données fondamentale de PyTorch.
- Normaliser les données : Nous ajustons les valeurs des pixels pour qu'elles aient une moyenne de 0 et un écart-type de 1. Cette étape est cruciale pour aider le réseau à converger plus rapidement et de manière plus stable.
- Charger les données par lots (batches) : Nous utilisons un
DataLoaderpour nous fournir les données en petits groupes (batches) de manière aléatoire. L'entraînement par lots est plus efficace et aide à la généralisation du modèle.
L'article suivant est un guide complet qui met en œuvre l'ensemble du processus. Nous allons nous baser sur son code tout au long de cette leçon.
MNIST Handwritten Digit Recognition in PyTorch
Cet article de Nextjournal fournit un excellent tutoriel pas à pas pour construire un CNN sur MNIST avec PyTorch. Nous allons commencer par la préparation des données.
Lisez la section "Preparing the Dataset". Concentrez-vous sur le bloc de code qui crée train_loader et test_loader. Essayez de comprendre le rôle de torchvision.datasets.MNIST, transforms.Compose, et torch.utils.data.DataLoader.
Pour résumer le code de la ressource, voici les lignes essentielles pour charger les données :
transform=torchvision.transforms.Compose([...]): Définit une séquence de transformations à appliquer aux images.torchvision.transforms.ToTensor(): Convertit l'image en un tenseur PyTorch.torchvision.transforms.Normalize((0.1307,), (0.3081,)): Normalise le tenseur avec la moyenne et l'écart-type spécifiques au jeu de données MNIST.train_loader = torch.utils.data.DataLoader(...): Crée un itérateur qui fournira les données d'entraînement par lots de 64 images, mélangées à chaque époque (shuffle=True).
2. Construire le modèle CNN
Maintenant que nos données sont prêtes, nous allons définir l'architecture de notre réseau. En PyTorch, la manière standard de faire cela est de créer une classe qui hérite de torch.nn.Module. Cette approche orientée objet, que nous avons introduite au module 10, est puissante car elle encapsule la structure et le comportement du réseau en une seule entité.
La vidéo suivante offre un excellent rappel sur la raison pour laquelle cette approche orientée objet est au cœur de PyTorch.
Build PyTorch CNN - Object Oriented Neural Networks
Cette vidéo de la chaîne deeplizard explique clairement comment et pourquoi nous utilisons des classes et nn.Module pour construire des réseaux dans PyTorch.
Regardez la section qui explique comment la programmation orientée objet s'intègre avec PyTorch. L'idée clé est que notre classe de réseau hérite de nn.Module et que nous devons définir deux méthodes principales : __init__ pour déclarer les couches et forward pour définir le passage des données.
Forts de ce rappel, nous allons définir notre propre classe Net.
- Dans la méthode
__init__, nous déclarons toutes les couches qui ont des paramètres à apprendre (les poids). Il s'agira de nos couchesnn.Conv2detnn.Linear. - Dans la méthode
forward, nous décrivons comment les données (x) traversent les couches définies dans__init__. C'est ici que nous appliquons également les opérations qui n'ont pas de poids, comme les fonctions d'activation (F.relu) et les couches de pooling (F.max_pool2d).
MNIST Handwritten Digit Recognition in PyTorch
Revenons à notre guide principal pour voir comment définir concrètement la classe du réseau.
Lisez attentivement la section "Building the Network". Observez comment la classe Net est structurée, avec les couches définies dans __init__ et le flux de données dans forward.
Analysons la structure de ce réseau :
self.conv1 = nn.Conv2d(1, 10, kernel_size=5): Première couche de convolution. Elle prend 1 canal en entrée (image en niveaux de gris), et produit 10 canaux en sortie (10 cartes de caractéristiques) avec un filtre de 5x5.self.conv2 = nn.Conv2d(10, 20, kernel_size=5): Seconde couche de convolution. Elle prend les 10 canaux de la couche précédente et en produit 20.self.fc1 = nn.Linear(320, 50): Première couche entièrement connectée.self.fc2 = nn.Linear(50, 10): Couche de sortie. 50 neurones en entrée, et 10 en sortie (un pour chaque chiffre de 0 à 9).
Une question cruciale se pose : d'où vient le nombre 320 dans nn.Linear(320, 50) ? C'est le résultat de l'aplatissement (flattening) de la sortie des couches convolutives. Calculons-le :
- Image d'entrée :
1 x 28 x 28(Canaux x Hauteur x Largeur) - Après
conv1(noyau 5x5, stride 1) : La taille devient24x24. La sortie est10 x 24 x 24. - Après
max_pool2d(noyau 2x2) : La taille est divisée par 2. La sortie est10 x 12 x 12. - Après
conv2(noyau 5x5, stride 1) : La taille devient8x8. La sortie est20 x 8 x 8. - Après
max_pool2d(noyau 2x2) : La taille est divisée par 2. La sortie est20 x 4 x 4. - Aplatissement : Nous transformons ce tenseur 3D en un vecteur 1D. Le nombre total d'éléments est
20 * 4 * 4 = 320. C'est le nombre de caractéristiques d'entrée pour notre première couche linéaire.
3. Entraînement et Évaluation du Modèle
Avec notre modèle défini, il est temps de l'entraîner. Ce processus, que nous avons déjà vu pour les réseaux de neurones simples, reste le même pour les CNNs. Il s'articule autour d'une boucle qui répète les étapes suivantes pour chaque lot de données :
- Mise à zéro des gradients :
optimizer.zero_grad() - Passe avant (Forward Pass) :
output = network(data) - Calcul de la perte (Loss) :
loss = criterion(output, target) - Passe arrière (Backward Pass) :
loss.backward() - Mise à jour des poids :
optimizer.step()
Avant de lancer la boucle, nous devons définir deux éléments essentiels :
- La fonction de perte (Loss Function) : Puisque nous faisons de la classification multi-classes,
nn.CrossEntropyLossest le choix standard. Elle combinelog_softmaxetnll_loss, ce qui est numériquement stable et pratique. - L'optimiseur (Optimizer) : Il s'agit de l'algorithme qui met à jour les poids du réseau.
optim.SGD(Descente de Gradient Stochastique) ouoptim.Adamsont des choix courants.
Nous allons également définir une fonction test pour évaluer la performance de notre modèle sur le jeu de données de test après chaque époque d'entraînement. Dans cette fonction, il est important d'utiliser network.eval() pour mettre le modèle en mode évaluation et with torch.no_grad() pour désactiver le calcul des gradients, ce qui accélère le processus.
MNIST Handwritten Digit Recognition in PyTorch
L'article met en place l'ensemble de la logique d'entraînement et d'évaluation dans deux fonctions distinctes, train() et test(), ce qui est une excellente pratique.
Lisez la section "Training the Model". Analysez attentivement le code des fonctions train et test. Assurez-vous de bien comprendre la séquence d'opérations dans la boucle for de la fonction train. Notez également l'utilisation de network.eval() et torch.no_grad() dans la fonction test.
Une fois ces fonctions définies, le code pour lancer l'entraînement est remarquablement simple : une boucle qui appelle train() et test() pour chaque époque.
MNIST Handwritten Digit Recognition in PyTorch
Maintenant, lançons l'entraînement et observons les résultats.
Lisez la courte section "Evaluating the Model's Performance". Regardez le résultat de l'entraînement. L'article atteint 97% de précision en seulement 3 époques, ce qui démontre la puissance des CNNs pour ce type de tâche.
Si vous préférez voir l'ensemble du processus de codage dans un format vidéo, la ressource suivante est un excellent complément.
Pytorch CNN example (Convolutional Neural Network)
Cette vidéo d'Aladdin Persson montre le codage d'un CNN pour MNIST en PyTorch du début à la fin.
Vous pouvez la parcourir pour renforcer votre compréhension. Les sections clés sont la définition du modèle, la passe avant, l'intégration dans la boucle d'entraînement, et les résultats finaux.
Conclusion
Félicitations ! Vous avez parcouru toutes les étapes nécessaires pour construire et entraîner un réseau de neurones convolutif complet. C'est une étape majeure dans votre parcours d'apprentissage profond.
Points clés à retenir :
- La préparation des données est une première étape essentielle, et
torchvisionla simplifie avec des outils commedatasets,transformsetDataLoader. - Les modèles PyTorch sont définis comme des classes héritant de
nn.Module, avec les couches déclarées dans__init__et la logique de flux de données dansforward. - Le calcul des dimensions d'entrée pour les couches linéaires après les couches convolutives est une étape technique clé qui nécessite de suivre la transformation de la taille du tenseur.
- La boucle d'entraînement pour un CNN suit le même schéma que pour un réseau de neurones simple :
forward pass -> loss -> backward pass -> optimizer step. - L'évaluation doit être effectuée en mode
eval()et sans calcul de gradient (torch.no_grad()) pour garantir des résultats corrects et une exécution efficace.
Vous avez maintenant les compétences pour construire un modèle de vision par ordinateur à partir de zéro pour un problème de classification simple. Mais que se passe-t-il si notre jeu de données est beaucoup plus complexe et que nous n'avons pas des millions d'images pour l'entraîner ? Dans la prochaine leçon, nous explorerons une technique extrêmement puissante appelée transfert d'apprentissage (transfer learning), où nous exploiterons la connaissance de modèles déjà entraînés sur des ensembles de données massifs pour résoudre nos propres problèmes.