• 10 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 23/04/2024

Transformez les variables pour faciliter l’apprentissage du modèle

Il ne vous aura pas échappé que les modèles n'acceptent comme données d'entrée que des chiffres. Si votre dataset d'entraînement ou vos échantillons de prédictions contiennent du texte, vous aurez une erreur de Python.

Le traitement apporté aux données catégoriques textuelles va dépendre du nombre de catégories prises par la variable. On distinguera :

  • le cas binaire où l'on a 2 catégories ;

  • et pour être très précis, les cas "un peu plus que 2" ou "carrément beaucoup".

Chargeons à nouveau les données et limitons-nous aux platanes :

dataset_url = "https://raw.githubusercontent.com/OpenClassrooms-Student-Center/8063076-Initiez-vous-au-Machine-Learning/master/data/paris-arbres-clean-2023-09-10.csv"
data = pd.read_csv(dataset_url)
df = data[data.libelle_francais == 'Platane'].copy()

Le cas binaire

La variable prend 2 valeurs distinctes et exclusives de type oui/non, vrai/faux, mort/vivant, homme/femme, positif/négatif au test, etc.

Sur le dataset des arbres, la variable  remarquable  est binaire.

df.remarquable.value_counts()
remarquable
NON 185232
OUI 179

Il est facile de transformer ces valeurs en chiffres en choisissant de manière arbitraire une valeur pour chaque catégorie :

df.loc[df.remarquable == NON, 'remarquable'] = 0
df.loc[df.remarquable == OUI, 'remarquable'] = 1

On obtient alors :

df.remarquable.value_counts()
remarquable
0 185232
1 179

Cas non binaire mais peu de catégories

Les choses se compliquent un peu quand on a plus de 2 catégories à encoder. On distingue :

  • les catégories ordonnées (ordinales) ;

  • des catégories non ordonnées.

Exemple :

Catégories ordonnées (ou ordinales)

Catégories non ordonnées

  • petit, moyen, grand

  • glacé, froid, chaud, tiède, brûlant

  • archi nul !, nul wesh !, super !, méga top !

  • bleu, rouge, violet, vert, gris

  • épicé, salé, sucré, onctueux, amer...

L'encodage ordonné

Prenons l'exemple du stade de développement des arbres et supposons que les valeurs de cette variable sont ordonnées de la manière suivante :

Jeune (arbre) < Jeune (arbre)Adulte < Adulte < Mature

Voici une méthode simple pour numériser ces catégories :

categories = ['Adulte', 'Jeune (arbre)Adulte', 'Jeune (arbre)', 'Mature']
for n, categorie in zip(range(1,len(categories)+1), categories):
  df.loc[
          df.stade_de_developpement == categorie,   
          'stade_de_developpement'
        ] = n

Et on obtiendra :

stade_de_developpement
3 21620
2 8356
1 5916
4 3346

avec une  équivalence (mapping) entre les catégories et les nombres

1 => Jeune (arbre)

...

4 => Mature

Vous pouvez installer cette librairie avec :

!pip install category_encoders

Pour encoder des catégories ordinales, nous utilisons la classe OrdinalEncoder.

Pour que l'ordre des catégories soit respecté dans l'encodage, il faut donner un mapping explicite à la fonction  fit()  comme ceci :

from category_encoders.ordinal import OrdinalEncoder
mapping =[ {'col': 'stade_de_developpement',
'mapping': {
            np.nan: 0,
            'Jeune (arbre)': 1,
            'Jeune (arbre)Adulte': 2,
            'Adulte': 3,
            'Mature': 4
            }
        } ]

encoder = OrdinalEncoder(mapping=mapping)
stade = encoder.fit_transform(df.stade_de_developpement)
stade.value_counts()

On remplace ensuite les catégories originales par leur équivalents numériques :

df['stade_de_developpement'] = stade.copy()

Et les stades de développement sont maintenant numérisés :

