ResNet est l’un des rares papiers de recherche en deep learning dont l’idée centrale tient en une ligne de code, mais dont la mise en production soulève des questions que le papier original ne traite pas. Passer du PDF de 2015 signé par Kaiming He et al. à un pipeline fonctionnel exige de comprendre ce que le papier dit, ce qu’il ne dit pas, et ce que les frameworks modernes ont ajouté depuis.
Le contrat de prétraitement ResNet : ce que le papier ne spécifie pas
La plupart des tutoriels commencent par l’architecture, les blocs résiduels, les connexions de saut. L’angle est légitime, mais il masque un problème plus immédiat : le preprocessing conditionne la qualité du transfer learning.
A voir aussi : Comment accéder au portail éducatif de l'université de créteil à partir de votre webmail ?
Un modèle ResNet pré-entraîné sur ImageNet attend des images redimensionnées et normalisées selon des paramètres précis. Si le resize ou la normalisation ne correspondent pas aux valeurs utilisées lors de l’entraînement initial, les features extraites par le backbone se dégradent. Le papier original ne détaille pas ces paramètres de manière opérationnelle, car il s’adresse à un public de chercheurs qui partage un cadre expérimental implicite.
En pratique, cela signifie qu’avant d’écrire une seule ligne d’entraînement, il faut aligner trois éléments : la résolution d’entrée, les moyennes par canal et les écarts types de normalisation. PyTorch, par exemple, expose ces valeurs dans la documentation de ses modèles pré-entraînés. Les ignorer ou les approximer produit des résultats qui semblent fonctionner sur un jeu de données jouet, mais qui s’effondrent sur des données réelles.
A voir aussi : Faire appel à un prestataire informatique : pourquoi ?

Bloc résiduel ResNet : de la formule mathématique au code PyTorch
L’idée maîtresse de ResNet repose sur une reformulation simple du problème d’apprentissage. Au lieu de demander à un bloc de couches d’apprendre directement une transformation souhaitée H(x), on lui demande d’apprendre le résidu F(x) = H(x) – x. La sortie du bloc devient alors F(x) + x, grâce à une connexion de saut qui additionne l’entrée à la sortie.
Cette addition élimine le principal obstacle à l’entraînement de réseaux très profonds : la disparition du gradient dans les premières couches. Le signal peut traverser le bloc sans altération via la connexion de saut, ce qui garantit un chemin de gradient direct entre la perte et les couches profondes.
Implémentation minimale d’un ResidualBlock
Un bloc résiduel standard en PyTorch se compose de deux convolutions 3×3, chacune suivie d’une normalisation par batch et d’une activation ReLU. La connexion de saut additionne le tenseur d’entrée au résultat de ces deux convolutions, avant une dernière activation.
- Deux couches Conv2d avec kernel 3×3 et padding 1 pour conserver les dimensions spatiales
- Deux couches BatchNorm2d qui stabilisent la distribution des activations à chaque étape
- Une addition (et non une concaténation) entre l’entrée et la sortie du bloc, ce qui impose que les dimensions correspondent
- Une activation ReLU après l’addition, qui produit la sortie finale du bloc
Quand les dimensions d’entrée et de sortie diffèrent (changement de nombre de canaux ou de résolution spatiale), le papier propose une convolution 1×1 sur la connexion de saut pour ajuster les dimensions. C’est ce qu’on appelle le bloc avec projection, par opposition au bloc identité.
Fine-tuning progressif et gel des couches : la stratégie opérationnelle
Le papier original décrit l’entraînement complet d’un réseau depuis zéro. En production, la majorité des cas d’usage reposent sur le transfer learning : on charge un ResNet pré-entraîné sur ImageNet, on remplace la dernière couche fully connected par une couche adaptée au nombre de classes cibles, et on entraîne.
La pratique qui s’est normalisée ces dernières années consiste à geler le backbone puis libérer progressivement les couches. On commence par n’entraîner que la tête de classification, avec un taux d’apprentissage relativement élevé. Puis on dégèle les derniers blocs résiduels, avec un taux d’apprentissage réduit, avant éventuellement de libérer l’ensemble du réseau.
Cette approche évite de détruire les features génériques apprises sur ImageNet (contours, textures, formes) pendant les premières itérations, lorsque la tête de classification produit encore des gradients erratiques. Les retours terrain divergent sur le nombre optimal de couches à dégeler et sur le rythme de dégel, car cela dépend fortement de la taille du jeu de données cible et de sa proximité avec ImageNet.
ResNet comme backbone : au-delà de la classification d’images
Les contenus pédagogiques présentent presque toujours ResNet comme un classifieur. Le réseau classe une image parmi N catégories, point final. Cette présentation reflète le papier original, mais pas l’usage actuel.
ResNet sert aujourd’hui de backbone dans des architectures hybrides pour des tâches très différentes de la classification. En segmentation sémantique, un ResNet tronqué (sans la couche fully connected finale) fournit les cartes de features à un décodeur de type U-Net. En détection d’objets, il alimente des architectures comme RetinaNet ou Faster R-CNN. Dans ces cas, la partie « tête » du réseau est entièrement remplacée par des modules spécialisés.
Cette polyvalence explique pourquoi ResNet reste un choix de référence malgré l’apparition d’architectures plus récentes. Le backbone a été validé sur un éventail de tâches suffisamment large pour que ses features soient considérées comme robustes et transférables.

