Compartir vía


Tutorial: Previsión de la demanda del servicio de alquiler de bicicletas con análisis de series temporales y ML.NET

Obtenga información sobre cómo predecir la demanda de un servicio de alquiler de bicicletas mediante el análisis de series temporales univariante en los datos almacenados en una base de datos de SQL Server con ML.NET.

En este tutorial, aprenderá a:

  • Comprender el problema
  • Carga de datos desde una base de datos
  • Creación de un modelo de previsión
  • Evaluación del modelo de previsión
  • Guardar un modelo de previsión
  • Uso de un modelo de previsión

Prerrequisitos

Resumen del ejemplo de previsión de series temporales

Este ejemplo es una aplicación de consola de C# que prevé la demanda de alquileres de bicicletas mediante un algoritmo de análisis de series temporales univariante conocido como Análisis de espectro singular. El código de este ejemplo se puede encontrar en el repositorio dotnet/machinelearning-samples en GitHub.

Comprender el problema

Para ejecutar una operación eficaz, la administración del inventario desempeña un papel clave. Tener demasiados productos en existencias significa que los productos sin vender sentados en los estantes no generan ingresos. Tener demasiado poco producto conduce a la pérdida de ventas y clientes que compran de competidores. Por lo tanto, la pregunta constante es, ¿cuál es la cantidad óptima de inventario que se debe mantener a mano? El análisis de series temporales ayuda a proporcionar una respuesta a estas preguntas examinando los datos históricos, identificando patrones y usando esta información para predecir valores en algún momento en el futuro.

La técnica para analizar los datos usados en este tutorial es un análisis de series temporales univariante. El análisis de series temporales univariante examina una única observación numérica durante un período de tiempo a intervalos específicos, como las ventas mensuales.

El algoritmo usado en este tutorial es Singular Spectrum Analysis(SSA). SSA funciona mediante la descomposición de una serie temporal en un conjunto de componentes principales. Estos componentes se pueden interpretar como las partes de una señal que corresponden a tendencias, ruido, estacionalidad y muchos otros factores. A continuación, estos componentes se reconstruyen y se usan para predecir valores algún tiempo en el futuro.

Creación de una aplicación de consola

  1. Cree una aplicación de consola de C# denominada "BikeDemandForecasting". Haga clic en el botón Siguiente .

  2. Elija .NET 8 como marco de trabajo que se va a usar. Haga clic en el botón Crear.

  3. Instalar el paquete NuGet de la versión 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.

    1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Administrar paquetes NuGet.
    2. Elija "nuget.org" como origen del paquete, seleccione la pestaña Examinar y busque Microsoft.ML.
    3. Active la casilla Incluir versión preliminar .
    4. Seleccione el botón Instalar .
    5. Seleccione el botón Aceptar en el cuadro de diálogo Vista previa de cambios y, a continuación, seleccione el botón Acepto en el cuadro de diálogo Aceptación de licencia si está de acuerdo con los términos de licencia de los paquetes enumerados.
    6. Repita estos pasos para System.Data.SqlClient y Microsoft.ML.TimeSeries.

Preparación y comprensión de los datos

  1. Cree un directorio denominado Data.
  2. Descargue el archivo de base de datos DailyDemand.mdf y guárdelo en el directorio Data.

Nota:

Los datos usados en este tutorial proceden del conjunto de datos de uso compartido de bicicletas de UCI. Hadi Fanaee-T y João Gama, "Etiquetado de eventos combinando detectores de conjuntos y conocimientos de fondo", Progreso en inteligencia artificial (2013): pp. 1-15, Springer Berlin Heidelberg, Web Link.

El conjunto de datos original contiene varias columnas correspondientes a la estacionalidad y el tiempo. Por motivos de brevedad y porque el algoritmo usado en este tutorial solo requiere los valores de una sola columna numérica, el conjunto de datos original se ha condensado para incluir solo las columnas siguientes:

  • dteday: fecha de la observación.
  • year: el año codificado de la observación (0=2011, 1=2012).
  • cnt: el número total de alquileres de bicicletas para ese día.

