Module 8 — Augmentation de données pour l'image
Passer d'ImageNet (1,2 million d'images) au jeu de déchets à trier (environ 2 500 photos réparties sur 6 classes) change tout. Sans augmentation, un ResNet50 gelé apprend en quelques époques par cœur ce qu'il faut apprendre, puis surajuste. Avec une augmentation bien pensée, le même modèle atteint 90 à 93 % de précision de validation. Ce module classe les techniques d'augmentation selon leur effet réel, montre comment les appliquer à la volée, et surtout liste les augmentations à ne pas faire — celles qui améliorent la validation générique tout en dégradant la tâche cible.
Retournements et recadrages : les fondations
Le retournement horizontal est l'augmentation la plus rentable de la vision. Sur presque toutes les tâches d'images naturelles, la version miroir d'un chat est un chat, la version miroir d'un carton est un carton. Le doublement virtuel du jeu de données est acquis sans effort et sans risque.
Le recadrage aléatoire est le second pilier. Sur une image d'origine 256x256, on tire un rectangle aléatoire, on le redimensionne à 224x224. Le classifieur voit à chaque époque une composition légèrement différente, ce qui l'empêche de surapprendre les positions précises et le force à s'appuyer sur les objets eux-mêmes.
from tensorflow import keras
from tensorflow.keras import layers
augmentation = keras.Sequential([
layers.RandomFlip("horizontal"),
layers.RandomCrop(224, 224),
layers.RandomRotation(0.05), # +/- 18 degres
layers.RandomZoom(0.1),
], name="augmentation")
Ces couches se comportent différemment en entraînement et en inférence : elles n'appliquent la transformation qu'en mode training=True, ce qui les rend cohérentes avec le reste du modèle Keras.
Jitter de couleur : à doser
La luminosité, le contraste, la saturation et la teinte varient d'une photo à l'autre selon l'éclairage, l'appareil et le post-traitement. Le jitter de couleur simule ces variations et rend le modèle moins sensible à un capteur particulier.
augmentation = keras.Sequential([
layers.RandomFlip("horizontal"),
layers.RandomBrightness(0.15),
layers.RandomContrast(0.15),
])
Le piège est de trop forcer. Sur le jeu de déchets à trier, la couleur porte de l'information — le vert distingue une bouteille en verre d'une bouteille en plastique similaire — et une variation de teinte de peut inverser la classe. Un jitter modéré (0,1 à 0,2) est prudent ; au-delà, on mesure.
Cutout et Mixup : les augmentations de deuxième génération
Deux techniques plus agressives ont émergé après 2017 et sont aujourd'hui la norme sur les benchmarks comp étitifs.
Cutout — DeVries et Taylor, 2017 — masque un rectangle aléatoire de l'image en noir (ou en gris moyen). Le classifieur ne peut plus s'appuyer sur une seule région distinctive et doit apprendre à agréger plusieurs indices.
import tensorflow as tf
def cutout(image, taille=32):
h, w = image.shape[0], image.shape[1]
y = tf.random.uniform((), 0, h, dtype=tf.int32)
x = tf.random.uniform((), 0, w, dtype=tf.int32)
masque = tf.ones((h, w, 1))
y0, y1 = tf.maximum(0, y - taille // 2), tf.minimum(h, y + taille // 2)
x0, x1 = tf.maximum(0, x - taille // 2), tf.minimum(w, x + taille // 2)
indices = tf.reshape(tf.range(y0, y1), (-1, 1, 1))
# implementation simplifiee, a completer avec tf.tensor_scatter_nd_update
return image # placeholder
Mixup — Zhang et al., 2018 — combine deux exemples et leurs étiquettes par une combinaison convexe. Pour un coefficient tiré d'une loi (typiquement ),
Le modèle apprend une frontière de décision plus lisse et se calibre mieux. Mixup n'a besoin que de deux exemples par lot et de leurs étiquettes en encodage un-parmi-n :
def mixup(lot, etiquettes, alpha=0.2):
lambd = tf.random.gamma((1,), alpha) / (tf.random.gamma((1,), alpha) + tf.random.gamma((1,), alpha))
perm = tf.random.shuffle(tf.range(tf.shape(lot)[0]))
lot_melange = lambd * lot + (1 - lambd) * tf.gather(lot, perm)
etiq_melange = lambd * etiquettes + (1 - lambd) * tf.gather(etiquettes, perm)
return lot_melange, etiq_melange
CutMix combine les deux : au lieu de masquer un rectangle en noir, on le remplace par la région correspondante d'une autre image, et on ajuste l'étiquette au prorata des surfaces.
Augmentation à la volée avec tf.data
Une augmentation calculée hors-ligne prend de la place et fixe le jeu virtuel. Une augmentation à la volée, appliquée à chaque époque dans le pipeline tf.data, donne des exemples toujours différents et ne coûte que le calcul par lot.
AUTOTUNE = tf.data.AUTOTUNE
def preparer(jeu, entrainement=False):
jeu = jeu.batch(32)
if entrainement:
jeu = jeu.map(lambda x, y: (augmentation(x, training=True), y),
num_parallel_calls=AUTOTUNE)
return jeu.prefetch(AUTOTUNE)
Deux règles :
- L'augmentation s'applique après le batch quand on utilise les couches Keras
Random*, parce qu'elles opèrent sur un lot déjà formé. - Elle s'applique avant le prefetch, sinon le prefetch pré-charge des exemples non augmentés qui seront rejetés.
Ce qu'il ne faut pas augmenter
Voici la partie la plus utile de ce module, parce qu'elle contredit un réflexe.
- Rotations sur des chiffres. Un
6retourné de 180° devient un9. Sur MNIST, la rotation aléatoire à est dévastatrice, et le retournement horizontal fait aussi basculer plusieurs paires (5 et 5 miroir n'ont pas la même signification qu'un chat miroir). - Retournement horizontal sur du texte. Une image d'un panneau routier avec du texte devient illisible ; le classifieur apprend que « du texte, dans un sens ou dans l'autre, c'est un panneau », ce qui masque des indices utiles.
- Rotations sur des radios thoraciques ou des mammographies. La latéralité gauche/droite du patient porte de l'information cliniquement pertinente ; la brouiller par un retournement horizontal invalide la tâche.
- Jitter de couleur agressif quand la couleur est la classe. Sur le tri des déchets, on l'a vu ; sur la classification de mélanomes par photo dermatologique, c'est encore plus critique.
- Ajout de bruit sur un OCR médical. Le bruit qu'on ajoute est différent du bruit réel du capteur, et le modèle apprend à ignorer un bruit qu'il ne rencontrera jamais tout en restant fragile au bruit réel.
La sur-augmentation est discrète : elle ne baisse pas la précision de validation, elle la maintient. C'est plus tard, en production, quand la vraie distribution ne comporte pas les rotations à 180° et que le modèle continue de tourner ses cartes mentalement, qu'on paie la note. Vérifier chaque nouvelle augmentation sur un échantillon visuellement avant de la mettre dans le pipeline.
Sur le fil rouge
Pour les photos de déchets à trier, l'augmentation utile est modeste : retournement horizontal, recadrage aléatoire, rotation à (une bouteille penchée reste une bouteille), jitter modéré de luminosité et contraste. On évite le jitter de teinte. On ajoute Mixup à dans la phase 2 du transfert (module 9), quand la tête est déjà stable — appliqué dès la phase 1, il rend l'apprentissage plus bruité qu'utile.
En résumé
- Retournement horizontal et recadrage aléatoire sont les augmentations à activer par défaut sur toute tâche d'images naturelles.
- Mixup et CutMix régularisent et calibrent mieux qu'un ajout d'exemples classiques, avec un coût nul en mémoire.
- L'augmentation s'applique à la volée dans
tf.data, après le batch et avant le prefetch. - Certaines augmentations détruisent la tâche (rotation sur chiffres, retournement sur texte, jitter de teinte quand la couleur est la classe) sans se voir sur la métrique de validation — il faut les auditer visuellement.
Module suivant : le transfert et l'affinage progressif d'un ResNet50 sur ce jeu de déchets, avec le piège classique de la normalisation par lots.