Leitfaden zur Abfrageerstellung

Übersicht

In diesem Abschnitt wird beschrieben, wie Sie Ihre Abfragen zur Anomalieerkennung ordnungsgemäß konfigurieren. Im Allgemeinen müssen Sie vier verschiedene Parametertypen abstimmen:

  • Datenauswahlparameter, die angeben, welche Daten in unsere Analyse aufgenommen werden. Beispiele: QueryDataSetRequest.dimensionNames, QueryDataSetRequest.pinnedDimensions und QueryDataSetRequest.testedInterval.
  • Zusammenfassungsparameter, die angeben, wie mehrere Ereignisse gruppiert werden, um einen Wert zu generieren, der verwendet wird, um Slices zu vergleichen. Beispiele: ForecastParams.aggregatedDimension.
  • Prognoseparameter, die zum Konfigurieren der für die Berechnung der erwarteten Werte verwendeten Prognosealgorithmen verwendet werden. Beispiele: ForecastParams.holdout, ForecastParams.forecastHistory und ForecastParams.minDensity.
  • Sensibilisierung von Parametern, die bei Änderung die Anzahl der gemeldeten Anomalien, die Latenz und die Rechenressourcen erhöhen oder verringern können. Beispiele: ForecastParams.maxPositiveRelativeChange, ForecastParams.maxNegativeRelativeChange und ForecastParams.forecastExtraWeight.

Vorbereitung

Folgen Sie der Einrichtungsanleitung in unserer Kurzanleitung, damit Sie alle Befehle in dieser Anleitung ausführen können.

Wir zeigen Ihnen, wie sich jeder Parameter auf die zurückgegebenen Ergebnisse auswirkt. Dazu fragen Sie das öffentliche Demo-Dataset ab, das bereits mit Daten aus dem GDELT-Projekt vorab geladen wurde.

Speichern Sie die folgende Abfrage als query.json in Ihrem Arbeitsverzeichnis:

{
  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
  }
}

Wie in der Kurzanleitung beschrieben, können Sie eine Abfrage mit dem folgenden Befehl ausführen:

$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/timeseries-insights-api-demo/datasets/webnlp-201901-202104:query

In diesem Leitfaden ändern wir die verschiedenen Parameter in query.json, um zu zeigen, wie sich die Ausgabe auf die Ausgabe auswirkt. Sie können die Datei bearbeiten und mithilfe des obigen gcurl-Befehls die Abfragen bearbeiten und die Abfragen abrufen. Diese werden in der Kurzanleitung als Offensive entfernt.

Datenauswahl

Eine Abfrage zur Anomalieerkennung prüft, ob in einem bestimmten Datensatz Anomalien innerhalb eines bestimmten Zeitintervalls sind. Dabei werden die Daten in einigen Dimensionen unterteilt und optional einige Werte für einige Dimensionen gefiltert. In diesem Abschnitt wird beschrieben, wie Sie diese Schritte zur Datenauswahl steuern.

Zeitintervall-Spezifikation

Mit QueryDataSetRequest.tested_interval können Sie festlegen, welches Zeitintervall auf Anomalien geprüft werden soll. In query.json geben wir an, dass wir Anomalien erkennen möchten, die am Tag des 15. April 2019 aufgetreten sind:

...
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
...

Wenn wir beispielsweise Anomalien feststellen möchten, die am 15. April 2019 zwischen 13 Uhr und 14:00 Uhr aufgetreten sind, legen wir stattdessen testedInterval auf:

...
  testedInterval: {
    startTime: "2019-04-15T13:00:00Z",
    length: "3600s"
  },
...

TIPP: Spielen Sie mit dem Wert testedInterval, indem Sie unterschiedliche Startzeiten und unterschiedliche Längen festlegen und herausfinden, wie die zurückgegebenen Anomalien variieren.

Datenschnitt

Bei der Erkennung von Anomalien innerhalb eines bestimmten Zeitintervalls müssen wir auch angeben, wie die Ereignisse in Datensegmente gruppiert werden. Setzen Sie dazu dimensionNames auf die Dimensionen, die Sie im Dataset aufteilen möchten.

