教程:将回归与 ML.NET 配合使用以预测价格

本教程演示如何使用 ML.NET 生成回归模型来预测价格,特别是纽约市的出租车费。

在本教程中,你将了解:

  • 准备和了解数据
  • 加载和转换数据
  • 选择学习算法
  • 定型模型
  • 评估模型
  • 使用预测模型

先决条件

创建控制台应用程序

  1. 创建名为“TaxiFarePrediction”的 C# 控制台应用程序。

  2. 选择 .NET 6 作为要使用的框架。 单击“创建” 按钮。

  3. 在项目中创建一个名为“数据”的目录来保存数据集和模型文件。

  4. 安装“Microsoft.ML”和“Microsoft.ML.FastTree”NuGet 包 :

    注意

    除非另有说明,否则本示例使用前面提到的 NuGet 包的最新稳定版本。

    在“解决方案资源管理器”中,右键单击项目,然后选择“管理 NuGet 包” 。 选择“nuget.org”作为包源,然后选择“浏览”选项卡并搜索“Microsoft.ML”,在列表中选择包,再选择“安装”按钮 。 选择“预览更改” 对话框上的“确定” 按钮,如果你同意所列包的许可条款,则选择“接受许可” 对话框上的“我接受” 按钮。 对 Microsoft.ML.FastTree NuGet 包执行相同操作。

准备和了解数据

  1. 下载 taxi-fare-train.csvtaxi-fare-test.csv 数据集,并将它们保存到先前创建的“数据”文件夹。 我们使用这些数据集定型机器学习模型,然后评估模型的准确性。 这些数据集最初来自 NYC TLC 出租车行程数据集

  2. 在“解决方案资源管理器”中,右键单击每个 *.csv 文件,然后选择“属性”。 在“高级”下,将“复制到输出目录”的值更改为“如果较新则复制” 。

  3. 打开“taxi-fare-train.csv”数据集并查看第一行中的列标题。 查看每个列。 了解数据并确定哪些列是“特征”以及哪些是“标签” 。

label 是要预测的列。 标识的 Features 是为模型提供的用来预测 Label 的输入。

提供的数据集包含以下列:

  • vendor_id: 出租车供应商的 ID 是一项特征。
  • rate_code: 出租车行程的费率类型是一项特征。
  • passenger_count: 行程中的乘客人数是一项特征。
  • trip_time_in_secs: 这次行程所花的时间。 希望在行程完成前预测行程费用。 当时并不知道行程有多长。 因此,行程时间不是一项特征,需要从模型删除此列。
  • trip_distance: 行程距离是一项特征。
  • payment_type: 付款方式(现金或信用卡)是一项特征。
  • fare_amount: 支付的总出租车费用是一个标签。

创建数据类

创建输入数据和预测类:

  1. 在“解决方案资源管理器” 中,右键单击项目,然后选择“添加” >“新项” 。

  2. 在“添加新项”对话框中,选择“类”并将“名称”字段更改为“TaxiTrip.cs”。 然后,选择“添加”按钮。

  3. 将以下 using 指令添加到新文件:

    using Microsoft.ML.Data;
    

删除现有类定义并向“TaxiTrip.cs”文件添加以下代码,其中有两个类 TaxiTripTaxiTripFarePrediction

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 类表示预测的结果。 它应用了单个浮动 FareAmount 字段,附带 ScoreColumnNameAttribute 属性。 对于回归任务,“分数”列包含预测的标签值。

注意

使用 float 类型来表示输入和预测数据类中的浮点值。

定义数据和模型路径

将以下附加的 using 语句添加到“Program.cs”文件顶部:

using Microsoft.ML;
using TaxiFarePrediction;

需要创建三个字段,用于保留有数据集的文件的路径,以及用于保存模型的文件的路径:

  • _trainDataPath 包含具有用于定型模型的数据集的文件的路径。
  • _testDataPath 包含具有用于评估模型的数据集的文件的路径。
  • _modelPath 包含用于存储定型模型的文件的路径。

将以下代码添加到 using 部分下方,以指定这些路径和 _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 环境,可以在模型创建工作流对象之间共享。 从概念上讲,它与实体框架中的 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")

