Kurz: Prognóza poptávky po půjčovně kol s analýzou časových řad a ML.NET
Naučte se předpovídat poptávku po službě půjčování kol pomocí analýzy časových řad bez proměnných na data uložená v SQL Server databázi s ML.NET.
V tomto kurzu se naučíte:
- Pochopení problému
- Načtení dat z databáze
- Vytvoření modelu prognózování
- Vyhodnocení modelu prognózování
- Uložení modelu prognózování
- Použití modelu prognózování
Požadavky
- Visual Studio 2022 s nainstalovanou úlohou Vývoj desktopových aplikací .NET
Přehled ukázky prognózování časových řad
Tato ukázka je konzolová aplikace pro C# .NET Core , která předpovídá poptávku po půjčovnách kol pomocí algoritmu analýzy jednorozměrné časové řady známého jako Singular Spectrum Analysis. Kód pro tuto ukázku najdete v úložišti dotnet/machinelearning-samples na GitHubu.
Pochopení problému
Aby bylo možné provádět efektivní operaci, hraje klíčovou roli správa inventáře. Příliš mnoho produktu na skladě znamená, že neprodané produkty, které jsou na regálech, negenerují žádné výnosy. Příliš málo produktů vede ke ztrátě prodeje a zákazníkům, kteří nakupují od konkurence. Konstantní otázkou proto je, jaké je optimální množství zásob, které je třeba mít po ruce? Analýza časových řad pomáhá poskytnout odpověď na tyto otázky tím, že se podívá na historická data, identifikuje vzory a pomocí těchto informací předpovídá hodnoty někdy v budoucnu.
Technika pro analýzu dat použitá v tomto kurzu je analýza časových řad bez přípony. Analýza jednorozměrných časových řad se dívá na jedno číselné pozorování v určitém časovém období v určitých intervalech, jako je například měsíční prodej.
V tomto kurzu se používá algoritmus SSA (Singular Spectrum Analysis). SSA funguje tak, že časovou řadu rozloží do sady hlavních součástí. Tyto komponenty lze interpretovat jako části signálu, které odpovídají trendům, šumu, sezónnosti a mnoha dalším faktorům. Pak se tyto komponenty rekonstruují a použijí se k předpovídání hodnot někdy v budoucnu.
Vytvoření konzolové aplikace
Vytvořte konzolovou aplikaci c# s názvem BikeDemandForecasting. Klikněte na tlačítko Další .
Jako architekturu, kterou chcete použít, zvolte .NET 6. Klikněte na tlačítko Vytvořit.
Instalace balíčku NuGet Microsoft.ML verze
Poznámka
Tato ukázka používá nejnovější stabilní verzi uvedených balíčků NuGet, pokud není uvedeno jinak.
- V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Spravovat balíčky NuGet.
- Jako zdroj balíčku zvolte "nuget.org", vyberte kartu Procházet a vyhledejteMicrosoft.ML.
- Zaškrtněte políčko Zahrnout předběžné verze .
- Vyberte tlačítko Nainstalovat .
- Pokud souhlasíte s licenčními podmínkami pro uvedené balíčky, vyberte tlačítko OK v dialogovém okně Náhled změn a pak v dialogovém okně Souhlas s licencí vyberte tlačítko Přijmout .
- Tento postup opakujte pro System.Data.SqlClient a Microsoft.ML.TimeSeries.
Příprava dat a jejich pochopení
- Vytvořte adresář s názvem Data.
- Stáhněte si soubor databáze DailyDemand.mdf a uložte ho do adresáře Data.
Poznámka
Data použitá v tomto kurzu pocházejí z datové sady sdílení kol UCI. Fanaee-T, Hadi a Gama, Joao, 'Event labeling combining ensemble detectors and background knowledge', Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg, Web Link.
Původní datová sada obsahuje několik sloupců odpovídajících sezónnosti a počasí. Kvůli stručnosti a vzhledem k tomu, že algoritmus použitý v tomto kurzu vyžaduje pouze hodnoty z jednoho číselného sloupce, byla původní datová sada zhuštěna tak, aby zahrnovala pouze následující sloupce:
- dteday: Datum pozorování.
- year: Kódovaný rok pozorování (0=2011, 1=2012).
- cnt: Celkový počet zapůjčení kol pro daný den.
Původní datová sada je v SQL Server databázi namapovaná na tabulku databáze s následujícím schématem.
CREATE TABLE [Rentals] (
[RentalDate] DATE NOT NULL,
[Year] INT NOT NULL,
[TotalRentals] INT NOT NULL
);
Následuje ukázka dat:
Datum pronájmu | Year (Rok) | TotalRentals |
---|---|---|
1/1/2011 | 0 | 985 |
1/2/2011 | 0 | 801 |
1/3/2011 | 0 | 1349 |
Vytvoření vstupních a výstupních tříd
Otevřete soubor Program.cs a nahraďte existující
using
příkazy následujícím kódem:using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Transforms.TimeSeries; using System.Data.SqlClient;
Vytvořte třídu
ModelInput
.Program
Pod třídu přidejte následující kód.public class ModelInput { public DateTime RentalDate { get; set; } public float Year { get; set; } public float TotalRentals { get; set; } }
Třída
ModelInput
obsahuje následující sloupce:- RentalDate: Datum pozorování.
- Rok: Zakódovaný rok pozorování (0=2011, 1=2012).
- TotalRentals: Celkový počet zapůjčení kol za daný den.
Vytvořte
ModelOutput
třídu pod nově vytvořenouModelInput
třídou.public class ModelOutput { public float[] ForecastedRentals { get; set; } public float[] LowerBoundRentals { get; set; } public float[] UpperBoundRentals { get; set; } }
Třída
ModelOutput
obsahuje následující sloupce:- ForecastedRentals: Předpovězené hodnoty pro předpovězené období.
- LowerBoundRentals: Předpovězené minimální hodnoty pro předpokládané období.
- UpperBoundRentals: Předpovězené maximální hodnoty pro předpokládané období.
Definování cest a inicializace proměnných
Pod příkazy using definujte proměnné pro uložení umístění dat, připojovacího řetězce a umístění pro uložení natrénovaného 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;";
Inicializujte
mlContext
proměnnou s novou instancíMLContext
tak, že po definování cest přidáte následující řádek.MLContext mlContext = new MLContext();
Třída
MLContext
je výchozím bodem pro všechny operace ML.NET a inicializace mlContext vytvoří nové prostředí ML.NET, které lze sdílet mezi objekty pracovního postupu vytváření modelu. KoncepčněDBContext
je podobný jako v Entity Frameworku.
Načtení dat
Vytvořte,
DatabaseLoader
který načte záznamy typuModelInput
.DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
Definujte dotaz, který načte data z databáze.
string query = "SELECT RentalDate, CAST(Year as REAL) as Year, CAST(TotalRentals as REAL) as TotalRentals FROM Rentals";
ML.NET algoritmy očekávají, že data budou typu
Single
. Číselné hodnoty pocházející z databáze, které nejsou typuReal
, hodnotu s plovoucí desetinou čárkou s jednoduchou přesností, je proto nutné převést naReal
.Sloupce
Year
aTotalRental
jsou v databázi celočíselné typy.CAST
Pomocí předdefinované funkce se obě přetypují naReal
.Vytvořte pro
DatabaseSource
připojení k databázi a spusťte dotaz.DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, connectionString, query);
Načtěte data do .
IDataView
IDataView dataView = loader.Load(dbSource);
Datová sada obsahuje data za dva roky. Pro trénování se používají pouze data z prvního roku, druhý rok se používá k porovnání skutečných hodnot s prognózou vytvořenou modelem. Filtrování dat pomocí
FilterRowsByColumn
transformaceIDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1); IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1);
Pro první rok jsou vybrány pouze hodnoty ve
Year
sloupci menší než 1 nastavením parametruupperBound
na hodnotu 1. Naopak pro druhý rok jsou hodnoty větší nebo rovné 1 vybrány nastavením parametrulowerBound
na hodnotu 1.
Definování kanálu analýzy časových řad
Definujte kanál, který používá SsaForecastingEstimator k prognózování hodnot v datové sadě časové řady.
var forecastingPipeline = mlContext.Forecasting.ForecastBySsa( outputColumnName: "ForecastedRentals", inputColumnName: "TotalRentals", windowSize: 7, seriesLength: 30, trainSize: 365, horizon: 7, confidenceLevel: 0.95f, confidenceLowerBoundColumn: "LowerBoundRentals", confidenceUpperBoundColumn: "UpperBoundRentals");
Vezme
forecastingPipeline
365 datových bodů pro první rok a vzorkuje nebo rozdělí datovou sadu časových řad do 30denních (měsíčních) intervalů podle parametruseriesLength
. Každý z těchto vzorků se analyzuje týdenním nebo 7denním intervalem. Při určování předpokládané hodnoty pro další období se k předpovědím použijí hodnoty z předchozích sedmi dnů. Model je nastavený na prognózu sedmi období do budoucnosti, jak je definováno parametremhorizon
. Vzhledem k tomu, že prognóza je informovaný odhad, není vždy 100% přesná. Proto je dobré znát rozsah hodnot v nejlepších a nejhorších scénářích definovaných horní a dolní hranicí. V tomto případě je úroveň spolehlivosti dolní a horní hranice nastavena na 95 %. Úroveň spolehlivosti lze odpovídajícím způsobem zvýšit nebo snížit. Čím vyšší je hodnota, tím širší je rozsah mezi horní a dolní hranicí, aby se dosáhlo požadované úrovně spolehlivosti.Použijte metodu
Fit
k trénování modelu a přizpůsobení dat dříve definovanémuforecastingPipeline
objektu .SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
Vyhodnocení modelu
Vyhodnoťte, jak dobře model funguje, tím, že předpovíte data na příští rok a porovnáte je se skutečnými hodnotami.
Vytvořte novou metodu nástroje s názvem
Evaluate
v dolní části souboru Program.cs .Evaluate(IDataView testData, ITransformer model, MLContext mlContext) { }
V rámci
Evaluate
metody předpovídáte data druhého roku pomocíTransform
metody s natrénovaným modelem.IDataView predictions = model.Transform(testData);
Pomocí metody získejte skutečné hodnoty z dat
CreateEnumerable
.IEnumerable<float> actual = mlContext.Data.CreateEnumerable<ModelInput>(testData, true) .Select(observed => observed.TotalRentals);
Pomocí metody získejte hodnoty prognózy
CreateEnumerable
.IEnumerable<float> forecast = mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true) .Select(prediction => prediction.ForecastedRentals[0]);
Vypočítejte rozdíl mezi skutečnými a předpokládanými hodnotami, běžně označovaný jako chyba.
var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
Změřte výkon výpočtem hodnot Střední absolutní chyba a Odmocněná střední kvadratická chyba.
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
K vyhodnocení výkonu se používají následující metriky:
- Střední absolutní chyba: Měří, jak blízko jsou predikce ke skutečné hodnotě. Tato hodnota se pohybuje mezi 0 a nekonečnem. Čím blíže k 0, tím lepší je kvalita modelu.
- Kořenová střední kvadratická chyba: Shrnuje chybu v modelu. Tato hodnota se pohybuje mezi 0 a nekonečnem. Čím blíže k 0, tím lepší je kvalita modelu.
Vypíše metriky do konzoly.
Console.WriteLine("Evaluation Metrics"); Console.WriteLine("---------------------"); Console.WriteLine($"Mean Absolute Error: {MAE:F3}"); Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
Zavolejte metodu
Evaluate
níže, která volá metoduFit()
.Evaluate(secondYearData, forecaster, mlContext);
Uložení modelu
Pokud jste s modelem spokojení, uložte si ho pro pozdější použití v jiných aplikacích.
Pod metodou
Evaluate()
vytvořteTimeSeriesPredictionEngine
.TimeSeriesPredictionEngine
je pohodlná metoda pro vytváření jednoduchých předpovědí.var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
Uložte model do souboru s názvem
MLModel.zip
určeným dříve definovanoumodelPath
proměnnou.Checkpoint
K uložení modelu použijte metodu .forecastEngine.CheckPoint(mlContext, modelPath);
Použití modelu k předpovídání poptávky
Pod metodou
Evaluate
vytvořte novou metodu nástroje s názvemForecast
.void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext) { }
V rámci
Forecast
metody použijte metoduPredict
k předpovídání výpůjčky na příštích sedm dnů.ModelOutput forecast = forecaster.Predict();
Zarovnejte skutečné hodnoty a hodnoty prognózy pro sedm období.
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"; });
Iterujte výstup prognózy a zobrazte ho v konzole.
Console.WriteLine("Rental Forecast"); Console.WriteLine("---------------------"); foreach (var prediction in forecastOutput) { Console.WriteLine(prediction); }
Spuštění aplikace
Pod voláním
Checkpoint()
metody volejte metoduForecast
.Forecast(secondYearData, 7, forecastEngine, mlContext);
Spusťte aplikaci. Na konzole by se měl zobrazit výstup podobný následujícímu. Pro stručnost byl výstup zhuštěný.
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
Kontrola skutečných a předpokládaných hodnot ukazuje následující relace:
I když prognózované hodnoty nepředpovívají přesný počet výpůjčků, poskytují užší rozsah hodnot, který operaci umožňuje optimalizovat využití prostředků.
Gratulujeme! Nyní jste úspěšně vytvořili model strojového učení časových řad, který předpovídá poptávku po půjčování kol.
Zdrojový kód pro tento kurz najdete v úložišti dotnet/machinelearning-samples .