In query.json werden die Daten nur nach der Dimension "EntityLOCATION" unterteilt: dimensionNames: ["EntityLOCATION"]. Sie sehen, dass alle Segmente in der Ergebnisliste einfach unterschiedliche Werte für die Dimension "EntityLocation" sind.

Wenn wir die Daten stattdessen über mehrere Dimensionen aufteilen möchten, können wir mehrere Dimensionsnamen angeben:

{
  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
  }
}

Beim Ausführen dieser Abfrage stellen wir fest, dass die resultierenden Segmente Werte über die beiden angegebenen Dimensionen "EntityLOCATION" und "EntityORGANIZATION" übernehmen. (Es wird nur das erste Anomaliesegment angezeigt, das zurückgegeben wird):

$ 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": {}
      },
...

Filtern

Wenn Sie nur eine Teilmenge des Datasets analysieren möchten, können Sie nur nach Ereignissen filtern, die bestimmte Werte für bestimmte Dimensionen enthalten, indem Sie pinnedDimensions festlegen.

Beispiel für das Filtern nach EntityORGANIZATION und das Segmentieren nach EntityLOCATION (nur das erste Ergebnis anzeigen):

$ 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": {}
      },
...

Aggregation

Die Standardaggregationsmethode zählt die Anzahl der Ereignisse im Segment.

Sie können auch angeben, dass Ereignisse aggregiert werden sollen, indem Sie eine numerische Dimension summieren. Das wird dadurch angegeben, dass für ForecastParams.aggregatedDimension die numerische Dimension festgelegt wird, die Sie summieren möchten.

Prognosekonfiguration

Zur Berechnung des erwarteten Werts während des getesteten Zeitintervalls verwenden wir Prognosealgorithmen, die anhand der historischen Werte für das Segment vorhersagen, welchen Wert der Wert für das Testintervall haben sollte.

HINWEIS: Die Werte für ein Segment sind durch die Aggregationsmethode angegeben.

Prognoseverlauf

Die Menge der in unserer Zeitachse enthaltenen Daten wird durch den Parameter ForecastParams.forecastHistory angegeben.

Im ersten Beispiel gilt:

  • Wir haben forecastHistory: "2592000s" festgelegt, um anzugeben, dass Ereignisse zwischen dem 16. März 2019 und dem 15. April 2019 (30 Tage, 259.000 Sekunden) abgerufen werden sollen und eine Zeitachse zwischen diesen beiden Tagen erstellt wird.
  • Jeder Datenpunkt in der Zeitachse deckt einen Zeitraum ab, der der Länge des getesteten Intervalls entspricht (in unserem Beispiel 86400s – 1 Tag).
  • Die Zeitachse hat 30 Punkte und jeder Punkt enthält den aggregierten Wert für die Ereignisse an diesem Kalendertag. Wenn wir als Aggregationsmethode zählen, hat der erste Zeitachsenwert den Wert der Gesamtzahl der Ereignisse am 16. März 2019, den zweiten vom 17. März 2019 usw.

Sie können die Verlaufswerte für Zeitachsen aufrufen, die im Feld ForecastResult.history zurückgegeben werden. Sie wird nur zurückgegeben, wenn in der Anfrage QueryDataSetRequest.returnTimeseries auf true festgelegt ist.

HINWEIS: Wir erstellen eine Zeitachse im Timeseries-Proto-Objekt als eine Sammlung von Punkten (definiert als TimeseriesPoint), die nach Zeit sortiert sind und einen Wert (TimeseriesPoint.value) der aggregierte Wert für das Segment während des Zeitintervalls [TimeseriesPoint.time, TimeseriesPoint.time + testedInterval.length].

Wenn Sie ForecastParams.forecastHistory erhöhen und einen längeren Verlauf aufnehmen, können Sie bestimmte saisonale Muster erfassen und möglicherweise die Genauigkeit der Prognose erhöhen, da sie mehr Daten enthält. Beispiel: Wenn monatliche Muster für die Saisonabhängigkeit von forecastHistory auf 30 Tage festgelegt sind, können diese Muster nicht erfasst werden. In diesem Fall sollten wir den forecastHistory. Wir haben also mehrere monatliche Muster in unseren analysierten Zeitachsen (z. B. wenn monatliche Muster von 100 bis 300 Tagen erkannt werden sollten), hängt aber auch von den tatsächlichen Mustern ab.

