Självstudie: Prognostisera efterfrågan på cykeluthyrningstjänsten med tidsserieanalys och ML.NET
Lär dig hur du förutser efterfrågan på en cykeluthyrningstjänst med hjälp av enstaka tidsserieanalyser på data som lagras i en SQL Server databas med ML.NET.
I den här guiden får du lära dig att:
- Förstå problemet
- Läsa in data från en databas
- Skapa en prognosmodell
- Utvärdera prognosmodell
- Spara en prognosmodell
- Använda en prognosmodell
Krav
- Visual Studio 2022 med arbetsbelastningen ".NET Desktop Development" installerad.
Översikt över exempel på tidsserieprognoser
Det här exemplet är ett C# .NET Core-konsolprogram som förutspår efterfrågan på cykeluthyrning med hjälp av en envariat algoritm för tidsserieanalys som kallas singularspektrumanalys. Koden för det här exemplet finns på lagringsplatsen dotnet/machinelearning-samples på GitHub.
Förstå problemet
För att kunna köra en effektiv åtgärd spelar lagerhantering en viktig roll. Att ha för mycket av en produkt i lager innebär att osålda produkter som sitter på hyllorna inte genererar några intäkter. Att ha för lite produkt leder till förlorad försäljning och kunder som köper från konkurrenter. Därför är den ständiga frågan, vad är den optimala mängden inventering att hålla till hands? Tidsserieanalys hjälper till att ge svar på dessa frågor genom att titta på historiska data, identifiera mönster och använda den här informationen för att prognostisera värden någon gång i framtiden.
Tekniken för att analysera data som används i den här självstudien är en univariate-tidsserieanalys. Univariate-tidsserieanalys tar en titt på en enda numerisk observation under en tidsperiod med specifika intervall, till exempel månadsförsäljning.
Algoritmen som används i den här självstudien är SSA (Singular Spectrum Analysis). SSA fungerar genom att dela upp en tidsserie i en uppsättning huvudkomponenter. Dessa komponenter kan tolkas som de delar av en signal som motsvarar trender, brus, säsongsvariationer och många andra faktorer. Sedan rekonstrueras dessa komponenter och används för att prognostisera värden någon gång i framtiden.
Skapa konsolprogram
Skapa ett C# -konsolprogram med namnet "BikeDemandForecasting". Klicka på knappen Nästa.
Välj .NET 6 som ramverk att använda. Klicka på knappen Skapa.
Installera Microsoft.ML version av NuGet-paketet
Anteckning
Det här exemplet använder den senaste stabila versionen av De NuGet-paket som nämns om inget annat anges.
- I Solution Explorer högerklickar du på projektet och väljer Hantera NuGet-paket.
- Välj "nuget.org" som paketkälla, välj fliken Bläddra och sök efter Microsoft.ML.
- Markera kryssrutan Inkludera förhandsversioner .
- Välj knappen Installera .
- Välj knappen OK i dialogrutan Förhandsgranska ändringar och välj sedan knappen Jag accepterar i dialogrutan Licensgodkännande om du godkänner licensvillkoren för de paket som anges.
- Upprepa de här stegen för System.Data.SqlClient och Microsoft.ML.TimeSeries.
Förbereda och förstå data
- Skapa en katalog med namnet Data.
- Ladda ned databasfilen DailyDemand.mdf och spara den i datakatalogen.
Anteckning
De data som används i den här självstudien kommer från UCI Bike Sharing Dataset. Fanaee-T, Hadi och Gama, Joao, "Event labeling combining ensemble detectors and background knowledge", Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg, Web Link.
Den ursprungliga datamängden innehåller flera kolumner som motsvarar säsongsvariationer och väder. För korthet och eftersom algoritmen som används i den här självstudien bara kräver värden från en enda numerisk kolumn, har den ursprungliga datamängden komprimerats så att den endast innehåller följande kolumner:
- dteday: Datumet för observationen.
- år: Det kodade året för observationen (0=2011, 1=2012).
- cnt: Det totala antalet cykeluthyrningar för den dagen.
Den ursprungliga datauppsättningen mappas till en databastabell med följande schema i en SQL Server databas.
CREATE TABLE [Rentals] (
[RentalDate] DATE NOT NULL,
[Year] INT NOT NULL,
[TotalRentals] INT NOT NULL
);
Följande är ett exempel på data:
RentalDate | Year | TotalRentals |
---|---|---|
1/1/2011 | 0 | 985 |
1/2/2011 | 0 | 801 |
1/3/2011 | 0 | 1349 |
Skapa indata- och utdataklasser
Öppna filen Program.cs och ersätt de befintliga
using
instruktionerna med följande:using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Transforms.TimeSeries; using System.Data.SqlClient;
Skapa klassen
ModelInput
.Program
Lägg till följande kod under klassen.public class ModelInput { public DateTime RentalDate { get; set; } public float Year { get; set; } public float TotalRentals { get; set; } }
Klassen
ModelInput
innehåller följande kolumner:- RentalDate: Datumet för observationen.
- År: Det kodade året för observationen (0=2011, 1=2012).
- TotalRentals: Det totala antalet cykeluthyrningar för den dagen.
Skapa
ModelOutput
klassen under den nyligen skapadeModelInput
klassen.public class ModelOutput { public float[] ForecastedRentals { get; set; } public float[] LowerBoundRentals { get; set; } public float[] UpperBoundRentals { get; set; } }
Klassen
ModelOutput
innehåller följande kolumner:- ForecastedRentals: De förutsagda värdena för den prognostiserade perioden.
- LowerBoundRentals: De förväntade minimivärdena för den prognostiserade perioden.
- UpperBoundRentals: De förväntade maxvärdena för den prognostiserade perioden.
Definiera sökvägar och initiera variabler
Under användningsinstruktionerna definierar du variabler för att lagra platsen för dina data, anslutningssträngen och var du kan spara den tränade modellen.
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;";
Initiera variabeln
mlContext
med en ny instans avMLContext
genom att lägga till följande rad när du har definierat sökvägarna.MLContext mlContext = new MLContext();
Klassen
MLContext
är en startpunkt för alla ML.NET åtgärder, och initiering av mlContext skapar en ny ML.NET miljö som kan delas mellan arbetsflödesobjekten för modellskapande. Det är ungefär som konceptuelltDBContext
i Entity Framework.
Läsa in data
Skapa
DatabaseLoader
som läser in poster av typenModelInput
.DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
Definiera frågan för att läsa in data från databasen.
string query = "SELECT RentalDate, CAST(Year as REAL) as Year, CAST(TotalRentals as REAL) as TotalRentals FROM Rentals";
ML.NET algoritmer förväntar sig att data ska vara av typen
Single
. Därför måste numeriska värden som kommer från databasen som inte är av typenReal
, ett flyttal med enkel precision, konverteras tillReal
.Kolumnerna
Year
ochTotalRental
är båda heltalstyper i databasen. Med hjälp av denCAST
inbyggda funktionen omvandlas båda tillReal
.Skapa en
DatabaseSource
för att ansluta till databasen och köra frågan.DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, connectionString, query);
Läs in data i en
IDataView
.IDataView dataView = loader.Load(dbSource);
Datamängden innehåller två års data. Endast data från det första året används för träning, det andra året hålls ut för att jämföra de faktiska värdena med den prognos som genereras av modellen. Filtrera data med transformeringen
FilterRowsByColumn
.IDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1); IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1);
För det första året väljs endast värdena i
Year
kolumnen mindre än 1 genom att parameternupperBound
anges till 1. För det andra året väljs däremot värden som är större än eller lika med 1 genom att ange parameternlowerBound
till 1.
Definiera pipeline för tidsserieanalys
Definiera en pipeline som använder SsaForecastingEstimator för att prognostisera värden i en tidsseriedatauppsättning.
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
Tar 365 datapunkter för det första året och tar exempel eller delar upp tidsseriedatamängden i 30-dagarsintervall (månadsvis) enligt parameternseriesLength
. Vart och ett av dessa exempel analyseras varje vecka eller ett 7-dagarsfönster. När du fastställer vad det prognostiserade värdet för de kommande perioderna är används värdena från föregående sju dagar för att göra en förutsägelse. Modellen är inställd på att prognostisera sju perioder in i framtiden enligt definitionen i parameternhorizon
. Eftersom en prognos är en välgrundad gissning är den inte alltid 100 % korrekt. Därför är det bra att känna till intervallet med värden i de bästa och sämsta scenarierna enligt definitionen i de övre och nedre gränserna. I det här fallet är konfidensnivån för de nedre och övre gränserna inställd på 95 %. Konfidensnivån kan ökas eller minskas i enlighet med detta. Ju högre värde, desto bredare är intervallet mellan de övre och nedre gränserna för att uppnå önskad konfidensnivå.Fit
Använd metoden för att träna modellen och anpassa data till tidigare definieradeforecastingPipeline
.SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
Utvärdera modellen
Utvärdera hur väl modellen presterar genom att prognostisera nästa års data och jämföra dem med de faktiska värdena.
Skapa en ny verktygsmetod som heter
Evaluate
längst ned i filen Program.cs .Evaluate(IDataView testData, ITransformer model, MLContext mlContext) { }
Evaluate
I metoden kan du prognostisera andra årets data med hjälpTransform
av metoden med den tränade modellen.IDataView predictions = model.Transform(testData);
Hämta de faktiska värdena från data med hjälp
CreateEnumerable
av metoden .IEnumerable<float> actual = mlContext.Data.CreateEnumerable<ModelInput>(testData, true) .Select(observed => observed.TotalRentals);
Hämta prognosvärdena med hjälp
CreateEnumerable
av metoden .IEnumerable<float> forecast = mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true) .Select(prediction => prediction.ForecastedRentals[0]);
Beräkna skillnaden mellan de faktiska värdena och prognosvärdena, som vanligtvis kallas felet.
var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
Mät prestanda genom att beräkna värdena Mean Absolute Error och Root Mean Squared Error.
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
För att utvärdera prestanda används följande mått:
- Genomsnittligt absolut fel: Mäter hur nära förutsägelserna är till det faktiska värdet. Det här värdet är mellan 0 och oändligt. Desto närmare 0, desto bättre kvalitet på modellen.
- Rotfel för genomsnittlig kvadrat: Sammanfattar felet i modellen. Det här värdet är mellan 0 och oändligt. Desto närmare 0, desto bättre kvalitet på modellen.
Mata ut måtten till konsolen.
Console.WriteLine("Evaluation Metrics"); Console.WriteLine("---------------------"); Console.WriteLine($"Mean Absolute Error: {MAE:F3}"); Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
Evaluate
Anropa metoden nedan och anropaFit()
metoden.Evaluate(secondYearData, forecaster, mlContext);
Spara modellen
Om du är nöjd med din modell sparar du den för senare användning i andra program.
Evaluate()
Under metoden skapar du enTimeSeriesPredictionEngine
.TimeSeriesPredictionEngine
är en bekvämlighetsmetod för att göra enkla förutsägelser.var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
Spara modellen i en fil som heter
MLModel.zip
enligt den tidigare definierademodelPath
variabeln.Checkpoint
Använd metoden för att spara modellen.forecastEngine.CheckPoint(mlContext, modelPath);
Använda modellen för att förutsäga efterfrågan
Evaluate
Under metoden skapar du en ny verktygsmetod med namnetForecast
.void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext) { }
Forecast
I metoden använder duPredict
metoden för att prognostisera uthyrningar för de kommande sju dagarna.ModelOutput forecast = forecaster.Predict();
Justera de faktiska värdena och prognosvärdena för sju perioder.
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"; });
Iterera genom prognosutdata och visa det i konsolen.
Console.WriteLine("Rental Forecast"); Console.WriteLine("---------------------"); foreach (var prediction in forecastOutput) { Console.WriteLine(prediction); }
Kör programmet
Nedan anropar
Checkpoint()
Forecast
metoden metoden .Forecast(secondYearData, 7, forecastEngine, mlContext);
Kör appen. Utdata som liknar nedan bör visas i konsolen. För korthet har utdata komprimerats.
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
Inspektion av faktiska och prognostiserade värden visar följande relationer:
Även om de prognostiserade värdena inte förutsäger det exakta antalet uthyrningar ger de ett smalare värdeintervall som gör att en åtgärd kan optimera användningen av resurser.
Grattis! Nu har du skapat en maskininlärningsmodell för tidsserier för att förutse efterfrågan på cykeluthyrning.
Du hittar källkoden för den här självstudien på lagringsplatsen dotnet/machinelearning-samples .