Esercitazione: Prevedere la domanda di servizio di noleggio biciclette con l'analisi delle serie temporali e ML.NET

Informazioni su come prevedere la domanda per un servizio di noleggio biciclette usando l'analisi delle serie temporali univariate sui dati archiviati in un database SQL Server con ML.NET.

In questa esercitazione verranno illustrate le procedure per:

  • Informazioni sul problema
  • Caricare i dati da un database
  • Creare un modello di previsione
  • Valutare il modello di previsione
  • Salvare un modello di previsione
  • Usare un modello di previsione

Prerequisiti

Panoramica dell'esempio di previsione delle serie temporali

Questo esempio è un'applicazione console C# .NET Core che prevede la richiesta di noleggio di biciclette usando un algoritmo di analisi delle serie temporali univariate noto come Singolare Spettro Analysis. Il codice per questo esempio è disponibile nel repository dotnet/machinelearning-samples in GitHub.

Informazioni sul problema

Per eseguire un'operazione efficiente, la gestione dell'inventario svolge un ruolo fondamentale. Troppi prodotti in magazzino significa prodotti invenduti seduti sugli scaffali che non generano ricavi. Il fatto di avere troppo poco prodotto comporta la perdita di vendite e clienti che acquistano dai concorrenti. Pertanto, la domanda costante è, qual è la quantità ottimale di inventario da mantenere? L'analisi delle serie temporali consente di fornire una risposta a queste domande esaminando i dati cronologici, identificando i modelli e usando queste informazioni per prevedere i valori in futuro.

La tecnica per l'analisi dei dati usati in questa esercitazione è univariate analisi della serie temporale. L'analisi di serie temporali univariate esamina un'unica osservazione numerica in un periodo di tempo a intervalli specifici, ad esempio le vendite mensili.

L'algoritmo usato in questa esercitazione è Singolare Spectrum Analysis(SSA). SSA funziona decomponendo una serie temporale in un set di componenti principali. Questi componenti possono essere interpretati come parti di un segnale che corrispondono a tendenze, rumore, stagionalità e molti altri fattori. Questi componenti vengono quindi ricostruiti e usati per prevedere i valori in futuro.

Creare un'applicazione console

  1. Creare un'applicazione console C# denominata "BikeDemandForecasting". Fare clic sul pulsante Next (Avanti).

  2. Scegliere .NET 6 come framework da usare. Fare clic sul pulsante Crea.

  3. Installare il pacchetto NuGet Microsoft.ML versione

    Nota

    Questo esempio usa la versione stabile più recente dei pacchetti NuGet menzionati a meno che non sia specificato in altro modo.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e selezionare Gestisci pacchetti NuGet.
    2. Scegliere "nuget.org" come origine pacchetto, selezionare la scheda Sfoglia , cercare Microsoft.ML.
    3. Selezionare la casella di controllo Includi versione preliminare .
    4. Selezionare il pulsante Installa.
    5. Selezionare il pulsante OK nella finestra di dialogo Anteprima modifiche e quindi selezionare il pulsante Accetto nella finestra di dialogo Accettazione della licenza se si accettano le condizioni di licenza per i pacchetti elencati.
    6. Ripetere questi passaggi per System.Data.SqlClient e Microsoft.ML.TimeSeries.

Preparare e identificare i dati

  1. Creare una directory denominata Data.
  2. Scaricare il file di database DailyDemand.mdf e salvarlo nella directory Dati.

Nota

I dati usati in questa esercitazione provengono dal set di dati UCI Bike Sharing. Fanaee-T, Hadi e Gama, Joao, 'Etichetta dell'evento che combina i rilevatori di ensemble e la conoscenza di background', Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg, Web Link.

Il set di dati originale contiene diverse colonne corrispondenti alla stagionalità e al meteo. Per brevità e perché l'algoritmo usato in questa esercitazione richiede solo i valori di una singola colonna numerica, il set di dati originale è stato condensato per includere solo le colonne seguenti:

  • dteday: data dell'osservazione.
  • anno: anno codificato dell'osservazione (0=2011, 1=2012).
  • cnt: numero totale di noleggio biciclette per quel giorno.

Il set di dati originale viene mappato a una tabella di database con lo schema seguente in un database SQL Server.

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

Di seguito è riportato un esempio dei dati:

