Nel primo articolo della serie abbiamo seguito un percorso che partiva dalla rilevazione di ciò che è insolito, passava dalla verifica della persistenza e arrivava all’identificazione di possibili cambiamenti di regime.
In questo secondo appuntamento il problema diventa più complesso, perché molti segnali interessanti non si presentano come un valore evidentemente anomalo. Possono emergere dalla combinazione di variabili che, considerate singolarmente, sembrano normali; possono manifestarsi attraverso parole diverse che descrivono uno stesso comportamento ancora privo di un vocabolario condiviso; oppure possono essere nascosti dentro migliaia di alert tecnicamente corretti, senza che sia immediatamente evidente quali meritino tempo, analisi e risorse.
La Weak Signals Detection può quindi essere vista come un processo composto da più livelli:
rilevare → verificare → contestualizzare → prioritizzare → decidere → monitorare
L’obiettivo non è produrre un numero sempre maggiore di anomalie o di topic, ma costruire un sistema capace di trasformare segnali deboli in evidenze utili per una decisione.
In questo articolo utilizzeremo tre esercizi Python per affrontare tre problemi differenti:
- anomalia multivariata: individuare combinazioni insolite attraverso Isolation Forest e distanza di Mahalanobis;
- tema emergente: individuare strutture semantiche nei testi con BERTopic e verificare se possono rappresentare un fenomeno nuovo;
- priorità decisionale: combinare persistenza, accelerazione, replicazione, novità, rilevanza e robustezza in un Weak Signal Score.
Il Weak Signal Score utilizzato nell’articolo è un metodo progettuale proposto per ordinare i segnali. Non è una metrica statistica standardizzata, non rappresenta una probabilità e non dimostra che un fenomeno avrà un determinato impatto futuro.
Il codice serve a costruire e confrontare evidenze. La verifica del contesto, la validazione nel tempo e la decisione finale rimangono attività che richiedono dati adeguati e conoscenza del problema applicativo.
In sintesi: la Weak Signals Detection combina rilevazione di anomalie, analisi temporale, individuazione di temi emergenti e valutazione del valore decisionale per identificare fenomeni ancora deboli ma potenzialmente rilevanti.
Esercizio 4 — Combinazioni insolite: Isolation Forest e Mahalanobis su dati multivariati
Scenario di business
Immaginiamo una piattaforma di servizi finanziari che osserva alcune caratteristiche della propria popolazione di clienti o prospect: età, reddito, area di residenza e modalità di lavoro.
Il responsabile analytics vuole individuare profili che non siano necessariamente estremi in una singola variabile, ma che presentino una combinazione poco frequente rispetto alla popolazione di riferimento.
Consideriamo, per esempio, un soggetto giovane con reddito elevato, residente in un’area rurale e impiegato completamente da remoto. Il reddito potrebbe essere alto ma non eccezionale; l’età potrebbe rientrare perfettamente nella distribuzione; il lavoro remoto potrebbe essere comune e anche la residenza rurale potrebbe non costituire un’anomalia isolata. La combinazione dei quattro attributi può però rappresentare una configurazione molto rara.
Dal punto di vista business, l’obiettivo non consiste semplicemente nel trovare “clienti strani”. Una configurazione rara può rappresentare un errore nei dati, un segmento emergente, un cambiamento nelle modalità di lavoro oppure un comportamento che richiede una revisione dei modelli di rischio e segmentazione.
In un sistema reale, quindi, il detector dovrebbe servire a creare una lista di casi da verificare, non a classificare automaticamente le persone come problematiche.
Obiettivi business
L’esercizio ha quattro obiettivi principali:
- individuare combinazioni di caratteristiche che sfuggono ai controlli univariati;
- ridurre il rischio di perdere segmenti emergenti perché nessuna singola variabile appare anomala;
- confrontare due detector con logiche differenti;
- costruire una procedura di revisione che tenga conto di precision, recall e costo operativo.
Perché i metodi univariati possono fallire
Un metodo univariato cerca valori estremi all’interno di una singola colonna. Se il reddito elevato rientra ancora nella distribuzione osservata, il cliente non viene segnalato. Lo stesso può accadere per età, lavoro remoto e area rurale.
Il problema nasce quando l’anomalia non riguarda il valore di una variabile, ma la relazione tra più variabili.
Un’osservazione può quindi essere perfettamente plausibile lungo ciascuna dimensione e contemporaneamente poco plausibile nello spazio multivariato.
Un fenomeno può essere visibile nelle interazioni tra variabili prima di diventare evidente nelle singole distribuzioni.
Questa distinzione è particolarmente importante nei contesti in cui le relazioni tra variabili hanno un significato operativo, come credito, antifrode, controllo qualità, cybersecurity, segmentazione dei clienti e monitoraggio industriale.
Distanza di Mahalanobis
La distanza di Mahalanobis misura quanto un punto è lontano dal centro della distribuzione tenendo conto della struttura di covarianza:
[math]\displaystyle D_M(x)=\sqrt{(x-\mu)^T\Sigma^{-1}(x-\mu)}[/math]
dove:
- [math]x[/math] è l’osservazione;
- [math]\mu[/math] è il vettore delle medie;
- [math]\Sigma[/math] è la matrice di covarianza;
- [math]\Sigma^{-1}[/math] è l’inversa della matrice di covarianza.
La differenza rispetto alla distanza euclidea è importante. Se due variabili tendono a crescere insieme, una combinazione che segue quella relazione può essere relativamente comune anche quando entrambe assumono valori elevati. Mahalanobis tiene conto proprio di questa struttura.
Il metodo funziona meglio quando la distribuzione è ragionevolmente descrivibile attraverso una struttura ellittica e quando la matrice di covarianza può essere stimata in modo stabile. Code pesanti, multimodalità, forte presenza di outlier e un numero elevato di variabili rispetto alle osservazioni possono rendere la stima meno affidabile.
In questo esercizio utilizziamo Ledoit-Wolf, uno stimatore shrinkage che tende a stabilizzare la matrice di covarianza e a migliorarne il condizionamento. Ledoit-Wolf non deve però essere interpretato come uno stimatore robusto agli outlier in senso stretto: se gli outlier sono numerosi o molto influenti, è opportuno valutare anche metodi robusti per posizione e covarianza.
Nel dataset didattico le variabili remote e rural sono codificate come 0/1. Questa rappresentazione permette di costruire un esempio multivariato semplice, ma in un progetto reale la natura delle variabili deve essere considerata esplicitamente. Quando il dataset contiene una combinazione complessa di variabili numeriche, ordinali e categoriche, può essere necessario utilizzare rappresentazioni o metriche specifiche per dati misti.
Isolation Forest
Isolation Forest segue una logica diversa. Costruisce alberi casuali e osserva quanto rapidamente ciascun punto viene isolato attraverso split successivi.
Un punto raro tende a essere isolato con pochi split, mentre un punto che si trova all’interno di una regione densa richiede generalmente più suddivisioni.
Il vantaggio principale, in questo contesto, è che non viene richiesta un’ipotesi parametrica esplicita sulla distribuzione dei dati. Anche Isolation Forest, tuttavia, non è privo di assunzioni operative: il risultato dipende dalla rappresentazione delle variabili, dalla struttura dei dati e dalla configurazione del modello, compreso il parametro contamination.
| Criterio | Mahalanobis | Isolation Forest |
|---|---|---|
| Idea principale | Distanza dalla struttura media e di covarianza | Isolamento rapido tramite alberi casuali |
| Ipotesi | Presuppone una struttura della covarianza adeguatamente rappresentata dal modello | Non richiede una distribuzione parametrica esplicita |
| Interpretabilità | Distanza rispetto alla struttura di riferimento | Facilità di isolamento del punto |
| Sensibilità | Covarianza, outlier e forma della distribuzione | Rappresentazione dei dati e configurazione del modello |
| Multimodalità | Può essere problematica se si usa un’unica struttura di covarianza | Può adattarsi meglio a strutture non ellittiche |
La differenza più interessante non riguarda quindi quale algoritmo sia “migliore”, ma il fatto che i due metodi osservino la rarità da prospettive diverse.
Codice Python
Il dataset è sintetico e serve a costruire un caso in cui le quattro variabili, considerate singolarmente, non rendono immediatamente evidente l’anomalia.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import IsolationForest
from sklearn.covariance import LedoitWolf
from scipy.spatial.distance import mahalanobis
rng = np.random.default_rng(23)
N = 5000
# Popolazione di riferimento:
# età ~ N(42, 12), reddito correlato con l'età
age = rng.normal(42, 12, N).clip(20, 75)
income = 1500 + 40 * age + rng.normal(0, 700, N)
# Variabili binarie codificate 0/1
remote = rng.binomial(1, 0.25, N)
rural = rng.binomial(1, 0.30, N)
X = np.column_stack([
age,
income,
remote,
rural
])
# 30 osservazioni con una combinazione insolita
anom_age = rng.normal(29, 2, 30).clip(22, 36)
anom_income = rng.normal(6500, 400, 30)
anom_remote = np.ones(30)
anom_rural = np.ones(30)
X_anom = np.column_stack([
anom_age,
anom_income,
anom_remote,
anom_rural
])
X_all = np.vstack([X, X_anom])
# Etichetta vera, disponibile soltanto perché il dataset è simulato
labels_true = np.array([0] * N + [1] * 30)
# -------------------------------------------------
# Isolation Forest
# -------------------------------------------------
iso = IsolationForest(
contamination=0.01,
random_state=42
)
iso_labels = iso.fit_predict(X_all)
iso_scores = -iso.score_samples(X_all)
# 1 = normale
# -1 = anomalia
# -------------------------------------------------
# Mahalanobis con Ledoit-Wolf
# -------------------------------------------------
lw = LedoitWolf().fit(X_all)
mu = lw.location_
Sigma_inv = np.linalg.pinv(lw.covariance_)
mahal = np.array([
mahalanobis(x, mu, Sigma_inv)
for x in X_all
])
# Soglia empirica al 99° percentile.
# Non rappresenta un test statistico calibrato all'1%.
thr_mahal = np.quantile(mahal, 0.99)
mahal_labels = (mahal > thr_mahal).astype(int)
# -------------------------------------------------
# Triangolazione
# -------------------------------------------------
both = (
(iso_labels == -1)
& (mahal_labels == 1)
)
# -------------------------------------------------
# Metriche
# -------------------------------------------------
def precision_recall(pred, true):
pred = np.asarray(pred, dtype=bool)
true = np.asarray(true, dtype=bool)
tp = (pred & true).sum()
fp = (pred & ~true).sum()
fn = (~pred & true).sum()
precision = tp / max(tp + fp, 1)
recall = tp / max(tp + fn, 1)
return precision, recall, tp, fp, fn
p_iso, r_iso, *_ = precision_recall(
iso_labels == -1,
labels_true
)
p_mah, r_mah, *_ = precision_recall(
mahal_labels == 1,
labels_true
)
p_both, r_both, *_ = precision_recall(
both,
labels_true
)
print(
f"Isolation Forest : Precision={p_iso:.2f} "
f"Recall={r_iso:.2f}"
)
print(
f"Mahalanobis : Precision={p_mah:.2f} "
f"Recall={r_mah:.2f}"
)
print(
f"Triangolazione : Precision={p_both:.2f} "
f"Recall={r_both:.2f}"
)
# -------------------------------------------------
# Visualizzazione
# -------------------------------------------------
fig, ax = plt.subplots(1, 2, figsize=(14, 5))
normal_mask = labels_true == 0
ax[0].scatter(
X_all[normal_mask, 0],
X_all[normal_mask, 1],
s=5,
alpha=0.3,
label="Normali"
)
ax[0].scatter(
X_all[~normal_mask, 0],
X_all[~normal_mask, 1],
s=40,
marker="*",
label="Anomalie vere"
)
ax[0].scatter(
X_all[iso_labels == -1, 0],
X_all[iso_labels == -1, 1],
s=80,
facecolors="none",
linewidth=2,
label="Isolation Forest"
)
ax[0].scatter(
X_all[mahal_labels == 1, 0],
X_all[mahal_labels == 1, 1],
s=120,
facecolors="none",
linewidth=1,
label="Mahalanobis"
)
ax[0].set_xlabel("Età")
ax[0].set_ylabel("Reddito")
ax[0].set_title("Anomalie multivariate")
ax[0].legend(fontsize=8)
ax[0].grid(alpha=0.3)
ax[1].hist(
iso_scores[labels_true == 0],
bins=50,
alpha=0.6,
density=True,
label="Normali"
)
ax[1].hist(
iso_scores[labels_true == 1],
bins=50,
alpha=0.6,
density=True,
label="Anomalie"
)
ax[1].set_xlabel("Anomaly score di Isolation Forest")
ax[1].set_ylabel("Densità")
ax[1].legend()
ax[1].grid(alpha=0.3)
plt.tight_layout()
plt.show()
Isolation Forest : Precision=0.53 Recall=0.90
Mahalanobis : Precision=0.59 Recall=1.00
Triangolazione : Precision=0.82 Recall=0.90
Come leggere gli output
La prima cosa da osservare sono precision e recall, perché il semplice numero di anomalie individuate non dice se il detector stia funzionando bene.
La precision risponde alla domanda: tra le osservazioni segnalate, quante sono realmente appartenenti alla classe anomala? In un contesto operativo questa metrica è collegata direttamente al costo del lavoro degli analisti. Una precision bassa significa infatti che una parte consistente delle risorse viene spesa per verificare casi che non corrispondono al fenomeno cercato.
Il recall misura invece quante delle anomalie realmente presenti sono state intercettate. Se il costo di perdere un segnale è elevato, il recall assume particolare importanza.
Nel nostro esempio le 30 anomalie sono conosciute perché il dataset è simulato. In un problema reale questa informazione normalmente non sarebbe disponibile in modo così pulito. Per questo, precision e recall non possono essere calcolate semplicemente su un dataset operativo privo di etichette affidabili.
La configurazione:
contamination=0.01
non deve essere interpretata come la scoperta del fatto che l’1% della popolazione sia realmente anomala. È una scelta del modello che determina la quota attesa di osservazioni considerate anomale dalla procedura.
Nel caso reale, la quota di anomalie dovrebbe essere validata attraverso campioni revisionati, storico degli alert, regole di business e feedback degli analisti.
Anche la soglia Mahalanobis è deliberatamente empirica:
thr_mahal = np.quantile(mahal, 0.99)
La soglia corrisponde al 99° percentile dei valori osservati nel dataset utilizzato. Non equivale quindi automaticamente a un test statistico con errore di primo tipo pari all’1%.
Se fossero appropriate specifiche ipotesi distributive, la distanza quadratica di Mahalanobis potrebbe essere confrontata con una distribuzione chi-quadrato. In questo esercizio scegliamo invece una soglia empirica perché stiamo costruendo una demo con variabili binarie e senza voler imporre una calibrazione teorica specifica.
Perché la triangolazione è interessante
Il vettore both contiene le osservazioni che vengono segnalate da entrambi i metodi.
L’intersezione può essere utile quando si vuole creare una coda di revisione ad alta priorità, perché due procedure con logiche differenti concordano sul fatto che l’osservazione meriti attenzione.
Non bisogna però assumere che l’intersezione migliori sempre il sistema. Riducendo l’insieme dei casi segnalati è possibile ridurre anche i falsi positivi, ma allo stesso tempo perdere alcune anomalie e quindi diminuire il recall.
In produzione è quindi preferibile confrontare almeno tre strategie:
- Isolation Forest;
- Mahalanobis;
- intersezione dei due detector.
La scelta finale dovrebbe dipendere non soltanto da precision e recall, ma anche dal costo operativo della revisione e dal costo associato alla perdita di un’anomalia rilevante.
Un dettaglio importante sulla visualizzazione
Il grafico mostra soltanto età e reddito, mentre il detector lavora sulle quattro variabili.
La figura è quindi una proiezione bidimensionale utile per interpretare il risultato, ma non rappresenta l’intero spazio in cui viene effettuata la rilevazione.
Obiettivi business dell’esercizio
In un’applicazione reale questo modello potrebbe essere utilizzato per:
- individuare segmenti clienti non ancora riconosciuti;
- generare casi da sottoporre a revisione antifrode;
- individuare combinazioni insolite nei processi produttivi;
- identificare configurazioni anomale nei sistemi informativi;
- individuare nuove popolazioni prima che diventino abbastanza numerose da emergere nei KPI aggregati.
Il detector non dovrebbe quindi produrre direttamente una decisione commerciale o di rischio. Il suo ruolo consiste nel portare all’attenzione del team configurazioni che meritano una verifica.
Mini-quiz
- Se
remoteeruralfossero indipendenti dall’età, l’anomalia scomparirebbe automaticamente? - Perché Ledoit-Wolf può essere preferibile alla covarianza empirica?
- Come gestiresti gli outlier segnalati soltanto da Isolation Forest?
Risposte
1. No. Nel codice remote e rural sono già generate indipendentemente dall’età. La rarità può comunque derivare dalla combinazione complessiva di età, reddito, lavoro remoto e residenza.
2. Ledoit-Wolf applica shrinkage e può produrre una matrice di covarianza meglio condizionata, risultando utile quando la stima empirica è instabile. Non elimina però automaticamente l’influenza degli outlier.
3. Li inserirei in una coda di revisione separata. Alcuni potrebbero essere veri segnali che Mahalanobis non riesce a rappresentare adeguatamente, mentre altri potrebbero essere falsi positivi. La decisione dovrebbe quindi dipendere dalla verifica dei casi e dal costo degli errori.
Esercizio 5 — Weak signals nei testi: BERTopic su query di ricerca
Scenario di business
Un’agenzia di digital marketing raccoglie ogni giorno le query inserite dagli utenti nel proprio CRM, nelle piattaforme di ricerca interna e nei sistemi di customer care.
La maggior parte delle richieste appartiene a categorie conosciute, come SEO, advertising, conversion rate optimization e gestione delle campagne. Nel tempo, tuttavia, possono comparire richieste che non corrispondono perfettamente alle tassonomie esistenti.
Immaginiamo che inizino ad apparire frasi come:
- “ottimizzare cataloghi per bot di acquisto”;
- “agenti autonomi che fanno acquisti”;
- “assistenti LLM che negoziano prezzi”;
- “business-to-agent e deleghe decisionali”.
Il problema non consiste soltanto nel contare quante volte compare una determinata parola. Il comportamento emergente può infatti essere descritto attraverso vocaboli differenti.
Una parte delle query può parlare di “agenti”, un’altra di “bot”, un’altra ancora di “assistenti virtuali”, senza utilizzare una terminologia comune.
Dal punto di vista business, il rischio consiste nel non riconoscere un nuovo comportamento del mercato perché il sistema di classificazione è stato costruito intorno a categorie storiche.
Obiettivi business
L’esercizio mira a:
- individuare strutture tematiche che non erano presenti nella tassonomia iniziale;
- ridurre la dipendenza dalle singole keyword;
- individuare possibili nuovi comportamenti o bisogni degli utenti;
- distinguere un tema realmente emergente da rumore, stagionalità o variazioni casuali;
- fornire evidenze utili per marketing, customer care, sviluppo prodotto e pianificazione strategica.
Perché TF-IDF può non essere sufficiente
Con TF-IDF, due documenti risultano particolarmente simili quando condividono termini o n-grammi.
Questo approccio può funzionare bene quando il vocabolario è già relativamente stabile. Diventa però meno efficace quando utenti diversi descrivono lo stesso fenomeno utilizzando parole differenti.
BERTopic costruisce invece una rappresentazione semantica attraverso embedding, riduzione dimensionale, clustering e rappresentazione dei termini caratteristici dei cluster.
Lo schema concettuale è:
Testi → Embedding → UMAP → HDBSCAN → c-TF-IDF → Topic
È importante però non attribuire al metodo una capacità che non possiede.
BERTopic individua strutture tematiche nel corpus; non dimostra automaticamente che un topic sia emergente.
L’emergenza è una proprietà temporale. Per stabilire che un tema sia davvero nuovo o in crescita occorre confrontare più finestre temporali e verificare la sua presenza storica, la sua crescita e la sua stabilità semantica.
Perché il Topic -1 merita attenzione
BERTopic non forza necessariamente ogni documento dentro un cluster.
I documenti che non trovano una struttura sufficientemente coerente possono essere assegnati al Topic -1.
Questo gruppo può contenere:
- rumore;
- errori;
- richieste ambigue;
- documenti molto specifici;
- testi troppo brevi;
- possibili proto-segnali.
Per questa ragione il Topic -1 non deve essere interpretato automaticamente come un topic emergente, ma neppure eliminato senza controllo.
In una pipeline operativa può essere trattato come una coda esplorativa da analizzare con una seconda procedura e da confrontare nel tempo.
Codice Python
Il dataset seguente contiene soltanto venti documenti. È intenzionalmente piccolo e deve essere considerato una dimostrazione didattica del metodo, non un corpus sufficiente per stimare topic stabili in produzione.
# Installazione, se necessaria:
# pip install bertopic sentence-transformers umap-learn hdbscan
from pathlib import Path
import numpy as np
import pandas as pd
from bertopic import BERTopic
from hdbscan import HDBSCAN
from sentence_transformers import SentenceTransformer
from sklearn.feature_extraction.text import CountVectorizer
from umap import UMAP
# -------------------------------------------------
# 1. Dati didattici
# -------------------------------------------------
docs = [
# SEO
"ottimizzazione seo per ecommerce e posizionamento organico",
"ricerca keyword a coda lunga per il blog aziendale",
"migliorare il ranking su google tramite link building e pr",
"core web vitals, tempo di caricamento e impatto sulla seo",
"strategia seo tecnica per siti multilingua e hreflang",
"scrivere articoli ottimizzati per i motori di ricerca",
"auditing tecnico per indicizzazione dei contenuti dinamici",
# Advertising
"campagne di advertising sui social media con roas elevato",
"gestione del budget ppc e ottimizzazione del tasso di conversione",
"retargeting dinamico per il recupero dei carrelli abbandonati",
"creazione di inserzioni sponsorizzate su facebook e instagram ads",
"a/b test strutturati sulle landing page per campagne a pagamento",
"analisi dei cpc e riduzione del costo di acquisizione cliente",
# Possibile segnale emergente:
# agentic commerce
"gli agenti ai autonomi stanno iniziando a fare acquisti da soli",
"come ottimizzare i cataloghi prodotti per i bot di acquisto",
"assistenti virtuali llm che negoziano prezzi sugli ecommerce",
"modelli di business to agent b2a e deleghe decisionali",
"integrazione di agenti intelligenti nei flussi di spesa b2b",
# Rumore / outlier
"problema con il login della piattaforma, password dimenticata",
"orari di apertura degli uffici di milano per la settimana prossima",
]
if len(docs) < 10:
raise ValueError(
"La demo richiede almeno 10 documenti."
)
# -------------------------------------------------
# 2. Configurazione
# -------------------------------------------------
# Encoder multilingue esplicito
embedding_model = SentenceTransformer(
"paraphrase-multilingual-MiniLM-L12-v2"
)
# Configurazione volutamente piccola per la demo.
# Su dati reali n_neighbors deve essere scelto in funzione
# della numerosità e della struttura del corpus.
umap_model = UMAP(
n_neighbors=2,
n_components=2,
min_dist=0.0,
metric="cosine",
random_state=42
)
hdbscan_model = HDBSCAN(
min_cluster_size=3,
min_samples=2,
metric="euclidean",
cluster_selection_method="eom",
prediction_data=True
)
# Gli n-grammi permettono di rappresentare anche espressioni
# composte, importanti nel caso delle query.
vectorizer_model = CountVectorizer(
ngram_range=(1, 2),
min_df=1,
stop_words=None
)
topic_model = BERTopic(
embedding_model=embedding_model,
umap_model=umap_model,
hdbscan_model=hdbscan_model,
vectorizer_model=vectorizer_model,
language="multilingual",
calculate_probabilities=True,
verbose=False
)
# -------------------------------------------------
# 3. Addestramento e assegnazione
# -------------------------------------------------
topics, probabilities = topic_model.fit_transform(docs)
documents = pd.DataFrame({
"document_id": np.arange(len(docs)),
"text": docs,
"topic": topics
})
# -------------------------------------------------
# 4. Report dei topic
# -------------------------------------------------
info = topic_model.get_topic_info().copy()
info["share"] = (
info["Count"] / len(docs)
)
# Regola didattica:
# un topic con almeno 3 documenti e quota inferiore al 30%
# viene considerato candidato a ulteriore analisi.
#
# Non si tratta di una definizione statistica di weak signal.
info["is_candidate"] = (
(info["Topic"] != -1)
& (info["share"] < 0.30)
& (info["Count"] >= 3)
)
print("\n=== Topic info ===")
print(
info[
[
"Topic",
"Count",
"share",
"Name",
"Representation",
"is_candidate"
]
].to_string(index=False)
)
print("\n=== Documenti assegnati ===")
print(
documents.to_string(index=False)
)
print("\n=== Topic candidati ===")
print(
info.loc[
info["is_candidate"],
[
"Topic",
"Count",
"share",
"Name",
"Representation"
]
].to_string(index=False)
)
print("\n=== Topic -1: documenti da revisionare ===")
print(
documents.loc[
documents["topic"] == -1,
["document_id", "text"]
].to_string(index=False)
)
# -------------------------------------------------
# 5. Salvataggio dei risultati
# -------------------------------------------------
out_dir = Path("bertopic_output")
out_dir.mkdir(exist_ok=True)
info.to_csv(
out_dir / "topic_info.csv",
index=False
)
documents.to_csv(
out_dir / "document_topics.csv",
index=False
)
# Le visualizzazioni vengono salvate come HTML.
try:
topic_model.visualize_barchart(
top_n_topics=10
).write_html(
out_dir / "topic_barchart.html"
)
topic_model.visualize_topics().write_html(
out_dir / "topic_map.html"
)
except Exception as exc:
print(
f"\nVisualizzazioni non salvate: "
f"{type(exc).__name__}: {exc}"
)
print(
f"\nOutput salvati in: {out_dir.resolve()}"
)
Come leggere gli output attesi
Il primo output importante è la tabella topic_info.

