LightGBM (Classification) : Guide complet — Principes, Exemples et Implémentation Python
Résumé
LightGBM (Light Gradient Boosting Machine) est un algorithme de gradient boosting développé par Microsoft, conçu pour être extrêmement rapide et efficace en mémoire tout en maintenant des performances de pointe. Contrairement aux algorithmes de boosting traditionnels comme XGBoost qui utilisent une croissance level-wise (niveau par niveau), LightGBM adopte une croissance leaf-wise (feuille par feuille), ce qui réduit considérablement le nombre de splits nécessaires et accélère l’entraînement. Deux innovations majeures le distinguent : le GOSS (Gradient-based One-Side Sampling) qui échantillonne les données selon leurs gradients, et l’EFB (Exclusive Feature Bundling) qui regroupe les features mutuellement exclusives. Résultat : un modèle qui peut être jusqu’à 20 fois plus rapide que les implémentations classiques, avec une précision équivalente voire supérieure.
Principe mathématique
Le cadre du Gradient Boosting
LightGBM s’inscrit dans le cadre du gradient boosting, où chaque nouvel arbre apprend à corriger les résidus (négatifs du gradient) du modèle précédent. Pour un problème de classification :
Étape 1 — Initialiser avec une constante, généralement le log-odds de la classe positive :
F₀(x) = log(p / (1 – p))
Étape 2 — Pour chaque boosting round t = 1 à T :
– Calculer les pseudo-résidus : r_it = −[∂L(y_i, F(x_i)) / ∂F(x_i)] évalué à F = F_{t−1}
– Ajuster un arbre de régression sur les résidus r_it
– Mettre à jour : F_t(x) = F_{t−1}(x) + η · h_t(x)
Étape 3 — Prédiction finale : ŷ = σ(F_T(x)) où σ est la fonction sigmoïde.
La fonction de perte pour la classification binaire est le log loss (entropie croisée binaire) :
L(y, F) = −y · log(p) − (1 − y) · log(1 − p) où p = σ(F(x))
GOSS : Gradient-based One-Side Sampling
Le GOSS est l’une des innovations clés de LightGBM. L’idée est simple mais puissante : les échantillons avec de grands gradients sont plus importants pour l’apprentissage, car ce sont les données que le modèle actuel prédit le plus mal.
Fonctionnement du GOSS :
- Trier les instances par valeur absolue du gradient.
- Conserver les top a × 100 % des instances (ceux avec les plus grands gradients).
- Randomly sampler b × 100 % d’instances supplémentaires parmi le reste.
- Appliquer un facteur de compensation multiplicateur (1 − a) / b aux gradients des instances échantillonnées aléatoirement, pour préserver l’estimation non biaisée du gain d’information.
Mathématiquement, le gain d’information pour un split j au seuil v est estimé en pondérant les gradients des instances du sous-échantillon aléatoire par le facteur de compensation. Cette approche réduit drastiquement le coût computationnel tout en préservant la précision, car les données bien entraînées (faibles gradients) contribuent moins à l’apprentissage futur.
EFB : Exclusive Feature Bundling
L’EFB repose sur une observation importante : dans les données creuses (sparse), de nombreuses features sont mutuellement exclusives (elles ne sont pas actives simultanément). Par exemple, dans un encodage one-hot, une seule feature parmi un groupe est active à la fois.
Procédure de l’EFB :
- Construire un graphe où chaque feature est un nœud.
- Connecter deux features si leur taux de conflit (co-occurrence non nulle) est inférieur à un seuil γ.
- Résoudre le problème de coloration de graphe pour regrouper les features compatibles.
- Bundler les features exclusives en une seule feature composite.
Résultat : réduction significative du nombre de features, donc accélération du training et réduction de la consommation mémoire, sans perte notable d’information.
Split par histogrammes
Au lieu de trier toutes les valeurs possibles pour chaque feature (comme le fait XGBoost par défaut), LightGBM discrétise les features continues en K buckets (généralement K = 255) et construit un histogramme des gradients pour chaque bucket.
Avantages :
- Mémoire : au lieu de stocker les gradients pour chaque échantillon, on ne stocke que K compteurs par feature.
- Vitesse : trouver le meilleur split se fait en parcourant K buckets au lieu de n échantillons.
- Régularisation implicite : la discrétisation agit comme un lissage, réduisant le risque de surapprentissage.
Le coût passe de O(n × features) par split à O(K × features), ce qui est dramatiquement plus efficace pour les grands jeux de données.
Croissance leaf-wise vs level-wise
C’est la différence fondamentale entre LightGBM et les autres implémentations de gradient boosting.
Level-wise (XGBoost, sklearn) : Développe tous les nœuds d’un niveau avant de passer au suivant. L’arbre est équilibré et symétrique, mais certains splits sont potentiellement inutiles, ce qui ralentit l’entraînement.
Leaf-wise (LightGBM) : Développe le nœud feuille avec le plus grand gain à chaque étape. L’arbre est asymétrique mais optimisé pour le gain. Le résultat est un modèle qui atteint une erreur d’entraînement plus faible avec moins de splits, donc plus rapidement.
Le risque de la croissance leaf-wise est le surapprentissage : sans régularisation stricte, l’arbre peut devenir excessivement profond d’un côté. C’est pourquoi les paramètres num_leaves et min_data_in_leaf sont essentiels.
Intuition
LightGBM = XGBoost en mode sport — plus rapide, moins gourmand en mémoire, mais attention au surapprentissage avec la croissance leaf-wise.
Imaginez que vous construisez un arbre de décision comme on optimiserait un budget. Avec l’approche level-wise, vous investissez uniformément dans chaque domaine, même si certains n’ont pas besoin de financement. Avec l’approche leaf-wise, vous investissez là où le rendement marginal est le plus élevé. Le résultat est un modèle plus performant avec moins d’effort, mais il faut surveiller que l’investissement ne se concentre pas trop sur un seul aspect (d’où la régularisation).
LightGBM est idéal lorsque :
- Vous avez des millions de lignes et XGBoost met des heures.
- La mémoire est limitée (l’approche par histogrammes réduit drastiquement l’empreinte).
- Vous avez besoin de résultats rapides pour de l’exploration ou des compétitions.
- Vos données sont creuses (l’EFB fait merveille).
En revanche, pour des petits jeux de données (quelques milliers de lignes), la différence est négligeable et XGBoost peut être préférable pour sa meilleure régularisation par défaut.
Implémentation Python
Installation
pip install lightgbm
Exemple complet : LightGBM classification
import lightgbm as lgb
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import numpy as np
# 1. Générer un jeu de données
X, y = make_classification(
n_samples=50000,
n_features=30,
n_informative=15,
n_redundant=5,
n_classes=2,
random_state=42
)
# 2. Division train/test
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# 3. Création du dataset LightGBM
train_data = lgb.Dataset(X_train, label=y_train)
eval_data = lgb.Dataset(X_test, label=y_test, reference=train_data)
# 4. Hyperparamètres
params = {
"objective": "binary",
"metric": "binary_logloss",
"boosting_type": "gbdt",
"num_leaves": 31,
"learning_rate": 0.05,
"feature_fraction": 0.8,
"bagging_fraction": 0.8,
"bagging_freq": 5,
"verbose": -1,
"min_data_in_leaf": 20,
"lambda_l1": 1.0,
"lambda_l2": 1.0,
}
# 5. Entraînement avec early stopping
model = lgb.train(
params,
train_data,
num_boost_round=1000,
valid_sets=[eval_data],
valid_names=["eval"],
callbacks=[
lgb.early_stopping(stopping_rounds=50),
lgb.log_evaluation(period=100),
],
)
# 6. Prédictions
y_pred_proba = model.predict(X_test)
y_pred = (y_pred_proba >= 0.5).astype(int)
# 7. Évaluation
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print("\nClassification Report:")
print(classification_report(y_test, y_pred))
print("\nMatrice de confusion:")
print(confusion_matrix(y_test, y_pred))
API sklearn : LGBMClassifier
LightGBM offre une interface compatible scikit-learn, pratique pour l’intégration dans des pipelines existants :
from lightgbm import LGBMClassifier
from sklearn.model_selection import cross_val_score
# API sklearn — plus simple d'utilisation
clf = LGBMClassifier(
n_estimators=500,
learning_rate=0.05,
num_leaves=31,
max_depth=-1,
min_child_samples=20,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=1.0,
reg_lambda=1.0,
random_state=42,
verbose=-1,
)
# Cross-validation
cv_scores = cross_val_score(clf, X_train, y_train, cv=5, scoring="accuracy")
print(f"CV Accuracy: {cv_scores.mean():.4f} (+/- {cv_scores.std():.4f})")
# Fit final
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(f"Test Accuracy: {accuracy_score(y_test, y_pred):.4f}")
# Importance des features
import matplotlib.pyplot as plt
lgb.plot_importance(clf, max_num_features=15, figsize=(10, 6))
plt.tight_layout()
plt.show()
Comparaison LightGBM vs GradientBoostingClassifier
import time
from sklearn.ensemble import GradientBoostingClassifier
# Taille significative pour voir la différence
X_large, y_large = make_classification(
n_samples=100000, n_features=50, n_informative=25,
n_classes=2, random_state=42,
)
X_tr, X_te, y_tr, y_te = train_test_split(
X_large, y_large, test_size=0.2, random_state=42, stratify=y_large,
)
# GradientBoostingClassifier
t0 = time.time()
gb = GradientBoostingClassifier(
n_estimators=200, max_depth=5, learning_rate=0.1, random_state=42,
)
gb.fit(X_tr, y_tr)
gb_time = time.time() - t0
gb_acc = accuracy_score(y_te, gb.predict(X_te))
# LGBMClassifier
t0 = time.time()
lgb_clf = LGBMClassifier(
n_estimators=200, num_leaves=31, learning_rate=0.1,
random_state=42, verbose=-1,
)
lgb_clf.fit(X_tr, y_tr)
lgb_time = time.time() - t0
lgb_acc = accuracy_score(y_te, lgb_clf.predict(X_te))
print(f"GradientBoosting : {gb_time:.2f}s — Accuracy: {gb_acc:.4f}")
print(f"LightGBM : {lgb_time:.2f}s — Accuracy: {lgb_acc:.4f}")
print(f"Ratio de vitesse : {gb_time / lgb_time:.1f}x")
Sur 100 000 échantillons, on observe typiquement un ratio de 10x à 20x en faveur de LightGBM, avec une précision comparable voire supérieure.
num_leaves vs max_depth
Le paramètre num_leaves est le principal contrôle de complexité en mode leaf-wise. Il définit le nombre maximum de feuilles dans un arbre, contrairement à max_depth qui contrôle la profondeur.
Relation théorique : num_leaves ≤ 2^(max_depth). En pratique, LightGBM utilise num_leaves comme contrainte principale et max_depth est souvent désactivé (−1).
Bon point de départ : num_leaves = 31 (environ 2^5 − 1). Pour plus de complexité : 63, 127. Pour plus de régularisation : 15, 7.
Une règle empirique : max_depth égal à 6 ⇒ num_leaves ≤ 63, mais en leaf-wise, on utilise souvent des valeurs inférieures à la valeur théorique maximale pour éviter le surapprentissage.
Early stopping
L’early stopping est crucial pour éviter le surapprentissage et gagner du temps :
# Avec l'API train
model = lgb.train(
params,
train_data,
num_boost_round=1000,
valid_sets=[eval_data],
valid_names=["eval"],
callbacks=[
lgb.early_stopping(stopping_rounds=50),
lgb.log_evaluation(period=0),
],
)
print(f"Meilleure itération: {model.best_iteration}")
# Avec l'API sklearn
clf = LGBMClassifier(n_estimators=1000, random_state=42, verbose=-1)
clf.fit(
X_train, y_train,
eval_set=[(X_test, y_test)],
callbacks=[lgb.early_stopping(50), lgb.log_evaluation(0)],
)
print(f"Best itération: {clf.best_iteration_}")
Hyperparamètres clés
| Paramètre | Rôle | Valeur typique | Impact |
|---|---|---|---|
n_estimators |
Nombre d’arbres | 100–1000 | Plus = meilleur mais risque surapprentissage (utilisé avec early stopping) |
learning_rate |
Taux d’apprentissage | 0,01–0,1 | Plus bas = plus stable, mais plus d’arbres nécessaires |
num_leaves |
Nombre max de feuilles | 20–128 | Principal contrôle leaf-wise. Plus élevé = plus complexe |
max_depth |
Profondeur max | −1 (illimitée) | Contrainte supplémentaire. −1 = contrôlé par num_leaves seul |
min_data_in_leaf |
Échantillons min par feuille | 10–100 | Régularisation clé pour éviter le surapprentissage leaf-wise |
feature_fraction |
Fraction de features par arbre | 0,5–0,9 | Régularisation + vitesse (similaire à colsample_bytree) |
bagging_fraction |
Fraction de données par arbre | 0,5–0,9 | Régularisation + vitesse (similaire à subsample) |
lambda_l1 |
Régularisation L1 | 0–10 | Sparsification des poids, sélection de features |
lambda_l2 |
Régularisation L2 | 0–10 | Lissage des poids, réduction de variance |
Réglage recommandé pour démarrer
params_defaut = {
"objective": "binary",
"metric": "binary_logloss",
"boosting_type": "gbdt",
"num_leaves": 31,
"learning_rate": 0.05,
"feature_fraction": 0.8,
"bagging_fraction": 0.8,
"bagging_freq": 5,
"min_data_in_leaf": 20,
"lambda_l1": 1.0,
"lambda_l2": 1.0,
"verbose": -1,
"n_estimators": 500,
}
Réglage automatique avec Optuna
import optuna
import lightgbm as lgb
def objective(trial):
params = {
"objective": "binary",
"metric": "binary_logloss",
"boosting_type": "gbdt",
"num_leaves": trial.suggest_int("num_leaves", 15, 127),
"learning_rate": trial.suggest_float("learning_rate", 0.01, 0.2),
"feature_fraction": trial.suggest_float("feature_fraction", 0.5, 1.0),
"bagging_fraction": trial.suggest_float("bagging_fraction", 0.5, 1.0),
"bagging_freq": trial.suggest_int("bagging_freq", 1, 10),
"min_data_in_leaf": trial.suggest_int("min_data_in_leaf", 5, 100),
"lambda_l1": trial.suggest_float("lambda_l1", 0, 10),
"lambda_l2": trial.suggest_float("lambda_l2", 0, 10),
"verbose": -1,
}
cv_results = lgb.cv(
params,
train_data,
nfold=5,
num_boost_round=500,
early_stopping_rounds=50,
seed=42,
)
return min(cv_results["binary_logloss-mean"])
study = optuna.create_study(direction="minimize")
study.optimize(objective, n_trials=50)
print(f"Meilleurs hyperparamètres: {study.best_params}")
Avantages et Limites
Avantages
- Vitesse exceptionnelle : Jusqu’à 20x plus rapide que XGBoost grâce à l’approche par histogrammes et la croissance leaf-wise.
- Efficace en mémoire : La discrétisation en buckets et l’EFB réduisent drastiquement la consommation mémoire.
- Support natif des features catégorielles : Pas besoin d’encodage one-hot, LightGBM gère directement les catégories.
- Parallélisation multi-GPU : Support distribué pour l’entraînement à grande échelle.
- GOSS : Réduction intelligente des données d’entraînement sans perte significative de précision.
- Interface scikit-learn : Intégration transparente dans les pipelines sklearn.
Limites
- Risque de surapprentissage : La croissance leaf-wise tend à créer des arbres profonds et asymétriques. Nécessite une régularisation attentive (
min_data_in_leaf,num_leaves). - Moins efficace sur petits datasets : Pour moins de 10 000 échantillons, la différence avec XGBoost ou Random Forest est négligeable.
- Sensibilité aux hyperparamètres : Plus sensible au réglage que XGBoost, notamment
num_leavesetmin_data_in_leaf. - Installation : Peut nécessiter des dépendances C++ sous certains systèmes (Microsoft C++ Build Tools sur Windows).
4 Cas d’usage
Cas 1 : Détection de fraude financière
Contexte : Classification binaire (fraude / non-fraude) sur des millions de transactions avec fort déséquilibre de classes.
Pourquoi LightGBM : Le GOSS préserve bien les échantillons “difficiles” (les fraudes, qui ont typiquement de grands gradients), et la vitesse permet de réentraîner fréquemment le modèle.
from lightgbm import LGBMClassifier
from sklearn.metrics import roc_auc_score
# scale_pos_weight pour gérer le déséquilibre
n_neg = (y_train == 0).sum()
n_pos = (y_train == 1).sum()
clf = LGBMClassifier(
n_estimators=1000,
num_leaves=63,
learning_rate=0.02,
min_data_in_leaf=50,
scale_pos_weight=n_neg / n_pos,
random_state=42,
)
clf.fit(X_train, y_train)
y_proba = clf.predict_proba(X_test)[:, 1]
print(f"ROC-AUC: {roc_auc_score(y_test, y_proba):.4f}")
Cas 2 : Prédiction d’attrition client (Churn)
Contexte : Prédire quels clients vont se désabonner dans les 30 prochains jours, sur un dataset de 500 000 clients avec 40 features mixtes.
clf = LGBMClassifier(
n_estimators=500,
num_leaves=31,
learning_rate=0.05,
feature_fraction=0.7,
bagging_fraction=0.7,
bagging_freq=5,
min_data_in_leaf=30,
lambda_l1=2.0,
lambda_l2=2.0,
random_state=42,
)
clf.fit(X_train, y_train)
# Interprétabilité avec SHAP
import shap
explainer = shap.TreeExplainer(clf)
shap_values = explainer.shap_values(X_test)
shap.summary_plot(shap_values[1], X_test, feature_names=feature_names)
Cas 3 : Classification multi-classes — Recommandation de contenu
Contexte : Classifier l’intention d’un utilisateur parmi 10 catégories de contenu.
clf = LGBMClassifier(
n_estimators=300,
num_leaves=50,
learning_rate=0.05,
objective="multiclass",
num_class=10,
min_data_in_leaf=20,
feature_fraction=0.8,
random_state=42,
)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(classification_report(y_test, y_pred))
Cas 4 : Compétition Kaggle avec données tabulaires
Contexte : Dataset de 2 millions de lignes, 200 features, classification binaire. Le temps d’entraînement est un facteur critique.
Stratégie LightGBM :
– Utiliser EFB pour réduire les 200 features creuses.
– GOSS avec top_rate = 0,2 et other_rate = 0,1.
– Early stopping à 100 rounds pour éviter le surapprentissage.
– 5-fold cross-validation avec métriques multiples.
params = {
"objective": "binary",
"metric": ["binary_logloss", "auc"],
"boosting_type": "gbdt",
"num_leaves": 95,
"learning_rate": 0.03,
"feature_fraction": 0.75,
"bagging_fraction": 0.8,
"bagging_freq": 5,
"min_data_in_leaf": 100,
"lambda_l1": 3.0,
"lambda_l2": 3.0,
"verbose": -1,
"force_row_wise": True,
"histogram_pool_size": -1,
}
model = lgb.train(
params,
train_data,
num_boost_round=5000,
valid_sets=[train_data, eval_data],
valid_names=["train", "valid"],
callbacks=[
lgb.early_stopping(100),
lgb.log_evaluation(200),
],
)
Bonnes pratiques
- Toujours utiliser l’early stopping : Fixez
n_estimatorshaut (1000–5000) et laissez l’early stopping trouver le bon nombre d’arbres. - Commencer avec
num_leaves = 31: C’est un bon compromis. Augmentez si underfitting, diminuez si overfitting. - Ajuster
min_data_in_leaf: Augmentez sur les petits datasets (50–100), diminuez sur les gros (5–20). - Utiliser les features catégorielles nativement : Spécifiez
categorical_featureplutôt que d’encoder manuellement. - Combiner avec SHAP : LightGBM est compatible avec SHAP pour l’interprétabilité des prédictions.
- Cross-validation K-fold : Sur des compétitions, utilisez
lgb.cv()pour une évaluation plus robuste.
Conclusion
LightGBM représente une évolution majeure du gradient boosting pour la classification. Sa croissance leaf-wise, son sampling intelligent GOSS, son bundling de features EFB, et son approche par histogrammes en font l’algorithme de référence pour les données tabulaires de grande taille. Cependant, cette puissance nécessite une régularisation plus attentive que XGBoost, en particulier pour éviter que la croissance leaf-wise ne conduise au surapprentissage. Pour les jeux de données de taille moyenne à grande, LightGBM offre un compromis vitesse-précision difficilement battable, ce qui explique son adoption massive dans les compétitions Kaggle et en production industrielle.
Voir aussi
- Énumérer les Sous-masques d’un Bitmask en Python : Guide Complet et Astuces Pratiques
- Maîtriser les Intersections en Python : Techniques et Astuces pour Optimiser vos Algorithmes