RentalDate Year TotalRentals
1/1/2011 0 985
1/2/2011 0 801
1/3/2011 0 1349

Creare classi di input e output

  1. Aprire il file Program.cs e sostituire le istruzioni esistenti using con quanto segue:

    using Microsoft.ML;
    using Microsoft.ML.Data;
    using Microsoft.ML.Transforms.TimeSeries;
    using System.Data.SqlClient;
    
  2. Creare la classe ModelInput. Sotto la Program classe aggiungere il codice seguente.

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

    La ModelInput classe contiene le colonne seguenti:

    • RentalDate: data dell'osservazione.
    • Anno: anno codificato dell'osservazione (0=2011, 1=2012).
    • TotalRentals: numero totale di noleggio biciclette per quel giorno.
  3. Creare ModelOutput una classe sotto la classe appena creata ModelInput .

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

    La ModelOutput classe contiene le colonne seguenti:

    • ForecastedRentals: i valori stimati per il periodo previsto.
    • LowerBoundRentals: i valori minimi stimati per il periodo previsto.
    • UpperBoundRentals: i valori massimi stimati per il periodo previsto.

Definire i percorsi e inizializzare le variabili

  1. Di seguito vengono definite le istruzioni using per archiviare la posizione dei dati, della stringa di connessione e della posizione in cui salvare il modello sottoposto a training.

    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. Inizializzare la variabile con una nuova istanza di MLContext aggiungendo la mlContext riga seguente dopo aver definito i percorsi.

    MLContext mlContext = new MLContext();
    

    La MLContext classe è un punto di partenza per tutte le operazioni di ML.NET e l'inizializzazione di mlContext crea un nuovo ambiente ML.NET che può essere condiviso tra gli oggetti del flusso di lavoro di creazione del modello. Dal punto di vista concettuale è simile a DBContext in Entity Framework.

Caricare i dati

  1. Creare DatabaseLoader che carica i record di tipo ModelInput.

    DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
    
  2. Definire la query per caricare i dati dal database.

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

    ML.NET algoritmi prevedono che i dati siano di tipo Single. Pertanto, i valori numerici provenienti dal database che non sono di tipo Real, un valore a virgola mobile a precisione singola, devono essere convertiti in Real.

    Le Year colonne e TotalRental sono entrambi tipi integer nel database. Usando la CAST funzione predefinita, sono entrambi cast in Real.

  3. Creare un DatabaseSource oggetto per connettersi al database ed eseguire la query.

    DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance,
                                    connectionString,
                                    query);
    
  4. Caricare i dati in un'interfaccia IDataView.

    IDataView dataView = loader.Load(dbSource);
    
  5. Il set di dati contiene due anni di dati. Solo i dati del primo anno vengono usati per il training, il secondo anno viene tenuto a confrontare i valori effettivi rispetto alla previsione prodotta dal modello. Filtrare i dati usando la FilterRowsByColumn trasformazione.

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

    Per il primo anno, solo i valori della Year colonna meno di 1 vengono selezionati impostando il upperBound parametro su 1. Al contrario, per il secondo anno, i valori maggiori o uguali a 1 vengono selezionati impostando il lowerBound parametro su 1.

Definire la pipeline di analisi delle serie temporali

  1. Definire una pipeline che usa SsaForecastingEstimator per prevedere i valori in un set di dati di serie temporali.

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

    Accetta forecastingPipeline 365 punti dati per il primo anno e campioni o suddivide il set di dati della serie temporale in intervalli di 30 giorni (mensili) come specificato dal seriesLength parametro . Ognuno di questi esempi viene analizzato tramite una finestra settimanale o di 7 giorni. Quando si determina il valore previsto per i periodi successivi, i valori dei sette giorni precedenti vengono usati per eseguire una stima. Il modello è impostato per prevedere sette periodi nel futuro, come definito dal horizon parametro . Poiché una previsione è un'ipotesi informata, non è sempre accurata al 100%. È quindi consigliabile conoscere l'intervallo di valori negli scenari migliori e peggiori, come definito dai limiti superiori e inferiori. In questo caso, il livello di confidenza per i limiti inferiori e superiori è impostato sul 95%. Il livello di confidenza può essere aumentato o ridotto di conseguenza. Maggiore è il valore, maggiore è l'intervallo compreso tra i limiti superiori e inferiori per ottenere il livello di confidenza desiderato.

  2. Usare il Fit metodo per eseguire il training del modello e adattare i dati all'oggetto definito in forecastingPipelineprecedenza.

    SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
    

