TP5 : monitoring, derive Evidently (2011 vs 2014) + impact perfs 2012-2014
This commit is contained in:
178
SYNTHESE_TP05.md
Normal file
178
SYNTHESE_TP05.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# TP05 - Synthèse : monitoring, dérive et redéploiement
|
||||
|
||||
Fil rouge : prédiction de la consommation électrique (kWh, 128 clients portugais, pas de 15 min).
|
||||
|
||||
Scénario : un modèle a été **entraîné sur 2011** et tourne toujours en production plusieurs années plus
|
||||
tard. En ingénieur MLOps, on vérifie si les données **2014** observées en production ont **dérivé** par
|
||||
rapport à l'entraînement, on analyse cette dérive avec **Evidently**, puis on mesure son **impact sur les
|
||||
performances** (2012 -> 2014) pour décider d'une stratégie de maintenance.
|
||||
|
||||
## Ce qui a été construit
|
||||
|
||||
- **`tp_module5_monitoring_derive.ipynb`** (notebook exécuté) : Partie 1 (moyennes + histogrammes de
|
||||
`lag_30d` 2011 vs 2014), Partie 2 (rapport de dérive Evidently `DataDriftPreset`), Partie 3 (modèle
|
||||
linéaire entraîné sur 2011, évalué sur 2011-2014). Réutilise `lab/constants.py` (chemins, cible, features).
|
||||
- **`tp05_evidently_drift_2011_vs_2014.html`** : rapport Evidently interactif complet (à ouvrir dans un
|
||||
navigateur).
|
||||
- **Choix technique** : la feature `lag_365d` (consommation 365 jours plus tôt) est **exclue** car elle
|
||||
est indéfinie (NaN) sur **toute** l'année 2011 (première année, pas d'historique 2010). On travaille donc
|
||||
sur les 5 features définies sur les deux périodes : `lag_1d, lag_7d, lag_30d, rolling_mean_7d,
|
||||
rolling_mean_30d`. Cet ensemble sert à la fois au rapport de dérive et au modèle entraîné sur 2011.
|
||||
|
||||
---
|
||||
|
||||
## Partie 1 - Observer une dérive
|
||||
|
||||
Feature `lag_30d`, sur les années complètes 2011 (train) et 2014 (production) :
|
||||
|
||||
| Année | Moyenne `lag_30d` | Effectif |
|
||||
|--------------|-------------------|------------|
|
||||
| 2011 (train) | **62.041 kWh** | 4 116 352 |
|
||||
| 2014 (prod) | **54.054 kWh** | 4 485 120 |
|
||||
| Écart | **-7.986 kWh** | **-12.9 %** |
|
||||
|
||||
**1.1 - Les deux distributions semblent-elles similaires ?**
|
||||
Non. Elles gardent la même **forme** générale (distribution asymétrique étalée vers la droite : beaucoup de
|
||||
petites consommations, une longue queue de fortes valeurs), mais l'histogramme 2014 est **décalé vers les
|
||||
valeurs plus faibles** et sa moyenne est nettement plus basse. Visuellement, le décalage est net.
|
||||
|
||||
**1.2 - La moyenne a-t-elle évolué entre 2011 et 2014 ?**
|
||||
Oui, franchement : de **62.04 kWh** à **54.05 kWh**, soit **-7.99 kWh (-12.9 %)**. La consommation moyenne
|
||||
(observée via `lag_30d`) a **baissé d'environ 13 %** entre l'entraînement et la production.
|
||||
|
||||
**1.3 - Cette évolution paraît-elle suffisamment importante pour parler de dérive ?**
|
||||
Une baisse de ~13 % de la moyenne est un signal **fort et cohérent** d'un changement de distribution : c'est
|
||||
un indice sérieux de dérive des données. Attention toutefois : la moyenne seule ne **prouve** pas une dérive
|
||||
statistique (deux distributions peuvent avoir des moyennes proches et des formes différentes, ou l'inverse).
|
||||
Il faut confirmer avec une analyse sur toute la distribution, avec un test et un seuil objectifs (Partie 2).
|
||||
|
||||
**1.4 - Cette première analyse est-elle suffisante pour conclure sur l'état du modèle ? Pourquoi ?**
|
||||
Non. Elle ne porte que sur **une** feature (`lag_30d`), via un **seul** indicateur (moyenne + comparaison
|
||||
visuelle), **sans test statistique ni seuil**, et elle ne dit **rien** des autres features ni de l'**impact
|
||||
réel sur la performance** du modèle. C'est un premier indice, pas une conclusion. Il faut (a) un outil de
|
||||
monitoring qui teste toutes les features (Partie 2) et (b) mesurer la performance dans le temps (Partie 3).
|
||||
|
||||
---
|
||||
|
||||
## Partie 2 - Analyse avec un outil de monitoring (Evidently)
|
||||
|
||||
Rapport `DataDriftPreset` comparant 2011 (référence) et 2014 (courant), échantillon de 200 000 lignes par
|
||||
période (seed fixe). Test choisi automatiquement par Evidently pour de grands échantillons numériques :
|
||||
**distance de Wasserstein normalisée**, seuil **0.1**.
|
||||
|
||||
| Feature | Test | Drift score | Seuil | Dérive ? |
|
||||
|--------------------|-------------------------------|-------------|-------|----------|
|
||||
| lag_1d | Wasserstein distance (normed) | 0.165 | 0.1 | Oui |
|
||||
| lag_7d | Wasserstein distance (normed) | 0.167 | 0.1 | Oui |
|
||||
| lag_30d | Wasserstein distance (normed) | 0.167 | 0.1 | Oui |
|
||||
| rolling_mean_7d | Wasserstein distance (normed) | 0.188 | 0.1 | Oui |
|
||||
| rolling_mean_30d | Wasserstein distance (normed) | 0.191 | 0.1 | Oui |
|
||||
|
||||
Verdict global : **`dataset_drift = True`**, **5 / 5 features en dérive (100 %)**.
|
||||
|
||||
**2.1 - Combien de features présentent une dérive ?**
|
||||
**Les 5** features analysées présentent une dérive (5 / 5, soit 100 %). Toutes dépassent le seuil de 0.1.
|
||||
|
||||
**2.2 - Toutes les features évoluent-elles de la même manière ?**
|
||||
Toutes dérivent, mais **pas avec la même intensité**. Les distances de Wasserstein vont de **0.165**
|
||||
(`lag_1d`) à **0.191** (`rolling_mean_30d`). Les **moyennes glissantes** (`rolling_mean_7d` 0.188,
|
||||
`rolling_mean_30d` 0.191) dérivent un peu **plus** que les **lags bruts** (0.165-0.167) : les indicateurs
|
||||
lissés/tendanciels captent davantage la baisse durable du niveau de consommation.
|
||||
|
||||
**2.3 - Identifiez deux métriques ou indicateurs du rapport. À quoi servent-ils ?**
|
||||
- **Le drift score par feature** (ici la distance de Wasserstein normalisée) : il **quantifie** l'écart
|
||||
entre la distribution de référence (2011) et la distribution courante (2014) pour chaque variable, et le
|
||||
compare à un **seuil** (0.1). Il sert à **détecter et localiser** la dérive, feature par feature.
|
||||
- **Le verdict global "Dataset Drift" + la part de colonnes en dérive** : il **agrège** les décisions par
|
||||
feature (nombre / pourcentage de colonnes dérivées, ici 5/5 = 100 %) en une conclusion **au niveau du
|
||||
dataset entier**. Il sert à **trancher globalement** (le dataset a-t-il dérivé, oui/non).
|
||||
(Le rapport affiche aussi, par feature, les **histogrammes de distribution référence vs courant**, utiles
|
||||
pour visualiser la nature du décalage.)
|
||||
|
||||
**2.4 - Le rapport conclut-il à une dérive globale du dataset ? Justifiez.**
|
||||
**Oui.** `dataset_drift = True`. Le critère par défaut d'Evidently (déclarer une dérive du dataset quand la
|
||||
**part de colonnes en dérive dépasse 50 %**) est **largement** franchi : **100 %** des colonnes dérivent.
|
||||
Le rapport conclut donc sans ambiguïté à une dérive globale des données d'entrée.
|
||||
|
||||
---
|
||||
|
||||
## Partie 3 - Mesurer l'impact sur les performances
|
||||
|
||||
Modèle (régression linéaire) entraîné **sur 2011 uniquement**, évalué sur chaque année complète :
|
||||
|
||||
| Année | Rôle | RMSE (kWh) | MAE (kWh) |
|
||||
|-------|-------------------|------------|-----------|
|
||||
| 2011 | train (référence) | 7.904 | 4.450 |
|
||||
| 2012 | évaluation (prod) | 7.595 | 4.178 |
|
||||
| 2013 | évaluation (prod) | 7.486 | 4.095 |
|
||||
| 2014 | évaluation (prod) | **7.101** | **3.911** |
|
||||
|
||||
**Résultat marquant : l'erreur ne se dégrade pas, elle diminue légèrement d'année en année.**
|
||||
|
||||
**3.1 - Peut-on comparer 2011 vs 2012 au même titre que 2012 vs 2013 ?**
|
||||
Non. **2011 est l'année d'entraînement** : l'erreur y est mesurée **in-sample** (le modèle a déjà vu ces
|
||||
données), donc **optimiste**. Comparer 2011 (in-sample) à 2012 (hors échantillon) mélange deux régimes
|
||||
différents, alors que 2012 vs 2013 compare **deux années hors échantillon** entre elles (comparaison
|
||||
homogène). De plus, le **niveau de consommation change** d'une année à l'autre : comparer des **RMSE bruts**
|
||||
est biaisé par l'échelle de la cible (voir 3.3). 2011 doit donc être traité comme **référence de train**,
|
||||
pas comme un point de comparaison équivalent aux années de production.
|
||||
|
||||
**3.2 - Observe-t-on une dégradation progressive ?**
|
||||
**Non.** En valeur absolue, la performance **s'améliore** légèrement : RMSE 7.90 -> 7.60 -> 7.49 -> 7.10 et
|
||||
MAE 4.45 -> 4.18 -> 4.10 -> 3.91 de 2011 à 2014. Aucune dégradation observée sur ces métriques.
|
||||
|
||||
**3.3 - Cette (absence de) dégradation est-elle cohérente avec la dérive détectée ?**
|
||||
À première vue c'est surprenant (dérive nette en Partie 2, mais pas de perte de performance), et c'est
|
||||
**instructif** : **une dérive des données ne signifie pas automatiquement une dégradation du modèle**.
|
||||
Explication : la consommation a **baissé** (~-13 % sur `lag_30d`, Partie 1), donc la **cible** (kWh) est plus
|
||||
petite en 2014 ; comme RMSE et MAE sont des erreurs **absolues** en kWh, elles **diminuent mécaniquement**
|
||||
quand l'échelle de la cible diminue. Pendant ce temps, la **relation features -> cible** (les lags prédisent
|
||||
la consommation courante) est restée **stable**. On a donc une dérive des données **sans** dérive de la
|
||||
relation. Pour comparer rigoureusement les années, il faudrait une métrique **indépendante de l'échelle**
|
||||
(MAPE, R², RMSE normalisé).
|
||||
|
||||
**3.4 - Peut-on conclure que le modèle 2011 est encore fiable en 2014 ?**
|
||||
Sur la base des erreurs absolues, il **ne s'est pas dégradé** (il fait même un peu mieux en kWh). Mais on ne
|
||||
peut pas **conclure** à sa pleine fiabilité sur ces seuls chiffres : (1) les métriques absolues sont
|
||||
**trompeuses** quand l'échelle de la cible change ; (2) on n'a mesuré que l'**amplitude** de l'erreur, pas un
|
||||
éventuel **biais systématique** (sur/sous-estimation) ni la performance par segment (client, saison).
|
||||
Verdict prudent : **pas de signe de dégradation**, mais à **confirmer** avec des métriques relatives et une
|
||||
analyse des résidus avant de déclarer le modèle fiable.
|
||||
|
||||
**3.5 - Que faudrait-il faire ensuite : surveiller, réentraîner, ou redéployer ?**
|
||||
**Surveiller.** La dérive des données est réelle, mais **sans impact mesuré** sur la performance : rien
|
||||
n'impose un réentraînement ou un redéploiement immédiat (ce serait du travail et du risque pour un gain non
|
||||
démontré). On met en place un **monitoring continu** (dérive des features + performance suivie avec une
|
||||
métrique relative + dérive des prédictions et de la cible dès que la vérité terrain arrive) et on **déclenche
|
||||
un réentraînement seulement si/quand** la performance se dégrade réellement ou qu'un concept drift apparaît.
|
||||
|
||||
**3.6 - Quel type de dérive semble en jeu ? Quelles analyses préconisez-vous ?**
|
||||
Type : **data drift** (dérive des covariables / *covariate shift*). Les distributions des features d'entrée
|
||||
ont changé (baisse durable du niveau de consommation), tandis que la **relation** features -> cible semble
|
||||
**stable** (pas de perte de performance) : **pas de concept drift évident**. Analyses préconisées :
|
||||
- suivre une **métrique de performance indépendante de l'échelle** (MAPE, R², nRMSE) dans le temps, plutôt
|
||||
que le RMSE brut ;
|
||||
- monitorer aussi la **dérive des prédictions** et de la **cible** (*target drift*), pas seulement des
|
||||
features en entrée ;
|
||||
- **analyser les résidus** (biais moyen, hétéroscédasticité) et **segmenter** (par client, par saison) ;
|
||||
- surtout, dès que la **vérité terrain** est disponible (avec délai), **recalculer la performance réelle en
|
||||
production** pour détecter précocement un éventuel **concept drift** : c'est la fermeture de la *feedback
|
||||
loop* MLOps (surveiller -> alerter -> réentraîner -> redéployer).
|
||||
|
||||
---
|
||||
|
||||
## Reproduire
|
||||
|
||||
Sur la VM, dans `~/tp` (venv `/opt/venvs/mlops`). Le TP05 est **100 % local** (données `/data/modelling`) :
|
||||
aucun secret réseau (MLflow, S3) n'est nécessaire.
|
||||
|
||||
```bash
|
||||
# Ré-exécuter le notebook (régénère les sorties + le rapport HTML)
|
||||
/opt/venvs/mlops/bin/jupyter nbconvert --to notebook --execute --inplace tp_module5_monitoring_derive.ipynb
|
||||
```
|
||||
|
||||
Consultation interactive (service `jupyter-tp`, token `mlops`) :
|
||||
|
||||
```
|
||||
https://jupyter.192-168-122-143.nip.io/lab/tree/tp_module5_monitoring_derive.ipynb?token=mlops
|
||||
```
|
||||
646
tp05_evidently_drift_2011_vs_2014.html
Normal file
646
tp05_evidently_drift_2011_vs_2014.html
Normal file
File diff suppressed because one or more lines are too long
597
tp_module5_monitoring_derive.ipynb
Normal file
597
tp_module5_monitoring_derive.ipynb
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user