定型模型的算法需要数字特性,所以必须将分类数据(VendorIdRateCodePaymentType)值转换为数字(VendorIdEncodedRateCodeEncodedPaymentTypeEncoded)。 为此,请使用 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 转换类将所有功能列合并到“功能”列。 默认情况下,学习算法仅处理“特征”列的特征。 添加以下代码:

.Append(mlContext.Transforms.Concatenate("Features", "VendorIdEncoded", "RateCodeEncoded", "PassengerCount", "TripDistance", "PaymentTypeEncoded"))

选择学习算法

此问题关于预测纽约市的出租车车费。 从表面看,似乎只取决于行程距离。 但是,由于其他因素(比如额外的乘客或使用信用卡而非现金付款),纽约的出租车供应商收费不同。 你想要预测价格值,该值是基于数据集中的其他因素的实际值。 要执行此操作,请选择回归机器学习任务。

FastTreeRegressionTrainer 机器学习任务追加到数据转换定义中,方法是在 Train() 中添加以下代码作为下一行代码:

.Append(mlContext.Regression.Trainers.FastTree());

定型模型

使模型适合训练 dataview 并通过在 Train() 方法中添加以下代码行来返回训练的模型:

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 是回归模型的另一种评估指标。 RSquared 在 0 和 1 之间取值。 值越接近 1,模型就越好。 将以下代码添加到 Evaluate 方法以显示 RSquared 值:

Console.WriteLine($"*       RSquared Score:      {metrics.RSquared:0.##}");

RMS 是回归模型的一种评估指标。 指标越低,模型就越好。 将以下代码添加到 Evaluate 方法以显示 RMS 值:

Console.WriteLine($"*       Root Mean Squared Error:      {metrics.RootMeanSquaredError:#.##}");

使用预测模型

使用下面的代码紧随 Evaluate 方法后创建 TestSinglePrediction 方法:

void TestSinglePrediction(MLContext mlContext, ITransformer model)
{

}

TestSinglePrediction 方法执行以下任务:

  • 创建测试数据的单个注释。
  • 根据测试数据预测费用。
  • 结合测试数据和预测进行报告。
  • 显示预测结果。

使用以下代码在 Evaluate 方法调用的下方添加对新方法的调用:

TestSinglePrediction(mlContext, model);

使用 PredictionEngine 通过将以下代码添加到 TestSinglePrediction() 来预测车费:

var predictionFunction = mlContext.Model.CreatePredictionEngine<TaxiTrip, TaxiTripFarePrediction>(model);

PredictionEngine 是一个简便 API,可使用它对单个数据实例执行预测。 PredictionEngine 不是线程安全。 可以在单线程环境或原型环境中使用。 为了在生产环境中提高性能和线程安全,请使用 PredictionEnginePool 服务,这将创建一个在整个应用程序中使用的 PredictionEngine 对象的 ObjectPool。 请参阅本指南,了解如何在 ASP.NET Core Web API 中使用 PredictionEnginePool

注意

PredictionEnginePool 服务扩展目前处于预览状态。

本教程使用此类中的一个测试行程。 稍后可以添加其他方案,以尝试使用此模型。 通过创建一个 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() 方法中添加以下代码作为下一行代码将其传递给 PredictionEngine

var prediction = predictionFunction.Predict(taxiTripSample);

Predict() 函数对单个数据实例进行预测。

若要显示指定行程的预测费用,请将下面的代码添加到 TestSinglePrediction 方法中:

Console.WriteLine($"**********************************************************************");
Console.WriteLine($"Predicted fare: {prediction.FareAmount:0.####}, actual fare: 15.5");
Console.WriteLine($"**********************************************************************");

运行此程序,查看测试用例的预测出租车费。

祝贺你! 你已成功生成用于预测出租车费的机器学习模型、评估其准确性,并用其进行预测。 可以在 dotnet/samples GitHub 存储库中找到本教程的源代码。

后续步骤

在本教程中,你了解了如何执行以下操作:

  • 准备和了解数据
  • 创建学习管道
  • 加载和转换数据
  • 选择学习算法
  • 定型模型
  • 评估模型
  • 使用预测模型

进入下一教程了解详细信息。