Tutorial: Detección de anomalías en ventas de productos con ML.NET
Aprenda a crear una aplicación de detección de anomalías para los datos de ventas de productos. En este tutorial se va a crear una aplicación de consola de .NET Core mediante C# en Visual Studio.
En este tutorial aprenderá a:
- Carga de los datos
- Crear una transformación para la detección de anomalías de picos
- Detectar anomalías de picos con la transformación
- Crear una transformación para la detección de anomalías de puntos de cambio
- Detectar anomalías de puntos de cambio con la transformación
Puede encontrar el código fuente para este tutorial en el repositorio dotnet/samples.
Requisitos previos
Visual Studio 2022 con la carga de trabajo "Desarrollo de escritorio de .NET" instalada.
Nota
El formato de datos en product-sales.csv
se basa en el conjunto de datos "Shampoo Sales Over a Three Year Period" (Ventas de champú en un período de tres años) procedente originalmente de DataMarket y proporcionado por Time Series Data Library (TSDL), creado por Rob Hyndman.
Conjunto de datos "Shampoo Sales Over a Three Year Period" con una licencia de conjunto de datos que se concede de acuerdo con DataMarket Default Open License.
Creación de una aplicación de consola
Cree una aplicación de consola en C# llamada "ProductSalesAnomalyDetection". Haga clic en el botón Next (Siguiente).
Seleccione .NET 6 como marco de trabajo que va a usarse. Haga clic en el botón Crear.
Cree un directorio denominado Datos en el proyecto para guardar los archivos del conjunto de datos.
Instale el paquete NuGet Microsoft.ML:
Nota
En este ejemplo se usa la versión estable más reciente de los paquetes NuGet mencionados, a menos que se indique lo contrario.
En el Explorador de soluciones, haga clic con el botón derecho en Administrar paquetes NuGet. Elija "nuget.org" como origen del paquete, seleccione la pestaña Examinar, busque Microsoft.ML y seleccione el botón Instalar. Seleccione el botón Aceptar en el cuadro de diálogo Vista previa de cambios y, a continuación, seleccione el botón Acepto del cuadro de diálogo Aceptación de la licencia en caso de que esté de acuerdo con los términos de licencia de los paquetes mostrados. Repita estos pasos para Microsoft.ML.TimeSeries.
Agregue las instrucciones
using
siguientes en la parte superior del archivo Program.cs:using Microsoft.ML; using ProductSalesAnomalyDetection;
Descarga de los datos
Descargue el conjunto de datos y guárdelo en la carpeta Datos que creó anteriormente:
Haga clic con el botón derecho en product-sales.csv y seleccione "Guardar vínculo (o destino) como...".
Asegúrese de que guarda el archivo *.csv en la carpeta Datos o de que, después de guardarlo en otro lugar, lo mueve a la carpeta Datos.
En el Explorador de soluciones, haga clic con el botón derecho en el archivo *.csv y seleccione Propiedades. En Avanzadas, cambie el valor de Copiar en el directorio de salida por Copiar si es posterior.
La siguiente tabla es una vista previa de datos del archivo *.csv:
Mes | ProductSales |
---|---|
1 - Ene | 271 |
2 - Ene | 150,9 |
..... | ..... |
1 - Feb | 199,3 |
..... | ..... |
Crear clases y definir rutas de acceso
A continuación, defina las estructuras de datos de la clase de entrada y de predicción.
Agregue una nueva clase a su proyecto:
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar > Nuevo elemento.
En el cuadro de diálogo Agregar nuevo elemento, seleccione Clase y cambie el campo Nombre a ProductSalesData.cs. A continuación, seleccione el botón Agregar.
El archivo ProductSalesData.cs se abre en el editor de código.
Agregue la siguiente instrucción
using
al principio de ProductSalesData.cs:using Microsoft.ML.Data;
Quite la definición de clase existente y agregue el código siguiente, que tiene dos clases,
ProductSalesData
yProductSalesPrediction
, al archivo ProductSalesData.cs:public class ProductSalesData { [LoadColumn(0)] public string? Month; [LoadColumn(1)] public float numSales; } public class ProductSalesPrediction { //vector to hold alert,score,p-value values [VectorType(3)] public double[]? Prediction { get; set; } }
ProductSalesData
especifica una clase de datos de entrada. El atributo LoadColumn especifica qué columnas (por índice de columna) del conjunto de datos se deben cargar.ProductSalesPrediction
especifica la clase de datos de predicción. En la detección de anomalías, la predicción consiste en una alerta que indica si hay una anomalía, una puntuación sin procesar y un valor p. Cuanto más se acerque el valor p a 0, más probable es que se haya producido una anomalía.Cree dos campos globales para contener la ruta de acceso del archivo de conjunto de datos recientemente descargado y la ruta de acceso del archivo de modelo guardado:
_dataPath
tiene la ruta de acceso al conjunto de datos utilizado para entrenar el modelo._docsize
tiene el número de registros en el archivo de conjunto de datos. Usará_docSize
para calcularpvalueHistoryLength
.
Agregue el código siguiente a la línea justo debajo de las instrucciones using para especificar esas rutas de acceso:
string _dataPath = Path.Combine(Environment.CurrentDirectory, "Data", "product-sales.csv"); //assign the Number of records in dataset file to constant variable const int _docsize = 36;
Inicialización de variables
Reemplace la línea
Console.WriteLine("Hello World!")
por el siguiente código para declarar e inicializar la variablemlContext
:MLContext mlContext = new MLContext();
La clase MLContext es un punto de partida para todas las operaciones de ML.NET. Al inicializar
mlContext
, se crea un entorno de ML.NET que se puede compartir entre los objetos del flujo de trabajo de creación de modelos. Como concepto, se parece aDBContext
en Entity Framework.
Carga de los datos
Los datos de ML.NET se representan como una interfaz IDataView. IDataView
es una manera flexible y eficiente de describir datos tabulares (numéricos y de texto). Los datos se pueden cargar desde un archivo de texto o u otros orígenes (por ejemplo, archivos de registro o base de datos SQL) en un objeto IDataView
.
Agregue el código siguiente después de crear la variable
mlContext
:IDataView dataView = mlContext.Data.LoadFromTextFile<ProductSalesData>(path: _dataPath, hasHeader: true, separatorChar: ',');
LoadFromTextFile() define el esquema de datos y lee en el archivo. Toma las variables de ruta de acceso de datos y devuelve
IDataView
.
Detección de anomalías en una serie temporal
La detección de anomalías marca comportamientos o eventos inesperados o poco habituales. Proporciona pistas sobre dónde buscar problemas y ayuda a responder la pregunta "¿es extraño esto?".
La detección de anomalías es el proceso de detectar valores atípicos de datos de series temporales, puntos en determinada serie temporal de entrada donde el comportamiento no es lo que se esperaba o es "extraño".
La detección de anomalías puede ser útil de muchas maneras. Por ejemplo:
Si tiene un automóvil, tal vez desee saber: ¿Es normal la lectura del medidor de gasolina o tengo una fuga? Si está supervisando el consumo de energía, desearía saber: ¿Hay una interrupción?
Hay dos tipos de anomalías de series temporales que se pueden detectar:
Los picos indican ráfagas temporales de comportamiento anómalo en el sistema.
Los puntos de cambio indican el inicio de cambios persistentes con el tiempo en el sistema.
En ML.NET, los algoritmos de la detección de picos de IID o de la detección de puntos de cambio de IID son adecuados para los conjuntos de datos independientes y distribuidos de manera idéntica. Se supone que los datos de entrada son una secuencia de puntos de datos que se muestrean independientemente de una distribución fija.
A diferencia de los modelos de los otros tutoriales, las transformaciones del detector de anomalías de series temporales funcionan directamente en los datos de entrada. El método IEstimator.Fit()
no necesita datos de entrenamiento para generar la transformación. Sin embargo, necesita el esquema de datos, que se proporciona mediante una vista de datos generada a partir de una lista vacía de ProductSalesData
.
Analizará los mismos datos de ventas de productos para detectar los picos y los puntos de cambio. El proceso de modelo de entrenamiento y compilación es el mismo para la detección de picos y la detección de puntos de cambio. La principal diferencia es el algoritmo de detección específico que se utiliza.
Detección de picos
El objetivo de la detección de picos es identificar ráfagas repentinas pero temporales que difieren significativamente de la mayoría de los valores de datos de las series temporales. Es importante detectar estos elementos, eventos u observaciones poco frecuentes y sospechosos de manera oportuna con el fin de minimizarlos. El siguiente enfoque puede utilizarse para detectar una variedad de anomalías, como interrupciones, ciberataques o contenido web viral. La siguiente imagen es un ejemplo de los picos de un conjunto de datos de serie temporal:
Agregar el método CreateEmptyDataView()
Agregue el método siguiente a Program.cs
:
IDataView CreateEmptyDataView(MLContext mlContext) {
// Create empty DataView. We just need the schema to call Fit() for the time series transforms
IEnumerable<ProductSalesData> enumerableData = new List<ProductSalesData>();
return mlContext.Data.LoadFromEnumerable(enumerableData);
}
CreateEmptyDataView()
genera un objeto de vista de datos vacío con el esquema correcto que se va a usar como entrada para el método IEstimator.Fit()
.
Crear el método DetectSpike()
El método DetectSpike()
realiza las acciones siguientes:
- Crea la transformación desde el estimador.
- Detecta picos en función de los datos históricos de ventas.
- Muestra los resultados.
Cree el método
DetectSpike()
al final del archivo Program.cs con el código siguiente:DetectSpike(MLContext mlContext, int docSize, IDataView productSales) { }
Use IidSpikeEstimator para entrenar el modelo para la detección de picos. Agréguelo a un método
DetectSpike()
en el siguiente código:var iidSpikeEstimator = mlContext.Transforms.DetectIidSpike(outputColumnName: nameof(ProductSalesPrediction.Prediction), inputColumnName: nameof(ProductSalesData.numSales), confidence: 95d, pvalueHistoryLength: docSize / 4);
Para crear la transformación de detección de picos, agregue lo que se indica a continuación como la siguiente línea de código en el método
DetectSpike()
:Sugerencia
Los parámetros
confidence
ypvalueHistoryLength
afectan a cómo se detectan los picos.confidence
determina la sensibilidad del modelo a los picos. Cuanto menor sea la confianza, más probabilidades hay de que el algoritmo detecte picos "más pequeños". El parámetropvalueHistoryLength
define el número de puntos de datos en una ventana deslizante. El valor de este parámetro suele ser un porcentaje de todo el conjunto de datos. Cuanto menor seapvalueHistoryLength
, con más rapidez se olvida el modelo de los picos grandes anteriores.ITransformer iidSpikeTransform = iidSpikeEstimator.Fit(CreateEmptyDataView(mlContext));
Agregue la siguiente línea de código para transformar los datos
productSales
como la siguiente línea en el métodoDetectSpike()
:IDataView transformedData = iidSpikeTransform.Transform(productSales);
El código anterior usa el método Transform() para hacer predicciones para varias filas de entrada de un conjunto de datos.
Convierta
transformedData
enIEnumerable
fuertemente tipado para mostrar fácilmente mediante el método CreateEnumerable() con este código:var predictions = mlContext.Data.CreateEnumerable<ProductSalesPrediction>(transformedData, reuseRowObject: false);
Cree una línea de encabezado de visualización mediante el código Console.WriteLine() siguiente:
Console.WriteLine("Alert\tScore\tP-Value");
Se mostrará la siguiente información en los resultados de detección de picos:
Alert
indica una alerta de pico para determinado punto de datos.Score
es el valorProductSales
para determinado punto de datos en el conjunto de datos.P-Value
La "P" significa probabilidad. Cuanto más se acerque el valor p a 0, más probable es que el punto de datos sea una anomalía.
Use el siguiente código para iterar en
predictions
IEnumerable
y mostrar los resultados:foreach (var p in predictions) { if (p.Prediction is not null) { var results = $"{p.Prediction[0]}\t{p.Prediction[1]:f2}\t{p.Prediction[2]:F2}"; if (p.Prediction[0] == 1) { results += " <-- Spike detected"; } Console.WriteLine(results); } } Console.WriteLine("");
Agregue la llamada al método
DetectSpike()
debajo de la llamada al métodoLoadFromTextFile()
:DetectSpike(mlContext, _docsize, dataView);
Resultados de la detección de picos
Los resultados deberían ser similares a los indicados a continuación. Durante el procesamiento, se muestran mensajes. Puede ver las advertencias o mensajes de procesamiento. Algunos de los mensajes se han quitado de los resultados siguientes para mayor claridad.
Detect temporary changes in pattern
=============== Training the model ===============
=============== End of training process ===============
Alert Score P-Value
0 271.00 0.50
0 150.90 0.00
0 188.10 0.41
0 124.30 0.13
0 185.30 0.47
0 173.50 0.47
0 236.80 0.19
0 229.50 0.27
0 197.80 0.48
0 127.90 0.13
1 341.50 0.00 <-- Spike detected
0 190.90 0.48
0 199.30 0.48
0 154.50 0.24
0 215.10 0.42
0 278.30 0.19
0 196.40 0.43
0 292.00 0.17
0 231.00 0.45
0 308.60 0.18
0 294.90 0.19
1 426.60 0.00 <-- Spike detected
0 269.50 0.47
0 347.30 0.21
0 344.70 0.27
0 445.40 0.06
0 320.90 0.49
0 444.30 0.12
0 406.30 0.29
0 442.40 0.21
1 580.50 0.00 <-- Spike detected
0 412.60 0.45
1 687.00 0.01 <-- Spike detected
0 480.30 0.40
0 586.30 0.20
0 651.90 0.14
Detección de puntos de cambio
Change points
son cambios permanentes en una distribución de valores de secuencia de eventos de serie temporal, como los cambios de nivel y las tendencias. Estos cambios persistentes duran mucho más tiempo que spikes
y podrían indicar eventos catastróficos. Change points
no son normalmente visibles a simple vista, pero se pueden detectar en los datos mediante enfoques como el del siguiente método. La siguiente imagen es un ejemplo de una detección de puntos de cambio:
Crear el método DetectChangepoint()
El método DetectChangepoint()
ejecuta las tareas siguientes:
- Crea la transformación desde el estimador.
- Detecta puntos de cambio en función de los datos históricos de ventas.
- Muestra los resultados.
Cree el método
DetectChangepoint()
, justo después de la declaración del métodoDetectSpike()
, mediante el código siguiente:void DetectChangepoint(MLContext mlContext, int docSize, IDataView productSales) { }
Cree iidChangePointEstimator en el método
DetectChangepoint()
con el código siguiente:var iidChangePointEstimator = mlContext.Transforms.DetectIidChangePoint(outputColumnName: nameof(ProductSalesPrediction.Prediction), inputColumnName: nameof(ProductSalesData.numSales), confidence: 95d, changeHistoryLength: docSize / 4);
Como hizo anteriormente, cree la transformación desde el estimador agregando la siguiente línea de código en el método
DetectChangePoint()
:Sugerencia
La detección de puntos de cambio se produce con un ligero retraso, ya que el modelo debe asegurarse de que la desviación actual es un cambio persistente y no solo algunos picos aleatorios antes de crear una alerta. La cantidad de este retraso es igual al parámetro
changeHistoryLength
. Al aumentar el valor de este parámetro, la detección de cambios alerta sobre cambios más persistentes, pero el resultado sería un retraso más largo.var iidChangePointTransform = iidChangePointEstimator.Fit(CreateEmptyDataView(mlContext));
Use el método
Transform()
para transformar los datos mediante la adición del código siguiente aDetectChangePoint()
:IDataView transformedData = iidChangePointTransform.Transform(productSales);
Como hizo antes, convierta
transformedData
enIEnumerable
fuertemente tipado para mostrar fácilmente mediante el métodoCreateEnumerable()
con este código:var predictions = mlContext.Data.CreateEnumerable<ProductSalesPrediction>(transformedData, reuseRowObject: false);
Cree un encabezado de visualización con el siguiente código como la siguiente línea en el método
DetectChangePoint()
:Console.WriteLine("Alert\tScore\tP-Value\tMartingale value");
Se mostrará la siguiente información en los resultados de detección de puntos de cambio:
Alert
indica una alerta de punto de cambio para determinado punto de datos.Score
es el valorProductSales
para determinado punto de datos en el conjunto de datos.P-Value
La "P" significa probabilidad. Cuanto más se acerque el valor P a 0, más probable es que el punto de datos sea una anomalía.Martingale value
se usa para identificar cuán "extraño" es un punto de datos, en función de la secuencia de valores de P.
Itere en
predictions
IEnumerable
y muestre los resultados con el siguiente código:foreach (var p in predictions) { if (p.Prediction is not null) { var results = $"{p.Prediction[0]}\t{p.Prediction[1]:f2}\t{p.Prediction[2]:F2}\t{p.Prediction[3]:F2}"; if (p.Prediction[0] == 1) { results += " <-- alert is on, predicted changepoint"; } Console.WriteLine(results); } } Console.WriteLine("");
Agregue la siguiente llamada al método
DetectChangepoint()
después de la llamada al métodoDetectSpike()
:DetectChangepoint(mlContext, _docsize, dataView);
Resultados de la detección de puntos de cambio
Los resultados deberían ser similares a los indicados a continuación. Durante el procesamiento, se muestran mensajes. Puede ver las advertencias o mensajes de procesamiento. Algunos de los mensajes se han quitado de los resultados siguientes para mayor claridad.
Detect Persistent changes in pattern
=============== Training the model Using Change Point Detection Algorithm===============
=============== End of training process ===============
Alert Score P-Value Martingale value
0 271.00 0.50 0.00
0 150.90 0.00 2.33
0 188.10 0.41 2.80
0 124.30 0.13 9.16
0 185.30 0.47 9.77
0 173.50 0.47 10.41
0 236.80 0.19 24.46
0 229.50 0.27 42.38
1 197.80 0.48 44.23 <-- alert is on, predicted changepoint
0 127.90 0.13 145.25
0 341.50 0.00 0.01
0 190.90 0.48 0.01
0 199.30 0.48 0.00
0 154.50 0.24 0.00
0 215.10 0.42 0.00
0 278.30 0.19 0.00
0 196.40 0.43 0.00
0 292.00 0.17 0.01
0 231.00 0.45 0.00
0 308.60 0.18 0.00
0 294.90 0.19 0.00
0 426.60 0.00 0.00
0 269.50 0.47 0.00
0 347.30 0.21 0.00
0 344.70 0.27 0.00
0 445.40 0.06 0.02
0 320.90 0.49 0.01
0 444.30 0.12 0.02
0 406.30 0.29 0.01
0 442.40 0.21 0.01
0 580.50 0.00 0.01
0 412.60 0.45 0.01
0 687.00 0.01 0.12
0 480.30 0.40 0.08
0 586.30 0.20 0.03
0 651.90 0.14 0.09
¡Enhorabuena! Ya ha creado correctamente los modelos de aprendizaje automático para detectar anomalías de picos y puntos de cambio en los datos de ventas.
Puede encontrar el código fuente para este tutorial en el repositorio dotnet/samples.
En este tutorial ha aprendido a:
- Carga de los datos
- Entrenar el modelo para la detección de anomalías de picos
- Detectar anomalías de picos con el modelo entrenado
- Entrenar el modelo para la detección de anomalías de puntos de cambio
- Detectar anomalías de puntos de cambio con el modelo entrenado
Pasos siguientes
Consulte el repositorio de GitHub con ejemplos de Machine Learning para explorar un ejemplo de detección de anomalías de datos de estacionalidad.