Machine Learning avec la régression logistique en Python
Apprenez comment fonctionne la régression logistique, comment entraîner des classifieurs binaires et multiclasses en Python avec scikit-learn, les évaluer et ajuster la régularisation.
La régression logistique est un algorithme de classification supervisée qui estime la probabilité qu'un échantillon appartienne à une classe particulière. Malgré le mot « régression » dans son nom, elle est utilisée pour des tâches de classification — prédire une étiquette catégorielle telle que spam/non-spam, malade/sain ou clic/pas de clic.
Ce chapitre couvre :
- Le fonctionnement de la régression logistique (la fonction sigmoïde et le log-odds)
- La construction d'un classifieur binaire en Python avec
scikit-learn - L'évaluation d'un classifieur au-delà de la simple exactitude
- La gestion des problèmes multiclasses
- La régularisation et quand l'ajuster
- La mise à l'échelle des caractéristiques et pourquoi c'est important
- Quand utiliser la régression logistique par rapport à d'autres classifieurs
Fonctionnement de la régression logistique
De la régression linéaire aux probabilités
La régression linéaire prédit une valeur continue. Si vous tentiez de l'utiliser pour la classification, les prédictions pourraient sortir de l'intervalle [0, 1], les rendant impossibles à interpréter comme des probabilités. La régression logistique résout ce problème en faisant passer la combinaison linéaire par la fonction sigmoïde :
σ(z) = 1 / (1 + e^(-z))Où z = w₀ + w₁x₁ + w₂x₂ + … + wₙxₙ est la somme pondérée des caractéristiques d'entrée. La sigmoïde écrase tout nombre réel dans l'intervalle (0, 1), donnant une estimation de probabilité valide.
Frontière de décision
Le modèle prédit la classe 1 lorsque la probabilité dépasse un seuil (par défaut 0.5), et la classe 0 sinon :
ŷ = 1 if σ(z) ≥ 0.5
ŷ = 0 if σ(z) < 0.5Le seuil σ(z) = 0.5 correspond à z = 0, ce qui définit la frontière de décision — un hyperplan dans l'espace des caractéristiques qui sépare les deux classes.
Log-odds (logit)
Prendre le logarithme du rapport de cotes montre pourquoi le modèle est linéaire dans les paramètres :
log(p / (1 - p)) = w₀ + w₁x₁ + … + wₙxₙChaque coefficient wᵢ représente la variation du log-odds pour une augmentation d'une unité de la caractéristique xᵢ, en maintenant les autres caractéristiques constantes. Cela rend la régression logistique interprétable.
Comment les paramètres sont appris
Contrairement à la régression linéaire, il n'existe pas de solution analytique. Le modèle minimise la fonction de coût log-loss (entropie croisée) à l'aide d'un optimiseur itératif (par défaut lbfgs dans scikit-learn) :
Loss = -1/m Σ [ yᵢ log(p̂ᵢ) + (1 - yᵢ) log(1 - p̂ᵢ) ]Une log-loss plus faible signifie que les probabilités prédites sont mieux calibrées par rapport aux vraies étiquettes.
Classification binaire en Python
L'exemple suivant utilise le jeu de données Breast Cancer Wisconsin — 569 échantillons, 30 caractéristiques numériques, cible binaire (malin = 1, bénin = 0). Il est fourni avec scikit-learn, donc aucun fichier externe n'est nécessaire.
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report
# 1. Load data
data = load_breast_cancer()
X, y = data.data, data.target # X: (569, 30) y: 0=malignant, 1=benign
# 2. Split into train (80 %) and test (20 %)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 3. Scale features — logistic regression converges faster and more reliably
# when features are on a similar scale
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train)
X_test_sc = scaler.transform(X_test)
# 4. Train
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(X_train_sc, y_train)
# 5. Evaluate
y_pred = clf.predict(X_test_sc)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
print(classification_report(y_test, y_pred, target_names=data.target_names))Résultat attendu :
Accuracy: 0.974
precision recall f1-score support
malignant 0.98 0.95 0.96 43
benign 0.97 0.99 0.98 71
accuracy 0.97 114
macro avg 0.97 0.97 0.97 114
weighted avg 0.97 0.97 0.97 114Le modèle atteint ~97 % d'exactitude sur les données de test. Notez que le LogisticRegression de scikit-learn ajoute une régularisation L2 par défaut (C=1.0), ce qui favorise la généralisation.
Pourquoi la mise à l'échelle des caractéristiques est importante
La régression logistique utilise une optimisation par gradient. Sans mise à l'échelle, une caractéristique avec de grandes valeurs (par exemple, le rayon moyen ~14) domine les mises à jour du gradient, ralentissant la convergence ou provoquant l'échec du solveur. StandardScaler transforme chaque caractéristique vers une moyenne nulle et une variance unitaire.
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Without scaling — needs more iterations, may warn about convergence
clf_raw = LogisticRegression(max_iter=200, random_state=42)
clf_raw.fit(X_train, y_train)
print(f"Unscaled accuracy : {clf_raw.score(X_test, y_test):.3f}")
# With scaling
scaler = StandardScaler()
clf_sc = LogisticRegression(max_iter=200, random_state=42)
clf_sc.fit(scaler.fit_transform(X_train), y_train)
print(f"Scaled accuracy : {clf_sc.score(scaler.transform(X_test), y_test):.3f}")Ajustez toujours le scaler uniquement sur l'ensemble d'entraînement, puis utilisez ce même scaler ajusté pour transformer les ensembles d'entraînement et de test — cela prévient la fuite de données.
Évaluation d'un classifieur
L'exactitude seule peut être trompeuse lorsque les classes sont déséquilibrées. Utilisez une matrice de confusion et les métriques qui en découlent.
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt
data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train)
X_test_sc = scaler.transform(X_test)
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(X_train_sc, y_train)
y_pred = clf.predict(X_test_sc)
cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(cm, display_labels=data.target_names)
disp.plot(cmap="Blues")
plt.title("Logistic Regression — Breast Cancer")
plt.tight_layout()
plt.savefig("confusion_matrix.png", dpi=150)
plt.show()Métriques clés issues de la matrice de confusion :
| Métrique | Formule | Signification |
|---|---|---|
| Précision | TP / (TP + FP) | Parmi tous les positifs prédits, combien sont vraiment positifs |
| Rappel (Sensibilité) | TP / (TP + FN) | Parmi tous les vrais positifs, combien le modèle a-t-il détectés |
| Score F1 | 2 × (P × R) / (P + R) | Moyenne harmonique de la précision et du rappel |
| Spécificité | TN / (TN + FP) | Parmi tous les vrais négatifs, combien le modèle a-t-il correctement rejetés |
En diagnostic médical, le rappel (sensibilité) est souvent plus important que la précision — un cancer malin manqué est pire qu'une fausse alerte.
Scores de probabilité et courbe AUC-ROC
Au lieu d'une prédiction binaire, vous pouvez récupérer la probabilité de la classe positive avec predict_proba() :
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_auc_score
data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(scaler.fit_transform(X_train), y_train)
# Probability of the positive class (benign = 1)
y_proba = clf.predict_proba(scaler.transform(X_test))[:, 1]
print(f"AUC-ROC: {roc_auc_score(y_test, y_proba):.3f}")Résultat attendu :
AUC-ROC: 0.997Un AUC proche de 1.0 signifie que le modèle classe les échantillons positifs au-dessus des négatifs de façon presque parfaite. Consultez le chapitre Courbe AUC-ROC pour savoir comment tracer et interpréter la courbe complète.
Classification multiclasse
Lorsque la cible comporte plus de deux classes, scikit-learn étend automatiquement la régression logistique. À partir de scikit-learn 1.5, le solveur lbfgs utilise toujours l'approche multinomiale (softmax), qui entraîne un seul modèle avec une couche de sortie softmax et minimise l'entropie croisée sur toutes les classes conjointement. C'est généralement plus précis que l'ancienne stratégie One-vs-Rest (OvR), qui entraînait un classifieur binaire séparé par classe.
Le jeu de données Iris comporte trois espèces de fleurs — un exemple naturel de multiclasse :
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
data = load_iris()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train)
X_test_sc = scaler.transform(X_test)
# From scikit-learn 1.5+, multinomial softmax is the default for lbfgs
clf = LogisticRegression(solver="lbfgs", max_iter=1000, random_state=42)
clf.fit(X_train_sc, y_train)
y_pred = clf.predict(X_test_sc)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
# Class probabilities for the first three test samples
print("\nClass probabilities (first 3 samples):")
for proba in clf.predict_proba(X_test_sc)[:3]:
print([f"{p:.3f}" for p in proba])Résultat attendu :
Accuracy: 1.000
Class probabilities (first 3 samples):
['0.011', '0.876', '0.113']
['0.964', '0.036', '0.000']
['0.000', '0.003', '0.997']Régularisation
La régularisation pénalise les grands coefficients pour éviter le surajustement. scikit-learn fournit deux types via le paramètre penalty :
| Paramètre | Type | Effet |
|---|---|---|
penalty='l2' (par défaut) | Ridge | Réduit tous les coefficients vers zéro ; conserve toutes les caractéristiques |
penalty='l1' | Lasso | Amène certains coefficients exactement à zéro ; sélection implicite de caractéristiques |
penalty='elasticnet' | Mix | Combine L1 et L2 ; nécessite solver='saga' |
penalty=None | Aucune | Pas de régularisation ; à utiliser uniquement si les données sont volumineuses et propres |
La force de la régularisation est contrôlée par C (l'inverse de la force de régularisation — un C plus petit signifie une régularisation plus forte) :
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np
data = load_breast_cancer()
X, y = data.data, data.target
results = {}
for C in [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]:
pipe = Pipeline([
("scaler", StandardScaler()),
("clf", LogisticRegression(C=C, max_iter=1000, random_state=42)),
])
scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
results[C] = scores.mean()
print(f"C={C:7.3f} CV accuracy: {scores.mean():.4f} ± {scores.std():.4f}")Ceci utilise la validation croisée pour trouver la valeur de C qui généralise le mieux. Pour une recherche systématique sur plusieurs hyperparamètres, consultez Grid Search.
Utiliser un Pipeline
Un Pipeline enchaîne le prétraitement et le modèle en un seul objet. Cela prévient les fuites de données accidentelles et simplifie la validation croisée et le déploiement :
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score
data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
pipe = Pipeline([
("scaler", StandardScaler()),
("clf", LogisticRegression(C=1.0, max_iter=1000, random_state=42)),
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
# Predict probabilities on a new sample (raw, unscaled)
new_sample = X_test[:1] # first test sample
print(f"Predicted class : {pipe.predict(new_sample)[0]}")
print(f"Class probability : {pipe.predict_proba(new_sample)[0]}")Résultat attendu :
Accuracy: 0.974
Predicted class : 1
Class probability : [0.11359025 0.88640975]Le pipeline gère la mise à l'échelle en interne — vous appelez predict() avec des valeurs de caractéristiques brutes.
Inspection des coefficients
Les coefficients entraînés révèlent quelles caractéristiques orientent la prédiction vers chaque classe. Les valeurs absolues plus grandes signifient une influence plus forte :
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import numpy as np
data = load_breast_cancer()
X, y = data.data, data.target
scaler = StandardScaler()
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(scaler.fit_transform(X), y)
# Sort by absolute coefficient value
coefs = clf.coef_[0] # shape (n_features,) for binary classification
sorted_idx = np.argsort(np.abs(coefs))[::-1]
print(f"{'Feature':<35} {'Coefficient':>12}")
print("-" * 48)
for i in sorted_idx[:5]:
print(f"{data.feature_names[i]:<35} {coefs[i]:>12.4f}")Résultat attendu (5 meilleures caractéristiques par poids absolu) :
Feature Coefficient
------------------------------------------------
worst texture -1.3206
radius error -1.2893
worst radius -1.0266
area error -0.9989
worst area -0.9947Les coefficients négatifs (après mise à l'échelle) orientent vers la classe 0 (malin) ; les coefficients positifs orientent vers la classe 1 (bénin).
Avantages et limitations
Quand utiliser la régression logistique
- Vous avez besoin d'estimations de probabilité, pas seulement d'étiquettes de classe.
- La relation entre les caractéristiques et le log-odds est approximativement linéaire.
- Vous avez besoin d'un modèle interprétable — les coefficients sont significatifs.
- Comme référence rapide avant d'essayer des modèles plus complexes comme les arbres de décision ou les méthodes d'ensemble.
- Les jeux de données sont volumineux (la régression logistique passe bien à l'échelle avec de nombreux échantillons).
Limitations
| Limitation | Atténuation |
|---|---|
| Suppose une frontière de décision linéaire | Utiliser des caractéristiques polynomiales ou passer à Decision Tree / K-Nearest Neighbors |
| Sensible à l'échelle des caractéristiques | Toujours appliquer StandardScaler ou MinMaxScaler |
| Difficultés avec les caractéristiques fortement corrélées | Supprimer ou régulariser avec L1 (penalty='l1') |
| Inadaptée aux interactions de caractéristiques très complexes | Utiliser des méthodes d'ensemble ou des réseaux de neurones |
Régression logistique vs. classifieurs apparentés
| Algorithme | Frontière de décision | Mise à l'échelle nécessaire | Sortie probabiliste |
|---|---|---|---|
| Régression logistique | Linéaire | Oui | Oui (calibrée) |
| Decision Tree | Non linéaire (alignée sur les axes) | Non | Oui (moins calibrée) |
| K-Nearest Neighbors | Non linéaire (basée sur les instances) | Oui | Oui |
| Linear Regression | Linéaire (sortie continue) | Oui | Non |
Points clés à retenir
- La régression logistique estime une probabilité à l'aide de la fonction sigmoïde ; la classe est assignée en appliquant un seuil à cette probabilité.
- Mettez toujours à l'échelle les caractéristiques avec
StandardScaleravant l'entraînement — cela accélère la convergence et améliore la précision. - Utilisez un Pipeline pour regrouper la mise à l'échelle et le modèle ; cela prévient les fuites de données et simplifie le déploiement.
- Évaluez avec la précision, le rappel, le score F1 et l'AUC-ROC plutôt que l'exactitude seule, en particulier avec des données déséquilibrées. Consultez les chapitres Matrice de confusion et Courbe AUC-ROC.
- Contrôlez le surajustement avec le paramètre
C(plus petit = régularisation plus forte) ; utilisez la validation croisée ou la recherche par grille pour l'ajuster. - Pour les problèmes multiclasses, utilisez
solver="lbfgs"(par défaut) ; scikit-learn 1.5+ utilise toujours le softmax (multinomial) qui gère bien les classes chevauchantes.