Zelfstudie: Vraag naar fietsverhuur voorspellen met tijdreeksanalyse en ML.NET
Meer informatie over het voorspellen van de vraag naar een fietsverhuurservice met behulp van een univariate tijdreeksanalyse op gegevens die zijn opgeslagen in een SQL Server-database met ML.NET.
In deze zelfstudie leert u het volgende:
- Inzicht in het probleem
- Gegevens laden uit een database
- Een prognosemodel maken
- Prognosemodel evalueren
- Een prognosemodel opslaan
- Een prognosemodel gebruiken
Vereisten
- Visual Studio 2022 met de workload '.NET Desktop Development' geïnstalleerd.
Voorbeeld van tijdreeksprognose
Dit voorbeeld is een C# .NET Core-consoletoepassing die de vraag naar fietsverhuur voorspelt met behulp van een univariate tijdreeksanalyse-algoritme dat bekend staat als Singular Spectrum Analysis. De code voor dit voorbeeld vindt u in de opslagplaats dotnet/machinelearning-samples op GitHub.
Inzicht in het probleem
Om een efficiënte bewerking uit te voeren, speelt voorraadbeheer een belangrijke rol. Als er te veel van een product op voorraad is, betekent dit dat onverkochte producten in de schappen geen omzet genereren. Als u te weinig producten hebt, leidt dit tot verloren verkoop en aankopen van klanten bij concurrenten. Daarom is de constante vraag, wat is de optimale hoeveelheid voorraad om bij de hand te houden? Tijdreeksanalyse biedt een antwoord op deze vragen door naar historische gegevens te kijken, patronen te identificeren en deze informatie te gebruiken om waarden enige tijd in de toekomst te voorspellen.
De techniek voor het analyseren van gegevens die in deze zelfstudie worden gebruikt, is een univariate tijdreeksanalyse. Bij een niet-variabele tijdreeksanalyse wordt gekeken naar één numerieke waarneming gedurende een bepaalde periode, zoals de maandelijkse verkoop.
Het algoritme dat in deze zelfstudie wordt gebruikt, is Singular Spectrum Analysis (SSA). SSA werkt door een tijdreeks op te splitsen in een set principal-onderdelen. Deze onderdelen kunnen worden geïnterpreteerd als de onderdelen van een signaal die overeenkomen met trends, ruis, seizoensgebondenheid en vele andere factoren. Vervolgens worden deze onderdelen gereconstrueerd en gebruikt om in de toekomst waarden te voorspellen.
Consoletoepassing maken
Maak een C# -consoletoepassing met de naam BikeDemandForecasting. Klik op de knop Next
Kies .NET 6 als het framework dat u wilt gebruiken. Klik op de knop Maken.
NuGet-pakket Microsoft.ML versie installeren
Notitie
In dit voorbeeld wordt de meest recente stabiele versie van de genoemde NuGet-pakketten gebruikt, tenzij anders vermeld.
- Klik in Solution Explorer met de rechtermuisknop op uw project en selecteer NuGet-pakketten beheren.
- Kies 'nuget.org' als pakketbron, selecteer het tabblad Bladeren en zoek naar Microsoft.ML.
- Schakel het selectievakje Voorlopige versie opnemen in.
- Selecteer de knop Installeren .
- Selecteer de knop OK in het dialoogvenster Voorbeeld van wijzigingen en selecteer vervolgens de knop Ik ga akkoord in het dialoogvenster Licentie-acceptatie als u akkoord gaat met de licentievoorwaarden voor de vermelde pakketten.
- Herhaal deze stappen voor System.Data.SqlClient en Microsoft.ML.TimeSeries.
De gegevens voorbereiden en begrijpen
- Maak een map met de naam Gegevens.
- Download het databasebestand DailyDemand.mdf en sla het op in de map Data.
Notitie
De gegevens die in deze zelfstudie worden gebruikt, zijn afkomstig van de UCI Bike Sharing Dataset. Fanaee-T, Hadi, and Gama, Joao, 'Event labeling combining ensemble detectors and background knowledge', Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg, Web Link.
De oorspronkelijke gegevensset bevat verschillende kolommen die overeenkomen met seizoensgebondenheid en weer. Voor de beknoptheid en omdat voor het algoritme dat in deze zelfstudie wordt gebruikt, alleen de waarden uit één numerieke kolom zijn vereist, is de oorspronkelijke gegevensset ingekort om alleen de volgende kolommen op te nemen:
- dteday: de datum van de waarneming.
- year: Het gecodeerde jaar van de waarneming (0=2011, 1=2012).
- cnt: Het totale aantal fietsverhuur voor die dag.
De oorspronkelijke gegevensset is toegewezen aan een databasetabel met het volgende schema in een SQL Server database.
CREATE TABLE [Rentals] (
[RentalDate] DATE NOT NULL,
[Year] INT NOT NULL,
[TotalRentals] INT NOT NULL
);
Hier volgt een voorbeeld van de gegevens:
RentalDate | Year | TotalRentals |
---|---|---|
1/1/2011 | 0 | 985 |
1/2/2011 | 0 | 801 |
1/3/2011 | 0 | 1349 |
Invoer- en uitvoerklassen maken
Open het bestand Program.cs en vervang de bestaande
using
instructies door het volgende:using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Transforms.TimeSeries; using System.Data.SqlClient;
Maak de klasse
ModelInput
. Voeg onder deProgram
klasse de volgende code toe.public class ModelInput { public DateTime RentalDate { get; set; } public float Year { get; set; } public float TotalRentals { get; set; } }
De
ModelInput
klasse bevat de volgende kolommen:- RentalDate: De datum van de waarneming.
- Year: Het gecodeerde jaar van de waarneming (0=2011, 1=2012).
- TotalRentals: Het totale aantal fietsverhuur voor die dag.
Maak
ModelOutput
een klasse onder de zojuist gemaakteModelInput
klasse.public class ModelOutput { public float[] ForecastedRentals { get; set; } public float[] LowerBoundRentals { get; set; } public float[] UpperBoundRentals { get; set; } }
De
ModelOutput
klasse bevat de volgende kolommen:- ForecastedRentals: de voorspelde waarden voor de voorspelde periode.
- LowerBoundRentals: de voorspelde minimumwaarden voor de voorspelde periode.
- UpperBoundRentals: de voorspelde maximumwaarden voor de voorspelde periode.
Paden definiëren en variabelen initialiseren
Definieer onder de using-instructies variabelen voor het opslaan van de locatie van uw gegevens, connection string en waar het getrainde model moet worden opgeslagen.
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;";
Initialiseer de
mlContext
variabele met een nieuw exemplaar vanMLContext
door de volgende regel toe te voegen nadat u de paden hebt gedefinieerd.MLContext mlContext = new MLContext();
De
MLContext
klasse is een startpunt voor alle ML.NET bewerkingen. Als u mlContext initialiseert, wordt er een nieuwe ML.NET omgeving gemaakt die kan worden gedeeld met de werkstroomobjecten voor het maken van modellen. Het is conceptueelDBContext
vergelijkbaar met in Entity Framework.
De gegevens laden
Maak die
DatabaseLoader
records van het typeModelInput
laadt.DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
Definieer de query om de gegevens uit de database te laden.
string query = "SELECT RentalDate, CAST(Year as REAL) as Year, CAST(TotalRentals as REAL) as TotalRentals FROM Rentals";
ML.NET algoritmen verwachten dat gegevens van het type
Single
zijn. Daarom moeten numerieke waarden die afkomstig zijn uit de database die niet van het typeReal
zijn, een drijvendekommawaarde met één precisie, worden geconverteerd naarReal
.De
Year
kolommen enTotalRental
zijn beide typen gehele getallen in de database. Met behulp van deCAST
ingebouwde functie worden ze beide cast naarReal
.Maak een
DatabaseSource
om verbinding te maken met de database en voer de query uit.DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, connectionString, query);
Laad de gegevens in een
IDataView
.IDataView dataView = loader.Load(dbSource);
De gegevensset bevat twee jaar aan gegevens. Alleen gegevens van het eerste jaar worden gebruikt voor de training, het tweede jaar wordt gebruikt om de werkelijke waarden te vergelijken met de prognose die door het model wordt geproduceerd. Filter de gegevens met behulp van de
FilterRowsByColumn
transformatie.IDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1); IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1);
Voor het eerste jaar worden alleen de waarden in de
Year
kolom kleiner dan 1 geselecteerd door deupperBound
parameter in te stellen op 1. Omgekeerd worden voor het tweede jaar waarden groter dan of gelijk aan 1 geselecteerd door delowerBound
parameter in te stellen op 1.
Pijplijn voor tijdreeksanalyse definiëren
Definieer een pijplijn die gebruikmaakt van de SsaForecastingEstimator om waarden in een tijdreeksgegevensset te voorspellen.
var forecastingPipeline = mlContext.Forecasting.ForecastBySsa( outputColumnName: "ForecastedRentals", inputColumnName: "TotalRentals", windowSize: 7, seriesLength: 30, trainSize: 365, horizon: 7, confidenceLevel: 0.95f, confidenceLowerBoundColumn: "LowerBoundRentals", confidenceUpperBoundColumn: "UpperBoundRentals");
De
forecastingPipeline
neemt 365 gegevenspunten voor het eerste jaar en steekt de tijdreeksgegevensset op in intervallen van 30 dagen (maandelijks), zoals opgegeven door deseriesLength
parameter. Elk van deze voorbeelden wordt geanalyseerd via een wekelijks of een periode van 7 dagen. Bij het bepalen van de voorspelde waarde voor de volgende periode(en), worden de waarden van de afgelopen zeven dagen gebruikt om een voorspelling te doen. Het model is ingesteld om zeven perioden in de toekomst te voorspellen, zoals gedefinieerd door dehorizon
parameter . Omdat een prognose een geïnformeerde schatting is, is deze niet altijd 100% nauwkeurig. Daarom is het goed om het bereik van waarden in de beste en slechtste scenario's te kennen, zoals gedefinieerd door de boven- en ondergrens. In dit geval wordt het betrouwbaarheidsniveau voor de onder- en bovengrens ingesteld op 95%. Het betrouwbaarheidsniveau kan dienovereenkomstig worden verhoogd of verlaagd. Hoe hoger de waarde, hoe breder het bereik is tussen de boven- en ondergrens om het gewenste betrouwbaarheidsniveau te bereiken.Gebruik de
Fit
methode om het model te trainen en de gegevens aan te passen aan de eerder gedefinieerdeforecastingPipeline
.SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
Het model evalueren
Evalueer hoe goed het model presteert door de gegevens van het volgende jaar te voorspellen en deze te vergelijken met de werkelijke waarden.
Maak een nieuwe hulpprogrammamethode met de naam
Evaluate
onderaan het bestand Program.cs .Evaluate(IDataView testData, ITransformer model, MLContext mlContext) { }
Binnen de
Evaluate
methode kunt u de gegevens van het tweede jaar voorspellen met behulp van deTransform
methode met het getrainde model.IDataView predictions = model.Transform(testData);
Haal de werkelijke waarden op uit de gegevens met behulp van de
CreateEnumerable
-methode.IEnumerable<float> actual = mlContext.Data.CreateEnumerable<ModelInput>(testData, true) .Select(observed => observed.TotalRentals);
Haal de prognosewaarden op met behulp van de
CreateEnumerable
-methode.IEnumerable<float> forecast = mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true) .Select(prediction => prediction.ForecastedRentals[0]);
Bereken het verschil tussen de werkelijke waarden en de prognosewaarden, ook wel de fout genoemd.
var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
Meet de prestaties door de waarden Gemiddelde absolute fout en Wortel gemiddelde kwadratische fout te berekenen.
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
Voor het evalueren van de prestaties worden de volgende metrische gegevens gebruikt:
- Gemiddelde absolute fout: meet hoe dicht voorspellingen bij de werkelijke waarde liggen. Deze waarde ligt tussen 0 en oneindig. Hoe dichter bij 0, hoe beter de kwaliteit van het model.
- Root Mean Squared Error: geeft een overzicht van de fout in het model. Deze waarde ligt tussen 0 en oneindig. Hoe dichter bij 0, hoe beter de kwaliteit van het model.
Voer de metrische gegevens uit naar de console.
Console.WriteLine("Evaluation Metrics"); Console.WriteLine("---------------------"); Console.WriteLine($"Mean Absolute Error: {MAE:F3}"); Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
Roep de
Evaluate
onderstaande methode aan en roep de methode aanFit()
.Evaluate(secondYearData, forecaster, mlContext);
Het model opslaan
Als u tevreden bent met uw model, slaat u het op voor later gebruik in andere toepassingen.
Maak onder de
Evaluate()
methode eenTimeSeriesPredictionEngine
.TimeSeriesPredictionEngine
is een handige methode om enkele voorspellingen te doen.var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
Sla het model op in een bestand met de naam
MLModel.zip
zoals opgegeven door de eerder gedefinieerdemodelPath
variabele. Gebruik deCheckpoint
methode om het model op te slaan.forecastEngine.CheckPoint(mlContext, modelPath);
Het model gebruiken om de vraag te voorspellen
Maak onder de
Evaluate
methode een nieuwe hulpprogrammamethode met de naamForecast
.void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext) { }
Gebruik in de
Forecast
methode de methode om dePredict
huurperiode voor de komende zeven dagen te voorspellen.ModelOutput forecast = forecaster.Predict();
Lijn de werkelijke waarden en de prognosewaarden voor zeven perioden uit.
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"; });
Doorloop de uitvoer van de prognose en geef deze weer op de console.
Console.WriteLine("Rental Forecast"); Console.WriteLine("---------------------"); foreach (var prediction in forecastOutput) { Console.WriteLine(prediction); }
De toepassing uitvoeren
Roep de methode aan onder het
Checkpoint()
aanroepen van deForecast
methode.Forecast(secondYearData, 7, forecastEngine, mlContext);
Voer de toepassing uit. Uitvoer die vergelijkbaar is met de onderstaande moet worden weergegeven op de console. Kortheidshalve is de uitvoer ingekort.
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
Inspectie van de werkelijke en geraamde waarden toont de volgende relaties:
Hoewel de voorspelde waarden niet het exacte aantal huurwoningen voorspellen, bieden ze een smaller bereik van waarden waarmee een bewerking het gebruik van resources kan optimaliseren.
Gefeliciteerd U hebt nu een machine learning-model voor tijdreeksen gebouwd om de vraag naar fietsverhuur te voorspellen.
U vindt de broncode voor deze zelfstudie in de opslagplaats dotnet/machinelearning-samples .