Session 2 : Data First !

Ne pensez pas que le principal défi réside dans la mise en place d’optimisations complexes : le véritable enjeu est la qualité des données. Toute négligence dans leur préparation expose à des erreurs discrètes mais redoutables, capables d’empêcher le modèle d’apprendre correctement.

Ainsi, lorsque les données ne sont pas correctement préparées, les performances resteront faibles ou stagnantes, quels que soient les efforts fournis. Il est donc essentiel de traiter ces problèmes en priorité pour que le transfer learning puisse fonctionner correctement.

Data Leakage

Symptôme

Après plusieurs heures d’expérimentation — variation d’optimiseur, de learning rate, ou de stratégie de fine-tuning — un constat surprenant s’impose : le modèle plafonne systématiquement autour de 88 % d’accuracy. Phénomène d’autant plus étrange qu’il s’agit déjà d’un très bon score pour le problème considéré.

Observations :

  • Avoir une accuracy qui ne change pas quelle que soit l’optimisation appliquée est étrange

  • Obtenir un quasi best score sans aucune optimisation est encore plus étrange

Analyse

A force de chercher, en examinant le dataset Oxford-IIIT Pet, on remarque que certains animaux ont été pris en photo plusieurs fois dans des conditions légèrement différentes. C’est compréhensible, certaines espèces sont rares et il est normal dans ce cas de prendre plusieurs photos de l’animal en question dans différentes poses. Cependant, nous avions habituellement créé nos sets d’images de train et de validation en découpant aléatoirement le set de train fourni par Torchvision. Cette approche est normalement « professionnelle » et ne posent pas problème. Mais dans le contexte d’Oxford-IIIT Pet, en utilisant cette répartition, des images d’un même animal risquent de se retrouver à la fois dans le set de train et dans celui de validation.

Conséquence

Le modèle retrouve dans le set de validation des individus déjà observés pendant l’entraînement, avec un angle de vue cependant différent. Mais alors, il ne s’agit plus de reconnaître une race, mais implicitement un individu. Ce phénomène introduit une fuite d’information (data leakage) qui a deux effets :

  • Une accuracy artificiellement élevée dès les premières itérations

  • Un plateau rapide de l’accuracy rendant les optimisations inefficaces

Ainsi, les performances observées ne reflètent pas la capacité du modèle à généraliser sa connaissance pour classifier de nouveaux individus, mais sa capacité à reconnaître des individus déjà connus.

Bilan

Pour corriger cela, nous utiliserons l’intégralité du set de train d’Oxford-III Pet pour composer le set de train du transfer learning et nous utiliserons le set de test d’Oxford-III Pet pour créer le set de validation. C’est la seule option qui nous permettent de garantir que les individus présents dans le set de train et de validation sont différents.

Data Leakage (fuite de données) se produit lorsque le modèle traite, directement ou indirectement, des informations du set de validation pendant l’entraînement.

Preprocessing mismatch

Dans le cadre du Transfer Learning, nous allons injecter de nouvelles images dans un réseau qui a été préentrainé sur ImageNet. Ainsi, notre pipeline de prétraitement des images (Transform) doit rester compatibles avec celui utilisé lors du préentraînement.

Le pipeline d’ImageNet suit les étapes suivantes :

  • Traitement d’images couleurs RGB

  • Redimensionnement de l’image suivant le plus grand côté - transforms.Resize(224)

  • Recadrage central ou aléatoire → 224×224

  • Conversion en tenseur avec des valeurs dans [0,1]

  • Normalisation avec les statistiques issues d’ImageNet (mean, std)

Modifier fortement ce pipeline revient à changer la distribution des entrées, ce qui perturbe les représentations apprises et dégrade les performances.

Format couleur

Le modèle a été préentraîné sur des images RGB avec un ordre de canaux précis ; ce format doit être conservé à l’identique. Toute modification (BGR, niveaux de gris) introduit un décalage de distribution qui dégrade fortement les performances.

Résolution

Aujourd’hui, les modèles entraînés sur ImageNet travaillent avec des résolutions d’image en entrée de 224×224. Les features sont donc appris avec cette taille d’images. Il est possible d’injecter des images de plus faible résolution, mais elles doivent rester dans le même ordre de grandeur. Par exemple, nous pourrons tester 160×160 afin de réduire par 2 la quantité de données à traiter, mais nous serons déjà dans les limites de l’acceptable.

Ainsi, injecter des images 64×64 représente un risque de perte de performance car les features risquent de ne pas être réutilisables. Dans ces conditions, on peut avoir une chute de performance réelle avec 10 à 30 points d’accuracy de moins ou plus.

Normalisation

Un modèle pré-entraîné a appris à partir d’images normalisées. Par exemple, pour ImageNet, ses caractéristiques sont :

