Udostępnij przez


Samouczek: prognozowanie zapotrzebowania na usługę wypożyczania rowerów przy użyciu analizy szeregów czasowych i ML.NET

Dowiedz się, jak prognozować zapotrzebowanie na usługę wypożyczania rowerów przy użyciu niezmiennej analizy szeregów czasowych na danych przechowywanych w bazie danych programu SQL Server z ML.NET.

W tym poradniku nauczysz się, jak:

  • Omówienie problemu
  • Ładowanie danych z bazy danych
  • Tworzenie modelu prognozowania
  • Ocena modelu prognozowania
  • Zapisywanie modelu prognozowania
  • Korzystanie z modelu prognozowania

Wymagania wstępne

Omówienie przykładu prognozowania szeregów czasowych

Ten przykład jest aplikacją konsolową języka C# , która prognozuje zapotrzebowanie na wypożyczanie rowerów przy użyciu niezmiennego algorytmu analizy szeregów czasowych znanego jako Analiza pojedynczego spektrum. Kod dla tego przykładu można znaleźć w repozytorium dotnet/machinelearning-samples w witrynie GitHub.

Omówienie problemu

Aby uruchomić wydajną operację, zarządzanie zapasami odgrywa kluczową rolę. Posiadanie zbyt dużej ilości produktu w magazynie oznacza, że produkty niesprzedane siedzą na półkach, nie generując żadnych przychodów. Zbyt mało produktów prowadzi do utraty sprzedaży i klientów kupujących od konkurentów. W związku z tym stałe pytanie brzmi: jaka jest optymalna ilość zapasów do utrzymania na bieżąco? Analiza szeregów czasowych pomaga zapewnić odpowiedź na te pytania, analizując dane historyczne, identyfikując wzorce i używając tych informacji do prognozowania wartości w przyszłości.

Technika analizowania danych używanych w tym samouczku jest niezmienna analiza szeregów czasowych. Pojedyncza analiza szeregów czasowych analizuje pojedynczą obserwację liczbową w danym okresie w określonych interwałach, takich jak miesięczna sprzedaż.

Algorytm używany w tym samouczku to Analiza Pojedynczego Spektrum (SSA). Usługa SSA działa przez rozdzielenie szeregów czasowych na zestaw głównych składników. Te składniki można interpretować jako części sygnału, które odpowiadają trendom, szumowi, sezonowości i wielu innym czynnikom. Następnie te składniki są odtwarzane i używane do prognozowania wartości w przyszłości.

Tworzenie aplikacji konsolowej

  1. Utwórz aplikację konsolową języka C# o nazwie "BikeDemandForecasting". Kliknij przycisk Next (Dalej).

  2. Wybierz platformę .NET 8 jako platformę do użycia. Kliknij przycisk Utwórz.

  3. Instalowanie pakietu NuGet w wersji Microsoft.ML

    Uwaga / Notatka

    W tym przykładzie użyto najnowszej stabilnej wersji pakietów NuGet wymienionych, chyba że określono inaczej.

    1. W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt i wybierz polecenie Zarządzaj pakietami NuGet.
    2. Wybierz pozycję "nuget.org" jako źródło pakietu, wybierz kartę Przeglądaj , wyszukaj Microsoft.ML.
    3. Zaznacz pole wyboru Uwzględnij wersję wstępną .
    4. Wybierz przycisk Zainstaluj.
    5. Wybierz przycisk OK w oknie dialogowym Podgląd zmian , a następnie wybierz przycisk Akceptuję w oknie dialogowym Akceptacja licencji, jeśli zgadzasz się z postanowieniami licencyjnymi dla pakietów wymienionych.
    6. Powtórz te kroki dla programów System.Data.SqlClient i Microsoft.ML.TimeSeries.

Przygotowywanie i zrozumienie danych

  1. Utwórz katalog o nazwie Dane.
  2. Pobierz plik bazy danych DailyDemand.mdf i zapisz go w katalogu Data.

Uwaga / Notatka

Dane używane w tym samouczku pochodzą z zbioru danych UCI Bike Sharing. Hadi Fanaee-T i João Gama, "Etykietowanie zdarzeń łączące detektory zbiorowe i wiedzę tła", Progress in Artificial Intelligence (2013): s. 1-15, Springer Berlin Heidelberg, Link internetowy.

Oryginalny zestaw danych zawiera kilka kolumn odpowiadających sezonowości i pogodzie. W przypadku zwięzłości i dlatego, że algorytm używany w tym samouczku wymaga tylko wartości z jednej kolumny liczbowej, oryginalny zestaw danych został skondensowany w celu uwzględnienia tylko następujących kolumn:

  • dteday: data obserwacji.
  • year: zakodowany rok obserwacji (0=2011, 1=2012).
  • cnt: Całkowita liczba wypożyczeń rowerów w tym dniu.

