Guida alla creazione di query

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 e QueryDataSetRequest.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 e ForecastParams.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 e ForecastParams.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 a testedIntervalForecastUpperBound.
  • ForecastParameters.maxNegativeRelativeChange specifica in che modo il valore effettivo può diminuire rispetto a testedIntervalForecastLowerBound
  • 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

Passaggi successivi