Ausgehend vom ersten Beispiel sollten Sie testedInterval.length auf 3600s (1 Stunde) reduzieren und ForecastParams.forecastHistory auf 8640000s (100 Tage) erhöhen. Hierdurch wird die Anzahl der Punkte in der Zeitachse history auf 2.400 erhöht, sodass der Vorgang präziser ist und ein längerer Zeitraum abgedeckt wird.

Holdout/Testintervall

Zur Bewertung der Qualität unserer Vorhersage trainieren wir nur die ersten X% der Zeitachse. Der letzte Teil der Zeitachse wird zu Bewertungszwecken gespeichert und vom Parameter ForecastParams.holdout gesteuert.

Der Parameter ForecastParams.holdout steht für einen Prozentsatz im Bereich von 0 bis 100 und gibt an, wie viele Daten für Testzwecke aufbewahrt werden sollen. In unserem Beispiel haben wir holdout: 10.0 angegeben. Die letzten 10% der Zeitachse werden also beibehalten, um zu beurteilen, wie gut die Vorhersage basierend auf den ersten 90% funktioniert.

Wenn wir in unserem ersten Beispiel 30 Zeitachsenpunkte haben, bedeutet dies, dass die ersten 27 Punkte für das Training und die letzten 3 Punkte für das Testen/Auswerten des Modells erhalten bleiben.

Die Fehler, die wir während des Hold-Zeitraums messen, werden in ForecastResult.holdoutErrors zurückgegeben. Diese Fehler werden zur Berechnung der unteren und oberen Grenzen (testedIntervalForecastLowerBound und testedIntervalForecastUpperBound) verwendet. Höhere Holdout-Fehler führen zu einer stärkeren Prognosebindung und bei niedrigeren Fehlern liegen uns engere Grenzen vor (und gegen ForecastResult.testedIntervalForecast).

Der Holdout-Wert sollte sich in den niedrigen Prozentpunkten (3 bis 10 %) befinden. Er sollte jedoch genügend Datenpunkte enthalten, da diese Fehler für die Berechnung der Prognosegrenzen erforderlich sind.

Minimale Dichte

Die Mindestdichte, die durch den Parameter ForecastParams.minDensity angegeben wird, gibt an, wie viele nicht leere Zeitachsen mit einer Zeitachse vorhanden sein müssen, um als potenzielle Anomalie betrachtet zu werden.

Ähnlich wie beim Holdout handelt es sich um einen Prozentwert im Bereich von 0 bis 100.

Die Anzahl der Punkte wird mit der Anzahl der erwarteten Punkte zwischen testedInterval.startTime - forecastHistory und testedInterlal.startTime verglichen.

Beispiel: Wenn wir 30 erwartete Zeitachsenpunkte haben (wie im ersten Beispiel, in dem testedInterval.length auf 1 Tag und forecastHistory auf 30 Tage festgelegt wird) und legen wir minDensity fest. 80 an, dann akzeptieren wir nur Zeitachsen, die 24 Punkte und mindestens ein Ereignis enthalten.

HINWEIS: In unserem Beispiel wurde minDensity auf 0 gesetzt. Wir empfehlen, beim Erkennen von Spitzen auch auf 0 gesetzt zu werden.

Horizont

Der durch ForecastParams.horizonTime angegebene Zeithorizont gibt einfach an, wie viel in die Zukunft vom Testintervall aus vorhergesagt werden soll, um die Werte basierend auf der historischen Zeitachse vorherzusagen.

Wenn QueryDataSetRequest.returnTimeseries auf "true" gesetzt wird, werden die prognostizierten Zeitachsen für jedes Segment in ForecastResult.forecast zurückgegeben und enthält die vorhergesagten Werte zwischen testedInterval.startTime + testedInterval.length und testedInterval.startTime + testedInterval.length + horizonTime.

Empfindlichkeit