Oryginalny zestaw danych jest mapowany na tabelę bazy danych z następującym schematem w bazie danych programu SQL Server.

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

Poniżej przedstawiono przykładowe dane:

DataWypożyczenia Rok TotalRentals
1/1/2011 0 985
1/2/2011 0 801
1/3/2011 0 1349

Tworzenie klas wejściowych i wyjściowych

  1. Otwórz plik Program.cs i zastąp istniejące using dyrektywy następującymi:

    using Microsoft.ML;
    using Microsoft.ML.Data;
    using Microsoft.ML.Transforms.TimeSeries;
    using System.Data.SqlClient;
    
  2. Utwórz ModelInput klasę. Program Poniżej klasy dodaj następujący kod.

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

    Klasa ModelInput zawiera następujące kolumny:

    • RentalDate: data obserwacji.
    • Rok: zakodowany rok obserwacji (0=2011, 1=2012).
    • TotalRentals: łączna liczba wypożyczeń rowerów dla tego dnia.
  3. Utwórz ModelOutput klasę poniżej nowo utworzonej ModelInput klasy.

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

    Klasa ModelOutput zawiera następujące kolumny:

    • ForecastedRentals: przewidywane wartości dla prognozowanego okresu.
    • LowerBoundRentals: przewidywane wartości minimalne dla prognozowanego okresu.
    • UpperBoundRentals: przewidywane wartości maksymalne dla prognozowanego okresu.

Definiowanie ścieżek i inicjowanie zmiennych

  1. using Poniżej dyrektywy definiują zmienne do przechowywania lokalizacji danych, parametrów połączenia i miejsca zapisania wytrenowanego modelu.

    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. Zainicjuj zmienną mlContext za pomocą nowego wystąpienia MLContext, dodając następujący wiersz po zdefiniowaniu ścieżek.

    MLContext mlContext = new MLContext();
    

    Klasa MLContext jest punktem wyjścia dla wszystkich operacji ML.NET, a inicjowanie metody mlContext tworzy nowe środowisko ML.NET, które może być współużytkowane przez obiekty przepływu pracy tworzenia modelu. Jest ona podobna, koncepcyjnie, do DBContext w programie Entity Framework.

Ładowanie danych

  1. Utwórz DatabaseLoader , który ładuje rekordy typu ModelInput.

    DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
    
  2. Zdefiniuj zapytanie, aby załadować dane z bazy danych.

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

    ML.NET algorytmy oczekują, że dane będą typu Single. W związku z tym wartości liczbowe pochodzące z bazy danych, które nie są typu Real, czyli nie są wartością zmiennoprzecinkową o pojedynczej precyzji, muszą zostać przekonwertowane na Real.

    Kolumny Year i TotalRental są typami liczb całkowitych w bazie danych. Przy użyciu wbudowanej CAST funkcji są one rzutowane na Real.

  3. Utwórz element , DatabaseSource aby nawiązać połączenie z bazą danych i wykonać zapytanie.

    DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance,
                                    connectionString,
                                    query);
    
  4. Załaduj dane do obiektu IDataView.

    IDataView dataView = loader.Load(dbSource);
    
  5. Zestaw danych zawiera dane z dwóch lat. Tylko dane z pierwszego roku są używane do trenowania, drugi rok jest przechowywany w celu porównania rzeczywistych wartości z prognozą wygenerowaną przez model. Przefiltruj FilterRowsByColumn dane przy użyciu przekształcenia.

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

    W pierwszym roku wybierane są tylko wartości w kolumnie mniejszej Year niż 1 przez ustawienie parametru upperBound na 1. Z drugiej strony dla drugiego roku wartości większe lub równe 1 są wybierane przez ustawienie parametru lowerBound na 1.

Definiowanie potoku analizy szeregów czasowych

  1. Zdefiniuj potok przetwarzania, który używa SsaForecastingEstimator do prognozowania wartości w zestawie danych szeregów czasowych.

    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 pobiera 365 punktów danych dla pierwszego roku i dzieli lub próbuje zestaw danych szeregów czasowych na 30-dniowe (miesięczne) interwały, określone przez parametr seriesLength. Każda z tych próbek jest analizowana co tydzień lub przez 7 dni. Podczas określania, jaka jest prognozowana wartość dla następnych okresów, wartości z poprzednich siedmiu dni są używane do przewidywania. Model jest ustawiony na prognozowanie siedmiu okresów w przyszłości zgodnie z definicją parametru horizon . Ponieważ prognoza jest świadomym zgadywaniem, nie zawsze jest to 100% dokładne. Dlatego dobrze jest znać zakres wartości w najlepszych i najgorszych scenariuszach zdefiniowanych przez górne i dolne granice. W tym przypadku poziom ufności dla dolnej i górnej granicy wynosi 95%. Poziom ufności można odpowiednio zwiększyć lub zmniejszyć. Im większa wartość, tym szerszy zakres znajduje się między górną i dolną granicą, aby osiągnąć żądany poziom ufności.

  2. Fit Użyj metody , aby wytrenować model i dopasować dane do wcześniej zdefiniowanego forecastingPipelineelementu .

    SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
    

