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
- Visual Studio 2022 con il carico di lavoro "NET Desktop Development" installato.
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
Creare un'applicazione console C# denominata "BikeDemandForecasting". Fare clic sul pulsante Next (Avanti).
Scegliere .NET 6 come framework da usare. Fare clic sul pulsante Crea.
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.
- In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e selezionare Gestisci pacchetti NuGet.
- Scegliere "nuget.org" come origine pacchetto, selezionare la scheda Sfoglia , cercare Microsoft.ML.
- Selezionare la casella di controllo Includi versione preliminare .
- Selezionare il pulsante Installa.
- 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.
- Ripetere questi passaggi per System.Data.SqlClient e Microsoft.ML.TimeSeries.
Preparare e identificare i dati
- Creare una directory denominata Data.
- 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
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;
Creare la classe
ModelInput
. Sotto laProgram
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.
Creare
ModelOutput
una classe sotto la classe appena creataModelInput
.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
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;";
Inizializzare la variabile con una nuova istanza di
MLContext
aggiungendo lamlContext
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 aDBContext
in Entity Framework.
Caricare i dati
Creare
DatabaseLoader
che carica i record di tipoModelInput
.DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
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 tipoReal
, un valore a virgola mobile a precisione singola, devono essere convertiti inReal
.Le
Year
colonne eTotalRental
sono entrambi tipi integer nel database. Usando laCAST
funzione predefinita, sono entrambi cast inReal
.Creare un
DatabaseSource
oggetto per connettersi al database ed eseguire la query.DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, connectionString, query);
Caricare i dati in un'interfaccia
IDataView
.IDataView dataView = loader.Load(dbSource);
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 ilupperBound
parametro su 1. Al contrario, per il secondo anno, i valori maggiori o uguali a 1 vengono selezionati impostando illowerBound
parametro su 1.
Definire la pipeline di analisi delle serie temporali
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 dalseriesLength
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 dalhorizon
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.Usare il
Fit
metodo per eseguire il training del modello e adattare i dati all'oggetto definito inforecastingPipeline
precedenza.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.
Creare un nuovo metodo di utilità denominato
Evaluate
nella parte inferiore del file Program.cs .Evaluate(IDataView testData, ITransformer model, MLContext mlContext) { }
All'interno del
Evaluate
metodo prevedere i dati del secondo anno usando ilTransform
metodo con il modello sottoposto a training.IDataView predictions = model.Transform(testData);
Ottenere i valori effettivi dai dati usando il
CreateEnumerable
metodo .IEnumerable<float> actual = mlContext.Data.CreateEnumerable<ModelInput>(testData, true) .Select(observed => observed.TotalRentals);
Ottenere i valori delle previsioni usando il
CreateEnumerable
metodo .IEnumerable<float> forecast = mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true) .Select(prediction => prediction.ForecastedRentals[0]);
Calcolare la differenza tra i valori effettivi e di previsione, comunemente definiti errore.
var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
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.
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");
Chiamare il
Evaluate
metodo seguente chiamando ilFit()
metodo .Evaluate(secondYearData, forecaster, mlContext);
Salvare il modello
Se si è soddisfatti del modello, salvarlo per usarlo in seguito in altre applicazioni.
Sotto il
Evaluate()
metodo creare un oggettoTimeSeriesPredictionEngine
.TimeSeriesPredictionEngine
è un metodo pratico per eseguire stime singole.var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
Salvare il modello in un file denominato
MLModel.zip
come specificato dalla variabile definita inmodelPath
precedenza. Usare ilCheckpoint
metodo per salvare il modello.forecastEngine.CheckPoint(mlContext, modelPath);
Usare il modello per prevedere la domanda
Sotto il
Evaluate
metodo creare un nuovo metodo di utilità denominatoForecast
.void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext) { }
All'interno del
Forecast
metodo usare ilPredict
metodo per prevedere i noleggi per i sette giorni successivi.ModelOutput forecast = forecaster.Predict();
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"; });
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
Sotto la chiamata al
Checkpoint()
metodo chiamare ilForecast
metodo .Forecast(secondYearData, 7, forecastEngine, mlContext);
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:
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 .