チュートリアル: ML.NET で回帰を使用して価格を予測する
このチュートリアルでは、ML.NET を使用して、料金 (具体的にはニューヨーク市のタクシー運賃) を予測する回帰モデルを構築する方法を示します。
このチュートリアルでは、次の作業を行う方法について説明します。
- データを準備して理解する
- データを読み込んで変換する
- 学習アルゴリズムを選択する
- モデルをトレーニングする
- モデルを評価する
- モデルを使用して予測を行う
必須コンポーネント
- .NET デスクトップ開発ワークロードがインストールされた Visual Studio 2022。
コンソール アプリケーションの作成
"TaxiFarePrediction" という名前の C# コンソール アプリケーションを作成します。
使用するフレームワークとして [.NET 6] を選択します。 [作成] ボタンをクリックします。
プロジェクトに Data という名前のディレクトリを作成して、データ セットとモデルのファイルを保存します。
Microsoft.ML と Microsoft.ML.FastTree NuGet パッケージをインストールします。
注意
このサンプルでは、特に明記されていない限り、記載されている最新の安定バージョンの NuGet パッケージを使用します。
ソリューション エクスプローラーで、プロジェクトを右クリックし、 [NuGet パッケージの管理] を選択します。 [パッケージ ソース] として [nuget.org] を選択します。 [参照] タブを選択し、「Microsoft.ML」を検索します。一覧からそのパッケージを選択し、 [インストール] ボタンを選択します。 [変更のプレビュー] ダイアログの [OK] を選択します。表示されているパッケージのライセンス条項に同意する場合は、 [ライセンスの同意] ダイアログの [同意する] を選択します。 Microsoft.ML.FastTree NuGet パッケージに対して同じことを行います。
データを準備して理解する
taxi-fare-train.csv データ セットと taxi-fare-test.csv データ セットをダウンロードして、前の手順で作成済みの Data フォルダーに保存します。 これらのデータ セットを使用して、機械学習モデルをトレーニングし、モデルの正確度を評価します。 これらのデータ セットは、NYC TLC Taxi Trip データ セットから取得したものです。
ソリューション エクスプローラーで、各 *.csv ファイルを右クリックし、[プロパティ] を選択します。 [詳細設定] で、 [出力ディレクトリにコピー] の値を [新しい場合はコピーする] に変更します。
taxi-fare-train.csv データ セットを開き、最初の行の列ヘッダーを確認します。 各列を確認してください。 データについて把握し、どの列が特徴で、どの列がラベルであるかを確認します。
label
は予測する列です。 識別された Features
は、Label
を予測するためにモデルを指定する入力です。
提供されているデータ セットには、次の列が含まれます。
- vendor_id: タクシー会社の ID は特徴です。
- rate_code: タクシー旅行のレートの種類は特徴です。
- passenger_count: 旅行の乗客数は特徴です。
- trip_time_in_secs: 旅行の所要時間です。 旅行が終わる前に、旅行の運賃を予測したいと考えます。 その時点では、旅行の所要時間はわかりません。 したがって、旅行の所要時間は特徴ではなく、この列はモデルから除外します。
- trip_distance: 旅行距離は特徴です。
- payment_type: 支払い方法 (現金またはクレジット カード) は特徴です。
- fare_amount: 支払った合計タクシー運賃はラベルです。
データ クラスを作成する
入力データと予測のためのクラスを作成します。
ソリューション エクスプローラーで、プロジェクトを右クリックし、 [追加]>[新しい項目] を選択します。
[新しい項目の追加] ダイアログ ボックスで、 [クラス] を選択し、 [名前] フィールドを TaxiTrip.cs に変更します。 次に [追加] を選択します。
以下の
using
ディレクティブを新しいファイルに追加します。using Microsoft.ML.Data;
既存のクラス定義を削除し、2 つのクラス TaxiTrip
と TaxiTripFarePrediction
を含む次のコードを TaxiTrip.cs ファイルに追加します。
public class TaxiTrip
{
[LoadColumn(0)]
public string? VendorId;
[LoadColumn(1)]
public string? RateCode;
[LoadColumn(2)]
public float PassengerCount;
[LoadColumn(3)]
public float TripTime;
[LoadColumn(4)]
public float TripDistance;
[LoadColumn(5)]
public string? PaymentType;
[LoadColumn(6)]
public float FareAmount;
}
public class TaxiTripFarePrediction
{
[ColumnName("Score")]
public float FareAmount;
}
TaxiTrip
は入力データ クラスであり、各データ セット列の定義が含まれています。 LoadColumnAttribute 属性を使用して、データ セット内のソース列のインデックスを指定します。
TaxiTripFarePrediction
クラスは予測結果を表します。 このクラスには、Score
ColumnNameAttribute 属性が適用された 1 つの浮動小数点型フィールド FareAmount
が含まれています。 回帰タスクの場合、Score 列には、予測ラベル値が含まれます。
注意
入力データと予測データのクラス内の浮動小数点値を表すには、float
型を使います。
データおよびモデルのパスを定義する
次に示す追加の using
ステートメントを Program.cs ファイルの先頭に追加します。
using Microsoft.ML;
using TaxiFarePrediction;
データ セットを含むファイルのパスとモデルを保存するファイルのパスを保持するための 3 つのフィールドを追加する必要があります。
_trainDataPath
には、モデルのトレーニングに使用するデータ セットがあるファイルへのパスが含まれます。_testDataPath
には、モデルの評価に使用するデータ セットがあるファイルへのパスが含まれます。_modelPath
には、トレーニング済みモデルが格納されるファイルへのパスが含まれます。
usings セクションのすぐ下に次のコードを追加して、これらのパスと _textLoader
変数を指定します。
string _trainDataPath = Path.Combine(Environment.CurrentDirectory, "Data", "taxi-fare-train.csv");
string _testDataPath = Path.Combine(Environment.CurrentDirectory, "Data", "taxi-fare-test.csv");
string _modelPath = Path.Combine(Environment.CurrentDirectory, "Data", "Model.zip");
すべての ML.NET 操作は、MLContext クラスで始まります。 mlContext
を初期化することで、モデル作成ワークフローのオブジェクト間で共有できる新しい ML.NET 環境が作成されます。 これは Entity Framework における DBContext
と概念的には同じです。
変数の初期化
Console.WriteLine("Hello World!")
の行を、mlContext
変数を宣言して初期化する次のコードに置き換えます。
MLContext mlContext = new MLContext(seed: 0);
次のコード行を追加して、Train
メソッドを呼び出します。
var model = Train(mlContext, _trainDataPath);
Train()
メソッドは次のタスクを実行します。
- データを読み込みます。
- データを抽出して変換します。
- モデルをトレーニングする。
- モデルを返します。
Train
メソッドは、モデルをトレーニングします。 次のコードを使用して、直下にそのメソッドを作成します。
ITransformer Train(MLContext mlContext, string dataPath)
{
}
データを読み込んで変換する
ML.NET では、数値またはテキストの表形式データを記述する柔軟で効率的な方法として、IDataView インターフェイスを使います。 IDataView
は、テキスト ファイルを読み込むか、またはリアルタイムで読み込むことができます (たとえば、SQL データベースまたはログ ファイル)。 Train()
メソッドの最初の行として、次のコードを追加します。
IDataView dataView = mlContext.Data.LoadFromTextFile<TaxiTrip>(dataPath, hasHeader: true, separatorChar: ',');
タクシー運賃を予測したいので、FareAmount
列は、予測する Label
です (モデルの出力)。 CopyColumnsEstimator
変換クラスを使って FareAmount
をコピーし、次のコードを追加します。
var pipeline = mlContext.Transforms.CopyColumns(outputColumnName: "Label", inputColumnName:"FareAmount")
モデルをトレーニングするアルゴリズムには、数値の特徴が必要であるため、カテゴリ データ (VendorId
、RateCode
、および PaymentType
) の値を数値 (VendorIdEncoded
、RateCodeEncoded
、およびPaymentTypeEncoded
) に変換する必要があります。 それを行うには、異なる数値キーの値を各列内の異なる値に割り当てる OneHotEncodingTransformer 変換クラスを使用し、次のコードを追加します。
.Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "VendorIdEncoded", inputColumnName:"VendorId"))
.Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "RateCodeEncoded", inputColumnName: "RateCode"))
.Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "PaymentTypeEncoded", inputColumnName: "PaymentType"))
データの準備の最後の手順では、mlContext.Transforms.Concatenate
変換クラスを使用して、すべての特徴列を Features 列に結合します。 既定では、学習アルゴリズムは Features 列の特徴のみを処理します。 次のコードを追加します。
.Append(mlContext.Transforms.Concatenate("Features", "VendorIdEncoded", "RateCodeEncoded", "PassengerCount", "TripDistance", "PaymentTypeEncoded"))
学習アルゴリズムを選択する
この問題の中心となるのは、ニューヨーク市のタクシー運賃の予測です。 一見すると、単に乗車距離に依存すると思われるかもしれません。 しかし、ニューヨークのタクシー会社は追加の乗客数や現金でなくクレジット カードによる支払いなど、その他の要因によって請求額を変えます。 データ セット内の他の要因に基づき、実際の値である価格値を予測します。 それを行うには、回帰機械学習タスクを選択します。
Train()
内に次のコード行として以下を追加することで、データ変換定義に FastTreeRegressionTrainer 機械学習タスクを追加します。
.Append(mlContext.Regression.Trainers.FastTree());
モデルをトレーニングする
Train()
メソッドに次のコード行を追加して、モデルをトレーニング dataview
に適合させ、トレーニング済みモデルを返します。
var model = pipeline.Fit(dataView);
Fit() メソッドでは、データセットを変換して、トレーニングを適用することにより、モデルがトレーニングされます。
Train()
メソッドで次のコード行を使用して、トレーニング済みモデルを返します。
return model;
モデルを評価する
次に、品質保証と検証のためのテスト データを使って、モデルのパフォーマンスを評価します。 次のコードを利用して、Train()
の直後に Evaluate()
メソッドを作成します。
void Evaluate(MLContext mlContext, ITransformer model)
{
}
Evaluate
メソッドは次のタスクを実行します。
- テスト データ セットを読み込む。
- 回帰エバリュエーターを作成する。
- モデルを評価し、メトリックを作成する。
- メトリックを表示する。
次のコードを使用して、Train
メソッド呼び出しのすぐ下に新しいメソッドへの呼び出しを追加します。
Evaluate(mlContext, model);
LoadFromTextFile() メソッドを使って、テスト データセットを読み込みます。 次のコードを Evaluate
メソッドに追加することで、このデータセットを品質チェックとして使って、モデルを評価します。
IDataView dataView = mlContext.Data.LoadFromTextFile<TaxiTrip>(_testDataPath, hasHeader: true, separatorChar: ',');
次に、以下のコードを Evaluate()
に追加して、Test
データを変換します。
var predictions = model.Transform(dataView);
Transform() メソッドによって、テスト データセットの入力行に対する予測が行われます。
RegressionContext.Evaluate
メソッドは、指定されたデータセットを使用して PredictionModel
の品質メトリックを計算します。 これによって返される RegressionMetrics オブジェクトには、回帰エバリュエーターによって計算されるメトリック全体が含まれます。
これらを表示してモデルの品質を判定するには、最初にメトリックを取得する必要があります。 Evaluate
メソッドに次のコード行を追加します。
var metrics = mlContext.Regression.Evaluate(predictions, "Label", "Score");
予測セットができたら、Evaluate() メソッドがモデルを評価します。これは予測された値をテスト データセット内の実際の Labels
と比較して、モデルのパフォーマンスのメトリックを返します。
次のコードを追加してモデルを評価し、評価メトリックを生成します。
Console.WriteLine();
Console.WriteLine($"*************************************************");
Console.WriteLine($"* Model quality metrics evaluation ");
Console.WriteLine($"*------------------------------------------------");
RSquared は回帰モデルのもう 1 つの評価メトリックです。 RSquared は 0 から 1 までの値を取ります。 値が 1 に近づくほど、優れたモデルになります。 次のコードを Evaluate
メソッドに追加して、RSquared 値を表示します。
Console.WriteLine($"* RSquared Score: {metrics.RSquared:0.##}");
RMS は回帰モデルの評価メトリックの 1 つです。 RMS が低いほど、優れたモデルになります。 Evaluate
メソッドに次のコードを追加することで、RMS 値を表示します。
Console.WriteLine($"* Root Mean Squared Error: {metrics.RootMeanSquaredError:#.##}");
モデルを使用して予測を行う
Evaluate
メソッドの直後に、次のコードを使用して TestSinglePrediction
メソッドを作成します。
void TestSinglePrediction(MLContext mlContext, ITransformer model)
{
}
TestSinglePrediction
メソッドは次のタスクを実行します。
- テスト データの 1 つのコメントを作成します。
- テスト データに基づいて運賃合計を予測します。
- テスト データと予測をレポート用に結合する。
- 予測された結果を表示する。
次のコードを使用して、Evaluate
メソッド呼び出しのすぐ下に新しいメソッドへの呼び出しを追加します。
TestSinglePrediction(mlContext, model);
次のコードを TestSinglePrediction()
に追加することで、PredictionEngine
を使って運賃を予測します。
var predictionFunction = mlContext.Model.CreatePredictionEngine<TaxiTrip, TaxiTripFarePrediction>(model);
PredictionEngine は、データの 1 つのインスタンスに対して予測を実行できる便利な API です。 PredictionEngine
はスレッド セーフではありません。 シングル スレッド環境またはプロトタイプ環境で使用できます。 運用環境でパフォーマンスとスレッド セーフを向上させるには、PredictionEnginePool
サービスを使用します。これにより、アプリケーション全体で使用するできる PredictionEngine
オブジェクトの ObjectPool
が作成されます。 ASP.NET Core Web API で PredictionEnginePool
を使用する方法については、こちらのガイドを参照してください。
注意
PredictionEnginePool
サービスの拡張機能は、現在プレビュー段階です。
このチュートリアルでは、このクラス内の 1 つのテスト用の旅行を使用します。 モデルを試行するために後で他のシナリオを追加できます。 TaxiTrip
のインスタンスを作成して、TestSinglePrediction()
メソッドでコストのトレーニング済みモデルの予測をテストするために、旅行を追加します。
var taxiTripSample = new TaxiTrip()
{
VendorId = "VTS",
RateCode = "1",
PassengerCount = 1,
TripTime = 1140,
TripDistance = 3.75f,
PaymentType = "CRD",
FareAmount = 0 // To predict. Actual/Observed = 15.5
};
次に、TestSinglePrediction()
メソッドの次のコード行として以下を追加して、タクシー乗車データの 1 つのインスタンスに基づいて運賃を予測し、それを PredictionEngine
に渡します。
var prediction = predictionFunction.Predict(taxiTripSample);
Predict() 関数では、データの 1 つのインスタンスに対して予測を行います。
指定された旅行の予測運賃を表示するために、次のコードを TestSinglePrediction
メソッドに追加します。
Console.WriteLine($"**********************************************************************");
Console.WriteLine($"Predicted fare: {prediction.FareAmount:0.####}, actual fare: 15.5");
Console.WriteLine($"**********************************************************************");
プログラムを実行し、テスト ケースに対して予測されたタクシー運賃を確認します。
おめでとうございます! これで、タクシー旅行運賃を予測する機械学習モデルが正常に構築され、その正確度が評価され、このモデルを使用した予測が行われました。 このチュートリアルのソース コードは、dotnet/samples GitHub リポジトリで確認できます。
次の手順
このチュートリアルでは、次の作業を行う方法を学びました。
- データを準備して理解する
- 学習パイプラインを作成する
- データを読み込んで変換する
- 学習アルゴリズムを選択する
- モデルをトレーニングする
- モデルを評価する
- モデルを使用して予測を行う
さらに詳しく学習するには、次のチュートリアルに進んでください。
.NET