Panoramica
In questa sezione viene descritto come configurare correttamente le query di rilevamento delle anomalie. A un livello alto, vi sono quattro tipi di parametri da ottimizzare:
- Parametri di selezione dei dati che specificano quali dati vengono inclusi nella nostra analisi. Esempi:
QueryDataSetRequest.dimensionNames
,QueryDataSetRequest.pinnedDimensions
eQueryDataSetRequest.testedInterval
. - Aggregazione che specificano come vengono raggruppati più eventi per produrre un valore utilizzato per confrontare le sezioni. Esempi:
ForecastParams.aggregatedDimension
. - Parametri di previsione, utilizzati per configurare gli algoritmi di previsione utilizzati per calcolare i valori previsti. Esempi:
ForecastParams.holdout
,ForecastParams.forecastHistory
eForecastParams.minDensity
. - Regolazione della sensibilità che, se modificata, può aumentare o ridurre il numero di anomalie segnalate, la latenza e le risorse di calcolo. Esempi:
ForecastParams.maxPositiveRelativeChange
,ForecastParams.maxNegativeRelativeChange
eForecastParams.forecastExtraWeight
.
Prerequisiti
Segui le istruzioni riportate nella nostra Guida rapida per assicurarti di poter eseguire tutti i comandi in questa guida.
Mostreremo in che modo ogni parametro influisce sui risultati restituiti eseguendo una query sul set di dati demo pubblico che abbiamo già precaricato con i dati del progetto GDELT.
Salva la seguente query come query.json
nella directory di lavoro:
{
dimensionNames: ["EntityLOCATION"],
testedInterval: {
startTime: "2019-04-15T00:00:00Z",
length: "86400s"
},
forecastParams: {
forecastHistory: "2592000s",
holdout: 10.0,
minDensity: 0.0,
maxPositiveRelativeChange: 1.0,
maxNegativeRelativeChange: 1.0,
forecastExtraWeight: 200.0
}
}
Come spiegato nella guida rapida, puoi inviare una query con il seguente comando:
$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query
Nel corso di questa guida, modificheremo parametri diversi in query.json
per mostrare come incide sull'output. Per procedere, modifica il tuo testo
e invia nuovamente le query utilizzando il comando gcurl
precedente, che è stato
designato come in Guida rapida.
Selezione dei dati
Una query di rilevamento delle anomalie valuta se vi sono anomalie in un determinato set di dati, entro un determinato intervallo di tempo, selezionando i dati di alcune dimensioni e, facoltativamente, filtrando alcuni valori per alcune dimensioni. In questa sezione ti spiegheremo come controllare tutti questi passaggi per la selezione dei dati.
Specifica intervallo di tempo
Possiamo specificare l'intervallo di tempo che vogliamo testare per le anomalie impostando QueryDataSetRequest.tested_interval
. In query.json
, specifichiamo che vogliamo rilevare
le anomalie che si sono verificate il giorno 15 aprile 2019:
...
testedInterval: {
startTime: "2019-04-15T00:00:00Z",
length: "86400s"
},
...
Se, ad esempio, volessi rilevare anomalie che si sono verificate il 15 aprile
per le 13:00 e le 14:00, imposteremo
testedInterval
su:
...
testedInterval: {
startTime: "2019-04-15T13:00:00Z",
length: "3600s"
},
...
SUGGERIMENTO: gioca con il valore testedInterval
impostando orari di inizio e durate diverse e scopri in che modo le anomalie restituite variano.
Controllo filtro
Quando rileviamo anomalie in un determinato intervallo di tempo, dobbiamo anche
specificare come vengono raggruppati gli eventi in sezioni di dati. Ciò avviene impostando
dimensionNames
l'insieme di dimensioni da suddividere nel set di dati.
In query.json
registriamo solo i dati per la dimensione "EntityLOCATION":
dimensionNames: ["EntityLOCATION"]
. Come puoi notare, nell'elenco dei risultati in cui tutte le sezioni sono semplicemente valori diversi per la dimensione "EntityLocation".
Se, invece, vogliamo suddividere i dati tra più dimensioni, possiamo specificare più nomi di dimensioni:
{
dimensionNames: ["EntityLOCATION", "EntityORGANIZATION"],
testedInterval: {
startTime: "2019-04-15T00:00:00Z",
length: "86400s"
},
forecastParams: {
forecastHistory: "2592000s",
holdout: 10.0,
minDensity: 0.0,
maxPositiveRelativeChange: 1.0,
maxNegativeRelativeChange: 1.0,
forecastExtraWeight: 50.0
}
}
Quando esegui questa query, notiamo che le sezioni risultanti prendono i valori delle due dimensioni specificate, "EntityLOCATION" e "EntityORGANIZATION" (mostraamo solo la prima sezione di anomalie suggerita).
$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query
{
"name": "projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104",
"anomalyDetectionResult": {
"anomalies": [
{
"dimensions": [
{
"name": "EntityLOCATION",
"stringVal": "Seine"
},
{
"name": "EntityORGANIZATION",
"stringVal": "AP"
}
],
"result": {
"holdoutErrors": {},
"trainingErrors": {},
"forecastStats": {
"numAnomalies": 1
},
"testedIntervalActual": 344
},
"status": {}
},
...
Filtri
Se ti interessa analizzare solo un sottoinsieme di set di dati, puoi filtrare solo gli eventi con determinati valori per determinate dimensioni impostando pinnedDimensions
.
Esempio di applicazione di filtri per EntityORGANIZATION
e suddivisione per EntityLOCATION
(visualizzazione del primo risultato):
$ cat query.json
{
dimensionNames: ["EntityLOCATION"],
pinnedDimensions: [
{
name: "EntityORGANIZATION",
stringVal: "AP"
}
],
testedInterval: {
startTime: "2019-04-15T00:00:00Z",
length: "86400s"
},
forecastParams: {
forecastHistory: "2592000s",
holdout: 10.0,
minDensity: 0.0,
maxPositiveRelativeChange: 1.0,
maxNegativeRelativeChange: 1.0,
forecastExtraWeight: 250.0
}
}
$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query
{
"name": "projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104",
"anomalyDetectionResult": {
"anomalies": [
{
"dimensions": [
{
"name": "EntityLOCATION",
"stringVal": "Seine"
}
],
"result": {
"holdoutErrors": {},
"trainingErrors": {},
"forecastStats": {
"numAnomalies": 1
},
"testedIntervalActual": 344
},
"status": {}
},
{
"dimensions": [
{
"name": "EntityLOCATION",
"stringVal": "Ile de la Cite"
}
],
"result": {
"holdoutErrors": {},
"trainingErrors": {},
"forecastStats": {
"numAnomalies": 1
},
"testedIntervalActual": 282
},
"status": {}
},
...
Aggregazione
Il metodo di aggregazione predefinito conteggia il numero di eventi nella sezione.
Possiamo anche specificare di voler aggregare gli eventi sommando una dimensione numerica. Questo valore viene impostato impostando ForecastParams.aggregatedDimension
sulla dimensione numerica da sommare.
Configurazione previsione
Per calcolare il valore previsto durante l'intervallo di tempo testato, utilizziamo algoritmi di previsione che, sulla base dei valori storici della sezione, calcolano il valore che dovrebbe essere durante l'intervallo testato.
NOTA: i valori di una sezione sono assegnati dal metodo di aggregazione.
Cronologia delle previsioni
La quantità di dati contenuti nella serie temporale è fornita dal parametro ForecastParams.forecastHistory
.
Nel nostro esempio iniziale:
- Abbiamo impostato
forecastHistory: "2592000s"
, che ci comunica che dobbiamo recuperare gli eventi tra il 16 marzo 2019 e il 15 aprile 2019 (30 giorni, ossia 2592000 secondi) e formando una serie temporale tra questi due giorni. - Ogni punto dati nella serie temporale coprirà un periodo di tempo uguale alla
durata dell'intervallo testato (nel nostro esempio,
86400s
- 1 giorno). - La serie temporale sarà composta da 30 punti, ognuno con il valore aggregato per gli eventi di quel giorno di calendario. Presumendo il calcolo come metodo di aggregazione, il primo punto della serie temporale avrà come valore il numero totale di eventi il 16 marzo 2019, il secondo punto dal 17 marzo 2019 e così via.
È possibile visualizzare i valori storici per la serie temporale restituita nel campo
ForecastResult.history
(restituisce solo se
QueryDataSetRequest.returnTimeseries
è impostato su true
nella richiesta).
NOTA: stiamo modellando una serie temporale nel protocollo Timeseries
come
raccolta di punti (definita come TimeseriesPoint
), ordinati per data e avere come valore (TimeseriesPoint.value
) il valore aggregato della sezione durante l'intervallo di tempo [TimeseriesPoint.time
, TimeseriesPoint.time + testedInterval.length
].
Aumentando il valore ForecastParams.forecastHistory
e includendo una cronologia più lunga, puoi acquisire determinati pattern della stagionalità e, potenzialmente, incrementare la precisione della previsione, poiché conterrà più dati. Ad esempio, se disponiamo di pattern di stagionalità mensili, impostando forecastHistory
su 30 giorni non potremo acquisire questi modelli, in questo caso dovremmo aumentare il valore forecastHistory
, perciò abbiamo diversi pattern mensili nella nostra serie temporale analizzata (ad esempio, i pattern mensili rilevati da 100-300 giorni dovrebbero essere sufficienti, ma dipende anche dai modelli effettivi).
Nell'esempio iniziale, prova a ridurre il numero di testedInterval.length
da 3600s
((1 ora) e ad aumentare ForecastParams.forecastHistory
fino a 8640000s
(100 giorni). In questo modo, il numero di punti della serie temporale di history
aumenterà a 2400, il che lo rende più granulare e copre un periodo di tempo più lungo.
Intervallo di blocco/test
Per valutare la qualità della nostra previsione, prediligiamo solo il primo X% della
serie temporale. L'ultima parte della serie temporale viene mantenuta per la valutazione e viene controllata dal parametro ForecastParams.holdout
.
Il parametro ForecastParams.holdout
rappresenta una percentuale (nell'intervallo da 0 a 100) e indica la quantità di dati da conservare per i test. Nel nostro esempio abbiamo specificato holdout: 10.0
, pertanto manteniamo l'ultimo 10% della serie temporale per valutare l'andamento della previsione in base al primo 90%.
Considerando che, nel nostro esempio iniziale, abbiamo 30 punti della serie temporale, ciò presuppone che i primi 27 punti vengano mantenuti per l'addestramento e gli ultimi 3 per la valutazione/valutazione del modello.
Gli errori che misuriamo durante il periodo di blocco vengono restituiti in ForecastResult.holdoutErrors
e questi errori vengono utilizzati per calcolare i limiti inferiori e superiore della previsione (testedIntervalForecastLowerBound
e testedIntervalForecastUpperBound
). Gli errori di holding più alti ci consentono di avere un limite di previsione più ampio, mentre quelli più bassi ci permetteranno di avere limiti più rigidi (e più vicini al valore di ForecastResult.testedIntervalForecast
).
Il valore di holding dovrebbe essere in punti percentuali bassi (3%-10%), ma dovrebbe garantire che contenga punti dati sufficienti quando sono necessari questi errori per il calcolo dei limiti di previsione.
Densità minima
La densità minima, data dal parametro ForecastParams.minDensity
, determina la quantità di serie non vuote (contenenti almeno un evento) punti che una serie temporale deve contenere per poter essere considerata come potenziale anomalia.
Simile al blocco, è un valore percentuale nell'intervallo 0-100.
Il numero di punti viene confrontato con il numero di punti previsti compreso tra
testedInterval.startTime - forecastHistory
e testedInterlal.startTime
.
Ad esempio, se abbiamo 30 punti previsti per la serie temporale (come nel nostro primo esempio che imposta testedInterval.length
su 1 giorno e forecastHistory
da 30 giorni) e impostiamo minDensity
fino a 80, accetteremo solo serie temporali
che contengono 24 punti con almeno un evento.
NOTA: nel nostro esempio, imposti minDensity
su 0 e suggeriamo che, quando rileviamo picchi, sia impostato anche su 0.
Orizzonte
L'orizzonte temporale, specificato da ForecastParams.horizonTime
, specifica semplicemente quanto in futuro, dall'intervallo testato, dobbiamo prevedere i valori in base alla serie storica.
Se QueryDataSetRequest.returnTimeseries
è impostato su true, la serie temporale prevista verrà restituita in ForecastResult.forecast
per ogni sezione e conterrà i valori previsti tra testedInterval.startTime +
testedInterval.length
e testedInterval.startTime + testedInterval.length +
horizonTime
.
Regolazione del livello di sensibilità
In base ai limiti previsti e al valore effettivo durante l'intervallo testato per una sezione, possiamo classificarla oppure meno come un'anomalia in base ai parametri di sensibilità. Questi parametri sono:
ForecastParameters.maxPositiveRelativeChange
specifica di quanto possa aumentare il valore effettivo rispetto atestedIntervalForecastUpperBound
.ForecastParameters.maxNegativeRelativeChange
specifica in che modo il valore effettivo può diminuire rispetto atestedIntervalForecastLowerBound
ForecastParameters.forecastExtraWeight
viene utilizzato come peso aggiuntivo quando si confrontano i limiti effettivi e i limiti di previsione. Avere una ponderazione maggiore è utile se il tuo obiettivo è quello di ignorare le variazioni assoluti più piccole.
NOTA: abbiamo parametri diversi per i cambiamenti positivi e negativi, per consentire ai nostri clienti di avere soglie diverse per picchi e cali o per disattivare l'impostazione di una o l'altra alti maiuscole.
NOTA: il valore pratico massimo per maxNegativeRelativeChange
è 1,0 per
una serie temporale positiva (ad esempio, se viene conteggiato come numero di eventi il metodo di aggregazione) come valore
100% del valore effettivo.
Come base di riferimento, prova a impostare tutti questi parametri di sensibilità su 0:
{
dimensionNames: ["EntityLOCATION"],
testedInterval: {
startTime: "2019-04-15T00:00:00Z",
length: "86400s"
},
forecastParams: {
forecastHistory: "2592000s",
holdout: 10.0,
minDensity: 0.0,
maxPositiveRelativeChange: 0.0,
maxNegativeRelativeChange: 0.0,
forecastExtraWeight: 0.0
}
}
Questa è la query più sensibile che possiamo eseguire e segnalerà come anomalie
tutte le sezioni con testedIntervalActual
al di fuori dei limiti
[testedIntervalForecastLowerBound
, testedIntervalForecastUpperBound
].
Se, da un lato, questo approccio può sembrare simile (o utile in alcune applicazioni), in linea con le nostre priorità sarebbe eliminare la maggior parte di queste anomalie.
L'esecuzione di questa query di base comporterà in2500 anomalie:
$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query \
| grep "testedIntervalActual" | wc -l
2520
Se aumentiamo sia il valore maxPositiveRelativeChange
sia il valore maxNegativeRelativeChange
per 1, 0, noteremo che il numero di anomalie è ridotto a ~ 1800, il che equivale a eliminare sezioni con variazioni minori, ma siamo comunque un numero elevato di
categorie classificate come anomalie.
Supponendo che siamo interessati solo ai picchi più alti delle notizie di quel giorno, potremmo anche aumentare il forecastExtraWeight
, che filtrerà i settori con volumi ridotti. Impostiamo il valore su 200, 0 per fornirci il nostro
query.json
:
{
dimensionNames: ["EntityLOCATION"],
testedInterval: {
startTime: "2019-04-15T00:00:00Z",
length: "86400s"
},
forecastParams: {
forecastHistory: "2592000s",
holdout: 10.0,
minDensity: 0.0,
maxPositiveRelativeChange: 1.0,
maxNegativeRelativeChange: 1.0,
forecastExtraWeight: 200.0
}
}
L'esecuzione della query precedente produrrà solo tre anomalie, tutte associate all'incendio di Notre Dame, che era l'evento più citato nelle notizie del 15 aprile 2019:
$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query \
| grep stringVal
"stringVal": "Ile de la Cite"
"stringVal": "Notre Dame"
"stringVal": "Seine"
Prestazioni delle query e utilizzo delle risorse
Oltre a ridurre i rumori e filtrare le sezioni non interessanti, la riduzione del livello di rilevamento di anomalie può ridurre significativamente la latenza delle query e l'utilizzo delle risorse.
Con l'aumento di forecastExtraWeight
in genere si traduce in una replica più evidente in sezioni anomali, è anche il parametro di sensibilità principale da aumentare con l'obiettivo di migliorare le prestazioni delle query.
Puoi giocare con diversi parametri di sensibilità per vedere in che modo influiscono sul rendimento della query. Ad esempio:
$ cat query.json
{
dimensionNames: ["EntityLOCATION"],
testedInterval: {
startTime: "2019-04-15T00:00:00Z",
length: "86400s"
},
forecastParams: {
forecastHistory: "2592000s",
holdout: 10.0,
minDensity: 0.0,
maxPositiveRelativeChange: 0.0,
maxNegativeRelativeChange: 0.0,
forecastExtraWeight: 0.0
}
}
$ time gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query \
| grep stringVal | wc -l
2580
real 0m26.765s
user 0m0.618s
sys 0m0.132s
Possiamo notare che i parametri più sensibili hanno portato alla nostra query ~ 26,7. Se riduci la sensibilità, viene aumentata la velocità massima di ~ 3,4:
$ cat query.json
{
dimensionNames: ["EntityLOCATION"],
testedInterval: {
startTime: "2019-04-15T00:00:00Z",
length: "86400s"
},
forecastParams: {
forecastHistory: "2592000s",
holdout: 10.0,
minDensity: 0.0,
maxPositiveRelativeChange: 1.0,
maxNegativeRelativeChange: 1.0,
forecastExtraWeight: 200.0
}
}
$ time gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query \
| grep stringVal | wc -l
3
real 0m3.412s
user 0m0.681s
sys 0m0.047s