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

Ö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

  1. Skapa ett C# -konsolprogram med namnet "BikeDemandForecasting". Klicka på knappen Nästa.

  2. Välj .NET 6 som ramverk att använda. Klicka på knappen Skapa.

  3. 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.

    1. I Solution Explorer högerklickar du på projektet och väljer Hantera NuGet-paket.
    2. Välj "nuget.org" som paketkälla, välj fliken Bläddra och sök efter Microsoft.ML.
    3. Markera kryssrutan Inkludera förhandsversioner .
    4. Välj knappen Installera .
    5. 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.
    6. Upprepa de här stegen för System.Data.SqlClient och Microsoft.ML.TimeSeries.

Förbereda och förstå data

  1. Skapa en katalog med namnet Data.
  2. 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

  1. Ö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;
    
  2. 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.
  3. Skapa ModelOutput klassen under den nyligen skapade ModelInput 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

  1. 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;";
    
  2. Initiera variabeln mlContext med en ny instans av MLContext 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 konceptuellt DBContext i Entity Framework.

Läsa in data

  1. Skapa DatabaseLoader som läser in poster av typen ModelInput.

    DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
    
  2. 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 typen Real, ett flyttal med enkel precision, konverteras till Real.

    Kolumnerna Year och TotalRental är båda heltalstyper i databasen. Med hjälp av den CAST inbyggda funktionen omvandlas båda till Real.

  3. Skapa en DatabaseSource för att ansluta till databasen och köra frågan.

    DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance,
                                    connectionString,
                                    query);
    
  4. Läs in data i en IDataView.

    IDataView dataView = loader.Load(dbSource);
    
  5. 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 parametern upperBound 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 parametern lowerBound till 1.

Definiera pipeline för tidsserieanalys

  1. 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 parametern seriesLength . 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 parametern horizon . 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å.

  2. Fit Använd metoden för att träna modellen och anpassa data till tidigare definierade forecastingPipeline.

    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.

  1. Skapa en ny verktygsmetod som heter Evaluate längst ned i filen Program.cs .

    Evaluate(IDataView testData, ITransformer model, MLContext mlContext)
    {
    
    }
    
  2. Evaluate I metoden kan du prognostisera andra årets data med hjälp Transform av metoden med den tränade modellen.

    IDataView predictions = model.Transform(testData);
    
  3. 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);
    
  4. Hämta prognosvärdena med hjälp CreateEnumerable av metoden .

    IEnumerable<float> forecast =
        mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true)
            .Select(prediction => prediction.ForecastedRentals[0]);
    
  5. Beräkna skillnaden mellan de faktiska värdena och prognosvärdena, som vanligtvis kallas felet.

    var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
    
  6. 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.
  7. 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");
    
  8. Evaluate Anropa metoden nedan och anropa Fit() 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.

  1. Evaluate() Under metoden skapar du en TimeSeriesPredictionEngine. TimeSeriesPredictionEngine är en bekvämlighetsmetod för att göra enkla förutsägelser.

    var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
    
  2. Spara modellen i en fil som heter MLModel.zip enligt den tidigare definierade modelPath 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

  1. Evaluate Under metoden skapar du en ny verktygsmetod med namnet Forecast.

    void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext)
    {
    
    }
    
  2. Forecast I metoden använder du Predict metoden för att prognostisera uthyrningar för de kommande sju dagarna.

    ModelOutput forecast = forecaster.Predict();
    
  3. 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";
            });
    
  4. 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

  1. Nedan anropar Checkpoint()Forecast metoden metoden .

    Forecast(secondYearData, 7, forecastEngine, mlContext);
    
  2. 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:

Jämförelse mellan faktiska och prognostiserade

Ä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 .

Nästa steg