Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
docs
scripts
src
README.md
THIRD-PARTY-NOTICES.txt
eShopDashboardML.sln

README.md

eShopDashboardML - 销售预测

ML.NET 版本 API 类型 状态 应用程序类型 数据类型 场景 机器学习任务 算法
v1.3.1 动态 API 最新版本 ASP.NET Core Web应用程序和控制台应用程序 SQL Server 和 .csv 文件 销售预测 回归 FastTreeTweedie Regression, Single Spectrum Analysis

eShopDashboardML是一个使用ML.NET 进行(每个产品和每个地区)销售预测的Web应用程序。

概述

这个终端示例应用程序通过展现以下主题着重介绍ML.NET API的用法:

  1. 如何训练,建立和生成ML模型
  2. 如何使用经过训练的ML模型做下个月的销售预测

该应用程序还使用一个SQL Server数据库存储常规产品目录和订单信息,就像许多使用SQL Server的典型Web应用程序一样。在本例中,由于它是一个示例,因此默认情况下使用localdb SQL数据库,因此不需要设置真正的SQL Server。在第一次运行Web应用程序时,将创建localdb数据库并包含示例数据。

如果要使用真正的SQL Server或Azure SQL数据库,只需更改应用程序中的连接字符串即可。

当你运行应用程序时,它会打开一个网页,上面有一个搜索框,上面写着“输入一个产品”。你可以输入任何产品,例如“瓶子”。然后与关键字“瓶子”相关的产品列表将显示在自动完成建议中。选择任何产品后,该产品的销售预测将显示如下。

这是Web应用程序的一个销售预测屏幕截图示例:

image

设置

了解如何在 Visual Studio 中设置以及对代码的进一步说明:

ML.NET代码简介

问题

这个问题是基于之前的销售情况围绕地区和产品进行销售预测

数据集

为了解决这个问题,您建立了两个独立的ML模型,它们以以下数据集作为输入:

数据集
products stats next, productId, year, month, units, avg, count, max, min, prev
country stats next, country, year, month, max, min, std, count, sales, med, prev

数据集说明 - 转到此链接可获取有关数据集的详细信息。

ML 任务 - 回归预测与时间序列预测

这个示例显示了可用于预测的两种不同的ML任务和算法:

  • 回归 使用 FastTreeTweedie Regression
  • 时间序列 使用 Single Spectrum Analysis

回归 是一个有监督的机器学习任务,用于从一组相关的特征/变量预测下一个期间的值(在本例中是销售预测)。 回归 最适合线性数据。

时间序列 是一种估计技术,可用于预测未来的多个周期。时间序列在涉及难以区分趋势和模式的非线性数据的情况下很好地工作。

解决方案

为了解决这个问题,首先我们将建立ML模型,同时根据现有数据训练每个模型,评估其有多好,最后使用模型预测销售。

注意回归样本实现了两个独立的模型来预测线性数据:

  • 下一个周期(月)产品需求预测模型
  • 下一个周期(月)地区销售预测模型

时间序列样本实现产品在未来两个周期(月)的需求预测。时间序列样本使用与回归样本相同的产品,以便您可以比较两种算法的预测。

在学习/研究样本时,您可以选择专注于回归或时间序列。

Build -> Train -> Evaluate -> Consume

加载数据集

回归时间序列样本均通过使用TextLoader加载数据开始。 要使用TextLoader,我们必须指定代表数据模式的类的类型。 我们的类类型为ProductData

 public class ProductData
    {
        // The index of column in LoadColumn(int index) should be matched with the position of columns in the underlying data file.
        // The next column is used by the Regression algorithm as the Label (e.g. the value that is being predicted by the Regression model).
        [LoadColumn(0)]
        public float next;

        [LoadColumn(1)]
        public string productId;

        [LoadColumn(2)]
        public float year;

        [LoadColumn(3)]
        public float month;

        [LoadColumn(4)]
        public float units;

        [LoadColumn(5)]
        public float avg;

        [LoadColumn(6)]
        public float count;

        [LoadColumn(7)]
        public float max;

        [LoadColumn(8)]
        public float min;

        [LoadColumn(9)]
        public float prev;
    }

将数据集加载到DataView中。

var trainingDataView = mlContext.Data.LoadFromTextFile<ProductData>(dataPath, hasHeader: true, separatorChar:',');

在接下来的步骤中,我们将构建转换管道,指定要使用的训练器/算法,评估模型并测试其预测。 这是回归时间序列示例之间的步骤开始有所不同的地方-本演练的其余部分分别研究了每种算法。

回归

1. 回归: 创建管道

此步骤说明如何创建稍后用于构建和训练回归模型的管道。

具体来说,我们进行以下转换:

  • 连接当前特征生成名为NumFeatures的新列
  • 使用独热编码转换productId
  • 连接所有生成的特征生成名为Features的新列
  • 复制next列将其重命名为Label
  • 指定Fast Tree Tweedie训练器作为算法应用于模型