La colonna Count indica quanti documenti sono stati assegnati a ciascun topic, mentre share rappresenta la quota del corpus.
Nel nostro dataset ci aspettiamo che le query SEO e quelle relative all’advertising generino strutture tematiche relativamente coerenti, mentre le cinque query dedicate agli agenti AI possono formare un cluster distinto, se l’embedding e i parametri utilizzati riescono a riconoscere la loro vicinanza semantica.
Non è però corretto aspettarsi necessariamente un determinato numero di topic.
Il risultato dipende dalla configurazione di UMAP, HDBSCAN, dall’encoder utilizzato e dalla struttura effettiva del corpus. Anche l’identificativo numerico del topic non ha un significato stabile.
Il topic relativo agli agenti, per esempio, potrebbe essere identificato come 2 in una configurazione e come 1 dopo una modifica dei parametri. Il numero non rappresenta quindi un’identità permanente del fenomeno.
Quello che interessa è la coerenza semantica del gruppo e la sua evoluzione nel tempo.
Perché is_candidate non significa “weak signal”
Nel codice utilizziamo:
(info["share"] < 0.30) & (info["Count"] >= 3)
come semplice regola didattica.
Un topic poco frequente non è automaticamente un weak signal. Potrebbe essere poco frequente perché è irrilevante, casuale o molto specifico.
Allo stesso modo, un topic molto frequente non è necessariamente meno interessante. Un fenomeno già ampiamente diffuso può avere un’importanza strategica enorme anche se non è più “debole”.
Per parlare davvero di segnale emergente dobbiamo introdurre almeno tre dimensioni:
- novità: il tema era assente o molto raro nella storia precedente?
- accelerazione: la sua frequenza sta aumentando?
- rilevanza: la sua crescita potrebbe modificare una decisione?
Dal topic al segnale emergente
Supponiamo di analizzare le query ogni mese.
Nel mese corrente il tema “agenti AI che fanno acquisti” compare nel 5% delle query. Il valore assoluto è piccolo, ma potrebbe essere interessante se nei sei mesi precedenti la quota era praticamente nulla.
Viceversa, un topic che passa dal 20% al 30% durante il periodo natalizio potrebbe essere perfettamente spiegato dalla stagionalità.
La percentuale corrente, quindi, non è sufficiente.
Cinque query su venti rappresentano il 25% di un piccolo campione. Cinquanta query su diecimila rappresentano soltanto lo 0,5%, ma possono essere molto più interessanti se erano cinque il mese precedente.
La rilevanza di un segnale dipende quindi dalla combinazione tra dimensione, dinamica e contesto.
Confrontare topic nel tempo
Un ulteriore problema nasce dal fatto che gli identificativi dei topic non sono stabili.
Non è corretto prendere il “Topic 2” di gennaio e confrontarlo automaticamente con il “Topic 2” di febbraio.
Per confrontare le finestre temporali occorre allineare i cluster sulla base della similarità delle loro rappresentazioni, utilizzando, a seconda del progetto, embedding, rappresentazioni c-TF-IDF oppure entrambe.
In questo modo possiamo distinguere:
- un topic realmente nuovo;
- un topic già presente che sta crescendo;
- un topic stabile;
- un topic stagionale;
- un topic che compare e scompare casualmente.
Un tema come “regali di Natale” può aumentare rapidamente ogni anno senza rappresentare un nuovo fenomeno strutturale. Un tema emergente dovrebbe invece mostrare una novità rispetto alla storia precedente e una dinamica non spiegata soltanto dalla stagionalità.
Obiettivi business dell’esercizio
Una pipeline di questo tipo può essere utile per:
- customer care, per identificare nuove esigenze degli utenti;
- marketing, per intercettare nuovi comportamenti di ricerca;
- product management, per individuare bisogni non ancora rappresentati nei prodotti;
- competitive intelligence, per rilevare nuovi temi nel mercato;
- social listening, per individuare nuovi linguaggi e nuove categorie di discussione;
- CRM, per scoprire segmenti o esigenze che la tassonomia esistente non rappresenta.
Il risultato di BERTopic non dovrebbe essere utilizzato come decisione automatica. Deve essere interpretato come una mappa esplorativa del linguaggio utilizzato dagli utenti.
Mini-quiz
- Perché il Topic
-1va analizzato separatamente? - Che cosa può accadere riducendo
min_cluster_sizeda 3 a 2? - Come distingueresti un topic emergente da un topic stagionale?
Risposte
1. Il Topic -1 può contenere contemporaneamente rumore, richieste ambigue e documenti che non hanno trovato una struttura comune. Alcuni di questi possono però rappresentare proto-segnali.
2. Possono comparire più cluster, ma aumenta anche il rischio di creare gruppi troppo piccoli o semanticamente instabili. La scelta del parametro deve quindi essere accompagnata dalla verifica della coerenza dei topic.
3. Occorre confrontare finestre storiche. Un topic stagionale tende a ricomparire secondo una periodicità riconoscibile, mentre un topic emergente è assente o molto raro nella storia precedente e mostra una crescita non spiegata soltanto dal calendario.
Esercizio 6 — Dal segnale al valore decisionale: costruire un Weak Signal Score
Scenario di business
Una piattaforma B2B produce ogni giorno 10.000 alert derivanti da sistemi di anomaly detection, monitoraggio dei cambiamenti di regime e analisi dei topic.
Il problema non è più soltanto tecnico.
Il team analytics ha già strumenti capaci di produrre segnali, ma il numero degli alert è superiore alla capacità di analisi disponibile.
Il responsabile aziendale deve quindi decidere come allocare una risorsa limitata: il tempo degli analisti.
In questo scenario un sistema che individua 10.000 fenomeni può produrre meno valore di un sistema che ne individua 100 ma permette di concentrarsi sui 20 più rilevanti.
Per questo motivo il problema diventa una questione di prioritizzazione.
L’obiettivo non è stabilire matematicamente quali segnali siano importanti in assoluto, ma costruire un indice trasparente che permetta di rendere espliciti i criteri con cui vengono ordinati.
Obiettivi business
L’esercizio ha quattro obiettivi:
- ridurre il numero di alert che richiedono una revisione immediata;
- rendere espliciti i criteri con cui viene assegnata la priorità;
- collegare la priorità ai costi dei falsi positivi e dei falsi negativi;
- costruire un processo che possa essere calibrato attraverso il feedback degli analisti.
Sei criteri per descrivere un weak signal
Definiamo sei valori compresi tra 0 e 1.
Persistenza
Indica la frazione dei periodi in cui il fenomeno compare.
Un segnale osservato una sola volta può essere interessante, ma presenta meno evidenza di persistenza rispetto a un fenomeno osservato ripetutamente.
Accelerazione
Misura quanto rapidamente il fenomeno sta crescendo.
Una possibile implementazione consiste nel trasformare una pendenza temporale in un valore compreso tra 0 e 1.
Replicazione
Indica la frazione dei segmenti, mercati, aree geografiche o popolazioni in cui il fenomeno compare.
Un segnale osservato in un solo segmento può avere natura locale, mentre la sua presenza in più segmenti può rappresentare un’evidenza aggiuntiva.
Novità
Misura quanto il fenomeno è assente o raro nella storia precedente.
Questa dimensione è importante perché un fenomeno molto frequente ma noto da anni non è necessariamente un weak signal.
Rilevanza
Rappresenta il potenziale impatto sulla decisione aziendale.
Questa è la componente più direttamente collegata al business e richiede quindi una definizione coerente con il contesto.
Robustezza
Misura quanto il segnale rimane stabile modificando soglie, finestre temporali, campioni o metodi di rilevazione.
Un segnale che compare soltanto con una particolare configurazione del detector è meno robusto di uno che rimane visibile anche dopo piccole variazioni metodologiche.
Weak Signal Score come indice progettuale
Una possibile definizione utilizza una media geometrica pesata:
[math]\displaystyle WSS=\prod_i c_i^{w_i}, \qquad \sum_i w_i=1[/math]
dove [math]c_i[/math] sono i criteri e [math]w_i[/math] sono i pesi.
La media geometrica penalizza i criteri molto bassi. Se un segnale è estremamente debole in una dimensione importante, il valore complessivo viene ridotto in modo più marcato rispetto a una semplice somma pesata.
Questa è una scelta metodologica, non una legge universale.
Una somma pesata è più compensativa: un punteggio molto elevato in una dimensione può compensare un punteggio molto basso in un’altra. La media geometrica richiede invece una maggiore coerenza tra le dimensioni.
Il WSS definito in questo articolo è quindi un indice di priorità progettato per il caso d’uso.
Non è:
- una probabilità;
- una previsione;
- una misura standardizzata della letteratura;
- una stima automatica dell’impatto economico.
Per trasformare il punteggio in una misura empiricamente calibrata occorrerebbe collegarlo agli esiti osservati degli alert.
Routing degli alert
Possiamo utilizzare tre destinazioni operative:
INVESTIGATE: il segnale richiede analisi attiva;MONITOR: il segnale merita osservazione e raccolta di nuove evidenze;ARCHIVE: il segnale viene conservato nel log senza generare una notifica operativa.
Le soglie utilizzate nell’esempio sono decisioni organizzative.
Non esiste una soglia universale che stabilisca che un WSS pari a 0,45 debba necessariamente essere investigato.
Codice Python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
CRITERIA = [
"persistence",
"acceleration",
"replication",
"novelty",
"relevance",
"robustness",
]
DEFAULT_WEIGHTS = {
"persistence": 0.20,
"acceleration": 0.25,
"replication": 0.15,
"novelty": 0.15,
"relevance": 0.15,
"robustness": 0.10,
}
def validate_weights(weights, criteria=CRITERIA):
if set(weights) != set(criteria):
raise ValueError(
"I pesi devono corrispondere esattamente ai criteri"
)
if any(weight < 0 for weight in weights.values()):
raise ValueError(
"I pesi non possono essere negativi"
)
if not np.isclose(
sum(weights.values()),
1.0
):
raise ValueError(
"La somma dei pesi deve essere 1"
)
def weak_signal_score(
persistence,
acceleration,
replication,
novelty,
relevance,
robustness,
weights=None,
):
"""Media geometrica pesata per ordinare i weak signals."""
if weights is None:
weights = DEFAULT_WEIGHTS
validate_weights(weights)
values = {
"persistence": persistence,
"acceleration": acceleration,
"replication": replication,
"novelty": novelty,
"relevance": relevance,
"robustness": robustness,
}
# Limite numerico per evitare log(0).
# Il valore minimo non significa che il criterio valga davvero zero.
values = {
key: float(np.clip(value, 1e-6, 1.0))
for key, value in values.items()
}
return float(
np.exp(
sum(
weights[key] * np.log(values[key])
for key in CRITERIA
)
)
)
def decision_route(
wss,
theta_monitor=0.15,
theta_investigate=0.45,
):
if wss >= theta_investigate:
return "INVESTIGATE"
if wss >= theta_monitor:
return "MONITOR"
return "ARCHIVE"
signals = [
{
"id": "S1",
"desc": "Query 'notebook AI ready' su marketplace",
"persistence": 0.85,
"acceleration": 0.90,
"replication": 0.60,
"novelty": 0.95,
"relevance": 0.80,
"robustness": 0.75,
},
{
"id": "S2",
"desc": "Picco login falliti in un solo giorno",
"persistence": 0.05,
"acceleration": 0.20,
"replication": 0.10,
"novelty": 0.30,
"relevance": 0.40,
"robustness": 0.30,
},
{
"id": "S3",
"desc": "B2A: agenti AI fanno acquisti",
"persistence": 0.80,
"acceleration": 0.95,
"replication": 0.70,
"novelty": 0.90,
"relevance": 0.95,
"robustness": 0.85,
},
{
"id": "S4",
"desc": "Crescita query stagionale 'regali Natale'",
"persistence": 0.90,
"acceleration": 0.70,
"replication": 0.80,
"novelty": 0.10,
"relevance": 0.50,
"robustness": 0.90,
},
{
"id": "S5",
"desc": "Giovani, alto reddito, rurale e remoto",
"persistence": 0.60,
"acceleration": 0.55,
"replication": 0.30,
"novelty": 0.85,
"relevance": 0.70,
"robustness": 0.50,
},
]
df = pd.DataFrame(signals)
df["WSS"] = df.apply(
lambda row: weak_signal_score(
row["persistence"],
row["acceleration"],
row["replication"],
row["novelty"],
row["relevance"],
row["robustness"],
),
axis=1,
)
df["route"] = df["WSS"].apply(
decision_route
)
print(
df[
["id", "desc", "WSS", "route"]
].to_string(index=False)
)
# -------------------------------------------------
# Grafico dei punteggi
# -------------------------------------------------
fig, ax = plt.subplots(
figsize=(9, 5)
)
ax.barh(
df["id"],
df["WSS"]
)
ax.axvline(
0.45,
linestyle="--",
label="Soglia investigate"
)
ax.axvline(
0.15,
linestyle="--",
label="Soglia monitor"
)
ax.set_xlabel(
"Weak Signal Score"
)
ax.set_title(
"WSS e routing decisionale"
)
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()
# -------------------------------------------------
# Griglia dei criteri
# -------------------------------------------------
criteria_cols = CRITERIA
mat = df.set_index("id")[
criteria_cols
]
fig, ax = plt.subplots(
figsize=(10, 5)
)
im = ax.imshow(
mat.values,
aspect="auto",
vmin=0,
vmax=1
)
ax.set_xticks(
range(len(criteria_cols))
)
ax.set_xticklabels(
criteria_cols,
rotation=45,
ha="right"
)
ax.set_yticks(
range(len(df))
)
ax.set_yticklabels(
df["id"]
)
for i in range(len(df)):
for j in range(len(criteria_cols)):
ax.text(
j,
i,
f"{mat.values[i, j]:.2f}",
ha="center",
va="center",
fontsize=8
)
ax.set_title(
"Griglia dei criteri"
)
plt.colorbar(
im,
ax=ax
)
plt.tight_layout()
plt.show()
Come leggere i risultati
Con i pesi e le soglie utilizzate nell’esempio otteniamo circa:
| Segnale | WSS | Routing |
|---|---|---|
| S1 — Query “notebook AI ready” | 0,814 | INVESTIGATE |
| S2 — Picco di login falliti | 0,168 | MONITOR |
| S3 — B2A e agenti AI | 0,860 | INVESTIGATE |
| S4 — Query stagionale “regali Natale” | 0,547 | INVESTIGATE |
| S5 — Profilo giovane, ricco, rurale e remoto | 0,560 | INVESTIGATE |

