Un modèle qui remplace chaque coefficient par une courbe lisse. L'effet de l'ancienneté sur le risque de départ n'est plus une droite : il peut baisser, remonter à l'échéance du contrat, puis se stabiliser. Chaque courbe se trace et se lit séparément : c'est le compromis entre la précision et la clarté.
Une régression logistique dont chaque règle droite est remplacée par une règle souple. La règle se plie là où les données le demandent, et un ressort l'empêche de faire des zigzags.
L'ancienneté est décrite par une base de splines : des morceaux de courbe simples, raccordés en douceur, que le modèle combine pour épouser la forme des données.
Le modèle ajuste les morceaux, mais paie un prix pour chaque courbure. Le niveau de lissage est choisi automatiquement (REML dans mgcv).
Le score est la somme des courbes et des effets linéaires. Avec une loi binomiale, il est ensuite converti en probabilité, comme en régression logistique.
Ancienneté en mois, appels au support, montant mensuel, type de contrat, incidents récents, et la colonne à prédire : le client est-il parti ? L'ancienneté est traitée en courbe, le reste en effets linéaires.
Pour un client type sur le jeu d'exemple : environ 20 % de risque à 2 mois, 8 % à 12 mois, un rebond à 11 % autour de 24 mois, à l'échéance d'engagement, puis 6,5 % à 48 mois. Une droite aurait gommé ce rebond.
Sur les clients jamais vus, l'AUC atteint 0,80, contre environ 0,78 pour la régression logistique classique. Le vrai gain est ailleurs : la courbe désigne les moments où déclencher une action de rétention.
Noms donnés pour R (mgcv) et Python (scikit-learn, pyGAM).
Le nombre maximal de morceaux de la courbe. C'est un plafond : le lissage réduit ensuite la flexibilité réelle. Trop bas, un rebond étroit est gommé ; ici k = 20.
La façon de choisir le lissage dans mgcv. REML est la recommandation actuelle : plus stable que la valeur par défaut, GCV.Cp.
Les degrés de liberté effectifs, lus dans summary(). Proche de 1, l'effet est quasi linéaire ; plus haut, la courbe est réellement utilisée.
La loi de la cible, comme pour un GLM : binomial pour un oui / non, poisson pour un comptage, gaussian pour une valeur continue.
# Churn selon l'ancienneté : modèle additif généralisé (GAM) en R
library(mgcv)
clients <- read.csv("clients_churn.csv")
clients$contrat <- factor(clients$contrat)
set.seed(42)
idx <- sample(nrow(clients), round(0.7 * nrow(clients)))
train <- clients[idx, ]
test <- clients[-idx, ]
# s() : courbe lisse pour l'ancienneté, effets linéaires pour le reste
modele <- gam(churn ~ s(anciennete, k = 20) + appels_support + montant + contrat + incidents_3m,
family = binomial, data = train, method = "REML")
print(summary(modele)) # edf proche de 1 = effet quasi linéaire, plus haut = courbe
# Lecture de la courbe : risque d'un client type selon son ancienneté
type_client <- data.frame(anciennete = c(2, 6, 12, 18, 24, 30, 48), appels_support = 1, montant = 45,
contrat = factor("annuel", levels = levels(clients$contrat)), incidents_3m = 0)
print(round(100 * predict(modele, type_client, type = "response"), 1))
# AUC test (formule des rangs)
proba <- predict(modele, test, type = "response")
n1 <- sum(test$churn == 1); n0 <- sum(test$churn == 0)
cat("AUC test :", round((sum(rank(proba)[test$churn == 1]) - n1 * (n1 + 1) / 2) / (n1 * n0), 3), "\n")
# Churn selon l'ancienneté : modèle additif généralisé (GAM) en Python
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import SplineTransformer, OneHotEncoder
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
clients = pd.read_csv("clients_churn.csv")
X = clients[["anciennete", "appels_support", "montant", "contrat", "incidents_3m"]]
X_train, X_test, y_train, y_test = train_test_split(X, clients["churn"], test_size=0.3, random_state=42)
# Courbe lisse (spline) pour l'ancienneté, effets linéaires pour le reste
# scikit-learn n'a pas de GAM : splines + régression pénalisée en donnent l'essentiel (pyGAM pour un GAM complet)
prep = ColumnTransformer([("courbe", SplineTransformer(n_knots=12, degree=3), ["anciennete"]),
("contrat", OneHotEncoder(drop="first"), ["contrat"])], remainder="passthrough")
gam = make_pipeline(prep, LogisticRegression(max_iter=2000)).fit(X_train, y_train)
print("AUC test :", round(roc_auc_score(y_test, gam.predict_proba(X_test)[:, 1]), 3))
# Lecture de la courbe : risque d'un client type selon son ancienneté
type_ = pd.DataFrame({"anciennete": [2, 6, 12, 18, 24, 30, 48], "appels_support": 1, "montant": 45.0, "contrat": "annuel", "incidents_3m": 0})
print((pd.Series(gam.predict_proba(type_)[:, 1], index=type_["anciennete"]) * 100).round(1))
Un GLM relie la cible à une somme d'effets linéaires. Un GAM remplace tout ou partie de ces effets par des courbes lisses estimées sur les données. Il garde la même famille de lois et la même lecture variable par variable.
On trace la courbe de chaque variable : elle montre comment le score évolue quand la variable varie, les autres restant fixes. L'edf indique la complexité de la courbe. Sur une échelle logit, on convertit en probabilité pour un profil type, comme dans le code.
Oui : pyGAM et statsmodels (GLMGam) proposent des GAM pénalisés. Avec scikit-learn seul, SplineTransformer suivi d'une régression pénalisée donne une approximation proche, sans le choix automatique du lissage de mgcv, la référence en R.
Mêmes lois et liens, mais des effets linéaires. Le GAM part souvent d'un GLM existant.
Voir la fiche → des morceaux de droitesRemplace les courbes lisses par des segments raccordés, et cherche seul les nœuds et les interactions.
Voir la fiche → plus précis, moins lisibleCapte seul les interactions. Des outils comme SHAP sont alors nécessaires pour l'expliquer.
Voir la fiche →Dataistudio forme les équipes au machine learning et à l'IA, sur des cas concrets.
Nous utilisons des cookies de mesure d'audience et de suivi publicitaire pour comprendre la fréquentation du site et l'efficacité de nos annonces. Rien n'est déposé sans votre accord. En savoir plus