El conjunto de datos original se asigna a una tabla de base de datos con el esquema siguiente en una base de datos de SQL Server.

CREATE TABLE [Rentals] (
    [RentalDate] DATE NOT NULL,
    [Year] INT NOT NULL,
    [TotalRentals] INT NOT NULL
);

A continuación se muestra un ejemplo de los datos:

RentalDate Año TotalRentals
1/1/2011 0 985
1/2/2011 0 801
1/3/2011 0 1349

Creación de clases de entrada y salida

  1. Abra Program.cs archivo y reemplace las directivas existentes using por lo siguiente:

    using Microsoft.ML;
    using Microsoft.ML.Data;
    using Microsoft.ML.Transforms.TimeSeries;
    using System.Data.SqlClient;
    
  2. Crear ModelInput clase. Debajo de la Program clase , agregue el código siguiente.

    public class ModelInput
    {
        public DateTime RentalDate { get; set; }
    
        public float Year { get; set; }
    
        public float TotalRentals { get; set; }
    }
    

    La ModelInput clase contiene las columnas siguientes:

    • RentalDate: fecha de la observación.
    • Año: año codificado de la observación (0=2011, 1=2012).
    • TotalRentals: el número total de alquileres de bicicletas para ese día.
  3. Cree la clase ModelOutput debajo de la clase ModelInput recién creada.

    public class ModelOutput
    {
        public float[] ForecastedRentals { get; set; }
    
        public float[] LowerBoundRentals { get; set; }
    
        public float[] UpperBoundRentals { get; set; }
    }
    

    La ModelOutput clase contiene las columnas siguientes:

    • ForecastedRentals: los valores previstos para el período previsto.
    • LowerBoundRentals: los valores mínimos previstos para el período previsto.
    • UpperBoundRentals: los valores máximos previstos para el período previsto.

Definición de rutas de acceso e inicialización de variables

  1. Debajo de las using directivas se definen variables para almacenar la ubicación de los datos, la cadena de conexión y dónde guardar el modelo entrenado.

    string rootDir = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../"));
    string dbFilePath = Path.Combine(rootDir, "Data", "DailyDemand.mdf");
    string modelPath = Path.Combine(rootDir, "MLModel.zip");
    var connectionString = $"Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename={dbFilePath};Integrated Security=True;Connect Timeout=30;";
    
  2. Inicialice la mlContext variable con una nueva instancia de MLContext agregando la siguiente línea después de definir las rutas de acceso.

    MLContext mlContext = new MLContext();
    

    La MLContext clase es un punto de partida para todas las operaciones de ML.NET e inicializar mlContext crea un nuevo entorno de ML.NET que se puede compartir entre los objetos de flujo de trabajo de creación de modelos. Es similar, conceptualmente, a DBContext en Entity Framework.

Carga de los datos

  1. Cree DatabaseLoader que cargue registros de tipo ModelInput.

    DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
    
  2. Defina la consulta para cargar los datos de la base de datos.

    string query = "SELECT RentalDate, CAST(Year as REAL) as Year, CAST(TotalRentals as REAL) as TotalRentals FROM Rentals";
    

    ML.NET algoritmos esperan que los datos sean de tipo Single. Por lo tanto, los valores numéricos procedentes de la base de datos que no son de tipo Real, un valor de punto flotante de precisión única, deben convertirse en Real.

    Las Year columnas y TotalRental son tipos enteros en la base de datos. Con la CAST función integrada, ambas se convierten en Real.

  3. Cree un DatabaseSource para conectarse a la base de datos y ejecutar la consulta.

    DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance,
                                    connectionString,
                                    query);
    
  4. Cargue los datos en un IDataView.

    IDataView dataView = loader.Load(dbSource);
    
  5. El conjunto de datos contiene dos años de datos. Solo se usan datos del primer año para el entrenamiento, el segundo año se mantiene para comparar los valores reales con la previsión generada por el modelo. Filtre los datos mediante la FilterRowsByColumn transformación.

    IDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1);
    IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1);
    

    Para el primer año, solo se seleccionan los valores de la Year columna menor que 1 estableciendo el upperBound parámetro en 1. Por el contrario, para el segundo año, los valores mayores o iguales a 1 se seleccionan estableciendo el lowerBound parámetro en 1.

