Aller au contenu principal

Module 4 — Encodage : one-hot, ordinal, par la cible

Les modèles ne consomment que des nombres. Or les données réelles regorgent de catégories : ville, marque, code postal, type de contrat. Les transformer en nombres semble anodin — c'est en fait l'une des décisions les plus lourdes de l'ingénierie des variables, et le mauvais choix injecte silencieusement une information fausse.

Le piège de l'encodage naïf

La tentation immédiate : numéroter les catégories. Paris = 1, Lyon = 2, Marseille = 3. Le problème apparaît dès qu'on y réfléchit : le modèle lit des nombres ordonnés. Il en déduit que Marseille > Lyon > Paris, que l'écart entre Paris et Marseille vaut le double de celui entre Paris et Lyon, et qu'une moyenne entre Paris et Marseille donne Lyon.

Ces énoncés n'ont aucun sens, mais ils sont mathématiquement inscrits dans les données. Un modèle linéaire, qui multiplie la variable par un coefficient, s'appuiera dessus. Numéroter arbitrairement des catégories non ordonnées est une erreur, pas un raccourci.

One-hot : la référence pour les faibles cardinalités

L'encodage one-hot crée une colonne binaire par catégorie : la colonne de la catégorie présente vaut 1, les autres 0. Plus aucun ordre n'est suggéré, et chaque catégorie dispose de son propre coefficient.

from sklearn.preprocessing import OneHotEncoder
OneHotEncoder(handle_unknown="ignore", drop="first", sparse_output=False)

Deux paramètres importent réellement. handle_unknown="ignore" évite un plantage en production lorsqu'une catégorie absente de l'entraînement se présente — situation banale et sinon fatale. drop="first" supprime une colonne devenue redondante (si ce n'est aucune des autres, c'est nécessairement la première) ; c'est utile pour les modèles linéaires, où la colinéarité parfaite gêne, et inutile pour les arbres.

Sa limite est structurelle : la cardinalité. Une variable de code postal à 6 000 valeurs produit 6 000 colonnes. La matrice explose, les distances perdent leur sens (malédiction de la dimension), chaque colonne ne contient presque que des zéros et les catégories rares sont vues trop peu de fois pour être apprises. En pratique, one-hot convient jusqu'à quelques dizaines de catégories.

Encodage ordinal : quand l'ordre existe vraiment

Certaines catégories sont réellement ordonnées : « faible < moyen < élevé », « primaire < secondaire < supérieur ». Numéroter est alors non seulement licite mais souhaitable, car l'ordre est une information vraie qu'on transmet au modèle.

from sklearn.preprocessing import OrdinalEncoder
OrdinalEncoder(categories=[["faible", "moyen", "eleve"]])

Un point de méthode : spécifier explicitement l'ordre. Laissé libre, l'encodeur trie alphabétiquement et produit un ordre faux — « eleve, faible, moyen » — qui réintroduit exactement le problème que l'on croyait éviter.

Haute cardinalité : encodage par la cible et par fréquence

Pour les variables à nombreuses modalités, deux approches remplacent utilement one-hot.

L'encodage par fréquence remplace la catégorie par son nombre d'occurrences. Une seule colonne, aucune fuite, et une information souvent pertinente — un code produit vu 10 000 fois n'a pas le même statut qu'un code vu trois fois.

L'encodage par la cible remplace la catégorie par la moyenne de la cible pour cette catégorie. Une ville dont 8 % des clients se désabonnent devient 0,08. C'est très puissant : une seule colonne, une information directement liée à ce qu'on prédit, et une capacité à gérer des milliers de modalités.

Cette puissance a un revers, et il faut le nommer clairement : l'encodage par la cible utilise la cible, donc fuit par construction. Une catégorie n'apparaissant que trois fois se voit attribuer la moyenne de ces trois observations — le modèle mémorise ces lignes au lieu d'apprendre. Deux parades indispensables :

  • le lissage : mélanger la moyenne de la catégorie et la moyenne générale, en pondérant par l'effectif, pour que les catégories rares tendent vers la moyenne globale ;
  • le calcul hors échantillon : calculer la moyenne par validation croisée, chaque pli étant encodé avec des statistiques issues des autres plis.

La bibliothèque category_encoders fournit TargetEncoder avec lissage intégré, et scikit-learn propose désormais son propre TargetEncoder avec validation croisée interne. Recoder cela soi-même est un excellent moyen de se fabriquer une fuite.

Choisir selon la cardinalité et le modèle

Jusqu'à une dizaine de catégories, one-hot sans hésiter. De dix à cinquante, one-hot reste jouable, éventuellement après avoir regroupé les modalités rares dans un « Autres ». Au-delà, fréquence ou cible avec lissage. Et si le modèle est un gradient boosting, sachez que LightGBM et CatBoost traitent nativement les variables catégorielles — souvent mieux qu'un encodage manuel, notamment sur les hautes cardinalités.

En résumé

  • Numéroter des catégories non ordonnées injecte un ordre faux que les modèles linéaires exploiteront.
  • One-hot est la référence en faible cardinalité ; handle_unknown="ignore" protège la production, mais la méthode explose au-delà de quelques dizaines de modalités.
  • L'encodage ordinal ne convient qu'aux ordres réels, et l'ordre doit être déclaré explicitement.
  • En haute cardinalité, fréquence ou cible ; cette dernière fuit par construction et exige lissage et calcul hors échantillon.

Module suivant : les dates et les variables cycliques, où une information apparemment simple demande un encodage particulier.