Valutare il modello

Valutare le prestazioni del modello prevedendo i dati dell'anno successivo e confrontandoli con i valori effettivi.

  1. Creare un nuovo metodo di utilità denominato Evaluate nella parte inferiore del file Program.cs .

    Evaluate(IDataView testData, ITransformer model, MLContext mlContext)
    {
    
    }
    
  2. All'interno del Evaluate metodo prevedere i dati del secondo anno usando il Transform metodo con il modello sottoposto a training.

    IDataView predictions = model.Transform(testData);
    
  3. Ottenere i valori effettivi dai dati usando il CreateEnumerable metodo .

    IEnumerable<float> actual =
        mlContext.Data.CreateEnumerable<ModelInput>(testData, true)
            .Select(observed => observed.TotalRentals);
    
  4. Ottenere i valori delle previsioni usando il CreateEnumerable metodo .

    IEnumerable<float> forecast =
        mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true)
            .Select(prediction => prediction.ForecastedRentals[0]);
    
  5. Calcolare la differenza tra i valori effettivi e di previsione, comunemente definiti errore.

    var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
    
  6. Misurare le prestazioni calcolando i valori Mean Absolute Error e Root Mean Squared Error.Measure performance by computing the Mean Absolute Error and Root Mean Squared Error values.

    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
    

    Per valutare le prestazioni, vengono usate le metriche seguenti:

    • Errore assoluto medio: misura la vicinanza delle stime al valore effettivo. Questo valore è compreso tra 0 e infinito. Più vicino a 0, migliore è la qualità del modello.
    • Errore quadratico medio radice: riepiloga l'errore nel modello. Questo valore è compreso tra 0 e infinito. Più vicino a 0, migliore è la qualità del modello.
  7. Restituire le metriche nella console.

    Console.WriteLine("Evaluation Metrics");
    Console.WriteLine("---------------------");
    Console.WriteLine($"Mean Absolute Error: {MAE:F3}");
    Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
    
  8. Chiamare il Evaluate metodo seguente chiamando il Fit() metodo .

    Evaluate(secondYearData, forecaster, mlContext);
    

Salvare il modello

Se si è soddisfatti del modello, salvarlo per usarlo in seguito in altre applicazioni.

  1. Sotto il Evaluate() metodo creare un oggetto TimeSeriesPredictionEngine. TimeSeriesPredictionEngine è un metodo pratico per eseguire stime singole.

    var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
    
  2. Salvare il modello in un file denominato MLModel.zip come specificato dalla variabile definita in modelPath precedenza. Usare il Checkpoint metodo per salvare il modello.

    forecastEngine.CheckPoint(mlContext, modelPath);
    

Usare il modello per prevedere la domanda

  1. Sotto il Evaluate metodo creare un nuovo metodo di utilità denominato Forecast.

    void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext)
    {
    
    }
    
  2. All'interno del Forecast metodo usare il Predict metodo per prevedere i noleggi per i sette giorni successivi.

    ModelOutput forecast = forecaster.Predict();
    
  3. Allineare i valori effettivi e previsti per sette periodi.

    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. Scorrere l'output della previsione e visualizzarlo nella console.

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

Eseguire l'applicazione

  1. Sotto la chiamata al Checkpoint() metodo chiamare il Forecast metodo .

    Forecast(secondYearData, 7, forecastEngine, mlContext);
    
  2. Eseguire l'applicazione. L'output simile a quello riportato di seguito dovrebbe essere visualizzato nella console. Per brevità, l'output è stato condensato.

    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
    

L'ispezione dei valori effettivi e previsti mostra le relazioni seguenti:

Confronto effettivo e previsione

Anche se i valori previsti non stimano il numero esatto di noleggi, forniscono un intervallo più ristretto di valori che consente a un'operazione di ottimizzare l'uso delle risorse.

Congratulazioni! È stato creato un modello di Machine Learning time series per prevedere la domanda di noleggio di biciclette.

È possibile trovare il codice sorgente per questa esercitazione nel repository dotnet/machinelearning-samples .

Passaggi successivi