Poursuivant ma série sur la construction de réseaux de neurones convolutionnels classiques qui ont révolutionné le domaine de la vision par ordinateur au cours des dernières décennies, nous construirons ici VGG, un réseau convolutionnel très profond, à partir de zéro en utilisant PyTorch. Vous pouvez consulter les articles précédents de la série sur mon profil, notamment LeNet5 et AlexNet.
Comme avant, nous examinerons l’architecture et l’intuition derrière VGG et comment les résultats étaient à l’époque. Nous explorerons ensuite notre jeu de données, CIFAR100, et le chargons dans notre programme en utilisant un code économisant en mémoire. Ensuite, nous implémenterons VGG16 (le numéro se réfère au nombre de couches, il existe deux versions essentiellement VGG16 et VGG19) à partir de zéro en utilisant PyTorch et le trainons sur notre jeu de données, ainsi que l’évaluations sur notre jeu de test pour voir comment il se comporte sur des données non vues.
VGG
Sur la base des travaux de AlexNet, VGG se concentre sur un autre aspect crucial des réseaux de neurones convolutionnels (CNN), la profondeur. Il a été développé par Simonyan et Zisserman. Il se compose normalement de 16 couches convolutionnelles, mais peut être étendu à 19 couches (deux versions donc, VGG-16 et VGG-19). toutes les couches convolutionnelles se composent de filtres de taille 3×3. Vous pouvez en savoir plus sur le réseau dans le papier officiel ici
Architecture VGG16. Source
Chargement des données
Jeu de données
Avant de construire le modèle, l’une des choses les plus importantes dans tout projet de apprentissage automatique est de charger, analyser et pré-traiter le jeu de données. Dans cet article, nous utiliserons le jeu de données CIFAR-100. Ce jeu de données est similaire au CIFAR-10, sauf qu’il contient 100 classes chacune contenant 600 images. Il y a 500 images d’entraînement et 100 images de test par classe. Les 100 classes du CIFAR-100 sont regroupées en 20 superclasses. Chaque image est accompagnée d’une étiquette « fine » (la classe à laquelle elle appartient) et d’une étiquette « coarse » (la superclasse à laquelle elle appartient). Nous utiliserons l’étiquette « fine » ici. Voici la liste des classes dans le CIFAR-100 :
Liste des classes pour le jeu de données CIFAR-100
Importation des bibliothèques
Nous travaillerons principalement avec torch
(utilisé pour construire le modèle et l’entraîner), torchvision
(pour le chargement/traitement des données, contenant les jeux de données et les méthodes pour traiter ces jeux de données dans le domaine de la vision par ordinateur) et numpy
(pour la manipulation mathématique). Nous définirons également une variable device
afin que le programme puisse utiliser la GPU si elle est disponible
Chargement des données
torchvision
est une bibliothèque qui offre un accès facile à de nombreuses bases de données de vision par ordinateur et des méthodes pour préparer ces bases de données de manière aisée et intuitive
- Nous définissons une fonction
data_loader
qui retourne soit les données d’entraînement/validation, soit les données de test en fonction des arguments - Nous commençons par définir la variable
normalize
avec les valeurs moyennes et les écarts-types de chaque canal (rouge, vert et bleu) de la base de données. Ces valeurs peuvent être calculées manuellement, mais sont également disponibles en ligne. Cela est utilisé dans la variabletransform
où nous redimensionnons les données, les convertissons en tenseurs et les normalisons ensuite - Si l’argument
test
est vrai, nous charge simplement la partie de test de la base de données et l’affichons à l’aide de chargeurs de données (expliqués ci-dessous) - Dans le cas où
test
est faux (comportement par défaut également), nous chargeons la partie d’entraînement de la base de données et l’ions aléatoirement en une partie d’entraînement et une partie de validation (0,9:0,1) - Enfin, nous utilisons des chargeurs de données. Cela peut ne pas affecter la performance dans le cas d’une petite base de données comme CIFAR100, mais il peut vraiment limiter la performance dans le cas de grandes bases de données et est généralement considérée comme une bonne pratique. Les chargeurs de données nous permettent d’itérer sur les données en lots, et les données sont chargées lors de l’itération et ne sont pas toutes chargées en même temps dans le début de votre RAM.
VGG16 à partir de zéro
Pour construire le modèle à partir de zéro, nous devons d’abord comprendre comment les définitions de modèles fonctionnent dans torch
et les différents types de couches que nous utiliserons ici :
- Chaque modèle personnalisé doit hériter de la classe
nn.Module
car elle fournit certaines fonctions de base qui aident le modèle à s’entraîner. - Deuxièmement, il y a deux choses principales à faire. Premièrement, définir les différentes couches de notre modèle à l’intérieur de la fonction
__init__
et la séquence dans laquelle ces couches seront exécutées sur l’entrée à l’intérieur de la fonctionforward
Maintenant, définissons les différents types de couches que nous utilisons ici :
nn.Conv2d
: Ces sont les couches de convolution qui acceptent le nombre de canaux d’entrée et de sortie en argument, ainsi que la taille du filtre pour le kernel. Elle accepte également toutes les strides ou le padding si vous souhaitez l’appliquernn.BatchNorm2d
: Cela applique la normalisation par lot à la sortie de la couche de convolutionnn.ReLU
: C’est l’activation appliquée à diverses sorties du réseau.nn.MaxPool2d
: Cette fonction applique la max-pooling à l’output avec la taille de kernel donnéenn.Dropout
: Il s’agit de l’application de la dropout à l’output avec une probabilité donnéenn.Linear
: C’est essentiellement une couche pleinement connectéenn.Sequential
: Techniquement, ce n’est pas un type de couche, mais il permet de combiner différentes opérations qui font partie de la même étape
Avec ces connaissances, nous pouvons maintenant construire notre modèle VGG16 en utilisant l’architecture du papier :
VGG16 de zéro
Hyperparamètres
L’une des parties importantes de tout projet de machine ou de apprentissage profond est l’optimisation des hyperparamètres. ICI, nous ne testerons pas différentes valeurs pour ceux-ci, mais nous devrons les définir à l’avance. Cela inclut la définition du nombre d’epochs, du taille du lot, de taux d’apprentissage, de fonction de perte ainsi que de l’optimiseur
Définition des hyperparamètres
Entraînement
Nous sommes maintenant prêts à trainer notre modèle. Nous allons d’abord regarder comment nous entraînons notre modèle en torch
et ensuite voir le code :
- Pour chaque époch, nous parcourons les images et les étiquettes à l’intérieur de notre
train_loader
et déplaçons ces images et étiquettes sur le GPU si disponible. Cela se produit automatiquement - Nous utilisons notre modèle pour prédire sur les étiquettes (
model(images)
) et then calculer la perte entre les prédictions et les véritables étiquettes en utilisant notre fonction de perte (criterion(outputs, labels)
) - Ensuite, nous utilisons cette perte pour backpropagate (
loss.backward
) et mettre à jour les poids (optimizer.step()
). N’oubliez pas de mettre les gradients à zéro avant chaque mise à jour. Cela est fait en utilisantoptimizer.zero_grad()
- De plus, à la fin de chaque époch, nous utilisons notre jeu de vérification pour calculer l’exactitude du modèle également. Dans ce cas, nous n’avons pas besoin de gradient, donc nous utilisons
with torch.no_grad()
pour une évaluation plus rapide
Maintenant, nous combinons tout cela dans le code suivant :
Entraînement
Nous pouvons voir la sortie du code ci-dessus comme suit, qui montre que le modèle apprend vraiment car la perte est en diminution avec chaque époch :
Pertes d’entraînement
Testation
Pour la testation, nous utilisons exactement le même code que pour la validation mais avec le test_loader
:
Testation
En utilisant le code ci-dessus et en entraînant le modèle pendant 20 épochs, nous avons réussi à atteindre une précision de 75% sur le jeu de test.
Conclusion
Maintenant, concluons ce que nous avons fait dans cet article :
- Nous avons d’abord理解 l’architecture et les différents types de couches dans le modèle VGG-16.
- Ensuite, nous avons chargé et pré-traité le jeu de données CIFAR100 en utilisant
torchvision
. - Ensuite, nous avons utilisé
PyTorch
pour construire notre propre modèle VGG-16 à partir de zéro, en même temps que nous avons essayé de comprendre les différents types de couches disponibles danstorch
. - Finalement, nous avons entraîné et testé notre modèle sur le jeu de données CIFAR100, et le modèle semblait fonctionner bien avec une précision de 75% sur le jeu de test.
Travail Futur
En utilisant cet article, vous obtenez une bonne introduction et une formation pratique, mais vous apprendrez beaucoup plus si vous étendez cela et voyez ce que vous pouvez faire d’autre :
- Vous pouvez essayez d’utiliser différents jeux de données. Un de ces jeux de données est CIFAR10 ou une sous-partie du jeu de données ImageNet.
- Vous pouvez expérimenter avec différents hyperparamètres et voir la meilleure combinaison d’entre eux pour le modèle
- Enfin, vous pouvez essayez d’ajouter ou de supprimer des couches depuis le jeu de données pour voir leur impact sur la capacité du modèle. Autant que cela, essayez de construire la version VGG-19 de ce modèle.
Source:
https://www.digitalocean.com/community/tutorials/vgg-from-scratch-pytorch