Definir la canalización de análisis de series temporales

  1. Defina una canalización que use SsaForecastingEstimator para predecir los valores de un conjunto de datos de serie temporal.

    var forecastingPipeline = mlContext.Forecasting.ForecastBySsa(
        outputColumnName: "ForecastedRentals",
        inputColumnName: "TotalRentals",
        windowSize: 7,
        seriesLength: 30,
        trainSize: 365,
        horizon: 7,
        confidenceLevel: 0.95f,
        confidenceLowerBoundColumn: "LowerBoundRentals",
        confidenceUpperBoundColumn: "UpperBoundRentals");
    

    forecastingPipeline toma 365 puntos de datos para el primer año y muestra o divide el conjunto de datos de serie temporal en intervalos de 30 días (mensuales), según lo especificado por el parámetro seriesLength. Cada uno de estos ejemplos se analiza a través de una ventana semanal o de 7 días. Al determinar cuál es el valor previsto para los próximos períodos, los valores de los siete días anteriores se usan para realizar una predicción. El modelo se establece para predecir siete períodos en el futuro según lo definido por el horizon parámetro . Dado que una previsión es una estimación informada, no siempre es 100% precisa. Por lo tanto, es bueno conocer el intervalo de valores en los escenarios mejor y peor, tal como se define en los límites superior e inferior. En este caso, el nivel de confianza de los límites inferior y superior se establece en 95%. El nivel de confianza se puede aumentar o disminuir en consecuencia. Cuanto mayor sea el valor, más amplio será el intervalo entre los límites superior e inferior para lograr el nivel de confianza deseado.

  2. Use el Fit método para entrenar el modelo y ajustar los datos a la instancia de definida forecastingPipelineanteriormente.

    SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
    

Evaluación del modelo

Evalúe el rendimiento del modelo mediante la previsión de los datos del próximo año y su comparación con los valores reales.

  1. Cree un nuevo método de utilidad llamado Evaluate en la parte inferior del archivo Program.cs .

    Evaluate(IDataView testData, ITransformer model, MLContext mlContext)
    {
    
    }
    
  2. Dentro del Evaluate método , previsión de los datos del segundo año mediante el Transform método con el modelo entrenado.

    IDataView predictions = model.Transform(testData);
    
  3. Obtenga los valores reales de los datos mediante el CreateEnumerable método .

    IEnumerable<float> actual =
        mlContext.Data.CreateEnumerable<ModelInput>(testData, true)
            .Select(observed => observed.TotalRentals);
    
  4. Obtenga los valores de previsión mediante el CreateEnumerable método .

    IEnumerable<float> forecast =
        mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true)
            .Select(prediction => prediction.ForecastedRentals[0]);
    
  5. Calcule la diferencia entre los valores reales y de previsión, comúnmente denominado error.

    var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
    
  6. Mida el rendimiento calculando los valores de error absoluto medio y error cuadrático medio.

    var MAE = metrics.Average(error => Math.Abs(error)); // Mean Absolute Error
    var RMSE = Math.Sqrt(metrics.Average(error => Math.Pow(error, 2))); // Root Mean Squared Error
    

    Para evaluar el rendimiento, se usan las siguientes métricas:

    • Error absoluto medio: mide la proximidad de las predicciones con el valor real. Este valor oscila entre 0 e infinito. Cuanto más cerca de 0, mejor será la calidad del modelo.
    • Error cuadrático medio raíz: resume el error en el modelo. Este valor oscila entre 0 e infinito. Cuanto más cerca de 0, mejor será la calidad del modelo.
  7. Muestra las métricas en la consola.

    Console.WriteLine("Evaluation Metrics");
    Console.WriteLine("---------------------");
    Console.WriteLine($"Mean Absolute Error: {MAE:F3}");
    Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
    
  8. Llame al Evaluate método siguiente llamando al Fit() método .

    Evaluate(secondYearData, forecaster, mlContext);
    

