Guía de compilación de consultas

Descripción general

En esta sección, describiremos cómo configurar adecuadamente tus consultas de detección de anomalías. En un nivel alto, hay cuatro tipos de parámetros que debes ajustar:

  • Parámetros de selección de datos que especifican qué datos se incluyen en nuestro análisis. Ejemplos: QueryDataSetRequest.dimensionNames, QueryDataSetRequest.pinnedDimensions y QueryDataSetRequest.testedInterval.
  • Los parámetros de agregación que especifican cómo se agrupan varios eventos a fin de producir un valor que se usa para comparar segmentos. Ejemplos: ForecastParams.aggregatedDimension.
  • Parámetros de previsión que se usan para configurar los algoritmos de previsión usados para calcular los valores esperados. Ejemplos: ForecastParams.holdout, ForecastParams.forecastHistory y ForecastParams.minDensity.
  • Ajuste de sensibilidad, que cuando se cambia, puede aumentar o disminuir la cantidad de anomalías que se informan, la latencia y los recursos de procesamiento. Ejemplos: ForecastParams.maxPositiveRelativeChange, ForecastParams.maxNegativeRelativeChange y ForecastParams.forecastExtraWeight.

Requisitos previos

Sigue las instrucciones de configuración en nuestra guía de inicio rápido para asegurarte de que puedas ejecutar todos los comandos de esta guía.

Te mostraremos cómo el cambio de cada parámetro afecta los resultados que se muestran. Para ello, consultamos el conjunto de datos de demostración público que ya precargamos con datos del proyecto GDELT.

Guarda la siguiente consulta como query.json en tu directorio de trabajo:

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

Como se mencionó en la Guía de inicio rápido, puedes emitir una consulta con el siguiente comando:

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

A lo largo de esta guía, cambiaremos diferentes parámetros en query.json para mostrar cómo impacta el resultado. Para seguir el proceso, edita tu copia y vuelve a enviar las consultas con el comando gcurl que se encuentra en la guía de inicio rápido de Guía de inicio rápido.

Selección de datos

Una consulta de detección de anomalías se evalúa si hay anomalías en un conjunto de datos determinado, en un determinado intervalo de tiempo, si se filtran los datos en algunas dimensiones y, de forma opcional, filtra algunos valores para algunas dimensiones. En esta sección, detallaremos cómo controlar todos estos pasos de selección de datos.

Especificación de intervalo de tiempo

Para especificar qué intervalo de tiempo queremos analizar en busca de anomalías, configura QueryDataSetRequest.tested_interval. En query.json, especificamos que queremos detectar anomalías que se produjeron durante el día del 15 de abril de 2019:

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

Por ejemplo, si queremos detectar anomalías que ocurrieron el 15 de abril de 2019 entre las 1:00 p.m. y las 2:00 p.m., configuraremos la testedInterval como se muestra a continuación:

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

SUGERENCIA: Configura con el valor testedInterval mediante la configuración de diferentes horas de inicio y distintas longitudes, y observa cómo varían las anomalías mostradas.

Controles de filtro

Cuando se detectan anomalías en un intervalo de tiempo determinado, también debemos especificar cómo se agrupan los eventos en fragmentos de datos. Para ello, se establece dimensionNames en el conjunto de dimensiones que queremos segmentar en el conjunto de datos.

En query.json, solo se extraerán los datos por la dimensión "EntityLOCATION": dimensionNames: ["EntityLOCATION"]. Puedes observar en la lista de resultados que todas las porciones son simplemente valores diferentes para la dimensión "EntityLocation".

Si, en cambio, queremos dividir los datos en varias dimensiones, podemos especificar nombres de dimensiones múltiples:

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

Cuando ejecutes esta consulta, notarámos que las porciones resultantes toman valores en las dos dimensiones especificadas, “EntityLOCATION” y “EntityORGANIZATION” (solo se muestra la primera porción de anomalía que se muestra):

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

Filtros

Si te interesa analizar solo un subconjunto del conjunto de datos, podemos filtrar solo los eventos que tengan valores determinados en algunas dimensiones determinadas mediante la configuración de pinnedDimensions.

Ejemplo para filtrar por EntityORGANIZATION y dividir por EntityLOCATION (solo muestra el primer resultado):

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

Agregación

El método de agregación predeterminado cuenta el número de eventos en la porción.

También podemos especificar que queremos agregar eventos mediante la suma de una dimensión numérica. Esto se especifica mediante la configuración de ForecastParams.aggregatedDimension en la dimensión numérica que queremos sumar.

