ML.NET を使用して SQL Server データベースに格納されているデータに対する一変量時系列分析を使用して、自転車レンタル サービスの需要を予測する方法について説明します。
このチュートリアルでは、以下の内容を学習します。
- 問題を理解する
- データベースからデータを読み込む
- 予測モデルを作成する
- 予測モデルを評価する
- 予測モデルを保存する
- 予測モデルを使用する
[前提条件]
- .NET デスクトップ開発ワークロードがインストールされている Visual Studio 2022 以降。
時系列予測のサンプルの概要
このサンプルは、特異スペクトル分析と呼ばれる一変量時系列分析アルゴリズムを使用して自転車レンタルの需要を予測する C# コンソール アプリケーション です。 このサンプルのコードは、GitHub の dotnet/machinelearning-samples リポジトリにあります。
問題を理解する
効率的な運用を実行するために、在庫管理が重要な役割を果たします。 在庫のある製品が多すぎると、売れ残った製品が棚に座って収益を生み出さないことを意味します。 製品が少なすぎると、売上が失われ、顧客は競合他社から購入します。 したがって、一定の質問は、手元に保持する在庫の最適な量は何ですか? 時系列分析は、履歴データを確認し、パターンを特定し、この情報を使用して将来しばらくの間値を予測することで、これらの質問に対する回答を提供するのに役立ちます。
このチュートリアルで使用するデータを分析する手法は、一変量時系列分析です。 一変量時系列分析では、月次売上などの特定の間隔で、一定の期間にわたる単一の数値観測を確認します。
このチュートリアルで使用するアルゴリズムは 、単数形スペクトル分析 (SSA) です。 SSA は、時系列を一連の主要コンポーネントに分解することによって機能します。 これらのコンポーネントは、傾向、ノイズ、季節性、およびその他の多くの要因に対応する信号の部分として解釈できます。 その後、これらのコンポーネントが再構築され、将来の値を予測するために使用されます。
コンソール アプリケーションを作成する
"BikeDemandForecasting" という名前の C# コンソール アプリケーション を作成します。 [ 次へ ] ボタンをクリックします。
使用するフレームワークとして .NET 8 を選択します。 [作成] ボタンをクリックします。
バージョン Microsoft.ML NuGet パッケージをインストールする
注
このサンプルでは、特に明記されていない限り、記載されている最新の安定バージョンの NuGet パッケージを使用します。
- ソリューション エクスプローラーで、プロジェクトを右クリックし、[ NuGet パッケージの管理] を選択します。
- パッケージ ソースとして [nuget.org] を選択し、[ 参照 ] タブを選択し、 Microsoft.ML を検索します。
- [ プレリリースを含める ] チェックボックスをオンにします。
- [インストール] ボタンを選択します。
- [変更のプレビュー] ダイアログで [OK] ボタンを選択し、表示されているパッケージのライセンス条項に同意する場合は、[ライセンスの同意] ダイアログで [同意する] ボタンを選択します。
- System.Data.SqlClient と Microsoft.ML.TimeSeries に対してこれらの手順を繰り返します。
データを準備して理解する
- Data という名前のディレクトリを作成 します。
- DailyDemand.mdf データベース ファイルをダウンロードし、データ ディレクトリに保存します。
注
このチュートリアルで使用されるデータは 、UCI 自転車共有データセットから取得されます。 Hadi Fanaee-TとJoão Gama,「アンサンブル検出器とバックグラウンド知識の組み合わせによるイベントラベリング」, Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg, Web Link.
元のデータセットには、季節性と天気に対応する複数の列が含まれています。 簡潔にするために、また、このチュートリアルで使用するアルゴリズムでは単一の数値列の値のみが必要であるため、元のデータセットは次の列のみを含むように圧縮されています。
- dteday: 観測の日付。
- year: 観測値のエンコードされた年 (0=2011、1=2012)。
- cnt: その日の自転車レンタルの合計数。
元のデータセットは、SQL Server データベース内の次のスキーマを持つデータベース テーブルにマップされます。
CREATE TABLE [Rentals] (
[RentalDate] DATE NOT NULL,
[Year] INT NOT NULL,
[TotalRentals] INT NOT NULL
);
データのサンプルを次に示します。
| RentalDate | 年 | TotalRentals |
|---|---|---|
| 1/1/2011 | 0 | 985 |
| 1/2/2011 | 0 | 801 |
| 1/3/2011 | 0 | 1349 |
入力クラスと出力クラスを作成する
ファイル Program.cs 開き、既存の
usingディレクティブを次のように置き換えます。using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Transforms.TimeSeries; using System.Data.SqlClient;クラス
ModelInput作成します。Programクラスの下に、次のコードを追加します。public class ModelInput { public DateTime RentalDate { get; set; } public float Year { get; set; } public float TotalRentals { get; set; } }ModelInputクラスには、次の列が含まれています。- RentalDate: 観測の日付。
- 年: 観測値のエンコードされた年 (0=2011、1=2012)。
- TotalRentals: その日の自転車レンタルの合計数。
新しく作成
ModelOutputModelInputクラスの下にクラスを作成します。public class ModelOutput { public float[] ForecastedRentals { get; set; } public float[] LowerBoundRentals { get; set; } public float[] UpperBoundRentals { get; set; } }ModelOutputクラスには、次の列が含まれています。- ForecastedRentals: 予測期間の予測値。
- LowerBoundRentals: 予測期間の予測最小値。
- UpperBoundRentals: 予測期間の予測最大値。
パスの定義と変数の初期化
usingディレクティブの下で、データの場所、接続文字列、トレーニング済みモデルを保存する場所を格納する変数を定義します。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;";パスを定義した後に次の行を追加して、
mlContextの新しいインスタンスでMLContext変数を初期化します。MLContext mlContext = new MLContext();MLContextクラスは、すべての ML.NET 操作の開始点であり、mlContext を初期化すると、モデル作成ワークフロー オブジェクト間で共有できる新しい ML.NET 環境が作成されます。 概念的には、Entity Framework のDBContextに似ています。
データを読み込む
DatabaseLoader型のレコードを読み込むModelInputを作成します。DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();データベースからデータを読み込むクエリを定義します。
string query = "SELECT RentalDate, CAST(Year as REAL) as Year, CAST(TotalRentals as REAL) as TotalRentals FROM Rentals";ML.NET アルゴリズムでは、データが
Single型であることが想定されています。 したがって、単精度浮動小数点値であるReal型ではないデータベースからの数値は、Realに変換する必要があります。Year列とTotalRental列は、どちらもデータベース内の整数型です。CAST組み込み関数を使用すると、どちらもRealにキャストされます。データベースに接続してクエリを実行する
DatabaseSourceを作成します。DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, connectionString, query);データを
IDataViewに読み込みます。IDataView dataView = loader.Load(dbSource);データセットには、2 年分のデータが含まれています。 トレーニングには最初の年のデータのみが使用され、2 年目はモデルによって生成された予測と実際の値を比較するために保持されます。
FilterRowsByColumn変換を使用してデータをフィルター処理します。IDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1); IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1);最初の年の場合、
Yearパラメーターを 1 に設定すると、upperBound列の 1 未満の値のみが選択されます。 逆に、2 年目は、lowerBoundパラメーターを 1 に設定して、1 以上の値を選択します。
時系列分析パイプラインを定義する
SsaForecastingEstimator を使用して時系列データセットの値を予測するパイプラインを定義します。
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は、最初の年に 365 個のデータ ポイントを取り、seriesLengthパラメーターで指定された 30 日間 (毎月) 間隔に時系列データセットをサンプルまたは分割します。 これらの各サンプルは、週単位または 7 日間の期間で分析されます。 次の期間の予測値を決定する場合、過去 7 日間の値を使用して予測が行われます。 モデルは、horizonパラメーターで定義されている 7 つの期間を将来に予測するように設定されています。 予測は情報に基づく推測であるため、常に 100% 正確であるとは限りません。 したがって、上限と下限で定義されている最適なシナリオと最悪のシナリオで値の範囲を把握することをお勧めします。 この場合、下限と上限の信頼度は 95%に設定されます。 信頼レベルは、それに応じて増減できます。 値が大きいほど、目的の信頼度を実現するために、上限と下限の間の範囲が広くなります。Fitメソッドを使用してモデルをトレーニングし、前に定義したforecastingPipelineにデータを適合します。SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
モデルを評価する
来年のデータを予測し、実際の値と比較して、モデルのパフォーマンスを評価します。
Program.cs
Evaluate下部にという新しいユーティリティ メソッドを作成します。Evaluate(IDataView testData, ITransformer model, MLContext mlContext) { }Evaluateメソッド内で、トレーニング済みのモデルでTransformメソッドを使用して、2 年目のデータを予測します。IDataView predictions = model.Transform(testData);CreateEnumerableメソッドを使用して、データから実際の値を取得します。IEnumerable<float> actual = mlContext.Data.CreateEnumerable<ModelInput>(testData, true) .Select(observed => observed.TotalRentals);CreateEnumerableメソッドを使用して予測値を取得します。IEnumerable<float> forecast = mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true) .Select(prediction => prediction.ForecastedRentals[0]);実際の値と予測値の差を計算します。これは、一般的にエラーと呼ばれます。
var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);平均絶対誤差と平方二乗平均誤差の値を計算して、パフォーマンスを測定します。
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パフォーマンスを評価するために、次のメトリックが使用されます。
- 平均絶対誤差: 予測が実際の値にどの程度近いかを測定します。 この値の範囲は 0 から無限大です。 0 に近いほど、モデルの品質が向上します。
- 2乗平均平方根誤差: モデルの誤差を示す指標です。 この値の範囲は 0 から無限大です。 0 に近いほど、モデルの品質が向上します。
メトリックをコンソールに出力します。
Console.WriteLine("Evaluation Metrics"); Console.WriteLine("---------------------"); Console.WriteLine($"Mean Absolute Error: {MAE:F3}"); Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");次の
Evaluateメソッドを呼び出して、Fit()メソッドを呼び出します。Evaluate(secondYearData, forecaster, mlContext);
モデルを保存する
モデルに問題がない場合は、後で他のアプリケーションで使用できるように保存します。
Evaluate()メソッドの下にTimeSeriesPredictionEngineを作成します。TimeSeriesPredictionEngineは、単一の予測を行う便利な方法です。var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);前に定義した
MLModel.zip変数で指定されたmodelPathという名前のファイルにモデルを保存します。 モデルを保存するには、Checkpointメソッドを使用します。forecastEngine.CheckPoint(mlContext, modelPath);
モデルを使用して需要を予測する
Evaluateメソッドの下に、Forecastという新しいユーティリティ メソッドを作成します。void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext) { }Forecastメソッド内で、Predictメソッドを使用して、次の 7 日間のレンタルを予測します。ModelOutput forecast = forecaster.Predict();7 つの期間の実績値と予測値を調整します。
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"; });予測出力を反復処理し、コンソールに表示します。
Console.WriteLine("Rental Forecast"); Console.WriteLine("---------------------"); foreach (var prediction in forecastOutput) { Console.WriteLine(prediction); }
アプリケーションを実行する
次に、
Checkpoint()メソッドを呼び出すと、Forecastメソッドが呼び出されます。Forecast(secondYearData, 7, forecastEngine, mlContext);アプリケーションを実行します。 次のような出力がコンソールに表示されます。 簡潔にするために、出力は凝縮されています。
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
実際の値と予測値の検査では、次の関係が示されます。
予測値はレンタルの正確な数を予測していませんが、操作でリソースの使用を最適化できるより狭い範囲の値を提供します。
おめでとうございます! これで、自転車レンタルの需要を予測するための時系列機械学習モデルが正常に構築されました。
このチュートリアルのソース コードは 、dotnet/machinelearning-samples リポジトリにあります。
次のステップ
.NET