Samouczek: prognozowanie zapotrzebowania na usługi wypożyczania rowerów za pomocą analizy szeregów czasowych i ML.NET
Dowiedz się, jak prognozować zapotrzebowanie na usługę wypożyczania rowerów przy użyciu jednowariowej analizy szeregów czasowych na danych przechowywanych w bazie danych SQL Server z ML.NET.
Ten samouczek zawiera informacje na temat wykonywania następujących czynności:
- Omówienie problemu
- Ładowanie danych z bazy danych
- Tworzenie modelu prognozowania
- Ocena modelu prognozowania
- Zapisywanie modelu prognozowania
- Korzystanie z modelu prognozowania
Wymagania wstępne
- Program Visual Studio 2022 z zainstalowanym obciążeniem ".NET Desktop Development".
Omówienie przykładu prognozowania szeregów czasowych
Ten przykład jest aplikacją konsolową platformy .NET Core w języku C# , która prognozuje zapotrzebowanie na wynajem rowerów przy użyciu jednowariowego 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 można było uruchomić wydajną operację, zarządzanie zapasami odgrywa kluczową rolę. Posiadanie zbyt dużej ilości produktu w magazynie oznacza, że produkty niesprzedazone 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, aby utrzymać się pod ręką? Analiza szeregów czasowych pomaga zapewnić odpowiedź na te pytania, przeglądają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 polega na niezmiennej analizie szeregów czasowych. Pojedyncza analiza szeregów czasowych analizuje pojedynczą obserwację liczbową w danym okresie w określonych odstępach czasu, takich jak miesięczna sprzedaż.
Algorytm używany w tym samouczku to Pojedyncza analiza spektrum (SSA). 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 odpowiadającego 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
Utwórz aplikację konsolową języka C# o nazwie "BikeDemandForecasting". Kliknij przycisk Dalej.
Wybierz platformę .NET 6 jako platformę do użycia. Kliknij przycisk Utwórz.
Instalowanie pakietu NuGet w wersji Microsoft.ML
Uwaga
W tym przykładzie użyto najnowszej stabilnej wersji pakietów NuGet wymienionych, chyba że określono inaczej.
- W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt i wybierz polecenie Zarządzaj pakietami NuGet.
- Wybierz pozycję "nuget.org" jako źródło pakietu, wybierz kartę Przeglądaj , wyszukaj Microsoft.ML.
- Zaznacz pole wyboru Dołącz wersję wstępną .
- Wybierz przycisk Zainstaluj .
- 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 wymienionych pakietów.
- Powtórz te kroki dla programów System.Data.SqlClient i Microsoft.ML.TimeSeries.
Przygotowywanie i zrozumienie danych
- Utwórz katalog o nazwie Dane.
- Pobierz plik bazy danych DailyDemand.mdf i zapisz go w katalogu Data.
Uwaga
Dane używane w tym samouczku pochodzą z zestawu danych UCI Bike Sharing. Fanaee-T, Hadi i Gama, Joao, "Etykietowanie zdarzeń łączących detektory zespołu i wiedzę w tle", Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Berlin, Web Link.
Oryginalny zestaw danych zawiera kilka kolumn odpowiadających sezonowości i pogodzie. W przypadku zwięzłości i ponieważ algorytm używany w tym samouczku wymaga tylko wartości z jednej kolumny liczbowej, oryginalny zestaw danych został skrócony 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 dla tego dnia.
Oryginalny zestaw danych jest mapowany na tabelę bazy danych z następującym schematem w bazie danych SQL Server.
CREATE TABLE [Rentals] (
[RentalDate] DATE NOT NULL,
[Year] INT NOT NULL,
[TotalRentals] INT NOT NULL
);
Poniżej przedstawiono przykład danych:
Wypożyczenie | Year (Rok) | TotalRentals |
---|---|---|
1/1/2011 | 0 | 985 |
1/2/2011 | 0 | 801 |
1/3/2011 | 0 | 1349 |
Tworzenie klas wejściowych i wyjściowych
Otwórz plik Program.cs i zastąp istniejące
using
instrukcje następującymi instrukcjami:using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Transforms.TimeSeries; using System.Data.SqlClient;
Utwórz klasę
ModelInput
.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: Całkowita liczba wypożyczeń rowerów dla tego dnia.
Utwórz
ModelOutput
klasę poniżej nowo utworzonejModelInput
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
Poniżej instrukcji using zdefiniuj 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;";
Zainicjuj
mlContext
zmienną przy użyciu nowego wystąpieniaMLContext
, 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, doDBContext
w programie Entity Framework.
Ładowanie danych
Utwórz
DatabaseLoader
, że ładuje rekordy typuModelInput
.DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
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ą typuReal
, należy przekonwertować wartość zmiennoprzecinkową o pojedynczej precyzji naReal
wartość .Kolumny
Year
iTotalRental
są zarówno typami całkowitymi w bazie danych. Za pomocą wbudowanejCAST
funkcji są one rzutowane naReal
.Utwórz element ,
DatabaseSource
aby nawiązać połączenie z bazą danych i wykonać zapytanie.DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, connectionString, query);
Załaduj dane do elementu
IDataView
.IDataView dataView = loader.Load(dbSource);
Zestaw danych zawiera dwa lata wartości danych. 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
Year
kolumnie mniejszej niż 1, ustawiającupperBound
parametr na 1. Z drugiej strony dla drugiego roku wartości większe lub równe 1 są wybierane przez ustawienie parametrulowerBound
na 1.
Definiowanie potoku analizy szeregów czasowych
Zdefiniuj potok, który używa klasy 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");
Obiekt
forecastingPipeline
przyjmuje 365 punktów danych dla pierwszego roku i próbki lub dzieli zestaw danych szeregów czasowych na 30-dniowe (miesięczne) interwały określone przezseriesLength
parametr . Każda z tych próbek jest analizowana przez cotygodniowe lub 7-dniowe okno. 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ą parametruhorizon
. Ponieważ prognoza jest świadomym zgadywaniem, nie zawsze jest to 100% dokładne. W związku z tym dobrze jest znać zakres wartości w najlepszych i najgorszych scenariuszach, zgodnie z definicją w granicach górnych i dolnych. W tym przypadku poziom ufności dla dolnych i górnych granic wynosi 95%. Poziom ufności można odpowiednio zwiększyć lub zmniejszyć. Im wyższa wartość, tym szerszy zakres znajduje się między górną i dolną granicą w celu osiągnięcia żądanego poziomu ufności.Fit
Użyj metody , aby wytrenować model i dopasować dane do wcześniej zdefiniowanegoforecastingPipeline
elementu .SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
Ocena modelu
Oceń, jak dobrze działa model, prognozując dane z przyszłego roku i porównując je z rzeczywistymi wartościami.
Utwórz nową metodę narzędzia o nazwie
Evaluate
w dolnej części pliku Program.cs .Evaluate(IDataView testData, ITransformer model, MLContext mlContext) { }
Evaluate
Wewnątrz metody prognozowanie danych z drugiego roku przy użyciuTransform
metody z wytrenowanym modelem.IDataView predictions = model.Transform(testData);
Pobierz rzeczywiste wartości z danych przy użyciu
CreateEnumerable
metody .IEnumerable<float> actual = mlContext.Data.CreateEnumerable<ModelInput>(testData, true) .Select(observed => observed.TotalRentals);
Pobierz wartości prognozy przy użyciu
CreateEnumerable
metody .IEnumerable<float> forecast = mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true) .Select(prediction => prediction.ForecastedRentals[0]);
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);
Mierzenie wydajności przez obliczanie wartości Błędu bezwzględnego średniej i średniej kwadratu głównego.
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.
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");
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.
Evaluate()
Poniżej metody utwórz elementTimeSeriesPredictionEngine
.TimeSeriesPredictionEngine
jest wygodną metodą tworzenia pojedynczych przewidywań.var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
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
Evaluate
Poniżej metody utwórz nową metodę narzędzia o nazwieForecast
.void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext) { }
Forecast
Wewnątrz metody użyjPredict
metody , aby prognozować wypożyczenie przez następne siedem dni.ModelOutput forecast = forecaster.Predict();
Wyrównuj wartości rzeczywiste i prognozowane 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"; });
Iteruj dane wyjściowe prognozy i wyświetlaj je w konsoli.
Console.WriteLine("Rental Forecast"); Console.WriteLine("---------------------"); foreach (var prediction in forecastOutput) { Console.WriteLine(prediction); }
Uruchamianie aplikacji
Poniżej wywołania
Checkpoint()
metody wywołaj metodęForecast
.Forecast(secondYearData, 7, forecastEngine, mlContext);
Uruchom aplikację. Dane wyjściowe podobne do poniższych powinny pojawić się w konsoli programu . 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:
Chociaż prognozowane wartości nie przewidują dokładnej liczby wypożyczeń, zapewniają bardziej wąski zakres wartości, które umożliwiają operację optymalizacji wykorzystania zasobów.
Gratulacje! Udało Ci się utworzyć model uczenia maszynowego szeregów czasowych w celu prognozowania zapotrzebowania na wypożyczanie rowerów.
Kod źródłowy tego samouczka można znaleźć w repozytorium dotnet/machinelearning-samples .