Passa al contenuto principale

Scelte di modellazione: razionale

Documenta le decisioni progettuali del progetto. Per i concetti teorici, vedi la sezione Teoria.

1. Famiglia di modello: KMeans/MiniBatch

Tre alternative considerate:

ModelloQuando preferirloEsito per Ames-IoT
MiniBatchKMeansCluster sferici, dataset grandeScelto: 230k righe, 3 regimi ben separati
KMeans fullQuando MiniBatch non convergeSub-ottimale: 10× più lento, gain ~1%
GaussianMixtureCluster ellissoidali, soft assignmentROC-AUC simile, 5× più lento, no gain

Razionale: dopo StandardScaler, le 3 famiglie di feature (sensori grezzi, rolling, zscore) hanno scale paragonabili. Cluster sferici (KMeans) bastano. MiniBatchKMeans dà la migliore performance/tempo.

2. K = numero di cluster

Selezione automatica via silhouette su K ∈ 10.

Atteso (e confermato in pratica): K=4-5 vince. La logica fisica:

  • 3 regimi noti (regime2).
  • Spesso K=K_regimi + 1-2 per assorbire transizioni e regime borderline.

K=3 è teoricamente "perfetto" se i regimi fossero ben separati nello spazio delle feature. In pratica le rolling/zscore creano regioni intermedie (transizioni di regime durano qualche minuto), e il modello preferisce K=4-5.

3. Feature engineering: rolling window=15 minuti

Trade-off tra:

  • Window troppo piccolo (1-5 min): rumoroso, le statistiche non si stabilizzano. Bene per anomalie istantanee, male per trend.
  • Window grande (60+ min): smoothing eccessivo, perdita di sensibilità a eventi puntuali.
  • 15 min: sweet spot per macchine industriali con cicli di ~minuti. Calibrato dalla letteratura su predictive maintenance.

Configurabile via config.rolling_window_min. Per processi più lenti (es. flusso continuo) salire a 30-60 min.

4. Soglia: percentile 99° sul training

Scelte alternative considerate:

StrategiaProContro
Percentile p99 sul trainingIndipendente dalle label, interpretabileAssume training normale
Percentile basato su validation labelledOttimizzato per F1Leakage del ground-truth
3σ rule (μ + 3σ delle distanze)Standard statisticoAssume distribuzione gaussiana — falso per le distanze
Threshold per cluster (locale)Cattura cluster di varia densitàPiù complesso, meno interpretabile

p99 dà ~1% di FP atteso sul training (dove tutto è "normale"). Sul test si ottiene tipicamente 2-3% di flag — più la prevalenza di anomalie reali (~2.4%).

Configurabile via CLI --threshold-percentile.

5. Wrangling: ffill/bfill per asset, limit=5

I 3.85% di NaN del dataset sono concentrati sui sensori. Strategia:

  • forward-fill dentro un asset (limit 5 minuti = 5 campioni): copre buchi brevi di trasmissione.
  • backward-fill come fallback (per i primi minuti di un asset).
  • Residui → 0 + <col>_was_nan flag: il modello sa quando un valore è "sintetizzato".

Alternativa scartata: imputazione con media/mediana per asset. Funziona, ma non rispetta la natura temporale: un valore mancante a t è tipicamente più simile a t-1 che alla mediana globale.

6. Validazione: precision/recall + ROC + PR + fault_code_recall

anomaly_label è parziale (dichiarato dal PW). Per leggere bene i risultati, riportiamo TUTTE le metriche:

  • Precision/Recall/F1: standard.
  • ROC-AUC: discriminative ability.
  • PR-AUC: più informativa su classi sbilanciate.
  • fault_code_recall: fra i punti con fault_code_true != 0, quanti ne abbiamo catturati.

fault_code_true è generato sinteticamente per coprire tutti i guasti reali; quindi è un ground-truth più completo di anomaly_label. La sua recall è una stima più affidabile della vera capacità di detection.

7. Cosa NON abbiamo fatto

TecnicaPerché lo si farebbePerché l'abbiamo evitata
Per-regime thresholdI 3 regimi hanno variabilità diverseAumenta complessità; valore aggiunto ~5% F1
Per-asset thresholdOgni asset ha proprie caratteristicheStessa logica, +complessità
Isolation ForestApproccio non basato su distanzeFunziona ma non è "clustering" come da PW
Local Outlier Factor (LOF)Cattura anomalie in cluster densiO(n2)O(n^2) in inference, prohibitivo per stream
AutoencoderCattura non-linearità complesseRichiede tuning più aggressivo, meno interpretabile
Ensemble (KMeans + GMM voting)Più robustoMarginale guadagno (~1-2% F1)

Tutte sono estensioni naturali documentate.

8. Risultati di riferimento

Run con configurazione default (iot-detect --quick):

SetPrecisionRecallF1ROC-AUCPR-AUCfault_code_recall
Train0.3000.1560.2050.8200.1860.173
Test0.4360.2780.3400.8790.3150.287

Lettura:

  • ROC-AUC test = 0.88: forte capacità discriminativa.
  • PR-AUC = 0.31: 13× sopra random (prevalenza 2.4%).
  • F1 = 0.34: trade-off precision/recall regolabile dalla soglia.
  • fault_code_recall = 0.29: cattura il 29% dei guasti sintetici. Significativo considerando che molti fault sono punti isolati che il clustering "vede" solo se cadono molto fuori dai centroidi.

Migliorabile abbassando soglia a p95 (più recall, meno precision) o aggiungendo modelli sequenziali per i collective.