Configuración de previsión

Para calcular el valor esperado durante el intervalo de tiempo probado, utilizamos algoritmos de previsión, que, según los valores históricos de la porción, predice qué valor debe ser durante el intervalo probado.

NOTA: El método de agregación proporciona los valores de un segmento.

Historial de previsión

El número ForecastParams.forecastHistory de datos que contiene nuestra serie temporal

En nuestro ejemplo inicial:

  • Establecemos forecastHistory: "2592000s", por lo que nos indica que debemos recuperar los eventos entre el 16 de marzo de 2019 y el 15 de abril de 2019 (30 días, que es 2592 segundos) y forman una serie temporal entre estos dos días.
  • Cada dato en la serie temporal abarca un período de tiempo igual a la duración del intervalo probado (por ejemplo, 86400s - 1 día en nuestro ejemplo).
  • La serie temporal tendrá 30 puntos, cada uno con el valor agregado de los eventos de ese día calendario. Si se tiene en cuenta el método de agregación, el primer punto de serie temporal tendrá el valor total de eventos el 16 de marzo de 2019, el segundo punto del 17 de marzo de 2019, etcétera.

Puedes ver los valores históricos de las series temporales que se muestran en el campo ForecastResult.history (solo se mostrará si QueryDataSetRequest.returnTimeseries está configurado como true en la solicitud).

NOTA: Modelamos una serie temporal en el proto Timeseries como una colección de puntos (definido como TimeseriesPoint), ordenado por tiempo y con valor (TimeseriesPoint.value) el valor agregado de la porción durante el intervalo de tiempo [TimeseriesPoint.time, TimeseriesPoint.time + testedInterval.length].

Si aumentas el ForecastParams.forecastHistory y se incluye un historial más largo, podrás capturar ciertos patrones de estacionalidad y, posiblemente, aumentar la exactitud de la previsión, ya que contendrá más datos. Por ejemplo, si tenemos patrones de estacionalidad mensuales, la forecastHistory configurada en 30 días no nos permitirá capturar esos patrones, por lo que, en este caso, debemos aumentar el forecastHistory por lo que tenemos varios patrones mensuales en nuestra serie temporal analizada (p.ej., para que los patrones mensuales se detecten 100-300 días deberían ser suficientes, pero también depende de los patrones reales).

En nuestro ejemplo inicial, intenta reducir el valor de testedInterval.length a 3600s (1 hora) y aumentar ForecastParams.forecastHistory a 8640000s (100 días). Esto aumentará la cantidad de puntos de la serie temporal history a 2400, lo que lo hará más detallado y cubrirá un período más largo.

Intervalo de espera/prueba

Para evaluar la calidad de nuestra predicción, solo entrenamos en el primer X% de la serie temporal. La última parte de la serie temporal se conserva con fines de evaluación y se controla con el parámetro ForecastParams.holdout.

El parámetro ForecastParams.holdout representa un porcentaje (en el rango de 0 a 100) y nos indica cuántos datos deberíamos mantener para la prueba. En nuestro ejemplo, especificamos holdout: 10.0, por lo que conservamos el último 10% de las series temporales para evaluar el rendimiento de la predicción según el primer 90%.

Si tenemos 30 puntos de serie temporales en nuestro ejemplo inicial, esto implica que los primeros 27 puntos se conservan para el entrenamiento y los últimos 3 para la prueba y evaluación del modelo.

Los errores que medimos durante el período de exclusión se muestran en ForecastResult.holdoutErrors y esos errores se usan para calcular los límites inferiores y superiores de la previsión (testedIntervalForecastLowerBound y testedIntervalForecastUpperBound). Los errores de aumento más altos nos darán un límite más amplio de previsión, mientras que los errores más bajos nos otorgan límites más estrictos (y más cerca de ForecastResult.testedIntervalForecast).

El valor de exclusión debe estar en los puntos de porcentaje bajos (3%-10%), pero debe garantizar que contenga suficientes datos, ya que estos errores son necesarios para el cálculo de los límites de previsión.

Densidad mínima

La densidad mínima, proporcionada por el parámetro ForecastParams.minDensity, especifica cuántos puntos no vacíos (que contienen al menos un evento) de una serie temporal deben contener una serie temporal para que se considere una posible anomalía.

Al igual que el modo de exclusión, es un valor porcentual en el rango de 0 a 100.

La cantidad de puntos se compara con la cantidad de puntos esperados entre testedInterval.startTime - forecastHistory y testedInterlal.startTime.