Du notebook au pipeline de production : les étapes que le papier ne couvre pas
Un notebook Jupyter qui entraîne un ResNet sur un jeu de données de fleurs n’est pas un système de production. La transition implique une modularisation que les tutoriels classiques n’abordent pas.
- Séparation du code de transformation des données (resize, normalisation, augmentation) dans un module dédié, réutilisable entre l’entraînement et l’inférence
- Gestion explicite du DataLoader avec paramétrage du nombre de workers, du batch size et du prefetch pour exploiter le GPU sans goulot d’étranglement côté CPU
- Sauvegarde du modèle au format adapté au déploiement (state_dict pour PyTorch, ONNX pour l’interopérabilité), avec versioning des poids
- Validation sur un jeu de test isolé, jamais vu pendant l’entraînement ni pendant le choix des hyperparamètres
La reproductibilité dépend de la fixation des graines aléatoires et du contrôle des versions de bibliothèques. Un même code peut produire des résultats différents entre deux versions de PyTorch si les algorithmes cuDNN sous-jacents changent.
Choix de la variante ResNet
ResNet existe en plusieurs profondeurs. Les variantes les plus courantes sont ResNet-18, ResNet-34, ResNet-50, ResNet-101 et ResNet-152. Les variantes à partir de ResNet-50 utilisent des blocs bottleneck (convolution 1×1, puis 3×3, puis 1×1) au lieu de blocs simples à deux convolutions 3×3. Le bottleneck réduit le nombre de paramètres tout en maintenant la capacité du réseau.
Le choix dépend de la taille du jeu de données cible et des ressources de calcul disponibles. Un ResNet-50 pré-entraîné constitue souvent le meilleur point de départ pour du transfer learning, car il offre un bon compromis entre richesse des features et coût d’entraînement. Un ResNet-18 suffit quand les données cibles sont peu nombreuses et que le risque de surapprentissage est élevé.
Le passage du papier au code opérationnel n’est pas une question de complexité algorithmique. L’architecture elle-même se code en quelques dizaines de lignes. La difficulté réside dans tout ce qui entoure le modèle : le prétraitement exact, la stratégie de gel et de dégel, la modularisation du pipeline, et le choix de la bonne variante pour le bon problème. Ce sont ces décisions, absentes du papier de 2015, qui séparent un prototype fonctionnel d’un système fiable.

