Aller au contenu

Phase 2 : Entraînement Neuronal

La collecte est terminée. Vos disques durs sont remplis de logs binaires, mais pour l'instant, c'est de la matière inerte. La Phase 2 est celle de l'alchimie : nous allons transmuter ces gigaoctets de données brutes en un réseau de neurones capable de percevoir, comprendre et agir.

Bienvenue dans le pytorch_lab, la forge où naissent vos modèles de conduite autonome. Ici, nous utilisons l'Apprentissage Supervisé (Supervised Learning) pour enseigner à l'IA l'art du pilotage par l'exemple.

training_workflow

Du Prototypage à la Production

L'histoire a commencé sur des Notebooks Jupyter (visualisation, petits tests d'architecture). Mais pour l'entraînement intensif à grande échelle, nous avons migré vers une structure professionnelle : le PyTorch Lab. C'est cette structure modulaire qui est déployée sur le cluster de calcul (DCE) via SLURM pour des sessions d'entraînement ininterrompus.

1. Préparation des Données

Le challenge principal n'est pas le modèle, mais la qualité des données. Notre script de chargement (data.py) implémente plusieurs stratégies cruciales.

I. Filtrage Qualitatif (Cleaning)

Avant même de parler d'équilibre, il faut nettoyer l'historique. Nous rejetons systématiquement les frames où la voiture conduit mal (oscillations, sortie de route). Le critère principal est l'Erreur Latérale (CTE).

  • Seuil de Rejet : Tout échantillon avec CTE > 2.75m est impitoyablement supprimé.

II. Lazy Loading & Indexation

Comme vu précédemment, nos données sont stockées en binaire par paquets de 500 frames. Pour ne pas saturer la RAM, nous chargeons les images "à la demande" (Lazy Loading). Un index global (dataset_index.csv) est généré une seule fois pour mapper chaque frame à son offset disque exact.

III. Équilibrage