Ocena modelu

Oceń, jak dobrze działa model, prognozując dane w przyszłym roku i porównując je z rzeczywistymi wartościami.

  1. Utwórz nową metodę narzędzia o nazwie Evaluate w dolnej części pliku Program.cs .

    Evaluate(IDataView testData, ITransformer model, MLContext mlContext)
    {
    
    }
    
  2. Evaluate Wewnątrz metody prognozowanie danych z drugiego roku przy użyciu Transform metody z wytrenowanym modelem.

    IDataView predictions = model.Transform(testData);
    
  3. Pobierz rzeczywiste wartości z danych przy użyciu CreateEnumerable metody .

    IEnumerable<float> actual =
        mlContext.Data.CreateEnumerable<ModelInput>(testData, true)
            .Select(observed => observed.TotalRentals);
    
  4. Pobierz wartości prognozy przy użyciu CreateEnumerable metody .

    IEnumerable<float> forecast =
        mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true)
            .Select(prediction => prediction.ForecastedRentals[0]);
    
  5. Oblicz różnicę między wartościami rzeczywistymi i prognozami, często określanymi jako błąd.

    var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
    
  6. Mierzenie wydajności przez obliczenie wartości Błędu bezwzględnego średniej i średniej średniokwadratowej.

    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
    

    Aby ocenić wydajność, używane są następujące metryki:

    • Średni błąd bezwzględny: mierzy, jak bliskie są przewidywania rzeczywistej wartości. Ta wartość waha się od 0 do nieskończoności. Im bliżej 0, tym lepsza jakość modelu.
    • Główny błąd średniokwadratowy: podsumowuje błąd w modelu. Ta wartość waha się od 0 do nieskończoności. Im bliżej 0, tym lepsza jakość modelu.
  7. Wyprowadź metryki do konsoli.

    Console.WriteLine("Evaluation Metrics");
    Console.WriteLine("---------------------");
    Console.WriteLine($"Mean Absolute Error: {MAE:F3}");
    Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
    
  8. Wywołaj metodę Evaluate poniżej wywołującą metodę Fit() .

    Evaluate(secondYearData, forecaster, mlContext);
    

Zapisywanie modelu

Jeśli model jest zadowalający, zapisz go do późniejszego użycia w innych aplikacjach.

  1. Evaluate() Poniżej metody utwórz element TimeSeriesPredictionEngine. TimeSeriesPredictionEngine jest wygodną metodą tworzenia pojedynczych przewidywań.

    var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
    
  2. Zapisz model w pliku o nazwie MLModel.zip określonej przez wcześniej zdefiniowaną modelPath zmienną. Checkpoint Użyj metody , aby zapisać model.

    forecastEngine.CheckPoint(mlContext, modelPath);
    

Prognozowanie zapotrzebowania przy użyciu modelu

  1. Evaluate Poniżej metody utwórz nową metodę narzędzia o nazwie Forecast.

    void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext)
    {
    
    }
    
  2. Forecast Wewnątrz metody użyj Predict metody , aby prognozować wypożyczenie w ciągu najbliższych siedmiu dni.

    ModelOutput forecast = forecaster.Predict();
    
  3. Wyrównywanie rzeczywistych i prognozowanych wartości dla siedmiu okresów.

    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. Iteruj dane wyjściowe prognozy i wyświetlaj je w konsoli programu .

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

Uruchamianie aplikacji

  1. Poniżej wywołaj metodę Checkpoint() wywołaj metodę Forecast .

    Forecast(secondYearData, 7, forecastEngine, mlContext);
    
  2. Uruchom aplikację. Dane wyjściowe podobne do poniższych powinny pojawić się na konsoli. W celu zwięzłości dane wyjściowe zostały skondensowane.

    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
    

Inspekcja rzeczywistych i prognozowanych wartości pokazuje następujące relacje:

Porównanie wartości rzeczywistych i prognozowanych

Chociaż prognozowane wartości nie przewidują dokładnej liczby wypożyczeń, zapewniają bardziej wąski zakres wartości, które umożliwiają operacjom optymalizowanie ich wykorzystania zasobów.

Gratulacje! Udało Ci się utworzyć model uczenia maszynowego szeregów czasowych w celu prognozowania zapotrzebowania na wynajem rowerów.

Kod źródłowy tego samouczka można znaleźć w repozytorium dotnet/machinelearning-samples .

Dalsze kroki