df['stade_de_developpement'].value_counts(dropna = False)
stade_de_developpement
3   21620
2   8356
1   5916
0   3350
4   3346

Notez que la forme du mapping est un peu spéciale. Elle permet d'encoder plusieurs colonnes à la fois en fournissant pour chaque colonne un dictionnaire d'équivalence.

Notez aussi que l'on peut spécifier l'encodage des valeurs manquantes. Ici  np.nan => 0  .

Cas non ordonné : Si l'on remplace de la même façon les valeurs d'une catégorie non ordonnée par une suite de nombres, on introduit un ordre fictif dans les catégories. Cette information supplémentaire d'ordre pourrait être utilisée par le modèle alors qu'elle ne correspond à rien de concret.

On cherche donc une technique d'encodage qui n'introduit pas cette information inutile.

L'encodage one-hot

Le  principe est d'associer pour chaque valeur de la catégorie une nouvelle variable binaire qui indique si la variable originale avait la valeur en question. Si la variable prend N valeurs distinctes, on introduira donc N-1 nouvelles variables, la Nième étant redondante.

Prenons un exemple avec nos arbres et la variable  domanialité  qui prend les 9 valeurs suivantes :

domanialite
Alignement      35747
CIMETIERE       3121
Jardin          2038
DASCO           816
PERIPHERIQUE    603
DJS             182
DFPE            71
DAC             7
DASES           3

Pour que l'exemple soit plus clair, ne gardons que les arbres des 3 domanialités les plus fréquentes :

df = df [df.domanialite.isin(['Alignement', 'Jardin', 'CIMETIERE'])].copy()
df.reset_index(drop = True, inplace = True)

On va créer 2 variables booléennes :  is_alignement  et  is_jardin  , telles que :

is_alignement =  1 if (domanialite = 'Alignement') else 0
is_jardin =  1 if (domanialite = 'Jardin') else 0

Comme les catégories sont exclusives, un arbre ne peut pas être à la fois dans un alignement ou un jardin et dans un cimetière, la valeur cimetière n'a pas besoin d'être explicitée par une variable is_cimetiere. On a logiquement :

domanialite = CIMETIERE si is_alignement =0 et is_jardin = 0

Tout cela est un peu lourd. Imaginez faire cela à la main pour une variable à 5, 10 ou plus de valeurs.

from sklearn import preprocessing
enc = preprocessing.OneHotEncoder()
labels = enc.fit_transform(df.domanialite.values.reshape(-1, 1)).toarray()
labels.shape
(40906, 3)

On a donc 3 colonnes correspondant aux 3 valeurs de domanialité.

Il faut ensuite intégrer 2 de ces 3 colonnes (la 3e est redondante) et supprimer la colonne originale avec un peu de manipulation de pandas.dataframe.

df = pd.concat([df,
        pd.DataFrame(columns = ['is_alignement','is_jardin'],
                      data = labels[:, :2])],
        axis = 1)

De plus, la plupart de ces nouvelles variables (  is_peripherique  ,  is_alignement  , etc.) sont  principalement constituées de 0. Par exemple, la domanialité  PERIPHERIQUE  a seulement 5 225 arbres sur un total de 207 641 arbres, soit 2,5 % des arbres. La colonne binaire  is_peripherique  correspondant à cette valeur va être à 97,5 % des zéros, donc extrêmement peu informative.

De même si on considère les variables comme  espece  qui a 559 valeurs différentes, l'encodage  one hot  ajoutera 558 nouvelles variables au dataset, la plupart truffées de 0 sans grande valeur.

On va donc noyer les informations contenues dans les 7 variables restantes dans la masse des variables dédiées aux catégories de l'espèce.

En conclusion, le one hot encoding n'est réellement utilisable que lorsque le nombre de valeurs de la variable à numériser est faible par rapport aux nombre de variables utiles du dataset.

Passons maintenant aux choses sérieuses !

L'encodage binaire

Prenons un exemple de la variable domanialité qui a 9 variables.