Basierend auf den prognostizierten Grenzen und dem tatsächlichen Wert während des getesteten Intervalls für ein Segment werden wir es möglicherweise als Anomalie basierend auf den Empfindlichkeitsparameter klassifizieren. Diese Parameter sind:

  • ForecastParameters.maxPositiveRelativeChange gibt an, wie viel der tatsächliche Wert im Vergleich zu testedIntervalForecastUpperBound erhöht werden kann.
  • ForecastParameters.maxNegativeRelativeChange gibt an, wie stark der tatsächliche Wert im Vergleich zu testedIntervalForecastLowerBound verringert werden kann.
  • ForecastParameters.forecastExtraWeight wird als zusätzliche Gewichtung beim Vergleich der tatsächlichen und der Prognosegrenzen verwendet. Ein höheres Gewicht kann hilfreich sein, wenn kleinere kleinere Varianzen ignoriert werden sollen.

HINWEIS Wir haben verschiedene Parameter für positive und negative Änderungen, weil wir unseren Kunden die Möglichkeit geben möchten, unterschiedliche Schwellenwerte für Lastspitzen und -senkungen festzulegen oder eine der anderen zu deaktivieren, indem sie sehr hohe Höchstwerte festlegen.

HINWEIS: Der maximale praktische Wert für maxNegativeRelativeChange beträgt 1,0 für eine positive Zeitachse (z. B. wenn die Anzahl der Ereignisse als Aggregationsmethode gezählt wird), da ein Wert nicht mehr als 100% des tatsächlichen Werts

Als Basis versuchen Sie, alle diese Empfindlichkeitsparameter auf 0 zu setzen:

{
  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
  }
}

Dies ist die sensibelste Abfrage, die wir ausführen können, und markiert alle Anomalien, die testedIntervalActual außerhalb der Grenzen [testedIntervalForecastLowerBound, testedIntervalForecastUpperBound] haben. Dies mag auf den ersten Blick scheinen, was wir wollen (und in einigen Anwendungen nützlich sein könnten), doch werden wir die meisten Anomalien ignorieren, da dies zu falsch positiven Ergebnissen führt.

Die Ausführung dieser Baseline führt zu ca. 2.500 Anomalien.

$ 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

Wenn wir sowohl maxPositiveRelativeChange als auch maxNegativeRelativeChange auf 1,0 erhöhen, stellen wir fest, dass die Anzahl der Anomalien auf etwa 1.800 reduziert wird. Dies ist jedoch nicht für Segmente mit kleineren Abweichungen vorgesehen, aber dennoch eine große Anzahl von Segmenten klassifiziert ist, die als Anomalien eingestuft werden.

Wenn wir nur an den größten Spitzen in den Nachrichten an diesem Tag interessiert sind, könnten wir auch forecastExtraWeight erhöhen. Dadurch werden Ereignisse mit geringem Volumen herausgefiltert. Legen wir dafür den Wert 200.0 fest, das den endgültigen query.json erhält.

{
  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
  }
}

Bei der Ausführung der vorherigen Abfrage werden nur drei Anomalien zurückgegeben, die alle mit dem Feuer von Noter Dame verknüpft sind. Dies war das am häufigsten genannte Ereignis in den Nachrichten am 15. April 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"

Abfrageleistung und Ressourcennutzung

Neben der Reduzierung der Geräusche und dem Herausfiltern von weniger relevanten Segmenten kann die Reduzierung der Empfindlichkeit bei der Anomalieerkennung auch die Abfragelatenz und die Ressourcennutzung erheblich reduzieren.

Eine Erhöhung der forecastExtraWeight führt in der Regel zu einer spürbaren Reduzierung der anormalen Slice-Ereignisse. Dieser Parameter ist außerdem der wichtigste Empfindlichkeitsparameter, der erhöht werden sollte, um die Abfrageleistung zu verbessern.

Sie können mit verschiedenen Empfindlichkeitsparametern experimentieren, um zu sehen, wie sie sich auf die Leistung der Abfrage auswirken. Beispiel:

$ 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

Wir stellen fest, dass die stärksten Parameter dazu führen, dass unsere Abfrage ~26.7s dauert. Durch die Reduzierung der Empfindlichkeit erhöht sich der Geschwindigkeitswert um bis zu 3,4 Sekunden:

$ 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

Nächste Schritte