📱 MobileNet – Le Guide Ultime
Deep Dive : 🚀 Depthwise Separable Convolutions (v1), Inverted Residuals (v2), Squeeze-and-Excite (v3).
1. C'est quoi MobileNet ?
Famille de CNN (Google) ultra-efficaces pour les appareils "Edge" (Mobiles, Raspberry Pi).
MobileNet Edge AI2. Philosophie : Efficacité
Réduire les Params (Taille) & FLOPs (Calcul) vs VGG/ResNet. Le "plugin" d'efficacité.
FLOPs Efficiency3. 🚀 v1: Depthwise Separable
L'innovation "plugin" clé. Sépare Conv en Depthwise + Pointwise (1x1).
4. "Addons": Multiplicateurs
Width Multiplier (α) (filtres) & Resolution Multiplier (ρ) (taille image).
Alpha Rho5. v2: Inverted Residuals
L'évolution. Bottleneck inversé (Étroit -> Large -> Étroit) + "Skip Connections".
6. v3: NAS & Squeeze-Excite
Neural Arch Search. "Plugin" Squeeze-and-Excite (attention) + h-swish.
7. 📊 Chiffres (Params/FLOPs)
VGG16 (138M) vs ResNet50 (25M) vs MobileNetV1 (4.2M params). Les chiffres clés.
Performance FLOPs8. Code (Keras)
tf.keras.applications.MobileNetV2(). include_top=False, alpha=...
9. Code (PyTorch)
torchvision.models.mobilenet_v2(pretrained=True). Utilisation.
10. Projet: Transfer Learning
L'usage n°1. Remplacer la "tête" (classifier) pour un projet custom (ex: Raspberry Pi).
11. Addons: Déploiement
TensorFlow Lite (TFLite) (plugin n°1), Quantization (FP16, INT8), CoreML.
12. Projets & Liens
CV sur Raspberry Pi, Apps (iOS/Android). Liens (Papiers, tfhub.dev).
Projects LinksMobileNet est une famille d'architectures de Réseaux de Neurones Convolutifs (CNN) publiée par Google (Andrew G. Howard et al.) en 2017.
Sa philosophie est l'efficacité (efficiency).
Alors que VGG et ResNet visaient la *précision maximale* (au détriment de la taille et de la vitesse), MobileNet vise le *meilleur compromis* pour tourner sur des appareils à faible puissance :
- Smartphones (iOS, Android)
- Appareils "Edge" (Raspberry Pi, Google Coral, NVIDIA Jetson)
- Applications web (tensorflow.js)
MobileNet n'est pas *un* modèle, mais une recette (un "plugin") pour construire des CNNs légers, basée sur l'innovation des "Depthwise Separable Convolutions" (voir 1.3).
Pour un modèle "Edge", la précision (mAP) n'est pas le seul critère. Deux autres sont vitaux :
| Critère | Description | Pourquoi c'est important ? |
|---|---|---|
| 1. Paramètres (Poids) | Le nombre de "poids" (weights) du réseau. | Taille de l'application (Stockage). (ex: VGG16 = 528 MB. Une app mobile ne peut pas peser 528 MB). |
| 2. FLOPs (Floating Point Ops) | Le nombre d'opérations (calculs) nécessaires pour une inférence. | Vitesse (Latence) & Batterie. (Un CPU mobile ne peut pas faire 15 GFLOPs en temps réel). |
L'Ordre de Grandeur (Le "Problème")
| Modèle | Paramètres (Taille) | FLOPs (Calcul) |
|---|---|---|
| VGG-16 | ~138 Millions | ~15.5 GigaFLOPs |
| ResNet-50 | ~25.6 Millions | ~4.1 GigaFLOPs |
| MobileNetV1 | ~4.2 Millions | ~0.5 GigaFLOPs |
Résultat : MobileNet est ~30x plus petit et ~30x plus rapide que VGG, pour une perte de précision (mAP) minime.
C'est l'innovation *fondamentale* de MobileNet (inspirée de Xception). L'idée est de "casser" une convolution 3x3 standard (qui est très chère) en *deux* étapes très légères.
Diagramme (Standard vs MobileNet)
1. Convolution Standard (Conv 3x3)
"Mélange" les canaux (cross-channel) ET l'espace (spatial) en 1 étape.
(Input: 128x128x64)
|
▼
[Conv 3x3 (128 filtres)]
(Coût: (3*3*64) * 128 = 73,728 params)
|
▼
(Output: 128x128x128)
2. Depthwise Separable (MobileNet)
Sépare le travail en deux "plugins" (étapes).
(Input: 128x128x64)
|
▼
[A: Depthwise Conv 3x3] (Ne fait que le "spatial")
(Coût: (3*3*1) * 64 = 576 params)
|
▼
(Output: 128x128x64)
|
▼
[B: Pointwise Conv 1x1] (Ne fait que le "cross-channel")
(Coût: (1*1*64) * 128 = 8,192 params)
|
▼
(Output: 128x128x128)
Le Résultat (Chiffres)
Standard Conv : 73 728 Params.
MobileNet (DW + PW) : 576 + 8 192 = 8 768 Params.
Gain : 8.4x moins de calculs et de paramètres (pour un résultat quasi-identique). C'est le "plugin" d'efficacité de MobileNet.
MobileNet (v1) a introduit deux "addons" (hyperparamètres) qui permettent de *rétrécir* (ou grossir) l'architecture pour l'adapter à un besoin (ex: plus rapide vs plus précis).
| Hyperparamètre | Description | Impact |
|---|---|---|
1. Width Multiplier (Alpha α) | Le "multiplicateur de largeur". ( alpha < 1.0, ex: 0.75, 0.5) | Réduit le nombre de filtres (canaux) dans *chaque* couche. ( 64 filtres * 0.75 = 48 filtres). Impact (Params/FLOPs) : ~α² (Quadratique !). |
2. Resolution Multiplier (Rho ρ) | Le "multiplicateur de résolution". ( rho < 1.0, ex: 0.714) | Réduit la taille de l'image d'entrée (Input). (ex: 224px * 0.714 = 160px). Impact (FLOPs) : ~ρ² (Quadratique). |
Exemple (MobileNetV1 (1.0, 224))
- Base (1.0, 224) : 4.2M Params, 569M FLOPs, 70.9% mAP (ImageNet)
- (Width) (0.5, 224) : 1.3M Params, 149M FLOPs, 63.7% mAP
- (Resolution) (1.0, 160) : 4.2M Params, 290M FLOPs, 68.4% mAP
MobileNetV2 (2018) améliore v1 en résolvant 2 problèmes :
1. Les Depthwise Conv sont fragiles (ils "meurent" si les "features" en entrée sont nulles).
2. Il n'y a pas de "skip connections" (ResNet) pour aider le gradient.
La solution est le "Inverted Residual Bottleneck" (Bloc Goulot Inversé).
ResNet (Bottleneck) vs MobileNetV2 (Inverted)
ResNet (voir 1.5 ResNet) utilise un "plugin" Bottleneck : Large -> Étroit -> Large.
MobileNetV2 fait l'inverse : Étroit -> Large -> Étroit.
Diagramme (Bloc v2)
(Input: 28x28x64)
|
+-----------------(Skip Connection)-----------------+
| |
▼ |
[ 1. Conv 1x1 (Expansion) -> 28x28x384 (Large) ] | (Facteur 6x)
| |
▼ |
[ 2. Depthwise Conv 3x3 -> 28x28x384 ] |
| |
▼ |
[ 3. Conv 1x1 (Projection) -> 28x28x64 (Étroit) ] |
| (Note: SANS ReLU ! "Linear Bottleneck") |
▼ |
+---------------------[ ADDITION ] <----------------+
|
▼
(Output)
Pourquoi ? L'astuce est que le "skip" (ResNet) connecte les parties *larges* (Bottleneck). Ici, le "skip" connecte les parties *étroites* (Inverted). Cela force le réseau à garder l'information essentielle dans le "goulot étroit".
MobileNetV3 (2019) n'est pas une "révolution" (comme v1 ou v2), mais une *optimisation* de v2. L'architecture a été "découverte" par NAS (Neural Architecture Search) : une IA qui "teste" des milliers d'architectures pour trouver la plus rapide.
Les 2 "Plugins" de v3
1. Squeeze-and-Excite (SE)
Un "plugin" (addon) ajouté à certains "Inverted Residual Blocks" (de v2).
L'idée : "Apprendre au réseau à *pondérer* l'importance des canaux (features)".
- Squeeze : "Presse" (via Global Avg Pooling) les features (ex:
1x1x128) en 1 vecteur. - Excite : Fait passer ce vecteur dans 2 couches FC (un "mini-réseau") pour prédire un "score" (importance) pour *chaque* canal.
- Scale : Multiplie les canaux originaux par ces scores.
Résultat : Le réseau "apprend" à "augmenter le volume" des canaux utiles (ex: "texture de fourrure") et à "baisser le volume" des canaux inutiles (ex: "couleur du ciel").
2. h-swish (Nouvelle Activation)
Remplace ReLU/swish par h-swish, une version "plus rapide" (moins chère en CPU/Batterie) de la fonction d'activation swish.
Comparaison des "poids lourds" (VGG/ResNet) vs les "poids plumes" (MobileNet) sur ImageNet (Classification).
| Modèle | mAP (Top-1) | Paramètres (Taille) | FLOPs (Calcul) |
|---|---|---|---|
| VGG-16 (2014) | 71.5% | ~138 Millions | ~15.5 GFLOPs |
| ResNet-50 (2015) | 75.2% | ~25.6 Millions | ~4.1 GFLOPs |
| MobileNetV1 (1.0, 224) | 70.9% | ~4.2 Millions | ~0.57 GFLOPs |
| MobileNetV2 (1.0, 224) | 72.0% | ~3.5 Millions | ~0.30 GFLOPs |
| MobileNetV3-Large | 75.2% | ~5.4 Millions | ~0.22 GFLOPs |
Conclusion (L'impact du "plugin" MobileNet) :
MobileNetV3-Large atteint la *même précision* que ResNet-50 (75.2%), mais avec 5x moins de paramètres (5.4M vs 25.6M) et 18x moins de calculs (0.22 GFLOPs vs 4.1 GFLOPs).
L'écosystème Keras (intégré à TensorFlow) fournit MobileNet (v1, v2, v3) comme "plugin" (keras.applications).
Inférence Simple (Keras)
import tensorflow as tf
from tensorflow.keras.applications.mobilenet_v2 import MobileNetV2, preprocess_input, decode_predictions
from tensorflow.keras.preprocessing import image
import numpy as np
# 1. Charger MobileNetV2 pré-entraîné (avec la "tête")
model_full = MobileNetV2(weights='imagenet', include_top=True)
img = image.load_img('mon_chat.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])
Mode "Feature Extractor" (include_top=False)
C'est la base du Transfer Learning (voir 3.1).
# 2. Charger SANS la "tête" (classifier)
# (On peut aussi utiliser le "plugin" alpha (largeur))
model_base = MobileNetV2(weights='imagenet',
include_top=False,
alpha=0.75,
input_shape=(224, 224, 3))
# 'preds_features' n'est PAS (1, 1000),
# c'est la sortie du "Backbone" (ex: 1, 7, 7, 1280)
preds_features = model_base.predict(x)
Le "plugin" torchvision (l'addon officiel de PyTorch pour la vision) fournit également MobileNet v2 et v3 pré-entraînés.
Inférence Simple (PyTorch)
import torch
from torchvision import models, transforms
from PIL import Image
# 1. Charger MobileNetV3 (Large) pré-entraîné
model = models.mobilenet_v3_large(pretrained=True)
model.eval() # IMPORTANT: Mode évaluation
# 2. Définir les transforms (standard ImageNet)
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_chat.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)
top_class_index = torch.argmax(probabilities).item()
print(f"Prédiction: Classe {top_class_index}")
C'est l'usage n°1 de MobileNet.
Le Problème : Je veux créer un classifieur "Poubelle Jaune / Verte / Verre" pour un Raspberry Pi. Je n'ai que 3000 images et j'ai besoin d'une inférence rapide (CPU).
La Solution : Le Transfer Learning (Apprentissage par Transfert) sur MobileNet.
On "gèle" (freeze) le "Backbone" (l'extracteur de features) et on ne ré-entraîne *que* la "Tête" (classifier) pour nos 3 classes.
Le Workflow (PyTorch)
import torch
import torch.nn as nn
from torchvision import models
# 1. Charger MobileNetV3 (le "backbone") pré-entraîné
model = models.mobilenet_v3_small(pretrained=True)
# 2. Geler (Freeze) le backbone
# (On ne veut pas ré-entraîner les 2.5M de poids de Google)
for param in model.parameters():
param.requires_grad = False
# 3. Remplacer la "Tête" (Classifier)
# (Le classifier de V3 s'appelle 'classifier')
num_features = model.classifier[0].in_features
num_classes = 3 # (Jaune, Vert, Verre)
# On remplace la tête FC de 1000 classes par la nôtre
model.classifier = nn.Sequential(
nn.Linear(num_features, 128),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(128, num_classes)
)
# 4. Compiler et entraîner
# (On n'entraîne QUE les poids de notre nouvelle tête)
optimizer = torch.optim.Adam(model.classifier.parameters(), lr=0.001)
# ... (Boucle d'entraînement) ...
Avoir un .pth (PyTorch) ou .h5 (Keras) de 4.2M de params, c'est bien. L'avoir en 1.5M, c'est mieux. C'est le rôle des "addons" (convertisseurs) de déploiement.
1. TensorFlow Lite (TFLite) - Le "Plugin" Mobile
C'est le "plugin" (framework) n°1 pour déployer sur Android, Raspberry Pi, et Microcontrôleurs (MCU).
Il prend un modèle Keras/TF (.h5) et le convertit en .tflite (un format plat, optimisé).
2. La Quantization (Le "Plugin" d'optimisation)
C'est l'étape *cruciale* pour l'inférence "Edge".
Le Problème : Par défaut, les poids (params) sont stockés en Float32 (32 bits). C'est lourd et lent pour un CPU mobile.
La Solution : On "quantize" (quantifie) le modèle en Float16 (taille / 2) ou INT8 (taille / 4).
Exemple (Conversion TFLite + Quantization)
import tensorflow as tf
# 1. Charger le modèle Keras (ex: MobileNetV2)
model = tf.keras.applications.MobileNetV2()
# 2. Créer le convertisseur TFLite
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 3. (Optionnel) Activer l'optimisation (Quantization FP16)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
# 4. Convertir
tflite_model = converter.convert()
# 5. Sauver le "plugin" (le fichier .tflite)
with open('mobilenet_v2_fp16.tflite', 'wb') as f:
f.write(tflite_model)
Résultat : Le modèle .h5 (14MB) devient .tflite (7MB) et tourne 2x plus vite sur un GPU mobile.
Exemples de Projets
- Détection (SSD-MobileNet) : Le "plugin" MobileNet est le "backbone" standard pour les détecteurs d'objets légers (ex: SSD-MobileNetV2) utilisés sur les caméras de sécurité ou les Raspberry Pi.
- Classification (Mobile) : Applications "Shazam" pour plantes/animaux (ex: "Seek by iNaturalist").
- Pose (Webcam) : Inférence tensorflow.js (
tfjs) dans le navigateur pour de l'analyse de posture (fitness, etc.) sans serveur.
- MobileNets v1 (Papier 2017) "Efficient Convolutional Neural Networks for Mobile Vision Applications". (Depthwise Conv).
- MobileNet v2 (Papier 2018) "Inverted Residuals and Linear Bottlenecks".
- MobileNet v3 (Papier 2019) "Searching for MobileNetV3" (NAS, Squeeze-Excite).
- TensorFlow Lite (Addon TFLite) Le "plugin" de déploiement n°1 pour MobileNet sur "Edge".
