← Retourner à la liste des articles
Image blog
Auteur

Par Maxime Jumelle

CTO & Co-Founder

Publié le 27 nov. 2020

Catégorie Machine Learning

Les pipelines de scikit-learn

Dans la plupart des projets de Machine Learning, le jeu de données utilisé pour calibrer le modèle doit subir toute une série de transformations. Encodage de variables catégorielles, de la normalisation, du feature scaling et autres techniques spécifiques.

Cependant, cette série de transformations doit être appliquée plus d'une fois. D'une part, au moment où il faut entraîner le modèle, et d'autre part lorsqu'il faut obtenir une prédiction pour de nouvelles données. Comment ne pas mélanger les différentes étapes et garantir un traitement consistant entre ces deux étapes ? C'est tout l'intérêt des pipelines de scikit-learn. Ces pipelines vont résoudre un certain nombre de problèmes.

  • Toutes les opérations de transformations vont être centralisées dans le pipeline.
  • Il sera plus facile d'intégrer ce pipelines à plusieurs endroits dans le code source.
  • Il est modulable et peut ponctuellement être modifié.
  • La gestion des versions du pipeline sera plus simple.

Pour illustrer les pipelines de scikit-learn, nous allons utiliser le jeu de données suivant.

import pandas as pd

dataset = pd.read_csv(
  "https://dv495y1g0kef5.cloudfront.net/single_notebooks/data/car_acc.csv",
  header=0,
  names=["buying", "maint", "doors", "persons", "lug_boot", "safety", "eval"]
)
dataset.head()

Chaque ligne représente une voiture. Les variables explicatives sont :

  • buying (qualitative) : un prix d’achat (bas, moyen, haut et très haut).
  • maint (qualitative) : entretien nécessaire pour le fonctionnement (bas, moyen, haut et très haut).
  • doors (quantitative) : le nombre de portes.
  • persons (quantitative) : le nombre de personnes.
  • lug_boot (qualitative) : la taille du coffre (petit, moyen et grand).
  • safety (qualitative) : le niveau de sécurité (faible, modéré et fort).

La variable que l’on souhaite modéliser (i.e. la variable réponse) est nommée eval et représente le niveau de satisfaction de la voiture (unacc pour non satisfait ou acc pour satisfait).

Les Transformers

Un Transformer est un objet scikit-learn qui permet d'appliquer une transformation (encodage, normalisation, ...) sur un DataFrame. L'intérêt du Transformer est double.

  • Pouvoir définir une méthode de transformation sur-mesure.
  • Pouvoir appliquer cette méthode sur un DataFrame quelconque.

À lire aussi : découvrez notre formation MLOps


Nous allons commencer par encoder les colonnes lug_boot et safety en one-hot avec le OneHotEncoder.

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder

one_hot_encoder = Pipeline(
  steps=[
    ('one_hot', OneHotEncoder(handle_unknown='ignore'))
  ]
)

ℹ️ Pas besoin de définir pour l'instant les colonnes qui vont subir cette transformation : cela sera effectué lorsque l'on assemblera les Transformers.

Nous pouvons également construire un Transformer sur mesure avec FunctionTransformer. En particulier, nous allons construire un Transformer pour certaines variables explicatives pour lesquelles nous considérons qu'il n'est pas nécessaire d'utiliser un OneHotEncoder. Les colonnes buying et maint prendront comme valeur 0, 1, 2 ou 3 (correspondant à low, med, high et vhigh).

from sklearn.preprocessing import FunctionTransformer

encoding = { "low": 0, "med": 1, "high": 2, "vhigh": 3 }

def grad_encoder(df):
  for col in df.columns:
    df[col] = df[col].apply(lambda x: encoding[x])
  return df

eval_encoder = Pipeline(
  steps=[
    ('grad', FunctionTransformer(grad_encoder))
  ]
)

Enfin, il y a quelques cas particuliers pour les colonnes doors et persons : la valeur 5more est présente dans la colonne doors s'il y a 5 portes ou plus, et de même dans la colonne persons lorsque le véhivule peut accueillir 6 passagers ou plus.

Dans ce cas, puisque nous souhaitons uniquement des valeurs numériques dans ces colonnes, nous allons construire un dernier Transformer pour encoder ces variables.

def num_encoder(df):
  for col in df.columns:
    df[col] = df[col].apply(lambda x: 5 if x == "5more" else x)
    df[col] = df[col].apply(lambda x: 6 if x == "more" else x)
  return df

num_encoder = Pipeline(
  steps=[
    ('num', FunctionTransformer(num_encoder))
  ]
)

Une fois terminé, il ne reste plus qu'à combiner tous ces Transformers dans un ColumnTransformer : cela va permettre d'appliquer chaque Transformer sur un ensemble de colonnes sur-mesure. Plutôt que d'écrire un Transformer par colonne, cela permet de gagner du temps en appliquant la même méthode d'encodage sur plusieurs colonnes.

