Vision par Ordinateur avec PyTorch en 2026 : CNN, Transfer Learning et Questions d'Entretien
Guide complet sur la vision par ordinateur avec PyTorch : réseaux de neurones convolutifs, transfer learning et préparation aux entretiens techniques en deep learning.

La vision par ordinateur représente l'un des domaines les plus dynamiques de l'intelligence artificielle. Avec PyTorch comme framework de prédilection, les développeurs et data scientists peuvent construire des modèles sophistiqués de classification d'images, de détection d'objets et de segmentation sémantique. Ce guide explore les concepts fondamentaux des CNN, les techniques de transfer learning et les questions fréquentes en entretien technique.
La maîtrise des CNN et du transfer learning constitue un atout majeur pour les postes en machine learning. Les recruteurs évaluent souvent la compréhension pratique de ces concepts lors des entretiens techniques.
Fondamentaux des Réseaux de Neurones Convolutifs
Les réseaux de neurones convolutifs (CNN) forment l'architecture de base pour le traitement d'images. Contrairement aux réseaux fully-connected, les CNN exploitent la structure spatiale des données visuelles grâce à des opérations de convolution.
L'architecture typique d'un CNN comprend plusieurs types de couches : les couches de convolution extraient des caractéristiques locales, les couches de pooling réduisent la dimensionnalité, et les couches fully-connected effectuent la classification finale.
import torch
import torch.nn as nn
import torch.nn.functional as F
class SimpleCNN(nn.Module):
def __init__(self, num_classes: int = 10):
super(SimpleCNN, self).__init__()
# Convolutional layers
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
# Batch normalization
self.bn1 = nn.BatchNorm2d(32)
self.bn2 = nn.BatchNorm2d(64)
self.bn3 = nn.BatchNorm2d(128)
# Pooling and dropout
self.pool = nn.MaxPool2d(2, 2)
self.dropout = nn.Dropout(0.5)
# Fully connected layers
self.fc1 = nn.Linear(128 * 4 * 4, 512)
self.fc2 = nn.Linear(512, num_classes)
def forward(self, x: torch.Tensor) -> torch.Tensor:
# Block 1: 32x32 -> 16x16
x = self.pool(F.relu(self.bn1(self.conv1(x))))
# Block 2: 16x16 -> 8x8
x = self.pool(F.relu(self.bn2(self.conv2(x))))
# Block 3: 8x8 -> 4x4
x = self.pool(F.relu(self.bn3(self.conv3(x))))
# Flatten and classify
x = x.view(-1, 128 * 4 * 4)
x = self.dropout(F.relu(self.fc1(x)))
x = self.fc2(x)
return xCe modèle illustre les composants essentiels : la batch normalization stabilise l'entraînement, le dropout prévient le surapprentissage, et le max pooling réduit progressivement les dimensions spatiales.
Configuration de l'Environnement et Préparation des Données
Avant d'entraîner un modèle, la préparation des données constitue une étape critique. PyTorch fournit des outils puissants pour le chargement et l'augmentation des données.
import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from typing import Tuple
def create_data_loaders(
data_dir: str,
batch_size: int = 32,
num_workers: int = 4
) -> Tuple[DataLoader, DataLoader]:
"""Create training and validation data loaders with augmentation."""
# Training transforms with augmentation
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.RandomRotation(15),
transforms.ColorJitter(
brightness=0.2,
contrast=0.2,
saturation=0.2
),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
# Validation transforms (no augmentation)
val_transform = 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]
)
])
# Create datasets
train_dataset = datasets.ImageFolder(
root=f"{data_dir}/train",
transform=train_transform
)
val_dataset = datasets.ImageFolder(
root=f"{data_dir}/val",
transform=val_transform
)
# Create data loaders
train_loader = DataLoader(
train_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=num_workers,
pin_memory=True
)
val_loader = DataLoader(
val_dataset,
batch_size=batch_size,
shuffle=False,
num_workers=num_workers,
pin_memory=True
)
return train_loader, val_loaderL'augmentation de données améliore la robustesse du modèle en créant des variations artificielles des images d'entraînement. Les transformations géométriques et colorimétriques augmentent la diversité du dataset sans nécessiter de nouvelles données.
Transfer Learning avec des Modèles Pré-entraînés
Le transfer learning permet d'exploiter des modèles entraînés sur de vastes datasets comme ImageNet. Cette approche réduit considérablement le temps d'entraînement et améliore les performances, particulièrement avec des datasets limités.
import torch
import torch.nn as nn
from torchvision import models
from torchvision.models import ResNet50_Weights, EfficientNet_B0_Weights
def create_transfer_model(
model_name: str,
num_classes: int,
freeze_backbone: bool = True
) -> nn.Module:
"""Create a transfer learning model with a custom classifier."""
if model_name == "resnet50":
# Load pre-trained ResNet50
weights = ResNet50_Weights.IMAGENET1K_V2
model = models.resnet50(weights=weights)
# Freeze backbone if specified
if freeze_backbone:
for param in model.parameters():
param.requires_grad = False
# Replace classifier
num_features = model.fc.in_features
model.fc = nn.Sequential(
nn.Dropout(0.5),
nn.Linear(num_features, 512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, num_classes)
)
elif model_name == "efficientnet_b0":
# Load pre-trained EfficientNet
weights = EfficientNet_B0_Weights.IMAGENET1K_V1
model = models.efficientnet_b0(weights=weights)
if freeze_backbone:
for param in model.parameters():
param.requires_grad = False
# Replace classifier
num_features = model.classifier[1].in_features
model.classifier = nn.Sequential(
nn.Dropout(0.5),
nn.Linear(num_features, num_classes)
)
else:
raise ValueError(f"Unknown model: {model_name}")
return model
def unfreeze_layers(model: nn.Module, num_layers: int) -> None:
"""Gradually unfreeze layers for fine-tuning."""
# Get all parameters as a list
params = list(model.parameters())
# Unfreeze the last n layers
for param in params[-num_layers:]:
param.requires_grad = TrueLa stratégie de gel progressif des couches optimise le processus d'entraînement. Les premières couches, qui détectent des caractéristiques génériques comme les bords et les textures, restent gelées. Les couches supérieures, spécifiques à la tâche, sont entraînées sur le nouveau dataset.
Boucle d'Entraînement Complète
Une boucle d'entraînement robuste intègre la validation, le suivi des métriques et la sauvegarde des meilleurs modèles.
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from typing import Dict, List
import copy
class Trainer:
def __init__(
self,
model: nn.Module,
train_loader: DataLoader,
val_loader: DataLoader,
learning_rate: float = 1e-4,
device: str = "cuda"
):
self.model = model.to(device)
self.train_loader = train_loader
self.val_loader = val_loader
self.device = device
self.criterion = nn.CrossEntropyLoss()
self.optimizer = optim.AdamW(
filter(lambda p: p.requires_grad, model.parameters()),
lr=learning_rate,
weight_decay=0.01
)
self.scheduler = optim.lr_scheduler.CosineAnnealingLR(
self.optimizer,
T_max=50
)
self.best_model_weights = None
self.best_accuracy = 0.0
self.history: Dict[str, List[float]] = {
"train_loss": [],
"val_loss": [],
"val_accuracy": []
}
def train_epoch(self) -> float:
"""Train for one epoch."""
self.model.train()
running_loss = 0.0
for images, labels in self.train_loader:
images = images.to(self.device)
labels = labels.to(self.device)
self.optimizer.zero_grad()
outputs = self.model(images)
loss = self.criterion(outputs, labels)
loss.backward()
self.optimizer.step()
running_loss += loss.item()
return running_loss / len(self.train_loader)
@torch.no_grad()
def validate(self) -> tuple[float, float]:
"""Validate the model."""
self.model.eval()
running_loss = 0.0
correct = 0
total = 0
for images, labels in self.val_loader:
images = images.to(self.device)
labels = labels.to(self.device)
outputs = self.model(images)
loss = self.criterion(outputs, labels)
running_loss += loss.item()
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
accuracy = correct / total
avg_loss = running_loss / len(self.val_loader)
return avg_loss, accuracy
def fit(self, epochs: int) -> Dict[str, List[float]]:
"""Full training loop."""
for epoch in range(epochs):
train_loss = self.train_epoch()
val_loss, val_accuracy = self.validate()
self.scheduler.step()
# Save history
self.history["train_loss"].append(train_loss)
self.history["val_loss"].append(val_loss)
self.history["val_accuracy"].append(val_accuracy)
# Save best model
if val_accuracy > self.best_accuracy:
self.best_accuracy = val_accuracy
self.best_model_weights = copy.deepcopy(
self.model.state_dict()
)
print(
f"Epoch {epoch+1}/{epochs} - "
f"Train Loss: {train_loss:.4f} - "
f"Val Loss: {val_loss:.4f} - "
f"Val Acc: {val_accuracy:.4f}"
)
# Load best weights
self.model.load_state_dict(self.best_model_weights)
return self.historyCette implémentation utilise l'optimiseur AdamW avec weight decay pour la régularisation et un scheduler cosine annealing pour ajuster dynamiquement le learning rate.
Techniques Avancées pour la Vision par Ordinateur
Les architectures modernes intègrent des mécanismes d'attention qui améliorent la capacité du modèle à se concentrer sur les régions pertinentes de l'image.
import torch
import torch.nn as nn
import torch.nn.functional as F
class ChannelAttention(nn.Module):
"""Squeeze-and-Excitation channel attention."""
def __init__(self, channels: int, reduction: int = 16):
super().__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channels, channels // reduction, bias=False),
nn.ReLU(inplace=True),
nn.Linear(channels // reduction, channels, bias=False),
nn.Sigmoid()
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y.expand_as(x)
class SpatialAttention(nn.Module):
"""CBAM spatial attention module."""
def __init__(self, kernel_size: int = 7):
super().__init__()
self.conv = nn.Conv2d(
2, 1,
kernel_size=kernel_size,
padding=kernel_size // 2,
bias=False
)
self.sigmoid = nn.Sigmoid()
def forward(self, x: torch.Tensor) -> torch.Tensor:
avg_out = torch.mean(x, dim=1, keepdim=True)
max_out, _ = torch.max(x, dim=1, keepdim=True)
attention = torch.cat([avg_out, max_out], dim=1)
attention = self.sigmoid(self.conv(attention))
return x * attentionLes mécanismes d'attention channel et spatial permettent au réseau d'apprendre quelles caractéristiques et quelles régions sont les plus importantes pour la tâche de classification.
Prêt à réussir tes entretiens Data Science & ML ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Questions d'Entretien Fréquentes en Vision par Ordinateur
Les entretiens techniques en deep learning couvrent généralement des concepts théoriques et pratiques. Voici les questions les plus courantes avec leurs réponses.
Question : Expliquez la différence entre valid et same padding dans les convolutions.
Le padding "valid" n'ajoute aucun pixel autour de l'image, ce qui réduit les dimensions de sortie. Le padding "same" ajoute des pixels pour maintenir les dimensions d'entrée identiques aux dimensions de sortie. Le choix dépend de l'architecture souhaitée et de la préservation de l'information spatiale aux bords.
Question : Comment le batch normalization améliore-t-il l'entraînement des CNN ?
Le batch normalization normalise les activations à chaque mini-batch, réduisant le covariate shift interne. Cette technique accélère la convergence, permet d'utiliser des learning rates plus élevés et agit comme régularisateur. Durant l'inférence, les statistiques de moyenne et variance sont calculées sur l'ensemble du dataset d'entraînement.
Question : Quand utiliser le transfer learning versus entraîner from scratch ?
Le transfer learning est préférable avec des datasets limités, des tâches similaires au dataset source, ou des contraintes de ressources computationnelles. L'entraînement from scratch convient aux datasets massifs, aux domaines très différents d'ImageNet (images médicales, satellites), ou lorsque les caractéristiques de bas niveau diffèrent significativement.
Question : Expliquez le concept de receptive field dans les CNN.
Le receptive field représente la région de l'image d'entrée qui influence un neurone particulier. Il augmente avec la profondeur du réseau grâce aux convolutions et poolings successifs. Un receptive field plus large permet au modèle de capturer des dépendances spatiales à plus grande échelle, essentiel pour la compréhension contextuelle.
Question : Comment gérer le déséquilibre de classes en classification d'images ?
Plusieurs stratégies existent : pondération des classes dans la loss function, suréchantillonnage des classes minoritaires, sous-échantillonnage des classes majoritaires, ou génération de données synthétiques via augmentation ciblée. Le choix dépend de la sévérité du déséquilibre et de la taille du dataset.
Métriques d'Évaluation pour la Vision par Ordinateur
L'évaluation rigoureuse d'un modèle nécessite des métriques adaptées à la tâche et au contexte métier.
import torch
import numpy as np
from sklearn.metrics import (
confusion_matrix,
classification_report,
precision_recall_fscore_support
)
from typing import Dict, List
@torch.no_grad()
def evaluate_model(
model: torch.nn.Module,
test_loader: torch.utils.data.DataLoader,
class_names: List[str],
device: str = "cuda"
) -> Dict:
"""Comprehensive model evaluation."""
model.eval()
all_predictions = []
all_labels = []
all_probabilities = []
for images, labels in test_loader:
images = images.to(device)
outputs = model(images)
probabilities = torch.softmax(outputs, dim=1)
_, predicted = outputs.max(1)
all_predictions.extend(predicted.cpu().numpy())
all_labels.extend(labels.numpy())
all_probabilities.extend(probabilities.cpu().numpy())
# Calculate metrics
precision, recall, f1, support = precision_recall_fscore_support(
all_labels,
all_predictions,
average=None
)
# Per-class metrics
per_class_metrics = {
class_names[i]: {
"precision": float(precision[i]),
"recall": float(recall[i]),
"f1_score": float(f1[i]),
"support": int(support[i])
}
for i in range(len(class_names))
}
# Overall metrics
accuracy = np.mean(np.array(all_predictions) == np.array(all_labels))
macro_f1 = np.mean(f1)
return {
"accuracy": float(accuracy),
"macro_f1": float(macro_f1),
"per_class": per_class_metrics,
"confusion_matrix": confusion_matrix(
all_labels,
all_predictions
).tolist()
}L'accuracy seule peut être trompeuse avec des classes déséquilibrées. Le F1-score macro fournit une vue plus équilibrée des performances sur toutes les classes.
Conclusion
La vision par ordinateur avec PyTorch offre des possibilités considérables pour résoudre des problèmes complexes de traitement d'images. La maîtrise des CNN, du transfer learning et des techniques d'attention constitue une base solide pour les praticiens du deep learning. La préparation aux entretiens techniques nécessite une compréhension approfondie de ces concepts, combinée à une expérience pratique de leur implémentation. L'écosystème PyTorch continue d'évoluer avec de nouvelles architectures et optimisations, rendant essentielle une veille technologique constante dans ce domaine en rapide évolution.

Écrit par
Anthony Fillion-MailletDéveloppeur fullstack, fondateur de SharpSkill
Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.
Mis à jour le 14 août 2026
Partager
Articles similaires

LangChain pour les Data Scientists en 2026 : LLMs, Agents et Questions d'Entretien
Guide complet sur LangChain pour les data scientists : maîtriser les LLMs, les agents autonomes et se préparer aux questions d'entretien technique en 2026.

MLOps en 2026 : MLflow, Model Registry et questions d'entretien technique
Questions d'entretien MLOps couvrant le cycle de vie ML, le suivi des expérimentations MLflow, la promotion via le model registry, les patterns de déploiement, la supervision de dérive et le system design pour 2026, avec du code Python et des réponses.

RAG et LLMs en 2026 : Guide Complet pour les Entretiens Data Science
Maîtrisez RAG (Retrieval-Augmented Generation) pour les entretiens data science en 2026. Pipelines, chunking sémantique, bases vectorielles, RAG agentique et évaluation en production.