Por ejemplo, si tenemos 30 puntos de serie temporales esperados (como tenemos en nuestro primer ejemplo que configura testedInterval.length como 1 día y forecastHistory a 30 días) y configuramos minDensity y solo aceptaremos series temporales que contengan 24 puntos con al menos un evento.

NOTA: En nuestro ejemplo, configuramos minDensity como 0 y sugerimos que, cuando se detecte picos, también se establece en 0.

Horizonte

El horizonte temporal, especificado por ForecastParams.horizonTime, especifica cuánto pasará en el futuro, desde el intervalo probado, debemos predecir los valores en función de la serie temporal histórica.

Si QueryDataSetRequest.returnTimeseries se establece como verdadero, la serie temporal prevista se mostrará en ForecastResult.forecast para cada porción y contendrá los valores previstos entre testedInterval.startTime + testedInterval.length y testedInterval.startTime + testedInterval.length + horizonTime.

Ajuste de sensibilidad

En función de los límites previstos y del valor real durante el intervalo probado para una porción, podemos clasificarla o no como una anomalía basada en los parámetros de sensibilidad. Estos parámetros son los siguientes:

  • ForecastParameters.maxPositiveRelativeChange especifica cuánto puede aumentar el valor real en comparación con el testedIntervalForecastUpperBound.
  • ForecastParameters.maxNegativeRelativeChange especifica cuánto puede disminuir el valor real en comparación con el testedIntervalForecastLowerBound.
  • ForecastParameters.forecastExtraWeight se usa como un peso adicional cuando se comparan los límites reales con los de la previsión. Un peso más alto es útil si te interesa ignorar variantes absolutas más pequeñas.

NOTA: Tenemos diferentes parámetros para los cambios positivos y negativos, ya que queremos que nuestros clientes tengan la flexibilidad de tener límites diferentes de picos y caídas, o inhabilitar uno o el otro mediante la configuración. mayúsculas altas.

NOTA: El valor práctico máximo para maxNegativeRelativeChange es 1.0 para la serie temporal positiva (p. ej., cuando se cuenta la cantidad de eventos como el método de agregación) como un valor no puede disminuir más de El 100% de su valor real

Como punto de referencia, configura todos estos parámetros de sensibilidad en 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
  }
}

Esta es la consulta más sensible que podemos ejecutar y marcará como anomalías todas las secciones que tengan testedIntervalActual fuera de los límites [testedIntervalForecastLowerBound, testedIntervalForecastUpperBound]. Si bien esto puede parecerse a lo que deseamos (y puede ser útil en algunas aplicaciones), en la práctica nos interesaría ignorar la mayoría de estas anomalías, ya que dará como resultado falsos positivos.

Ejecutar esta consulta de referencia generará aproximadamente 2,500 anomalías:

$ 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

Si aumentamos maxPositiveRelativeChange y maxNegativeRelativeChange a 1.0, notarámos que la cantidad de anomalías se reduce a ~1800, lo que se elimina de segmentos con variantes más pequeñas. Sin embargo, seguimos un número alto de partes clasificadas como anomalías.

Si suponemos que solo nos encantaría que se produzcan aumentos repentinos en las noticias durante ese día, también podríamos aumentar el nivel forecastExtraWeight, que filtrará los fragmentos de bajo volumen. Establezcamos esto en 200.0, lo que nos proporcionará nuestra query.json final:

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

La ejecución de la consulta anterior generará solo 3 anomalías, todas asociadas con el incendio de Notre Dame, que fue el evento más mencionado en las noticias el 15 de abril de 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"

Rendimiento de las consultas y uso de recursos

Además de reducir el ruido y filtrar segmentos no interesantes, la reducción de la sensibilidad de detección de anomalías también puede reducir de manera significativa la latencia de consulta y el uso de recursos.

Por lo general, cuando se aumenta el forecastExtraWeight, se produce la reducción más notable en segmentos anómalos, también es el parámetro principal de sensibilidad que se debe aumentar con el objetivo de mejorar el rendimiento de las consultas.

Puedes jugar con diferentes parámetros de sensibilidad para ver cómo afectan el rendimiento de la consulta. Por ejemplo:

$ 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

Observamos que los parámetros más sensibles llevan a nuestra consulta que toma ~26 s.7 s. Reducir la sensibilidad proporciona un aumento de velocidad de hasta 3.4 s:

$ 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

¿Qué sigue?

  • Conceptos de la API de estadísticas de serie temporal
  • Obtén más información sobre la API de REST