Dans un circuit, 80% du temps, on roule tout droit. Si on entraîne l'IA là-dessus, elle apprendra à toujours aller tout droit (considérant les virages comme du bruit statistique). Pour contrer cela, nous appliquons un filtre draconien :

  • Undersampling : On supprime aléatoirement 80% des lignes droites.
  • Oversampling : On duplique les virages (là où l'angle volant > 0.3 rad) par un facteur de 2.

Paramètres Configurables

Ces valeurs (Seuil 0.3 rad, Facteur Oversampling) ne sont pas gravées dans le marbre. Elles sont définies dans le fichier config.yaml et peuvent être ajustées selon la nervosité du circuit.

IV. Data Augmentation

Pour éviter que l'IA n'apprenne par cœur les textures du circuit ("Overfitting"), nous modifions légèrement chaque image à la volée avant de l'envoyer au réseau. Nous utilisons principalement des transformations photométriques :

  • ColorJitter : Variations aléatoires de Luminosité, Contraste, Saturation et Teinte.
  • Gaussian Blur : Léger flou pour simuler une mise au point imparfaite ou du mouvement.
  • Normalisation : Mise à l'échelle des pixels (0-1) pour stabiliser les gradients.

Expérimentations Passées : ROI & Crops

Durant la phase de R&D, nous avons également testé des méthodes de Region of Interest (ROI) consistant à rogner le haut de l'image (le ciel et le paysage) pour forcer le réseau à ne regarder que la route. Bien que théoriquement valide, cette méthode a été mise de côté pour l'instant au profit d'une architecture plus profonde capable de faire ce tri elle-même.

Cela force le modèle à se concentrer sur la géométrie de la route plutôt que sur la couleur exacte du bitume ou l'éclairage.


2. Architectures Neuronales

Dans ce laboratoire, nous ne nous limitons pas à une seule approche. Nous avons implémenté et comparé 3 architectures distinctes, chacune représentant une philosophie différente de la vision par ordinateur.

Modèle Entrée Philosophie Avantages
PilotNetResNet9Ch 9 Canaux (Concat) Force Brute Robuste et rapide. Utilise un ResNet18 pré-entraîné modifié.
PilotNetTransformer 9 Canaux (Concat) Spatial Attention Découpe l'image en "patches" (4x4) et laisse le Transformer lier les zones distantes.
MultiCameraTransformer 3 Images Séparées View Attention Traite chaque caméra séparément puis fusionne les 3 points de vue par Attention.

A. PilotNetResNet9Ch

Cette architecture est notre "tracteur" : simple, robuste et efficace. Elle repose sur le principe de Fusion Précoce (Early Fusion).

MultiCamTransformer

I. Le "Hack" des 9 Canaux

ResNet18 est conçu pour ImageNet (3 canaux RGB). Or, nous avons 3 caméras (Gauche, Centre, Droite). Au lieu de traiter chaque image séparément, nous les empilons sur la dimension des canaux (Channel-wise Concatenation) pour former un tenser d'entrée de dimension (B, 9, 128, 128).

II. Keep du Pré-entraînement

Le problème : une Conv1 standard attend 3 canaux. Si on la remplace par une couche à 9 canaux, on perd les poids pré-entraînés. Notre solution : Nous créons une nouvelle convolution Conv2d(9, 64, ...) et nous copions les poids originaux 3 fois sur l'axe des canaux.

Résultat : Au premier passage, le réseau "voit" une superposition des 3 images avec la capacité de détection de features d'ImageNet. Il n'a pas besoin de réapprendre à voir des bords ou des textures.

III. Tête de RégressionCustom

Nous supprimons la couche de classification finale (FC) de ResNet pour la remplacer par un bloc de régression dédié au pilotage :

  • Linear(512 -> 64) : Compression de l'information sémantique.
  • ReLU : Activation non-linéaire.
  • Dropout(0.5) : Pour éviter l'overfitting.
  • Linear(64 -> 1) : Sortie unique (Angle de braquage).

B. PilotNetTransformer

Cette architecture est une évolution Hybride. Elle mélange la puissance d'extraction de features du CNN avec la capacité de contexte global du Transformer. L'idée est de ne pas écraser toute l'image en un seul vecteur trop tôt, mais de raisonner sur des zones de l'image.

MultiCamTransformer

I. Extraction Spatiale

Comme pour la baseline, on utilise le Hack des 9 canaux. Cependant, on coupe le ResNet18 avant le Pooling final.

Sortie : Au lieu d'un vecteur, on récupère un cube de features de dimension (512, 4, 4). Cela représente une grille de 16 "super-pixels" contenant chacun 512 caractéristiques.

II. Séquence de Patches

Le Transformer ne comprend pas les images, il veut des séquences (comme des mots dans une phrase).

  1. Adaptation : On réduit la profondeur de 512 à 256 via une convolution 1x1 (Adapter).
  2. Aplatissement : On transforme la grille 4x4 en une suite de 16 tokens.

C'est ici que la magie opère : l'image est vue comme une phrase de 16 mots décrivant la scène.

III. Spatial Self-Attention

Ces 16 tokens passent dans un TransformerEncoder. Grâce au mécanisme de Self-Attention, chaque zone de l'image peut "discuter" avec les autres, quelle que soit leur distance. Exemple : La courbure du virage au lointain (haut de l'image) peut influencer la décision immédiate, même si la route juste devant le capot (bas de l'image) semble encore droite. Enfin, on fait la moyenne de ces tokens enrichis (Global Mean Pooling) pour prédire l'angle.

Résultats & Limites

Bien que l'Attention Spatiale améliore la robustesse globale, nos tests montrent que le gain reste modeste. La raison est structurelle : nous appliquons l'attention sur des "Patches" de 4x4 pixels (résolution très grossière). L'information fine est déjà diluée par le CNN avant même que le Transformer ne puisse la croiser.

C'est ce constat de "plafond de verre" qui nous a motivé à concevoir une troisième architecture, radicalement différente.

C. MultiCameraTransformer

Voici l'architecture la plus avancée ("State of the Art"), conçue pour pallier les limites des deux précédentes. Ici, on ne concatène pas les pixels. On traite chaque caméra comme une entité propre.

MultiCamTransformer

I. Tokenization par Caméra

Le réseau traite les 3 images (Gauche, Centre, Droite) séparément mais avec le même Backbone (ResNet18 partagé).

  • Entrée : 3 images distinctes (B, 3, 128, 128).
  • Backbone : Chaque image passe dans le CNN.
  • Sortie : On obtient 3 vecteurs de features de taille 512.

    Analogie : C'est comme si 3 observateurs différents regardaient la scène et envoyaient chacun un rapport écrit.

II. Embeddings de Position

Le Transformer est agnostique à l'ordre. Si on lui donne les 3 vecteurs bruts, il ne sait pas lequel vient de gauche ou de droite. Pour corriger cela, on ajoute des Camera Embeddings appris (Learnable Embeddings) :

  • Token_Gauche = Features_Gauche + ID_Gauche
  • Token_Centre = Features_Centre + ID_Centre
  • Token_Droit = Features_Droit + ID_Droit

III. Inter-View Attention

Ces 3 tokens entrent dans le Transformer. Le mécanisme d'attention permet de croiser les informations entre les vues.

  • Exemple : "Si la caméra de Gauche voit de l'herbe (sortie de route imminente), alors l'information de la caméra Centrale doit être interprétée différemment (il faut braquer à droite même si le centre semble vide)."

C'est une fusion sémantique de haut niveau, beaucoup plus puissante que la simple superposition de pixels du début.

3. Stratégie d'Entraînement

I. Fonction de Coût (Loss)

Le choix de la Loss est critique car nos valeurs de steering sont faibles (entre -1.0 et 1.0 radian).

  1. MSE (Mean Squared Error) : En mettant l'erreur au carré (\(e^2\)), une erreur de 0.1 devient 0.01. Cela "écrase" les gradients pour les petits ajustements, rendant la convergence lente et molle.
  2. MAE (Mean Absolute Error) : Initialement choisie car elle garde l'échelle linéaire (\(|e|\)). Elle est bonne, mais pose problème autour de zéro (gradient non-differentiable) et manque de force pour corriger les gros écarts.

Notre Choix : SmoothL1Loss C'est le meilleur des deux mondes. Elle se comporte comme une MSE (quadratique) proche de 0 pour lisser la convergence, et comme une MAE (linéaire) pour les grandes erreurs afin d'éviter les explosions de gradient.

\[ \text{SmoothL1}(x) = \begin{cases} 0.5 x^2 & \text{si } |x| < 1 \\ |x| - 0.5 & \text{sinon} \end{cases} \]

II. Optimiseur (Adam)

Nous utilisons l'algorithme Adam avec un Learning Rate de 1e-4 et un Weight Decay de 1e-4. Contrairement au SGD classique, Adam adapte le pas d'apprentissage pour chaque paramètre individuellement. * Pourquoi ? Cela permet de converger vite au début sans osciller à la fin, ce qui est crucial quand on entraîne sur des données bruiées.

III. Early Stopping

L'ennemi n°1 est l'Overfitting (l'IA apprend le circuit par cœur mais ne sait pas généraliser). Pour contrer cela, nous surveillons la val_loss (perte sur le jeu de validation) à chaque époque.

  • Mécanisme : Si la val_loss ne s'améliore pas pendant 3 époques consécutives (paramètre patience), l'entraînement s'arrête net.
  • Sauvegarde : Le système recharge automatiquement le dernier "meilleur modèle" connu avant de s'arrêter.
  • Résultat : On obtient toujours le modèle le plus générique possible, jamais celui qui a "trop appris".

4. Export ONNX

Une fois le modèle entraîné, il reste une étape cruciale : le sortir du laboratoire. PyTorch est excellent pour la recherche, mais trop lourd pour être embarqué tel quel dans une stack robotique temps réel.

Nous exportons donc notre champion au format ONNX (Open Neural Network Exchange). * Figer le Graphe : On transforme le code Python dynamique en un graphe de calcul statique. * Optimisation : Les couches sont fusionnées (ex: Conv + BatchNorm) pour accélérer le calcul.

Pourquoi ? Ce fichier .onnx est le seul artefact nécessaire pour le pilotage. Il sera chargé directement par le nœud ROS 2 de l'Autopilot (Phase 3), permettant au véhicule de prendre des décisions ultra-rapides (millisecondes) sans avoir besoin de toute la librairie d'entraînement.

Le modèle est prêt. Il est temps de le mettre au volant.