在设计管道之后,您可以将数据集加载到DataView中,而且此步骤只是配置,DataView是延迟加载,在下一步训练模型之前数据不会被加载。

建立模型并训练

var trainer = mlContext.Regression.Trainers.FastTreeTweedie("Label", "Features");

var trainingPipeline = mlContext.Transforms.Concatenate(outputColumnName: "NumFeatures", nameof(CountryData.year),
                                nameof(CountryData.month), nameof(CountryData.max), nameof(CountryData.min),
                                nameof(CountryData.std), nameof(CountryData.count), nameof(CountryData.sales),
                                nameof(CountryData.med), nameof(CountryData.prev))
                    .Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "CatFeatures", inputColumnName: nameof(CountryData.country)))
                    .Append(mlContext.Transforms.Concatenate(outputColumnName: "Features", "NumFeatures", "CatFeatures"))
                    .Append(mlContext.Transforms.CopyColumns(outputColumnName: "Label", inputColumnName: nameof(CountryData.next)))
                    .Append(trainer);

2. 回归: 评估模型

在本例中,回归模型的评估是在使用交叉验证方法训练模型之前执行的,因此您将获得指示模型准确度的指标。

var crossValidationResults = mlContext.Regression.CrossValidate(data:trainingDataView, estimator:trainingPipeline, numberOfFolds: 6, labelColumnName: "Label");

ConsoleHelper.PrintRegressionFoldsAverageMetrics(trainer.ToString(), crossValidationResults);

3. 回归: 训练模型

在建立管道之后,我们通过使用所选算法拟合或使用训练数据来训练预测回归模型。 在该步骤中,模型被建立,训练并作为对象返回:

var model = trainingPipeline.Fit(trainingDataView);

4. 回归: 保存模型

一旦创建和评估了回归模型,就可以将它保存到zip文件中,任何最终用户的应用程序都可以通过以下代码使用它:

using (var file = File.OpenWrite(outputModelPath))
    mlContext.Model.Save(model, trainingDataView.Schema, file);

5. 回归: 测试预测

zip文件中加载回归模型。

本示例使用产品样本数据的最后一个月来预测下个月的单位销售额。

ITransformer trainedModel;
using (var stream = File.OpenRead(outputModelPath))
{
    trainedModel = mlContext.Model.Load(stream,out var modelInputSchema);
}

var predictionEngine = mlContext.Model.CreatePredictionEngine<CountryData, CountrySalesPrediction>(trainedModel);

Console.WriteLine("** Testing Product 1 **");

// Build sample data
ProductData dataSample = new ProductData()
{
    productId = "263",
    month = 10,
    year = 2017,
    avg = 91,
    max = 370,
    min = 1,
    count = 10,
    prev = 1675,
    units = 910
};

// Predict the next period/month forecast to the one provided
ProductUnitPrediction prediction = predictionEngine.Predict(dataSample);
Console.WriteLine($"Product: {dataSample.productId}, month: {dataSample.month + 1}, year: {dataSample.year} - Real value (units): 551, Forecast Prediction (units): {prediction.Score}");

时间序列

1. 时间序列: 创建管道

此步骤说明如何创建稍后用于训练时间序列模型的管道。

具体来说,**Single Spectrum Analysis (SSA)**训练器是所使用的算法。此算法使用以下参数:

  • outputColumnName: 这是将用于存储预测的列的名称。列必须是single类型的向量。在后面的步骤中,我们定义了一个名为ProductUnitTimeSeriesPrediction的类,该类包含此输出列。
  • inputColumnName: 这是正在预测的列的名称。该列包含时间序列中时间戳处的值,并且必须是single类型。在我们的示例中,我们正在预测产品数量
  • windowSize: 该参数用于定义滑动时间窗,该滑动时间窗用于将时间序列数据分解为趋势、季节或噪声分量。通常,应该从窗口大小开始,窗口大小代表场景中的业务周期。在我们的示例中,产品数据基于12个月的周期,因此我们将选择一个窗口大小为12的倍数。
  • seriesLength:
  • trainSize:
  • horizon: 此参数指示要预测的时段数。在我们的示例中,我们指定了2,以表示将预测未来2个月的产品数量。
  • confidenceLevel: 此参数表示预测值落在指定间隔范围内的可能性。通常,.95是一个可接受的起点。
  • confidenceLowerBoundColumn: 这是用于存储每个预测值的下限置信区间的列的名称。ProductUnitTimeSeriesPrediction类也包含此输出列。
  • confidenceUpperBoundColumn: 这是将用于存储每个预测值的上限置信区间的列的名称。ProductUnitTimeSeriesPrediction类也包含此输出列。

具体来说,我们在管道中增加了以下训练器:

// Create and add the forecast estimator to the pipeline.
IEstimator<ITransformer> forecastEstimator = mlContext.Forecasting.ForecastBySsa(
    outputColumnName: nameof(ProductUnitTimeSeriesPrediction.ForecastedProductUnits),
    inputColumnName: nameof(ProductData.units),
    windowSize: 3,
    seriesLength: productDataSeriesLength,
    trainSize: productDataSeriesLength,
    horizon: 2,
    confidenceLevel: 0.95f,
    confidenceLowerBoundColumn: nameof(ProductUnitTimeSeriesPrediction.ConfidenceLowerBound),
    confidenceUpperBoundColumn: nameof(ProductUnitTimeSeriesPrediction.ConfidenceUpperBound));

2. 时间序列: 训练模型

在训练时间序列模型之前,我们首先必须筛选加载的数据集,以选择将用于预测销售的特定产品的数据序列。

var productId = 988;
IDataView productDataView = mlContext.Data.FilterRowsByColumn(allProductsDataView, nameof(ProductData.productId), productId, productId + 1);

接下来,我们使用指定产品的数据系列来训练模型。

// Train the forecasting model for the specified product's data series.
ITransformer forecastTransformer = forecastEstimator.Fit(productDataView);

3. 时间序列: 保存模型

要保存模型,我们首先必须创建TimeSeriesPredictionEngine用于获取预测和保存模型。

使用CheckPoint方法保存时间序列模型,该方法将模型保存到任何最终用户应用程序都可以使用的zip文件中:

// Create the forecast engine used for creating predictions.
TimeSeriesPredictionEngine<ProductData, ProductUnitTimeSeriesPrediction> forecastEngine = forecastTransformer.CreateTimeSeriesEngine<ProductData, ProductUnitTimeSeriesPrediction>(mlContext);

// Save the forecasting model so that it can be loaded within an end-user app.
forecastEngine.CheckPoint(mlContext, outputModelPath);

您可能会注意到这与上面的回归示例不同,后者使用保存方法来保存模型。时间序列是不同的,因为它要求在进行预测时用新的观测值不断更新模型的状态。因此,存在CheckPoint方法来更新和保存模型状态。这将在本示例后面的步骤中进一步详细说明。现在,请记住CheckPoint用于保存时间序列模型。

4. 时间序列: 测试预测

要获得预测,请从zip文件加载时间序列模型,并创建新的TimeSeriesPredictionEngine。在这之后,我们可以得到一个预测。

// Load the forecast engine that has been previously saved.
ITransformer forecaster;
using (var file = File.OpenRead(outputModelPath))
{
    forecaster = mlContext.Model.Load(file, out DataViewSchema schema);
}

// We must create a new prediction engine from the persisted model.
TimeSeriesPredictionEngine<ProductData, ProductUnitTimeSeriesPrediction> forecastEngine = forecastTransformer.CreateTimeSeriesEngine<ProductData, ProductUnitTimeSeriesPrediction>(mlContext); forecastEngine = forecaster.CreateTimeSeriesEngine<ProductData, ProductUnitTimeSeriesPrediction>(mlContext);

ProductUnitTimeSeriesPrediction originalSalesPrediction = forecastEngine.Predict();

创建TimeSeriesPredictionEngine时指定的ProductUnitTimeSeriesPrediction类型用于存储预测结果:

   public class ProductUnitTimeSeriesPrediction
    {
        public float[] ForecastedProductUnits { get; set; }

        public float[] ConfidenceLowerBound { get; set; }

        public float[] ConfidenceUpperBound { get; set; }
    }

记住,当我们使用ForecastBySsa方法创建SSA Forecasting Trainer时,我们提供了以下参数值:

  • horizon: 2
  • confidenceLevel: .95f

因此,当我们使用加载的模型调用Predict方法时,ForecastedProductUnits向量将包含两个预测值。同样,ConfidenceLowerBoundConfidenceUpperBound向量将分别包含基于指定的confidenceLevel两个值。

您可能注意到Predict方法有几个重载,它们接受以下参数:

  • horizon
  • confidenceLevel
  • ProductData example

这允许您在每次进行预测时为horizonconfidenceLevel指定新值。此外,还可以使用example参数为时间序列传递新的观察到的ProductData值。

当使用新的观察到的ProductData值调用Predict时,这将用时间序列中的这些数据点更新模型状态。然后,您可以选择通过调用CheckPoint方法将此模型保存到磁盘。

这在我们的示例中也可以看到:

ProductUnitTimeSeriesPrediction updatedSalesPrediction = forecastEngine.Predict(newProductData, horizon: 1);

 // Save the updated forecasting model.
 forecastEngine.CheckPoint(mlContext, outputModelPath);

引用

eShopDashboardML数据集是基于UCI(http://archive.ics.uci.edu/ml/datasets/online+retail) 的一个公共在线零售数据集

Daqing Chen, Sai Liang Sain, 和 Kun Guo, 在线零售业的数据挖掘: 基于RFM模型的数据挖掘客户细分案例研究, 数据库营销与客户战略管理杂志, Vol. 19, No. 3, pp. 197–208, 2012 (印刷前在线发布: 27 August 2012. doi: 10.1057/dbm.2012.17).

You can’t perform that action at this time.