三、ML.NET示例:使用时序分析和 ML.NET 预测自行车租赁服务需求
- 36氪教程:使用时序分析和 ML.NET 预测自行车租赁服务需求
了解如何通过 ML.NET 对 SQL Server 数据库中存储的数据进行单变量时序分析,以预测自行车租赁服务需求。
在本教程中,你将了解:
了解问题
从数据库加载数据
创建预测模型
评估预测模型
保存预测模型
使用预测模型
先决条件
已安装“.NET 桌面开发”工作负载的 Visual Studio 2022。
时序预测示例概述
此示例为 C# .NET Core 控制台应用程序,它使用单变量时序分析算法(称为单谱分析)来预测自行车租赁需求。 此示例的代码可以在 GitHub 上的 dotnet/machinelearning-samples 存储库找到。
了解问题
为了实现高效运营,其中库存管理的作用不可或缺。 产品库存过多意味着产品积压,无法产生收入。 产品库存过少会损失销售额,导致客户转而购买竞争对手的产品。 因此,一个永恒的问题就是:保有多少库存才最合适呢? 借助时序分析,可通过查看历史数据、识别模式并使用此信息来预测未来某个时间的值,从而帮助找到这些问题的答案。
此教程使用的数据分析技术为单变量时序分析。 单变量时序分析可按照特定间隔(如月销售额)查看一个时段内的单个数值观测。
本教程中使用的算法是单谱分析 (SSA)。 SSA 会将时序分解为一组主要成分, 可以将这些成分解释为信号的组成部分,对应于趋势、噪音、季节性及许多其他的因素。 然后重新构建这些成分,并用来预测未来某个时间的值。
创建控制台应用程序
1.创建一个名为“BikeDemandForecasting”的 C# 控制台应用程序。 单击“下一步”按钮。
2.选择 .NET 6 作为要使用的框架。 单击“创建” 按钮。
3.安装 Microsoft.ML 版本 NuGet 包
备注
除非另有说明,否则本示例使用前面提到的 NuGet 包的最新稳定版本。
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.在“解决方案资源管理器”中,右键单击项目,然后选择“管理 NuGet 包” 。
a.选择“nuget.org”作为“包源”,选择“浏览”选项卡,再搜索“Microsoft.ML”。
b.选中“包括预发行版”复选框。
c.选择“安装”按钮。
d.选择“预览更改”对话框中的“确定”按钮;如果同意所列包的许可条款,请选择“接受许可”对话框中的“我接受”按钮。
e.针对 System.Data.SqlClient 和 Microsoft.ML.TimeSeries 重复上述步骤 。
准备和了解数据
1.创建一个名为“Data”的目录。
2.下载 DailyDemand.mdf 数据库文件并将其保存到“Data”目录中。
备注
此教程使用的数据来自 UCI 自行车共享数据集。 作者 Fanaee-T,Hadi 和 Gama, Joao,“事件标签结合集合探测器和背景知识”,人工智能进展 (2013):1-15 页,Springer Berlin Heidelberg,网页链接。
原始数据集包含与季节和天气相对应的若干列。 为了简洁起见,并且由于本教程使用的算法仅需要单个数值列中的值,因此,已将原始数据集精简为仅包括以下列:
dteday:观测日期。
year:观测年份编码(0=2011,1=2012)。
cnt:观测日当天自行车租赁总数。
原始数据集映射到 SQL Server 数据库中具有以下架构的数据库表。
SQL复制
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 |
创建输入和输出类
1.打开 Program.cs 文件,将现有 using 语句替换为以下内容:
C#复制
using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Transforms.TimeSeries;
using System.Data.SqlClient;
2.创建 ModelInput 类。 在 Program 类下面,添加以下代码。
C#复制
public class ModelInput
{
public DateTime RentalDate { get; set; }
public float Year { get; set; }
public float TotalRentals { get; set; }
}
ModelInput 类包含以下列:
RentalDate:观测日期。
Year:观测年份编码(0=2011,1=2012)。
TotalRentals:观测日当天自行车租赁总数。
3.在新建的 ModelOutput 类的下面,创建 ModelInput 类。
C#复制
public class ModelOutput
{
public float[] ForecastedRentals { get; set; }
public float[] LowerBoundRentals { get; set; }
public float[] UpperBoundRentals { get; set; }
}
ModelOutput 类包含以下列:
ForecastedRentals:预测时段内的预测值。
LowerBoundRentals:预测时段内的最低预测值。
UpperBoundRentals:预测时段内的最高预测值。
定义路径并初始化变量
1.在 using 语句下,定义变量,用于存储数据位置、连接字符串,以及保存已训练模型的位置。
C#复制
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;";
2.通过在定义路径后添加以下行,使用新的 MLContext 实例初始化 mlContext 变量。
C#复制
MLContext mlContext = new MLContext();
执行所有 ML.NET 操作都是从 MLContext 类开始,初始化 mlContext 将创建一个新的 ML.NET 环境,可在模型创建工作流对象之间共享该环境。 从概念上讲,它与实体框架中的 DBContext 类似。
加载数据
1.创建 DatabaseLoader,用于加载 ModelInput 类型的记录。
C#复制
DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader
2.定义查询,以从数据库加载数据。
C#复制
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。
3.创建 DatabaseSource 以连接到数据库,并执行查询。
C#复制
DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance,
connectionString,
query);
4.将数据加载到 IDataView 中。
C#复制
IDataView dataView = loader.Load(dbSource);
5.此数据集包含两年的重要数据。 第一年的数据仅用于培训,第二年的数据用于将实际值与模型生成的预测进行比较。 使用 FilterRowsByColumn 转换筛选数据。
C#复制
IDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1);
IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1);
对于第一年,通过将 upperBound 参数设置为 1 来仅选择 Year 列中小于 1 的值。 相反,对于第二年,通过将 lowerBound 参数设置为 1 来仅选择大于或等于 1 的值。
定义时序分析管道
1.定义使用 SsaForecastingEstimator 预测时序数据集中的值的管道。
C#复制
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 个时段。 由于预测属于合理猜测,它不总是完全准确。 因此,最好了解上限和下限定义的最佳和最坏情况下的范围值。 在本案例中,设置的上下限可信度为 95%。 可信度可以相应地提高或降低。 值越高,上限和下限之间的范围越大,以便达到所需的可信度。
2.使用 Fit 方法培训模型,使数据适用于前面定义的 forecastingPipeline。
C#复制
SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
评估模型
通过预测下一年的数据并将其与实际值进行比较,评估模型的执行情况。
1.在 Program.cs 文件底部创建名为 Evaluate 的新实用工具方法。
C#复制
Evaluate(IDataView testData, ITransformer model, MLContext mlContext)
{
}
2.在 Evaluate 方法中,通过结合使用 Transform 方法和培训模型,预测第二年的数据。
C#复制
IDataView predictions = model.Transform(testData);
3.使用 CreateEnumerable 方法,从数据中获取实际值。
C#复制
IEnumerable
mlContext.Data.CreateEnumerable
.Select(observed => observed.TotalRentals);
4.使用 CreateEnumerable 方法获取预测值。
C#复制
IEnumerable
mlContext.Data.CreateEnumerable
.Select(prediction => prediction.ForecastedRentals[0]);
5.计算实际值和预测值之间的差值(通常称为“误差”)。
C#复制
var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
6.通过计算平均绝对误差和均方根误差值测量性能。
C#复制
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,模型的质量越好。
均方根误差:汇总模型中的错误。 此值介于 0 到无限大之间。 越接近 0,模型的质量越好。
7.将指标输出到控制台。
C#复制
Console.WriteLine("Evaluation Metrics");
Console.WriteLine("---------------------");
Console.WriteLine($"Mean Absolute Error: {MAE:F3}");
Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
8.在调用 Fit() 方法下方调用 Evaluate 方法。
C#复制
Evaluate(secondYearData, forecaster, mlContext);
保存模型
如果对模型满意,则保存它,以便以后用于其他应用程序。
1.在 Evaluate() 方法下面,创建 TimeSeriesPredictionEngine。 TimeSeriesPredictionEngine 是进行单个预测的一个便捷方法。
C#复制
var forecastEngine = forecaster.CreateTimeSeriesEngine
2.将此模型保存到由先前定义的 modelPath 变量指定的名为 MLModel.zip 的文件。 使用 Checkpoint 方法保存模型。
C#复制
forecastEngine.CheckPoint(mlContext, modelPath);
使用模型预测需求
1.在 Evaluate 方法下面,创建一个名为 Forecast 的新实用方法。
C#复制
void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine
{
}
2.在 Forecast 方法中,使用 Predict 方法预测接下来的 7 天的租赁数量。
C#复制
ModelOutput forecast = forecaster.Predict();
3.排列 7 个时段的实际值和预测值。
C#复制
IEnumerable
mlContext.Data.CreateEnumerable
.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";
});
4.循环访问预测输出,并在控制台上显示它。
C#复制
Console.WriteLine("Rental Forecast");
Console.WriteLine("---------------------");
foreach (var prediction in forecastOutput)
{
Console.WriteLine(prediction);
}
运行此应用程序
1.在调用 Checkpoint() 方法下方调用 Forecast 方法。
C#复制
Forecast(secondYearData, 7, forecastEngine, mlContext);
2.运行该应用程序。 控制台应显示类似以下内容的输出。 为简洁起见,输出已进行压缩。
text复制
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 存储库中找到本教程的源代码。