Guardar el modelo

Si está satisfecho con el modelo, guárdelo para usarlo más adelante en otras aplicaciones.

  1. Debajo del Evaluate() método, cree un TimeSeriesPredictionEngine. TimeSeriesPredictionEngine es un método de conveniencia para realizar predicciones únicas.

    var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
    
  2. Guarde el modelo en un archivo llamado MLModel.zip según lo especificado por la variable definida modelPath anteriormente. Use el Checkpoint método para guardar el modelo.

    forecastEngine.CheckPoint(mlContext, modelPath);
    

Uso del modelo para predecir la demanda

  1. Debajo del Evaluate método , cree un nuevo método de utilidad denominado Forecast.

    void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext)
    {
    
    }
    
  2. Dentro del Forecast método , use el Predict método para predecir alquileres durante los próximos siete días.

    ModelOutput forecast = forecaster.Predict();
    
  3. Alinee los valores reales y de previsión durante siete períodos.

    IEnumerable<string> forecastOutput =
        mlContext.Data.CreateEnumerable<ModelInput>(testData, reuseRowObject: false)
            .Take(horizon)
            .Select((ModelInput rental, int index) =>
            {
                string rentalDate = rental.RentalDate.ToShortDateString();
                float actualRentals = rental.TotalRentals;
                float lowerEstimate = Math.Max(0, forecast.LowerBoundRentals[index]);
                float estimate = forecast.ForecastedRentals[index];
                float upperEstimate = forecast.UpperBoundRentals[index];
                return $"Date: {rentalDate}\n" +
                $"Actual Rentals: {actualRentals}\n" +
                $"Lower Estimate: {lowerEstimate}\n" +
                $"Forecast: {estimate}\n" +
                $"Upper Estimate: {upperEstimate}\n";
            });
    
  4. Recorrer el resultado del pronóstico y mostrarlo en la consola.

    Console.WriteLine("Rental Forecast");
    Console.WriteLine("---------------------");
    foreach (var prediction in forecastOutput)
    {
        Console.WriteLine(prediction);
    }
    

Ejecutar la aplicación

  1. A continuación, al llamar al Checkpoint() método, se llama al Forecast método .

    Forecast(secondYearData, 7, forecastEngine, mlContext);
    
  2. Ejecute la aplicación. La salida similar a la siguiente debería aparecer en la consola. Por motivos de brevedad, el resultado se ha condensado.

    Evaluation Metrics
    ---------------------
    Mean Absolute Error: 726.416
    Root Mean Squared Error: 987.658
    
    Rental Forecast
    ---------------------
    Date: 1/1/2012
    Actual Rentals: 2294
    Lower Estimate: 1197.842
    Forecast: 2334.443
    Upper Estimate: 3471.044
    
    Date: 1/2/2012
    Actual Rentals: 1951
    Lower Estimate: 1148.412
    Forecast: 2360.861
    Upper Estimate: 3573.309
    

La inspección de los valores reales y previstos muestra las siguientes relaciones:

Comparación real frente a previsión

Aunque los valores previstos no predicen el número exacto de alquileres, proporcionan un intervalo de valores más estrecho que permite que una operación optimice su uso de recursos.

¡Felicidades! Ahora ha construido correctamente un modelo de aprendizaje automático de series temporales para pronosticar la demanda de alquiler de bicicletas.

Puede encontrar el código fuente de este tutorial en el repositorio dotnet/machinelearning-samples .

Pasos siguientes