Projet EDF MAD EDVANCE — Classification automatique multi-classes de consommateurs électriques industriels
Auteur : Emmanuel TSAGUE
Résultat : 91 % de précision en validation croisée — modèle exporté en production (.joblib)
- Contexte
- Problème métier
- Pourquoi ce problème est difficile
- Méthodes
- Résultats
- Valeur métier
- Architecture du projet
- Guide d'utilisation
- Notions clés expliquées
- Auteur
EDF MAD EDVANCE gère un portefeuille de grands comptes industriels : usines, datacenters, hôpitaux, collectivités, sites de production. Chaque consommateur est rattaché à une gamme tarifaire qui détermine ses conditions contractuelles, son pricing et les engagements de qualité de service.
Historiquement, l'affectation d'un consommateur à sa gamme était réalisée manuellement par des chargés de clientèle, à partir de fichiers Excel hétérogènes reçus des fournisseurs et des systèmes internes. Ce processus était :
- Lent : plusieurs jours de traitement pour un lot de dossiers
- Subjectif : les règles d'affectation variaient selon les experts
- Risqué : une mauvaise classification entraîne un tarif incorrect, un litige contractuel ou une perte de marge
Le jeu de données traité contient les consommateurs listés dans le registre officiel 2016, avec leurs caractéristiques techniques et contractuelles.
Ce projet existe parce que le volume de dossiers à traiter croît chaque année et que la saisie manuelle ne passe plus à l'échelle.
Comment automatiser et fiabiliser la classification de consommateurs électriques industriels dans 19 gammes métier, en éliminant les risques de biais et de fuite de données, afin de produire un modèle directement utilisable en production ?
La tâche est une classification multiclasse supervisée :
- Entrée : caractéristiques d'un consommateur (puissance souscrite, type de site, historique de consommation, données contractuelles...)
- Sortie : une des 19 gammes métier (ex. : BT ≤ 36 kVA, HTA, HTB, C5, C4, C3...)
- Contrainte : le modèle doit être fiable, traçable, et déployable sans refonte de l'infrastructure
Définition : La fuite de données se produit quand des informations de la variable cible "contaminent" les variables explicatives pendant l'entraînement. Le modèle apprend à tricher plutôt qu'à vraiment comprendre.
Dans ce projet, 38 colonnes ont été identifiées et supprimées parce qu'elles contenaient des informations directement dérivées de la gamme cible — comme si on donnait la réponse avant l'examen.
Exemple concret :
❌ Colonne supprimée : "code_tarif_applique"
→ Cette colonne EST la gamme, formulée différemment.
→ Un modèle entraîné avec elle aurait 99% de précision... mais ne prédirait rien de réel.
✅ Colonne conservée : "puissance_souscrite_kva"
→ C'est une caractéristique physique du client, observable AVANT de connaître sa gamme.
Leçon clé : Un bon modèle doit apprendre à partir d'informations qui existeraient dans un cas réel AVANT de connaître la réponse.
Certaines gammes ont des milliers de clients, d'autres en ont quelques dizaines. Un modèle naïf "apprend" à toujours prédire la classe majoritaire et obtient une précision apparente élevée — mais échoue sur les classes rares.
Le fichier source mélange : variables numériques continues, variables catégorielles, variables ordinales, valeurs manquantes. Un pipeline de prétraitement robuste est indispensable.
Le cœur du projet est un pipeline scikit-learn qui enchaîne toutes les étapes de manière reproductible et sans fuite :
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OrdinalEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import HistGradientBoostingClassifier
# Étape 1 : Prétraitement séparé par type de variable
preprocessor = ColumnTransformer([
("num", Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())
]), colonnes_numeriques),
("cat", Pipeline([
("imputer", SimpleImputer(strategy="most_frequent")),
("encoder", OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1))
]), colonnes_categorielles),
])
# Étape 2 : Classification
pipeline = Pipeline([
("preprocessor", preprocessor),
("classifier", HistGradientBoostingClassifier(
max_iter=300,
learning_rate=0.05,
max_leaf_nodes=31,
random_state=42
))
])Pourquoi un pipeline ? Un pipeline garantit que le prétraitement (scaling, encodage) est appris uniquement sur les données d'entraînement et appliqué de manière identique sur les données de test. Sans pipeline, on risque la fuite de données.
Intuition : Imaginez un jury de 300 experts qui votent en séquence. Chaque expert se spécialise sur les erreurs des précédents. Le résultat final est le consensus pondéré de tous.
Avantages pour ce projet :
- Gère nativement les valeurs manquantes (pas besoin d'imputation)
- Très efficace sur les données tabulaires hétérogènes
- Moins sujet au surapprentissage que Random Forest sur des petits jeux de données
- Rapide à entraîner même avec 300 estimateurs
from sklearn.model_selection import GridSearchCV
param_grid = {
"classifier__max_iter": [100, 200, 300],
"classifier__learning_rate": [0.01, 0.05, 0.1],
"classifier__max_leaf_nodes": [15, 31, 63],
}
grid_search = GridSearchCV(
pipeline,
param_grid,
cv=5, # Validation croisée 5-fold
scoring="balanced_accuracy", # Adaptée au déséquilibre des classes
n_jobs=-1 # Tous les cœurs CPU en parallèle
)Pourquoi balanced_accuracy ? Avec des classes déséquilibrées, l'accuracy classique est trompeuse. La balanced accuracy donne le même poids à chaque classe, indépendamment de son effectif.
Un modèle peut prédire "80% de chances d'être en gamme HTA" et se tromper 40% du temps. L'étalonnage corrige cette discordance.
from sklearn.calibration import CalibratedClassifierCV
calibrated_model = CalibratedClassifierCV(best_model, method="isotonic", cv=5)Utilité pratique : les probabilités étalonnées permettent d'appliquer un seuil d'abstention τ — si le modèle n'est pas confiant à plus de τ%, il refuse de prédire plutôt que de risquer une erreur.
import shap
explainer = shap.TreeExplainer(best_model["classifier"])
shap_values = explainer.shap_values(X_test_transformed)
shap.summary_plot(shap_values, X_test_transformed)Ce que SHAP apporte : pour chaque prédiction, SHAP explique quelle variable a le plus influencé la décision et dans quel sens. Un chargé de clientèle peut vérifier que la décision du modèle est cohérente avec son expertise métier.
| Métrique | Valeur | Interprétation |
|---|---|---|
| Précision globale | 91 % | 91 % des consommateurs correctement classés |
| Balanced Accuracy | > 88 % | Performance stable même sur les classes minoritaires |
| Proba max médiane | > 0.82 | Le modèle est confiant dans la majorité des cas |
| Couverture (seuil τ=0.5) | > 95 % | Moins de 5 % de dossiers renvoyés pour révision manuelle |
Artefacts de production exportés :
production_pipeline.joblib— pipeline complet prêt à l'emploiinput_schema.json— schéma de validation des données d'entréeproduction_columns.json— liste et ordre des colonnes attenduesproduction_classes.json— mapping index → label de gamme
Ce que ces artefacts permettent :
import joblib
import pandas as pd
# Chargement en 2 lignes
pipeline = joblib.load("models/production_pipeline.joblib")
predictions = pipeline.predict(df_nouveaux_clients)
# → Retourne directement les gammes préditesPourquoi est-ce utile ? Ce modèle transforme une tâche manuelle chronophage (plusieurs jours par lot) en une opération automatique de quelques secondes, quelle que soit la taille du lot.
Quelle décision cela améliore ? L'affectation tarifaire des consommateurs industriels — une décision qui engage des contrats de plusieurs années et des montants significatifs. Une erreur de gamme peut entraîner un litige contractuel, un écart de facturation ou une perte de client.
Quel gain cela produit ?
- Réduction du temps de traitement d'un lot : de plusieurs jours à quelques secondes
- Standardisation des décisions : mêmes règles appliquées à tous les dossiers
- Traçabilité complète : chaque décision est accompagnée d'une probabilité et des variables explicatives (SHAP)
Quel risque cela réduit ?
- Risque d'erreur humaine sur des portefeuilles de plusieurs milliers de clients
- Risque de litige contractuel lié à une mauvaise affectation tarifaire
- Risque de non-conformité réglementaire (les gammes tarifaires répondent à des obligations légales)
Quelle activité cela automatise ? La classification initiale des nouveaux clients et la révision périodique du portefeuille existant. L'application Streamlit permet aux équipes métier d'utiliser le modèle sans compétence technique.
classification-gamme-electrique/
│
├── ml_conso_elec.py # Script principal — pipeline complet
├── app.py # Application Streamlit (point d'entrée)
├── app_demo_gamme.py # Démo interactive Streamlit
│
├── models/
│ ├── production_pipeline.joblib # Pipeline de production (artefact principal)
│ ├── predict_cli.py # CLI batch pour prédictions en masse
│ └── app_streamlit.py # Interface web utilisateur
│
├── reports/
│ ├── input_schema.json # Schéma de validation des entrées
│ ├── production_columns.json # Colonnes attendues par le pipeline
│ ├── production_classes.json # Mapping index → gamme
│ ├── abstention_tradeoff.png # Courbe précision/couverture
│ ├── abstention_sweep.csv # Tableau des seuils testés
│ └── requirements.txt # Dépendances exactes
│
├── data/
│ └── Liste_consommateurs_electriques_2016.xlsx # Données source
│
├── requirements.txt
└── README.md
git clone https://github.com/TSAGUE25/classification-gamme-electrique.git
cd classification-gamme-electrique
pip install -r requirements.txtpython models/predict_cli.py \
-i "votre_fichier_clients.csv" \
-o "predictions_output.csv"Colonnes de sortie :
y_pred: gamme préditeproba_max: confiance du modèle (0 à 1)- Si
proba_max < τ: dossier renvoyé en révision manuelle
streamlit run app.pyL'application permet de :
- Déposer un fichier CSV de clients
- Obtenir les prédictions avec probabilités
- Visualiser les variables les plus influentes (SHAP)
- Exporter les résultats
uvicorn server:app --host 0.0.0.0 --port 8000
# Prédire via API
curl -X POST "http://localhost:8000/predict" \
-H "Content-Type: application/json" \
-d '{"rows": [{"puissance_souscrite_kva": 250, "type_site": "industrie"}], "top_k": 3, "threshold": 0.80}'À chaque lot traité, journaliser dans reports/batch_metrics_log.csv :
- Taille du lot
- Couverture après seuil τ
- Score de Brier (qualité de calibration)
- Balanced accuracy si labels disponibles
Alertes recommandées :
Brier > 0.4→ dégradation de la calibrationCouverture < couverture_référence - 10%→ dérive des données d'entrée
La fuite de données, c'est comme préparer un étudiant à un examen en lui donnant les réponses à l'avance. Il obtient 100%, mais ne sait rien faire dans un cas réel.
En Machine Learning : si les données d'entraînement contiennent des informations qui ne seraient pas disponibles au moment de la prédiction réelle, le modèle apprend à "tricher". Il obtient d'excellentes performances sur les données de test... mais échoue en production.
Comment détecter la fuite :
- Score anormalement élevé (>99%) sur une tâche difficile → suspicion
- Analyse des corrélations entre les features et la cible
- Suppression progressive des variables et mesure de l'impact sur le score
Dans ce projet : 38 colonnes supprimées après audit complet de chaque variable.
Un pipeline sklearn garantit que toutes les transformations (normalisation, encodage) sont apprises sur les données d'entraînement uniquement et appliquées à l'identique sur les données de test.
SANS pipeline (dangereux) :
scaler.fit_transform(X_train + X_test) → le scaler "voit" les données de test ❌
AVEC pipeline (correct) :
pipeline.fit(X_train) → apprend sur X_train uniquement
pipeline.transform(X_test) → applique la transformation apprise ✅
Le modèle ne doit pas toujours trancher. Si sa confiance est faible, mieux vaut renvoyer le dossier à un expert humain.
τ = 0.80 :
proba_max = 0.92 → Prédiction automatique (confiance élevée) ✅
proba_max = 0.65 → Renvoi en révision manuelle (trop incertain) ⚠️
| Catégorie | Outil | Usage |
|---|---|---|
| ML | scikit-learn 1.3 | Pipeline, ColumnTransformer, GridSearchCV |
| Modèle | HistGradientBoostingClassifier | Classification multiclasse |
| Calibration | CalibratedClassifierCV | Probabilités fiables |
| Interprétabilité | SHAP | Explication des prédictions |
| Sérialisation | joblib | Export du pipeline de production |
| Interface | Streamlit | Application web utilisateur |
| API | FastAPI | Endpoint REST pour intégration SI |
| Données | pandas, numpy | Manipulation et prétraitement |
Emmanuel TSAGUE
Ingénieur — EDF MAD EDVANCE
Formation Data Scientist — Datascientest 2024
GitHub : TSAGUE25
Projet EDF MAD EDVANCE — Classification automatique de consommateurs électriques industriels