feat(ml): replace logistic regression with MLP fusion and KS drift with ADWIN online learning

Replace the LogisticRegression meta-learner with a PyTorch MetaFusionMLP
(Linear(3,16)->BN->ReLU->Dropout->Linear(16,1)->Sigmoid) for non-linear
fusion of EIF, NF, and XGBoost scores. Replace KS-test + quantile digest
drift detection with ADWIN (adaptive sliding window, Hoeffding bound).
Replace weekly XGBoost batch retraining with River HoeffdingAdaptiveTree
for incremental online learning (learn_one per cycle). Update all thesis
documentation sections (2.4.2c, 2.4.3, 3.8, discussion, conclusion).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jacquin Antoine
2026-04-13 16:32:34 +02:00
parent c6cb12981c
commit 7894d39f1c
12 changed files with 502 additions and 306 deletions

View File

@ -413,20 +413,20 @@ Le système de détection combine trois « voix » complémentaires :
┌──────────────┼──────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌────▼──────┐
│ EIF │ │ AE │ │ XGBoost │
│ (semi- │ │ (semi- │ │(supervisé)│
│supervisé) │ │supervisé) │ │ │
│ EIF │ │ NF │ │ XGBoost │
│ (semi- │ │ (Normal- │ │(supervisé)│
│supervisé) │ │ izing │ │ │
│ │ │ Flow) │ │ │
└─────┬─────┘ └─────┬─────┘ └────┬──────┘
│ │ │
eif_norm ae_norm xgb_prob
eif_norm nf_norm xgb_prob
│ │ │
└──────────────┼──────────────┘
┌────────▼────────┐
Fusion LR
(régression
logistique,
│ ≥1000 labels) │
Meta-Model
Stacking MLP
(non-linéaire)
└────────┬────────┘
┌────────▼────────┐
@ -435,33 +435,47 @@ Le système de détection combine trois « voix » complémentaires :
└─────────────────┘
```
**Formule de combinaison** :
**Limites de la fusion linéaire**
```python
final = (1 - XGB_WEIGHT) × ((1 - AE_WEIGHT) × eif_norm + AE_WEIGHT × ae_norm) \
+ XGB_WEIGHT × xgb_prob
```
Valeurs par défaut : `AE_WEIGHT=0.30`, `XGB_WEIGHT=0.20`. Configurable via variables d'environnement pour ajustement en production sans modification du code.
**Fusion LR**
Le Fusion LR est une régression logistique activée lorsqu'au moins 1 000 étiquettes ont été accumulées (sessions DENY Anubis + annotations manuelles + seuillage de confiance).
Régression logistique : modèle linéaire probabiliste qui apprend des poids w pour chaque signal intermédiaire en minimisant l'entropie croisée binaire sur les étiquettes accumulées :
Une fusion linéaire — combinaison convexe pondérée ou régression logistique — ne peut capturer que des frontières de décision linéaires dans l'espace des scores intermédiaires. Or les signaux EIF, NF et XGBoost peuvent exhiber des interactions non-linéaires impossibles à modéliser par une combinaison linéaire :
```
P(bot) = σ(w1×eif + w2×ae + w3×xgb + w4×volume + bias)
Problème XOR des scores :
NF élevé
┌────────┐
EIF bas │ BOT ✓ │ ← combinaison inhabituelle = bot confirmé
├────────┤
EIF haut │normal ✗│ ← faux positif fréquent de l'EIF seul
└────────┘
NF bas
```
σ est la fonction sigmoïde : σ(z) = 1 / (1 + e^{-z})
Exemple concret : un bot utilisant un outilHeadless avec un JA4 fingerprint légitime (NF bas) mais un comportement de navigation atypique (EIF élevé). Le XGBoost peut compenser, mais la fusion linéaire ne peut apprendre la relation *« EIF élevé ET XGB élevé MAIS NF bas = bot »* — elle ne fait que sommer les contributions indépendantes.
Les poids w1w4 sont appris, permettant au système de calibrer automatiquement l'importance relative de chaque voix en fonction du type de trafic en production. En dessous de 1 000 étiquettes, le système revient aux poids fixes : `(eif: 0.50, ae: 0.30, xgb: 0.20)`.
**Stacking OOF (Out-of-Fold) et MLP méta-modèle**
Pour résoudre cette limitation, le système utilise un méta-modèle non-linéaire de type MLP (*Multi-Layer Perceptron*) entraîné via stacking Out-of-Fold :
1. **Prédictions OOF** : les modèles de base (EIF, NF, XGBoost) produisent des prédictions sur des plis de validation croisée temporelle, garantissant que le méta-modèle n'a jamais vu les données d'entraînement des modèles de base — évitant le surapprentissage (*information leakage*).
2. **Méta-modèle MLP** : un réseau de neurones à 2 couches apprend la fonction de fusion optimale :
```
MetaFusionMLP : [eif, nf, xgb] → Linear(3,16) → BatchNorm → ReLU → Dropout(0.2)
→ Linear(16,1) → Sigmoid → P(bot)
```
- **BatchNorm** : normalise les activations intermédiaires, stabilise l'apprentissage et régularise implicitement — crucial avec peu de données labellisées.
- **Dropout(0.2)** : désactive aléatoirement 20% des neurones pendant l'entraînement, prévenant la co-adaptation des poids.
- **Early stopping** (patience = 5 epochs) : arrête l'entraînement dès que la loss de validation ne s'améliore plus, évitant le surapprentissage.
- **Weight decay** ($\lambda = 10^{-4}$) : pénalité L2 sur les poids du MLP pour une régularisation supplémentaire.
Le MLP apprend des frontières de décision non-linéaires dans l'espace 3D `[eif_norm, nf_norm, xgb_prob]`, capable de résoudre les patterns XOR et les interactions conditionnelles entre les trois voix. Le système de détection peut ainsi combiner automatiquement les signaux de manière optimale en fonction du type de trafic observé en production.
**Calendrier de retraining** :
- XGBoost : hebdomadaire sur les étiquettes accumulées, après filtrage Cleanlab des labels SOC bruyants (voir ci-dessous)
- EIF : toutes les 24 heures
- AE : continu avec arrêt précoce sur la loss de validation
- HAT (supervisé) : apprentissage incrémental à chaque cycle (300s) sur les étiquettes accumulées, après filtrage Cleanlab des labels SOC bruyants (voir ci-dessous)
- EIF : toutes les 24 heures ou sur détection ADWIN
- NF : continu avec arrêt précoce sur la loss de validation
**Filtrage des labels SOC bruyants (Cleanlab)** :
@ -481,7 +495,7 @@ X_clean, y_clean = X[~noisy_mask], y[~noisy_mask]
Ce mécanisme protège le modèle contre l'empoisonnement par des faux positifs mal corrigés ou des biais de confirmation des analystes. Le taux de labels filtrés est loggé pour surveillance. En cas d'échec de Cleanlab (erreur mémoire, dépendance manquante), le pipeline revient aux données brutes sans interruption.
#### 2.4.3 Concept Drift et retraining adaptatif
#### 2.4.3 Concept Drift : ADWIN et Online Learning
**Définition du concept drift**
@ -494,28 +508,74 @@ En apprentissage automatique, le concept drift désigne le changement des propri
En détection de bots, le drift adversarial est particulièrement critique : les attaquants adaptent délibérément leurs outils pour contourner les modèles déployés.
**Test de Kolmogorov-Smirnov**
**Limites de l'approche KS + quantile digest**
Le test KS (Kolmogorov-Smirnov) mesure la différence maximale entre deux fonctions de distribution empiriques cumulées (ECDF) :
L'approche précédente détectait la dérive en sauvegardant 5 quantiles (p10, p25, p50, p75, p90) par feature à l'entraînement, puis en reconstruisant une distribution synthétique par interpolation de la CDF inverse et en appliquant un test de Kolmogorov-Smirnov entre cette distribution et la distribution courante. Cette méthode souffre de trois lacunes fondamentales :
1. **5 quantiles ne captent pas les distributions bimodales** : les features de trafic web comme `asset_ratio`, `post_ratio` et `orphan_ratio` ont souvent des distributions bimodales (deux populations de trafic distinctes). Cinq points ne suffisent pas à reconstruire fidèlement ces distributions — une dérive dans un mode peut être masquée par la stabilité de l'autre mode.
2. **Reconstruction par interpolation linéaire** : interpoler entre 5 quantiles suppose une distribution unimodale et lisse. Pour les distributions skewed à queue lourde (timing inter-requêtes, taille des payloads), l'interpolation sous-estime systématiquement les valeurs extrêmes, rendant le test KS peu fiable.
3. **Seuil de 30% arbitraire** : le seuil `DRIFT_THRESHOLD = 0.30` (30% de features en dérive) est un hyperparamètre non fondé statistiquement. Il est trop sensible pour les périodes de forte activité (faux positifs) et trop conservateur pour les attaques furtives ne touchant que quelques features.
**ADWIN : fenêtre glissante adaptative**
[ADWIN (ADaptive WINdowing, Bifet & Gavalda, 2007)](https://dl.acm.org/doi/10.1145/1242572.1242660) résout ces problèmes en maintenant une fenêtre de longueur variable sur le flux de données. Le principe :
1. **Fenêtre adaptative** : ADWIN maintient une fenêtre $W$ de valeurs récentes. La taille de $W$ s'ajuste automatiquement — elle grandit quand la distribution est stable et rétrécit quand un changement est détecté.
2. **Test de Hoeffding** : pour chaque coupe possible $W = W_0 \cup W_1$, ADWIN compare les moyennes $\hat{\mu}_0$ et $\hat{\mu}_1$ des deux sous-fenêtres. Si la différence dépasse la borne de Hoeffding :
$$|\hat{\mu}_0 - \hat{\mu}_1| \geq \sqrt{\frac{1}{2m} \ln\frac{4}{\delta}}$$
où $m = \frac{1}{1/|W_0| + 1/|W_1|}$ et $\delta$ est le paramètre de confiance, alors un changement est détecté et la sous-fenêtre la plus ancienne est supprimée.
3. **Pas de seuil arbitraire** : la détection repose uniquement sur la borne de Hoeffding (garantie probabiliste), paramétrée par $\delta$ (défaut : 0.002). Aucun seuil de « 30% de features en dérive » n'est nécessaire au niveau de chaque feature — seul le nombre de features driftant simultanément déclenche le retraining global.
**Architecture de monitoring ADWIN** :
```
D = max|F1(x) - F2(x)|
Cycle n (300s)
├── Calculer μ_f pour chaque feature f sur le trafic baseline
├── ADWIN_f.update(μ_f) pour chaque feature
│ └── Fenêtre interne W_f ajustée automatiquement
├── ADWIN_f.detected_change() ?
│ └── Si oui → feature f marquée « en dérive » ce cycle
└── Si > 30% features en dérive → flag retraining EIF/NF
Si > 50% features en dérive → alerte ADVERSARIAL_DRIFT
```
où F1 et F2 sont les ECDFs de la distribution courante et de la distribution de référence (baseline). Si D dépasse la valeur critique (déterminée par les tables de la distribution KS pour un niveau de confiance α), les deux échantillons sont considérés comme provenant de distributions différentes. Avantage : test non-paramétrique, aucune hypothèse sur la forme de la distribution.
**Online Learning : Hoeffding Adaptive Tree**
**Méthode de détection de dérive** :
Le retraining hebdomadaire par batch (`XGBClassifier.fit()` sur l'ensemble des labels accumulés) est remplacé par un apprentissage incrémental via [River](https://riverml.xyz/), une bibliothèque spécialisée en stream mining. Le modèle utilisé est le `HoeffdingAdaptiveTreeClassifier` (HAT) :
1. Sauvegarde avec chaque modèle sérialisé de l'approximation 5-quantiles par feature : (p10, p25, p50, p75, p90)
2. Génération d'échantillons synthétiques par interpolation de la CDF inverse (quantile function)
3. Test KS + divergence KL sur chaque feature entre la distribution courante et la baseline
4. Feature marquée comme « en dérive » si le test KS OU la divergence KL dépasse le seuil configuré
5. Retraining forcé si > 30 % des features dérivent simultanément
6. **Détection de dérive adversariale** : si de nombreuses features dérivent simultanément dans la même direction (score de corrélation directionnelle élevé), génération d'une alerte spécifique distinguant la manipulation intentionnelle (dérive adversariale coordonnée) de l'évolution organique (mises à jour navigateur ou changements de comportement naturels)
- **Arbre de décision incrémental** : construit l'arbre de décision progressivement, un exemple à la fois via `learn_one(x, y)`. À chaque split, le test de Hoeffding garantit que le split choisi est (probablement) le même que celui qu'un arbre batch aurait choisi avec les mêmes données.
- **Adaptatif** : utilise des estimateurs de fenêtre ADWIN à chaque nœud pour remplacer les statistiques obsolètes — le modèle s'adapte automatiquement au concept drift sans retraining explicite.
- **Apprentissage par cycle** : à chaque cycle de 300s, les nouveaux labels accumulés sont injectés un par un via `learn_one()`. Le modèle s'améliore continuellement, rendant le lourd retraining hebdomadaire obsolète.
**Validation gate** : si le taux d'anomalie sur le jeu de validation dépasse 20 % après retraining, le nouveau modèle est rejeté et le modèle précédent est conservé, avec génération d'une alerte (baseline contaminée).
```
Cycle 300s
├── Charger HAT sérialisé (pickle)
├── Pour chaque label accumulé ce cycle :
│ model.learn_one({feature: value, ...}, label)
├── Persister HAT mis à jour
└── Prédiction : model.predict_proba_many(df) → P(bot)
```
**Limites de l'approximation 5-quantiles** : adéquate pour les distributions unimodales, mais peut manquer les dérives bimodales dans `asset_ratio`, `post_ratio`, `orphan_ratio`. Extension à p5/p95 ou à t-digest identifiée comme travail futur.
Le passage au stream mining élimine trois problématiques majeures du batch training :
- **Latence de mise à jour** : de hebdomadaire à chaque cycle (300s)
- **Coût mémoire** : plus besoin de charger 50 000 labels en RAM
- **Stale model** : le modèle est toujours à jour par rapport au concept courant
**Validation gate** : conservée — si le taux d'anomalie sur le jeu de validation dépasse 20% après retraining EIF/NF, le nouveau modèle est rejeté et le modèle précédent conservé.
#### 2.4.4 Modélisation des phases d'attaque