首页 > 解决方案 > 带有 ML.NET 的 SSA 预测返回奇怪的结果

问题描述

我刚开始使用 ML.NET,所以我尝试在我的代码上调整Microsoft的教程,这应该可以预测未来的预订:

static void Main(string[] args)
{
    MLContext mlContext = new MLContext();
    ModelInput[] modelInputFirstYear = Database.Instance.GetBookingsOfYear(2018).ToArray();
    ModelInput[] modelInputSecondYear = Database.Instance.GetBookingsOfYear(2019).ToArray();
    modelInputFirstYear = FillDates(modelInputFirstYear); //FillDates fills the array on days where no bookings were made with a date and the TotalBookings = 0, so it has 365 items
    modelInputSecondYear = FillDates(modelInputSecondYear);
    IDataView firstYear = mlContext.Data.LoadFromEnumerable<ModelInput>(modelInputFirstYear);
    IDataView secondYear = mlContext.Data.LoadFromEnumerable<ModelInput>(modelInputSecondYear);

    var forecastingPipeline = mlContext.Forecasting.ForecastBySsa(
        outputColumnName: "ForecastedBookings",
        inputColumnName: "TotalBookings",
        windowSize: 7, 
        seriesLength: 30,
        trainSize: 365, 
        horizon: 7,
        confidenceLevel: 0.95f,
        confidenceLowerBoundColumn: "LowerBoundBookings",
        confidenceUpperBoundColumn: "UpperBoundBookings");

    SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYear);
    Evaluate(secondYear, forecaster, mlContext);
    var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
    Forecast(secondYear, 7, forecastEngine, mlContext);
}

private static void Evaluate(IDataView testData, ITransformer model, MLContext mlContext)
{
    IDataView predictions = model.Transform(testData);
    IEnumerable<float> actual = mlContext.Data.CreateEnumerable<ModelInput>(testData, true).Select(observed => observed.TotalBookings);
    IEnumerable<float> forecast = mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true).Select(prediction => prediction.ForecastedBookings[0]);
    var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
    var MAE = metrics.Average(error => Math.Abs(error));
    var RMSE = Math.Sqrt(metrics.Average(error => Math.Pow(error, 2))); 
    Console.WriteLine(" > Evaluation Metrics");
    Console.WriteLine($"     > Mean Absolute Error: {MAE:F3}");
    Console.WriteLine($"     > Root Mean Squared Error: {RMSE:F3}\n");
}

private static void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext)
{
    ModelOutput forecast = forecaster.Predict();
    IEnumerable<string> forecastOutput = mlContext.Data.CreateEnumerable<ModelInput>(testData, reuseRowObject: false)
    .Take(horizon)
    .Select((ModelInput rental, int index) =>
    {
        string rentalDate = rental.BookingDate.ToShortDateString();
        float actualRentals = rental.TotalBookings;
        float lowerEstimate = Math.Max(0, forecast.LowerBoundBookings[index]);
        float estimate = forecast.ForecastedBookings[index];
        float upperEstimate = forecast.UpperBoundBookings[index];
        return $"Date: {rentalDate}\n" +
        $"Actual Rentals: {actualRentals}\n" +
        $"Lower Estimate: {lowerEstimate}\n" +
        $"Forecast: {estimate}\n" +
        $"Upper Estimate: {upperEstimate}\n";
    });
    Console.WriteLine(" > Rental Forecast");
    Console.WriteLine("");
    foreach (var prediction in forecastOutput)
    {
        Console.WriteLine(prediction);
    }
}

ModelInputModelOutput类:

public class ModelInput 
{
    public DateTime BookingDate { get; set; }
    public float Year { get; set; }
    public float TotalBookings { get; set; }
}

public class ModelOutput
{
    public float[] ForecastedBookings { get; set; }
    public float[] LowerBoundBookings { get; set; }
    public float[] UpperBoundBookings { get; set; }
}

但预测值返回奇怪的结果:

应用程序的输出

标签: c#machine-learningml.net

解决方案


两个想法:

  1. 我们在国际化方面遇到了一些问题,我注意到您似乎使用逗号来表示小数点。您可以尝试使用美国格式的数据,看看这是否能解决您的问题?
  2. 负值可能不是不准确的。我注意到您在代码中取了下限和 0 的最大值,但我敢打赌,如果您返回实际的下限,它也将是负数。您的数据实际上可能暗示了负面趋势。

推荐阅读