mean = [0.485, 0.456, 0.406]
std  = [0.229, 0.224, 0.225]

Si vous oubliez de le faire ou si vous transmettez les caractéristiques du nouveau dataset et non celui d’ImageNet, vous aurez une perte de performance de l’ordre de -10 à -30 points d’accuracy.

Conclusion

Ainsi pour entraîner nos modèles nous choisissons les transforms suivantes :

from torchvision import transforms

# Normalisation ImageNet - indispensable pour un modèle pré-entraîné
imagenet_mean = [0.485, 0.456, 0.406]
imagenet_std = [0.229, 0.224, 0.225]

# Transform pour le training
train_transforms = transforms.Compose([
    transforms.Resize(160),
    transforms.CenterCrop(160),
    transforms.ToTensor(),
    transforms.Normalize(mean=imagenet_mean, std=imagenet_std),
])

# Transform pour la validation
val_transforms = transforms.Compose([
    transforms.Resize(160),
    transforms.CenterCrop(160),
    transforms.ToTensor(),
    transforms.Normalize(mean=imagenet_mean, std=imagenet_std),
])

Training bias

Training bias : situation où les données ou leur utilisation pendant l’entraînement introduisent un déséquilibre qui fausse l’apprentissage du modèle.

Shuffle

Il n’est pas nécessaire d’effectuer un shuffle sur le set de validation, mais il arrive parfois d’oublier de le faire sur le set d’entraînement. On se retrouve alors face à une erreur sournoise : elle ne provoque aucun plantage et le modèle continue d’apprendre avec des performances apparemment correctes. Cependant, l’apprentissage devient instable et tend à stagner ou à osciller, rendant toute amélioration difficile.

L’erreur est d’autant plus insidieuse qu’elle peut ne pas avoir d’effet immédiat. Par exemple, si vous traitez un dataset avec des images déjà mélangées, il n’y aura pas de conséquence. Par contre, si plus tard, vous traitez un dataset avec des images triées par catégories, dans ce cas, c’est un problème.

Split biaisé

Cela arrive lorsque vous devez gérer un split 80/20 sur un dataset avec peu d’images par catégorie. Quel est le problème ? Si on fait un shuffle du set complet et ensuite un découpage 80/20 pour constituer le set de validation et le set de train, il va apparaître un biais dans les catégories présentes dans le set de validation.

Prenons un exemple qui simule 100 images/categories avec n catégories :

import random
from collections import Counter

nbcat = 10
data = []
for i in range(nbcat) : data += [i]*100

random.shuffle(data)
n = int(0.2 * len(data))
subset = data[:n]

counts = Counter(subset)
total = len(subset)

# Pourcentages
for i in range(nbcat) :
    pct = 100 * counts[i] / total
    print(f"{i} : {pct:.2f}%")

Voici typiquement le genre de distribution biaisée que l’on peut obtenir dans le set de validation :

0 : 10.00%      1 : 11.00%      2 : 8.00%       3 : 9.50%       4 : 9.50%
5 : 8.50%       6 : 14.00%      7 : 7.50%       8 : 11.00%      9 : 11.00%

Ainsi, la catégorie 6 est deux fois plus représentée que la 7. La plupart du temps, ce problème n’est pas abordé car il y a généralement beaucoup plus d’images par catégorie, ce qui a pour effet d’atténuer ce problème. Néanmoins, si vous avez des classes avec moins de 500 images, il va falloir penser à utiliser une des méthode ci-dessous :

Pour un dataset équilibré

Ici, chaque classe a autant d’images. Ainsi, nous allons effectuer un shuffle+split pour chaque classe indépendamment. Puis ensuite, il suffit de fusionner les différents set de validation de chaque classe pour obtenir un set de validation complet et équilibré. Même stratégie dans pour construire le set de train.

Pour un dataset déséquilibré

Même stratégie que précédemment. Cependant, si une classe a 2 fois moins d’images que les autres, ce sera toujours le cas dans le set de train et de validation.

Pour compenser ce déséquilibre sur le set de train, deux options existent :

  • CrossEntropyLoss(weight=class_weights) : Attribue un poids plus élevé aux classes rares dans la fonction de perte afin de pénaliser davantage leurs erreurs.

  • WeightedRandomSampler : Modifie l’échantillonnage des données pour présenter plus fréquemment les exemples des classes rares pendant l’entraînement.

Pour compenser ce déséquilibre sur le set de validation, on peut :

  • Calculer la moyenne des accuracies de chaque classe

  • Forcer un nombre identique de représentants pour chaque classe

Marge de liberté

Il existe des contraintes importantes en transfer learning, mais certains paramètres ont un impact plus limité sur les performances finales. On peut notamment citer :

  • La taille du batch

  • L’optimiseur/scheduler

  • Le nombre d’epochs pour le nouvel entraînement