教程:使用回归和 ML.NET 预测价格

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

本教程中,您将学习如何:

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

先决条件

创建控制台应用程序

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

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

  3. 在项目中创建名为 Data 的目录以存储数据集和模型文件。

  4. 安装 Microsoft.MLMicrosoft.ML.FastTree NuGet 包:

    注释

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

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

准备和了解数据

  1. 下载 taxi-fare-train.csvtaxi-fare-test.csv 数据集,并将其保存到在上一步创建的 Data 文件夹中。 我们使用这些数据集来训练机器学习模型,然后评估模型的准确性。 这些数据集最初来自 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;
    

删除现有类定义,并将以下代码(具有两个类 TaxiTripTaxiTripFarePrediction添加到 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 表示预测的结果。 它具有一个浮点字段, FareAmount其中应用了一个 ScoreColumnNameAttribute 属性。 对于回归任务, “评分 ”列包含预测的标签值。

注释

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

定义数据和模型路径

将以下附加 using 指令添加到 Program.cs 文件的顶部:

using Microsoft.ML;
using TaxiFarePrediction;

需要创建三个字段来保存包含数据集的文件的路径,以及保存模型的文件:

  • _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)值转换为数字(RateCodePaymentTypeVendorIdEncodedRateCodeEncodedPaymentTypeEncoded)。 为此,请使用 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;

评估模型

接下来,使用测试数据评估模型性能,以确保质量保证和验证。 使用以下 Evaluate() 代码创建方法 Train()

void Evaluate(MLContext mlContext, ITransformer model)
{

}

该方法 Evaluate 执行以下任务:

  • 加载测试数据集。
  • 创建回归评估器。
  • 评估模型并创建指标。
  • 显示指标。

使用以下代码在Train方法调用的正下方插入对新方法的调用:

Evaluate(mlContext, model);

使用 LoadFromTextFile() 方法加载测试数据集。 通过在方法中添加以下代码 Evaluate ,使用此数据集评估模型作为质量检查:

IDataView dataView = mlContext.Data.LoadFromTextFile<TaxiTrip>(_testDataPath, hasHeader: true, separatorChar: ',');

接下来,通过将以下代码添加到以下代码Test来转换Evaluate()数据:

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:0.##}");

使用模型进行预测

TestSinglePrediction使用以下代码在方法后面Evaluate创建方法:

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该服务,该服务会创建一个ObjectPoolPredictionEngine对象用于整个应用程序。 请参阅本指南,了解如何在 ASP.NET Core Web API 中使用PredictionEnginePool

注释

PredictionEnginePool 服务扩展目前为预览版。

本教程在此类中使用了一次测试行程。 稍后可以添加其他方案来试验模型。 通过创建以下TestSinglePrediction()实例,添加一个行程,以测试方法中TaxiTrip训练的模型对成本的预测:

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 存储库中找到本教程的源代码。

后续步骤

在本教程中,你将学习到如何:

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

请继续学习下一篇教程,了解详细信息。