from sklearn.compose import ColumnTransformer

preprocessor = ColumnTransformer(
  transformers=[
    ('categorical', one_hot_encoder, ['lug_boot', 'safety']),
    ('grad', eval_encoder, ['buying', 'maint']),
    ('num', num_encoder, ['doors', 'persons']),
  ]
)

Par ailleurs, la documentation de scikit-learn présente une liste exhaustive des Transformers disponibles par défaut.

Le Classifieur

Une fois que le pipeline de preprocessing est en place, nous pouvons ajouter la couche prédictive, qui ici est un Random Forest.


À lire aussi : découvrez notre formation MLOps


from sklearn.ensemble import RandomForestClassifier

rf = Pipeline(
  steps=[
    ('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier())
  ]
)

La particularité, c'est que la variable rf n'est pas uniquement le modèle, mais renferme également les étapes de preprocessing que nous appliquons aux données.

Ainsi, qu'il s'agisse d'un fit ou d'un predict, les données subiront les étapes du preprocessing au préalable.

Comme toujours, nous séparons le jeu de données en un ensemble d'entraînement et un ensemble de test.

from sklearn.model_selection import train_test_split

X = dataset.drop('eval', axis=1)
y = dataset['eval'].apply(lambda x: 0 if x == "unacc" else 1)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=24)

Puis nous calibrons directement le pipeline contenant le preprocessing et le modèle.

rf.fit(X_train, y_train)

Si maintenant, on souhaite calculer un score, même principe, la fonction predict appliquera toutes les transformations au DataFrame renseigné.

from sklearn.metrics import f1_score

y_pred = rf.predict(X_test)
print("Score : {:2.1f}%".format(f1_score(y_test, y_pred) * 100))

Modularité des pipelines

L'intérêt des pipelines, en plus de la reproductibilité, est la modularité offerte par la combinaison linéaire des étapes. Par exemple, plutôt que d'avoir un seul modèle, nous souhaitons tester plusieurs modèles : il suffit juste de modifier la couche de classification, sans modifier le preprocessing.

Par exemple, ici, nous allons calibrer trois modèles : un Random Forest, un Gradient Boosting et un modèle binomial. Tous les trois auront le même jeu de données en entrée, ce qui permettra de les comparer plsu efficacement.

from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression

models_names = ["Random Forest", "Gradient Boosting", "Logistic Regression"]
# Contient les trois pipelines (une pour chaque modèle)
pipelines = [
  Pipeline(steps=[('preprocessor', preprocessor), ('classifier', RandomForestClassifier())]),
  Pipeline(steps=[('preprocessor', preprocessor), ('classifier', GradientBoostingClassifier())]),
  Pipeline(steps=[('preprocessor', preprocessor), ('classifier', LogisticRegression())])
]

Nous pouvons ensuite appeler chaque pipeline.

for p, name in zip(pipelines, models_names):
  p.fit(X_train, y_train)
  y_pred = p.predict(X_test)
  print("Score ({}) : {:2.1f}%".format(
    name,
    f1_score(y_test, y_pred) * 100
  ))

Nous vons directement ici que les deux premiers modèles sont bien meilleurs que le dernier. On pourrait ainsi imaginer la même technique si l'on effectuait une recherche par grille pour optimiser les hyper-paramètres.

Vous souhaitez vous former au MLOps ?

Articles similaires

Blog

20 sept. 2022

Machine Learning

Hugging Face est une startup française qui s'est fait connaître grâce à l'infrastructure NLP qu'ils ont développée. Aujourd'hui, elle est sur le point de révolutionner le domaine du Machine Learning et traitement automatique du langage naturel. Dans cet article, nous allons présenter Hugging Face et détailler les taches de base que cette librairie permet de réaliser. Nous allons également énumérer ses avantages et ses alternatifs.

Nada Belaidi

Équipe Blent

Data Scientist

Lire l'article

Blog

12 juil. 2022

Machine Learning

spaCy est une bibliothèque open-source pour le traitement avancé du langage naturel. Elle est conçue spécifiquement pour une utilisation en production et permet de construire des applications qui traitent et comprennent de grands volumes de texte.

Nada Belaidi

Équipe Blent

Data Scientist

Lire l'article

Blog

4 juil. 2022

Machine Learning

Un auto-encodeur est une structure de réseaux neuronaux profonds qui s'entraîne pour réduire la quantité de données nécessaires pour représenter une donnée d'entrée. Ils sont couramment utilisés en apprentissage automatique pour effectuer des tâches de compression de données, d'apprentissage de représentations et de détection de motifs.

Nada Belaidi

Équipe Blent

Data Scientist

Lire l'article