Le chiffre 9 en binaire s'écrit  1001  soit avec 4 digits. En associant à chaque digit une variable booléenne  0/1  on peut encoder les valeurs de cette variable à 9 valeurs sur 4 dimensions au lieu des 8 du one hot encoding.

On aura par exemple :

  • Alignement => 1 => 0000

  • Jardin  => 2 => 0001

  • CIMETIERE => 3 => 0010

  • DASES  => 9 => 1001

  • etc.

De même, la variable  espece  qui a 559 valeurs distinctes pour tout le dataset (pas seulement les platanes) ne nécessite alors plus que 10 variables au lieu de 558 (  $\(2^10 = 1024\)$ ).

Avec  category_encoders  , le codage binaire de la variable  espece  suit :

from category_encoders.binary import BinaryEncoder
encoder = BinaryEncoder()
espece_bin = encoder.fit_transform(data.espece)
espece_bin.shape
(207641, 10)
espece_bin.head()
espece_0  espece_1  espece_2  espece_3  espece_4  espece_5  espece_6  espece_7  espece_8  espece_9
0     0     0     0     0     0     0     0     0     0     1
1     0     0     0     0     0     0     0     0     1     0
2     0     0     0     0     0     0     0     0     1     1
3     0     0     0     0     0     0     0     1     0     0
4     0     0     0     0     0     0     0     1     0     1

Le Feature Engineering

Revenons vers des choses plus créatives que sont le feature engineering (ou en français : la création de nouvelles variables).

On peut néanmoins mentionner comme techniques répandues :

  • la mise en intervalle (bucket) des données continues ;

  • la transformation de variables existantes : puissance, inverse, racine carrée ou log, etc. ;

  • la création d’une variable  flag  qui indique si une autre variable est renseignée ou manquante, ou tout autre caractéristique notable ;

  • et la  compilation de plusieurs variables ensemble : par opérations sur les variables numériques, agrégation de textes, filtres sur les images, etc.

En fait, l'amélioration du dataset par le feature engineering revient à faire à un véritable travail de détective à partir :

  • de l'analyse statistique des variables ;

  • des connaissances du domaine ;

  • de l'intégration de datasets externes ;

  • de l'étude précise des erreurs de prédiction du modèle.

Dans notre contexte urbain forestier, nous pourrions augmenter notre dataset avec des données sur l'environnement de chaque arbre en intégrant les images de Google Maps obtenues à partir des coordonnées géographiques contenues dans le dataset.

Autre exemple de feature engineering. Dans le chapitre 2 de la partie 2, nous avons construit une régression linéaire sur le dataset advertising. Pour rappel, le revenu dépend du budget de publicité dans 3 médias : télévision, journaux et radio. La régression est de la forme  ventes ~ tv + radio + journaux  .

Après analyse visuelle de la relation entre les ventes et la télé, nous avons vérifié qu'ajouter le carré de la variable  tv  améliorerait significativement le score du modèle :

ventes ~ tv^2 + tv + radio + journaux

En résumé

  • Les variables catégoriques textuelles doivent être numérisées pour être compatibles avec le modèle.

  • La méthode la plus simple est l'encodage ordinal qui associe un entier à chaque catégorie. Il peut cependant induire un ordre qui n'est pas présent dans la variable originale.

  • L'encodage One Hot crée une nouvelle variable booléenne par valeur de la variable catégorique originale. 

  • Lorsque la variable catégorique originale prend beaucoup de valeurs, l'encodage One Hot va créer trop de nouvelles variables qui vont noyer l'information contenue dans les autres variables du dataset.

  • Une alternative consiste à appliquer l'encodage binaire, qui réduit fortement le nombre de variables supplémentaires qui sont nécessaires à l'encodage.

Dans le prochain chapitre, nous allons revenir sur la partie modèle du binôme ‘données - modèle’ avec un concept essentiel au Machine Learning : l'overfit !

Exemple de certificat de réussite
Exemple de certificat de réussite