Questi risultati non devono essere letti come una graduatoria assoluta dell’importanza dei fenomeni. Il WSS ordina i segnali secondo i criteri e i pesi che abbiamo deciso di utilizzare.
S3 presenta un punteggio elevato perché combina persistenza, accelerazione, replicazione, novità, rilevanza e robustezza su livelli relativamente alti.
S1 mostra una configurazione simile, pur con una replicazione inferiore rispetto a S3.
S2 rappresenta invece un caso molto diverso. Il picco dei login falliti è nuovo e può avere una certa rilevanza, ma la persistenza e la replicazione sono molto basse. La media geometrica penalizza questa combinazione e porta il segnale nella fascia MONITOR.
Il caso più istruttivo è però S4.
Il fenomeno “regali Natale” presenta persistenza, accelerazione, replicazione e robustezza elevate, ma la novità è soltanto 0,10. Il WSS rimane comunque superiore alla soglia di investigazione.
Questo risultato è utile perché mostra un limite della media geometrica: un singolo criterio basso non necessariamente annulla un segnale quando gli altri criteri sono elevati.
Se sappiamo già che il fenomeno è stagionale, non è necessario modificare arbitrariamente il peso della novità per ottenere il risultato desiderato. È più trasparente introdurre una regola di contesto specifica.
Gestire esplicitamente la stagionalità
def decision_route_with_seasonality(
wss,
novelty,
theta_monitor=0.15,
theta_investigate=0.45,
):
# Il segnale può essere rilevante,
# ma non rappresenta un weak signal nuovo.
if novelty < 0.20:
return "ARCHIVE_SEASONAL"
if wss >= theta_investigate:
return "INVESTIGATE"
if wss >= theta_monitor:
return "MONITOR"
return "ARCHIVE"
La distinzione è importante perché separa due funzioni diverse.
Il WSS misura una priorità composita, mentre la regola sulla stagionalità introduce un’informazione di contesto che il punteggio numerico da solo non rappresenta adeguatamente.
In un sistema reale potremmo avere altre regole analoghe, per esempio:
- eventi promozionali già pianificati;
- campagne marketing note;
- modifiche infrastrutturali;
- cambiamenti normativi già previsti;
- anomalie già spiegate da un evento operativo.
L’obiettivo non dovrebbe essere costruire un unico punteggio che sappia rappresentare tutto, ma creare un sistema in cui punteggio e contesto lavorino insieme.
Soglie e costi decisionali
La scelta della soglia dipende anche dal costo dei due tipi principali di errore.
Indichiamo con:
- [math]C_{FP}[/math] il costo di investigare un falso allarme;
- [math]C_{FN}[/math] il costo di ignorare un segnale reale.
Supponiamo che:
[math]\displaystyle C_{FP}=200[/math]
e
[math]\displaystyle C_{FN}=50.000[/math]
Il rapporto è:
[math]\displaystyle \frac{C_{FP}}{C_{FN}}=\frac{200}{50.000}=0,004[/math]
Questo rapporto rende evidente che i due errori hanno conseguenze economiche molto differenti.
Non permette però, da solo, di trasformare direttamente 0,004 in una soglia WSS.
Per determinare una soglia operativa servono almeno:
- uno storico degli alert;
- la registrazione delle decisioni degli analisti;
- la verifica degli esiti;
- una stima dei costi dei falsi positivi e dei falsi negativi;
- una valutazione della capacità operativa del team.
Se il costo di ignorare un segnale reale è molto elevato, può essere razionale aumentare il numero di casi inviati alla revisione, accettando un maggior numero di falsi positivi.
Se invece la capacità degli analisti è fortemente limitata, può essere necessario concentrarsi su una quota molto più piccola degli alert.
La soglia diventa quindi una decisione organizzativa supportata dai dati, non una proprietà intrinseca dell’algoritmo.
Discriminazione, calibrazione e Decision Value
A questo punto possiamo introdurre una distinzione fondamentale tra tre proprietà differenti di un sistema di Weak Signals Detection.
Discriminazione
La discriminazione riguarda la capacità del sistema di separare i segnali che meritano attenzione da quelli che non la meritano.
Se abbiamo uno storico validato, possiamo utilizzare metriche come:
- precision;
- recall;
- PR-AUC;
- precision@K.
Nel nostro scenario aziendale, Precision@20 è particolarmente interessante.
Se il responsabile può esaminare soltanto i primi 20 segnali, possiamo definire:
[math]\displaystyle \begin{aligned}
& Precision@20 \\
&{} = \frac{\text{segnali realmente utili tra i primi 20}}{20}
\end{aligned}[/math]
Questa metrica è spesso più vicina al problema operativo rispetto alla precision calcolata sull’intero universo degli alert.
Un sistema che classifica perfettamente i casi dal 21° al 10.000° posto, ma mette segnali poco utili nei primi 20, può essere poco efficace dal punto di vista organizzativo.
Calibrazione
Il WSS non è una probabilità.
Un WSS pari a 0,80 non significa che esista una probabilità dell’80% che il fenomeno produca una conseguenza aziendale.
Possiamo però verificare empiricamente se valori più elevati del WSS siano associati, nel nostro storico, a una maggiore frequenza di decisioni utili.
Per esempio, possiamo dividere gli alert in fasce di WSS e osservare:
| Fascia WSS | Alert | Decisioni utili | Tasso di utilità |
|---|---|---|---|
| 0,00–0,20 | … | … | … |
| 0,20–0,40 | … | … | … |
| 0,40–0,60 | … | … | … |
| 0,60–0,80 | … | … | … |
| 0,80–1,00 | … | … | … |
Se il tasso di utilità cresce in modo consistente con il WSS, abbiamo un’evidenza empirica che il ranking costruito dal modello contiene informazione.
Questa verifica è molto più significativa dell’affermazione secondo cui un determinato punteggio “significa” una certa probabilità.
Decision Value
La domanda finale riguarda il valore prodotto dal sistema.
Un sistema di detection non crea valore semplicemente perché scopre anomalie. Il valore nasce quando le evidenze contribuiscono a decisioni migliori.
Un possibile framework concettuale è:
[math]\displaystyle \begin{aligned}
DV &= \sum V(\text{decisioni informate}) \\
&{} \quad – \sum C_{FP} – \sum C_{FN}
\end{aligned}[/math]
Il termine Decision Value non deve essere interpretato come una metrica universale pronta all’uso. Il valore delle decisioni deve essere definito in relazione al contesto aziendale.
In alcuni casi può corrispondere a:
- ricavi incrementali;
- costi evitati;
- riduzione delle frodi;
- riduzione dei tempi di intervento;
- riduzione degli incidenti;
- aumento della retention;
- riduzione del rischio operativo.
Il vantaggio di questa impostazione consiste nel cambiare la domanda finale.
Non ci chiediamo soltanto quante anomalie il sistema abbia trovato, ma se le evidenze abbiano modificato una decisione e se quella decisione abbia prodotto un valore misurabile.
Mini-quiz
- Perché utilizzare una media geometrica invece di una somma pesata?
- Come gestiresti un segnale con WSS = 0,44, appena sotto la soglia di investigazione?
- Come cambieresti il routing se il costo di ignorare un segnale reale fosse molto alto?
Risposte
1. La media geometrica penalizza maggiormente i criteri molto bassi e richiede un equilibrio tra le diverse dimensioni. La somma pesata è più compensativa e consente a un criterio molto alto di compensare maggiormente una debolezza in un’altra dimensione.
2. Non lo considererei automaticamente irrilevante. Potrei inserirlo in una fascia di monitoraggio ad alta priorità e raccogliere ulteriori evidenze, per esempio osservando una finestra temporale più lunga o verificando se il fenomeno compare in una seconda fonte dati.
3. Potrei aumentare la quota di segnali sottoposti a revisione o abbassare la soglia operativa, verificando contemporaneamente la capacità degli analisti. La decisione dovrebbe essere supportata dal confronto tra costo della revisione e costo degli errori.
Commenti applicativi
Esercizio 4: l’anomalia vive nella combinazione
L’anomalia multivariata diventa particolarmente utile quando le singole colonne sembrano normali ma la relazione tra loro è insolita.
In un sistema antifrode, per esempio, non è necessariamente sospetto un importo elevato, così come non è necessariamente sospetto un dispositivo nuovo o una determinata localizzazione. La combinazione di più caratteristiche può tuttavia produrre una configurazione che merita revisione.
Lo stesso principio può essere applicato alla segmentazione dei clienti, al controllo qualità e al monitoraggio dei processi.
La difficoltà principale consiste nel definire una buona popolazione di riferimento. Se il dataset utilizzato per stimare la normalità contiene già una grande quantità di casi anomali, i detector possono imparare una rappresentazione distorta del comportamento ordinario.
Esercizio 5: il linguaggio nuovo non ha ancora un vocabolario stabile
L’analisi semantica diventa utile quando un nuovo comportamento viene descritto attraverso parole differenti prima che si formi un’etichetta condivisa.
Questo può accadere nel customer care, nelle ricerche interne, nelle recensioni, nei social media e nelle query dei motori di ricerca.
Un cluster semanticamente coerente non dimostra però che il fenomeno sia nuovo o importante. Per trasformare un topic in un possibile weak signal è necessario aggiungere la dimensione temporale e verificare la rilevanza per il business.
Il passaggio corretto è quindi:
topic → confronto storico → verifica della crescita → valutazione della rilevanza
Esercizio 6: il problema non è trovare alert, ma scegliere
Quando gli alert sono migliaia, il valore di un sistema di detection dipende anche dalla capacità di stabilire quali casi analizzare per primi.
Il WSS rende espliciti i criteri utilizzati per costruire la priorità e permette al team di discutere i pesi invece di nasconderli dentro una regola opaca.
La sua utilità aumenta ulteriormente quando il ranking viene collegato al feedback degli analisti.
Se gli alert con WSS elevato non producono mai decisioni utili, il problema non consiste necessariamente nel cambiare il modello di detection. Potrebbe essere necessario rivedere i criteri, i pesi, le soglie oppure la definizione stessa di “segnale utile”.
Una mappa operativa della Weak Signals Detection
| Livello | Domanda | Strumenti possibili |
|---|---|---|
| Rarità | Che cosa è insolito? | IQR, Z-score, MAD |
| Persistenza | Il fenomeno si ripete? | Tasso di persistenza, finestre temporali |
| Dinamica | Che cosa sta cambiando? | Slope, Mann-Whitney, change point detection |
| Combinazioni | Che cosa è insolito insieme? | Isolation Forest, Mahalanobis |
| Semantica | Di che cosa si comincia a parlare? | Embedding, BERTopic, UMAP, HDBSCAN |
| Decisione | Perché dovrebbe interessarci? | Weak Signal Score, costi, Precision@K, Decision Value |
Il percorso completo può essere rappresentato così:
Rilevazione
↓
Persistenza
↓
Contesto
↓
Verifica
↓
Prioritizzazione
↓
Decisione
↓
Monitoraggio
Il processo non termina necessariamente con la decisione.
Se l’organizzazione interviene, deve essere possibile osservare ciò che accade successivamente e utilizzare il risultato per migliorare il sistema.
Il ciclo diventa quindi:
Segnale
↓
Indagine
↓
Decisione
↓
Effetto
↓
Nuovo monitoraggio
↓
Nuova evidenza
Questa retroazione è particolarmente importante perché consente di passare da un sistema statico di alert a un processo di apprendimento organizzativo.
Conclusione
Un weak signal non è semplicemente un dato piccolo e non coincide necessariamente con un’anomalia statistica.
Può essere una configurazione rara di variabili, un comportamento che inizia a comparire nel linguaggio degli utenti, una variazione ancora troppo piccola per modificare un KPI aggregato oppure un fenomeno che diventa interessante soltanto quando viene osservato contemporaneamente in più segmenti.
Isolation Forest e Mahalanobis aiutano a individuare combinazioni insolite, ma adottano prospettive differenti e devono essere confrontati sulla base degli errori e dei costi operativi.
BERTopic può aiutare a individuare strutture semantiche che non emergono attraverso la semplice ricerca delle keyword, ma un topic non è automaticamente un fenomeno emergente. La dimensione temporale rimane indispensabile.
Il Weak Signal Score introduce infine un ulteriore livello: la prioritizzazione. Persistenza, accelerazione, replicazione, novità, rilevanza e robustezza possono essere utilizzate per costruire un ranking trasparente, purché sia chiaro che i pesi e le soglie sono scelte progettuali e non proprietà universali del fenomeno.
Per valutare davvero un sistema di questo tipo occorre andare oltre il numero di alert generati e osservare almeno tre aspetti: quanto bene discrimina, quanto è calibrato rispetto agli esiti osservati e quale valore produce sulle decisioni.
La direzione più utile non consiste quindi nel costruire un detector sempre più sensibile, ma nel collegare la detection a un processo che sappia distinguere un’anomalia casuale da un cambiamento che merita di essere compreso.
Il valore di un weak signal non dipende soltanto dalla sua rarità, ma dalla capacità di trasformare un’evidenza debole in una decisione informata.
Riferimenti
[1] Weak Signals Detection con Python: 3 esercizi su anomalie, persistenza e cambiamenti di regime
📚 Per approfondire: segnali deboli, outlier e Anomaly Detection
Un segnale anomalo non è necessariamente un errore e un outlier non dovrebbe essere eliminato automaticamente.
Questi approfondimenti mostrano come passare dall’identificazione dei segnali deboli alla rilevazione degli outlier,
utilizzando metodi robusti come IQR e MAD, fino alle pipeline di Anomaly Detection e all’analisi multivariata con la distanza di Mahalanobis.
👉Weak Signals Detection: come identificare segnali deboli ed emergenti nei dati
👉Outlier: cosa sono, come trovarli e rimuoverli con Pandas, IQR e Z-score
👉Anomaly Detection in produzione: pipeline con IQR, MAD e Isolation Forest
👉Distanza di Mahalanobis: dalla geometria all’Anomaly Detection in Python





