🏛️ VGG16 / VGG19 – Le Guide Ultime
Deep Dive : Architecture (3x3), ILSVRC 2014, VGG16 vs VGG19, Transfer Learning & Perceptual Loss.
1. C'est quoi VGG ?
Visual Geometry Group (Oxford). CNN "très profond" (ILSVRC 2014).
VGG CNN2. Philosophie : "La Profondeur"
L'innovation clé : petits filtres (3x3) empilés très profondément (vs gros filtres AlexNet).
3x3 Filter Architecture3. 📊 VGG16 vs VGG19
16 couches vs 19 couches. VGG19 est plus lourd (144M params) pour un gain de mAP minime.
VGG16 VGG194. 📈 Architecture (Blocs)
Une pile simple de blocs [Conv 3x3 - Conv 3x3 - MaxPool 2x2]. 138M params.
5. Le Filtre 3x3 (Magie)
2x (3x3 Conv) a le même champ réceptif que 1x (5x5 Conv) avec moins de poids.
6. Limites (Taille & Vitesse)
Obsolète pour la classif. ~140M params, >500MB sur disque. Très lent (vs ResNet).
Params Vitesse7. Code (PyTorch)
torchvision.models.vgg16(pretrained=True). model.features vs model.classifier.
8. Code (Keras)
tf.keras.applications.VGG16(). include_top=False (pour Transfer Learning).
9. 🚀 Cas: Transfer Learning
L'usage moderne. Geler features, remplacer classifier (FC layers).
10. Projet: Neural Style Transfer
Le "plugin" artistique. Utilise les "features" VGG pour séparer "contenu" et "style".
Style Transfer Gatys11. Addon: VGG Loss
Perceptual Loss. Utiliser VGG comme "juge" (plugin) pour les GANs, Super-Resolution.
Perceptual Loss GAN12. Héritage & Liens
L'ancêtre de ResNet et Vision Transformer (ViT). Liens (Papier, Roboflow).
ResNet ViTVGG (Visual Geometry Group) est le nom d'une famille de Réseaux de Neurones Convolutifs (CNN) créée par l'Université d'Oxford (K. Simonyan & A. Zisserman) en 2014.
L'Événement : C'est le "dauphin" (runner-up) du concours ILSVRC 2014 (gagné par GoogLeNet). Bien qu'il n'ait pas gagné, VGG est devenu *plus influent* que GoogLeNet en raison de sa simplicité architecturale.
VGG est la suite directe d'AlexNet (2012). Il a répondu à la question : "Que se passe-t-il si nous prenons AlexNet, que nous retirons ses "hacks" (gros filtres 11x11, LRN) et que nous le rendons *beaucoup, beaucoup plus profond* (deep) ?".
Chiffres Clés (ILSVRC 2014)
| Modèle | Profondeur | Erreur Top-5 | Innovation |
|---|---|---|---|
| GoogLeNet (Gagnant) | 22 couches | 6.7% | Module "Inception" (efficacité) |
| VGG (Dauphin) | 16-19 couches | 7.3% | Simplicité (Filtres 3x3 uniformes) |
| AlexNet (Référence 2012) | 8 couches | 15.3% | Le "Big Bang" |
L'innovation *majeure* de VGG n'est pas un "plugin" compliqué, c'est une simplification extrême.
AlexNet (2012) utilisait un "chaos" de tailles de filtres (11x11, 5x5, 3x3).
VGG (2014) a posé la question : "Peut-on *uniquement* utiliser le plus petit filtre possible (3x3) et l'empiler ?".
Le "Champ Réceptif" (Receptive Field)
L'astuce est que deux couches de filtres 3x3 (empilées) ont le *même* "champ réceptif" (la zone de l'image qu'elles "voient") qu'une seule couche 5x5.
...et trois couches 3x3 équivalent à une couche 7x7.
Pourquoi est-ce mieux ? (Diagramme)
1x Filtre 5x5 (Ancienne méthode)
(Image 5x5) -> [Conv 5x5] -> (Output 1x1) - Nb de Poids (Params) : 5*5 = 25 - Nb de "Non-linéarités" (ReLU) : 1
2x Filtres 3x3 (Méthode VGG)
(Image 5x5) -> [Conv 3x3] -> (Image 3x3) -> [ReLU]
-> [Conv 3x3] -> (Output 1x1) -> [ReLU]
- Nb de Poids (Params) : (3*3) + (3*3) = 18
- Nb de "Non-linéarités" (ReLU) : 2
Résultat : Un stack de 3x3 (style VGG) est plus profond (plus de ReLU, donc plus "intelligent") et a moins de paramètres (18 vs 25) pour le *même* champ réceptif. C'est le "plugin" architectural de VGG.
VGG16 et VGG19 sont les deux architectures principales du papier. La seule différence est que VGG19 ajoute 3 couches de convolution (Conv 3x3) supplémentaires.
| Modèle | Couches (Poids) | Paramètres (Total) | Taille (.pth) | Erreur Top-5 (ImageNet) |
|---|---|---|---|---|
| VGG16 | 13 Conv + 3 FC = 16 | ~138 Millions | ~528 MB | 7.15% (Keras) |
| VGG19 | 16 Conv + 3 FC = 19 | ~144 Millions | ~549 MB | 7.13% (Keras) |
Conclusion (Le "Trade-off")
VGG19 n'est *pas* meilleur que VGG16.
L'ajout de couches (et de 6M de paramètres) n'apporte (presque) aucun gain de précision (7.13% vs 7.15%). En fait, il "overfit" (sur-apprend) *davantage* que VGG16 sur de nombreux datasets.
Règle : Dans 99% des cas (surtout en Transfer Learning), on utilise VGG16. C'est le "sweet spot" (point d'équilibre) entre la profondeur et le sur-apprentissage.
L'architecture de VGG16 est belle de simplicité. C'est juste un empilement de "blocs" Conv-Conv-Pool.
La seule chose qui change est le nombre de filtres (profondeur), qui *double* après chaque MaxPool (64 -> 128 -> 256 -> 512).
Diagramme (Architecture VGG16)
(Input: Image 224x224x3)
|
▼
[ Bloc 1: Conv(64) x2 + MaxPool(2x2, S2) ] (-> 112x112x64)
|
▼
[ Bloc 2: Conv(128) x2 + MaxPool(2x2, S2) ] (-> 56x56x128)
|
▼
[ Bloc 3: Conv(256) x3 + MaxPool(2x2, S2) ] (-> 28x28x256)
|
▼
[ Bloc 4: Conv(512) x3 + MaxPool(2x2, S2) ] (-> 14x14x512)
|
▼
[ Bloc 5: Conv(512) x3 + MaxPool(2x2, S2) ] (-> 7x7x512)
|
| (Flatten -> 7*7*512 = 25088)
▼
[ FC 6 (Fully Connected) (4096 neurones) + ReLU + Dropout ]
|
▼
[ FC 7 (Fully Connected) (4096 neurones) + ReLU + Dropout ]
|
▼
[ FC 8 (Output) (1000 neurones) + Softmax ]
|
▼
(Sortie: 1000 classes ImageNet)
VGG19 ? C'est la même chose, mais les Blocs 3, 4, et 5 ont 4 couches Conv au lieu de 3.
L'innovation *majeure* de VGG n'est pas un "plugin" compliqué, c'est une simplification extrême.
AlexNet (2012) utilisait un "chaos" de tailles de filtres (11x11, 5x5, 3x3).
VGG (2014) a posé la question : "Peut-on *uniquement* utiliser le plus petit filtre possible (3x3) et l'empiler ?".
Le "Champ Réceptif" (Receptive Field)
L'astuce est que deux couches de filtres 3x3 (empilées) ont le *même* "champ réceptif" (la zone de l'image qu'elles "voient") qu'une seule couche 5x5.
...et trois couches 3x3 équivalent à une couche 7x7.
Pourquoi est-ce mieux ? (Diagramme)
1x Filtre 5x5 (Ancienne méthode)
(Image 5x5) -> [Conv 5x5] -> (Output 1x1) - Nb de Poids (Params) : 5*5 = 25 - Nb de "Non-linéarités" (ReLU) : 1
2x Filtres 3x3 (Méthode VGG)
(Image 5x5) -> [Conv 3x3] -> (Image 3x3) -> [ReLU]
-> [Conv 3x3] -> (Output 1x1) -> [ReLU]
- Nb de Poids (Params) : (3*3) + (3*3) = 18
- Nb de "Non-linéarités" (ReLU) : 2
Résultat : Un stack de 3x3 (style VGG) est plus profond (plus de ReLU, donc plus "intelligent") et a moins de paramètres (18 vs 25) pour le *même* champ réceptif. C'est le "plugin" architectural de VGG.
VGG a été une étape cruciale, mais il est aujourd'hui obsolète pour la *classification* (SOTA - State-of-the-Art) en raison de deux défauts majeurs :
1. Le Poids (138M+ Paramètres)
La quasi-totalité des paramètres de VGG (~120M sur 138M) ne sont *pas* dans les convolutions (le "Backbone"), mais dans les dernières couches "Fully Connected" (FC).
Le (7x7x512) est "aplati" (flatten) en 25 088 neurones, qui sont connectés à 4096, qui sont connectés à 4096.
Rien que la première couche FC (25088 * 4096) représente 102 millions de poids.
Conséquence : Le fichier .pth (poids) de VGG16 pèse ~528 MB.
En comparaison, ResNet-50 (qui est *plus* précis) ne pèse que ~98 MB.
2. La Vitesse (Compute)
138M de paramètres, c'est *beaucoup* de multiplications. VGG est lent à l'entraînement et lent à l'inférence par rapport aux architectures modernes (ResNet, MobileNet).
3. Le "Vanishing Gradient" (Obsolète)
VGG (19 couches) était la *limite* de ce qu'on pouvait entraîner. Plus profond (ex: 30 couches), le "vanishing gradient" (voir AlexNet 1.4) revenait.
L'arrivée de ResNet (2015) et ses "Skip Connections" (voir AlexNet 3.2) a résolu ce problème et a rendu VGG obsolète pour la classification.
Le "plugin" torchvision (l'addon officiel de PyTorch pour la vision) fournit le modèle VGG pré-entraîné sur ImageNet.
Exemple (Inférence simple)
import torch
from torchvision import models, transforms
from PIL import Image
# 1. Charger VGG16 pré-entraîné
# (Télécharge 528MB la 1ère fois)
model = models.vgg16(pretrained=True)
model.eval() # IMPORTANT: Mode évaluation (désactive Dropout)
# 2. Définir les transforms (standard VGG)
preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]),
])
# 3. Charger & Pré-processer l'image
img = Image.open("mon_chien.jpg")
img_t = preprocess(img)
batch_t = torch.unsqueeze(img_t, 0) # (Ajoute la dimension batch)
# 4. Inférence
with torch.no_grad():
output = model(batch_t) # (Output: [1, 1000])
# 5. Interpréter
probabilities = torch.nn.functional.softmax(output[0], dim=0)
top5_prob, top5_catid = torch.topk(probabilities, 5)
# (Charger les labels ImageNet...)
print(f"Classe prédite (ID): {top5_catid[0]}")
L'écosystème Keras (intégré à TensorFlow) fournit également VGG comme "plugin" (keras.applications).
include_top=False (Le "Plugin" Transfer Learning)
Keras rend le Transfer Learning (voir 2.3) *très* facile avec l'argument include_top.
import tensorflow as tf
from tensorflow.keras.applications.vgg16 import VGG16, preprocess_input, decode_predictions
from tensorflow.keras.preprocessing import image
import numpy as np
# --- 1. Inférence Simple (Avec la "Tête") ---
model_full = VGG16(weights='imagenet', include_top=True)
img = image.load_img('mon_chien.jpg', target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x) # (Normalisation Keras)
preds = model_full.predict(x)
print(decode_predictions(preds, top=3)[0])
# (Affiche les 3 top prédictions ImageNet)
# --- 2. Mode "Feature Extractor" (Sans la "Tête") ---
# (C'est la base du Transfer Learning, voir 2.3)
model_features = VGG16(weights='imagenet', include_top=False)
# 'preds_features' n'est PAS (1, 1000),
# c'est la sortie du "Backbone" (1, 7, 7, 512)
preds_features = model_features.predict(x)
C'est l'usage n°1 de VGG16 aujourd'hui.
Le Problème : Je veux classer "Hotdog" vs "Not Hotdog", mais je n'ai que 500 images.
La Solution : Le Transfer Learning (Apprentissage par Transfert).
Les premières couches d'un CNN (comme VGG) apprennent des features *génériques* (détection de bords, textures, coins). Ces "yeux" (entraînés sur 1.1M d'images ImageNet) sont *plus performants* que tout ce qu'on peut entraîner sur 500 images.
Le Workflow (Keras)
- Charger le "Backbone" : On charge VGG16 *sans* sa tête (
include_top=False). - Geler (Freeze) : On dit à Keras de ne *pas* ré-entraîner les 138M de poids (
trainable = False). - Ajouter notre "Tête" : On ajoute *nos propres* couches (
Dense) à la fin (ex:Dense(2)pour "hotdog" / "not_hotdog"). - Entraîner : On n'entraîne *que* notre (petite) tête sur nos 500 images.
import tensorflow as tf
from tensorflow.keras import layers, models
# 1. Charger VGG16 (le "backbone") pré-entraîné
vgg_base = tf.keras.applications.VGG16(
weights='imagenet',
include_top=False, # (Important !)
input_shape=(224, 224, 3)
)
# 2. Geler (Freeze) le backbone
vgg_base.trainable = False
# 3. Créer notre nouveau modèle (le "plugin" par-dessus)
model = models.Sequential([
vgg_base, # (Le backbone gelé)
# 4. Notre "Tête" (Classifier)
layers.Flatten(),
layers.Dense(256, activation='relu'),
layers.Dropout(0.5),
layers.Dense(1, activation='sigmoid') # (1 neurone: hotdog/not_hotdog)
])
# 5. Compiler et entraîner
# (On n'entraîne QUE les 3 couches Dense)
model.compile(optimizer='adam', ...)
model.fit(mon_dataset_hotdog, ...)
C'est le projet (l' "addon" artistique) qui a rendu VGG célèbre *en dehors* de la classification. C'est l'algorithme "Deep Dream" (Gatys et al., 2015) qui permet de "peindre" une photo avec le style d'un Van Gogh.
Pourquoi VGG ? (Contenu vs Style)
Les chercheurs ont découvert que les différentes couches d'un CNN (comme VGG) séparent le "contenu" du "style" :
- Couches "Basses" (ex:
conv1_1) : Les features sont simples (bords, couleurs). Elles représentent le Style. - Couches "Hautes" (ex:
conv4_2) : Les features sont sémantiques (formes, "visage", "maison"). Elles représentent le Contenu.
Le Processus (Simplifié)
- On prend 3 images :
Contenu(une photo),Style(ex: Van Gogh),Résultat(un bruit blanc). - On charge un VGG19 pré-entraîné (gelé).
- On lance une "boucle d'optimisation" (des milliers de fois) :
- a. (Content Loss) : On passe
ContenuetRésultatdans VGG. On calcule la "distance" (MSE) entre les "features hautes" (conv4_2). On ajusteRésultat. - b. (Style Loss) : On passe
StyleetRésultatdans VGG. On calcule la "distance" entre les "features basses" (via une "Matrice de Gram"). On ajusteRésultat.
Résultat : Le Résultat (le bruit blanc) "devient" l'image de Contenu, mais "peinte" avec la texture (Style) de Van Gogh.
C'est l'utilisation "plugin" la plus avancée de VGG. VGG (ou "Perceptual") Loss est une "fonction de coût" (Loss Function) utilisée pour entraîner *d'autres* réseaux (ex: des GANs, ou des réseaux de Super-Résolution).
Le Problème (Loss L1 / L2)
Objectif : Apprendre à une IA à "déflouter" (deblur) une image.
Loss Classique (L2/MSE) : loss = (image_predite - image_reelle) ** 2.
Problème : Le L2 encourage les images *floues* (c'est la "moyenne" la plus sûre). Le résultat est "correct" mais visuellement moche.
La Solution (VGG Loss)
L'idée : "Ne pas comparer les *pixels* (L2), mais comparer ce que *VGG pense* des images."
Diagramme (Entraînement de "MonModele")
(Image Floue)
|
▼
+-----------------+
| MonModele (Générateur)
| (Apprend)
+-----------------+
|
▼
(Image Prédite) ----+
|
(Image Réelle) -----|
▼
+----------------+
| VGG16 (Gelé) | (Le "Juge")
| (N'apprend PAS)|
+----------------+
|
▼
(Features Prédites)
(Features Réelles)
|
▼
(Calcul de la "Distance" L2 entre les *features*)
|
▼
(Loss) -> (Backpropagation vers "MonModele")
On "force" le générateur (MonModele) à produire une image qui, pour VGG, *ressemble* (perceptuellement) à l'image réelle. Cela produit des résultats beaucoup plus nets et réalistes.
AlexNet (2012) a prouvé que "Deep" (profond) était la voie. VGG (2014) a prouvé que "Très Profond" (Very Deep) avec des filtres *uniformes* (3x3) l'était encore plus.
VGG a été le "Backbone" standard (le "plugin" feature extractor) de 2014 à 2016, jusqu'à l'arrivée de ResNet (2015), qui a résolu le problème de la profondeur (Vanishing Gradient) et reste le roi aujourd'hui.
Généalogie (Backbones) : LeNet (1998) -> AlexNet (2012) -> VGG (2014) -> GoogLeNet (2014) -> ResNet (2015) -> Vision Transformer (ViT) (2020)
