统计学、机器学习和神经网络预测方法 在本教程中,我们将探讨如何通过为 M5 数据集中的每个时间序列利用最合适的模型来进行预测。我们将通过一项重要的技术——交叉验证来完成此任务。这种方法有助于我们评估模型的预测性能,并选择在每个时间序列上产生最佳性能的模型。

M5 数据集包含沃尔玛为期五年的分层销售数据。目标是预测未来 28 天的每日销售额。该数据集细分为美国的 50 个州,每个州有 10 家商店。

在时间序列预测和分析领域,一个更复杂的任务是确定最适合特定序列组的模型。通常,这个选择过程很大程度上依赖于直觉,而这可能并不一定与我们数据集的实际情况相符。

在本教程中,我们的目标是为 M5 基准数据集中的不同序列组提供一种更结构化、数据驱动的模型选择方法。该数据集在预测领域广为人知,它使我们能够展示我们方法的通用性和强大之处。

我们将训练来自各种预测范式的多种模型

StatsForecast

  • 基准模型:这些模型虽然简单,但对于提供预测问题的初步视角通常非常有效。我们将使用 SeasonalNaiveHistoricAverage 模型作为此类别。
  • 间歇性模型:对于需求零星、非连续的序列,我们将使用 CrostonOptimizedIMAPAADIDA 等模型。这些模型特别适用于处理零膨胀序列。
  • 状态空间模型:这些是使用系统数学描述进行预测的统计模型。来自 statsforecast 库的 AutoETS 模型属于此类。

MLForecast

机器学习:利用像 LightGBMXGBoostLinearRegression 这样的机器学习模型具有优势,因为它们能够发现数据中的复杂模式。我们将为此使用 MLForecast 库。

NeuralForecast

深度学习:像 Transformer (AutoTFT) 和神经网络 (AutoNHITS) 这样的深度学习模型使我们能够处理时间序列数据中复杂的非线性依赖关系。我们将使用 NeuralForecast 库来处理这些模型。

使用 Nixtla 库套件,我们将能够用数据驱动我们的模型选择过程,确保我们为数据集中的特定序列组利用最合适的模型。

大纲

  • 读取数据:在第一步,我们将数据集加载到内存中,以便进行后续的分析和预测。在此阶段,了解数据集的结构和细微之处非常重要。

  • 使用统计学和深度学习方法进行预测:我们应用了从基本统计技术到高级深度学习模型的各种预测方法。目标是根据我们的数据集生成未来 28 天的预测。

  • 在不同窗口上评估模型性能:我们在不同的窗口上评估模型的性能。

  • 为序列组选择最佳模型:利用性能评估,我们确定每个序列组的最佳模型。此步骤确保所选模型适合每个组的独特特征。

  • 过滤最佳预测:最后,我们过滤由我们选择的模型生成的预测,以获得最有前景的预测。这是我们的最终输出,代表了根据我们的模型对每个序列的最佳预测。

警告

本教程最初是使用 c5d.24xlarge EC2 实例执行的。

安装库

!pip install statsforecast mlforecast neuralforecast pyarrow

下载和准备数据

此示例使用 M5 数据集。它包含 30,490 个底部时间序列。

import pandas as pd
# Load the training target dataset from the provided URL
Y_df = pd.read_parquet('https://m5-benchmarks.s3.amazonaws.com/data/train/target.parquet')

# Rename columns to match the Nixtlaverse's expectations
# The 'item_id' becomes 'unique_id' representing the unique identifier of the time series
# The 'timestamp' becomes 'ds' representing the time stamp of the data points
# The 'demand' becomes 'y' representing the target variable we want to forecast
Y_df = Y_df.rename(
    columns={
        'item_id': 'unique_id', 
        'timestamp': 'ds', 
        'demand': 'y'
    }
)

# Convert the 'ds' column to datetime format to ensure proper handling of date-related operations in subsequent steps
Y_df['ds'] = pd.to_datetime(Y_df['ds'])

为简单起见,我们只保留一个类别

Y_df = Y_df.query('unique_id.str.startswith("FOODS_3")').reset_index(drop=True)
Y_df['unique_id'] = Y_df['unique_id'].astype(str)

基本绘图

使用来自 utilsforecast 库的 plot_series 函数绘制一些序列。此方法会打印数据集中的 8 个随机序列,对于基本探索性数据分析 (EDA) 非常有用。

from utilsforecast.plotting import plot_series
# Feature: plot random series for EDA
plot_series(Y_df)

# Feature: plot groups of series for EDA
plot_series(Y_df, ids=["FOODS_3_432_TX_2"])

使用 Stats、ML 和 Neural 方法创建预测。

StatsForecast

StatsForecast 是一个综合性库,提供一套流行的单变量时间序列预测模型,所有模型都专注于高性能和可伸缩性。

以下是使 StatsForecast 成为时间序列预测强大工具的原因

  • 局部模型集合:StatsForecast 提供了各种局部模型,这些模型可以单独应用于每个时间序列,使我们能够捕获每个序列中的独特模式。

  • 简单性:使用 StatsForecast,训练、预测和回测多个模型变得直截了当,只需几行代码即可完成。这种简单性使其成为初学者和有经验的从业者都方便使用的工具。

  • 速度优化:StatsForecast 中模型的实现针对速度进行了优化,确保大规模计算高效执行,从而减少模型训练和预测的总时间。

  • 水平可伸缩性:StatsForecast 的显着特点之一是其水平扩展能力。它与 Spark、Dask 和 Ray 等分布式计算框架兼容。此功能使其能够通过在集群中的多个节点上分配计算来高效处理大型数据集,使其成为大型时间序列预测任务的首选解决方案。

StatsForecast 接收一个模型列表以拟合每个时间序列。由于我们处理的是每日数据,将季节性设置为 7 会很有益。

from statsforecast import StatsForecast
# Import necessary models from the statsforecast library
from statsforecast.models import (
    # SeasonalNaive: A model that uses the previous season's data as the forecast
    SeasonalNaive,
    # Naive: A simple model that uses the last observed value as the forecast
    Naive,
    # HistoricAverage: This model uses the average of all historical data as the forecast
    HistoricAverage,
    # CrostonOptimized: A model specifically designed for intermittent demand forecasting
    CrostonOptimized,
    # ADIDA: Adaptive combination of Intermittent Demand Approaches, a model designed for intermittent demand
    ADIDA,
    # IMAPA: Intermittent Multiplicative AutoRegressive Average, a model for intermittent series that incorporates autocorrelation
    IMAPA,
    # AutoETS: Automated Exponential Smoothing model that automatically selects the best Exponential Smoothing model based on AIC
    AutoETS
)

我们通过实例化一个新的 StatsForecast 对象并使用以下参数来拟合模型

  • models:模型列表。从模型中选择您想要的模型并导入它们。
  • freq:一个字符串,指示数据的频率。(请参阅 panda 的可用频率。)
  • n_jobs:int,并行处理中使用的作业数,使用 -1 表示所有核心。
  • fallback_model:如果模型失败时使用的模型。任何设置都会传递到构造函数中。然后调用其 fit 方法并传入历史数据框。
horizon = 28
models = [
    SeasonalNaive(season_length=7),
    Naive(),
    HistoricAverage(),
    CrostonOptimized(),
    ADIDA(),
    IMAPA(),
    AutoETS(season_length=7)
]
# Instantiate the StatsForecast class
sf = StatsForecast(
    models=models,  # A list of models to be used for forecasting
    freq='D',  # The frequency of the time series data (in this case, 'D' stands for daily frequency)
    n_jobs=-1,  # The number of CPU cores to use for parallel execution (-1 means use all available cores)
    verbose=True,  # Show progress
)

预测方法会为接下来的 h 个周期生成预测。

此处的预测对象是一个新的数据框,其中包含一列,其中包含模型名称和 y hat 值。

此代码块测量 StatsForecast 类的预测函数运行所需的时间,该函数预测未来 28 天(h=28)。时间以分钟为单位计算并在最后打印出来。

from time import time

# Get the current time before forecasting starts, this will be used to measure the execution time
init = time()

# Call the forecast method of the StatsForecast instance to predict the next 28 days (h=28) 
fcst_df = sf.forecast(df=Y_df, h=28)

# Get the current time after the forecasting ends
end = time()

# Calculate and print the total time taken for the forecasting in minutes
print(f'Forecast Minutes: {(end - init) / 60}')
Forecast:   0%|          | 0/2000 [Elapsed: 00:00]
Forecast Minutes: 4.009805858135223
fcst_df.head()
unique_iddsSeasonalNaiveNaiveHistoricAverageCrostonOptimizedADIDAIMAPAAutoETS
0FOODS_3_001_CA_12016-05-231.02.00.4487380.3451920.3454770.3472490.381414
1FOODS_3_001_CA_12016-05-240.02.00.4487380.3451920.3454770.3472490.286933
2FOODS_3_001_CA_12016-05-250.02.00.4487380.3451920.3454770.3472490.334987
3FOODS_3_001_CA_12016-05-261.02.00.4487380.3451920.3454770.3472490.186851
4FOODS_3_001_CA_12016-05-270.02.00.4487380.3451920.3454770.3472490.308112

MLForecast

MLForecast 是一个强大的库,为时间序列预测提供自动特征创建,便于使用全局机器学习模型。它旨在实现高性能和可伸缩性。

MLForecast 的主要功能包括

  • 支持 sklearn 模型:MLForecast 与遵循 scikit-learn API 的模型兼容。这使得它高度灵活,并允许其与各种机器学习算法无缝集成。

  • 简单性:使用 MLForecast,训练、预测和回测模型只需几行代码即可完成。这种简化的简单性使其对各个专业水平的从业者都很友好。

  • 速度优化:MLForecast 旨在快速执行任务,这在处理大型数据集和复杂模型时至关重要。

  • 水平可伸缩性:MLForecast 能够使用 Spark、Dask 和 Ray 等分布式计算框架进行水平扩展。此功能使其能够通过在集群中的多个节点上分配计算来高效处理大型数据集,使其成为大型时间序列预测任务的理想选择。

from mlforecast import MLForecast
from mlforecast.lag_transforms import ExpandingMean
from mlforecast.target_transforms import Differences
from mlforecast.utils import PredictionIntervals
!pip install lightgbm xgboost
# Import the necessary models from various libraries

# LGBMRegressor: A gradient boosting framework that uses tree-based learning algorithms from the LightGBM library
from lightgbm import LGBMRegressor

# XGBRegressor: A gradient boosting regressor model from the XGBoost library
from xgboost import XGBRegressor

# LinearRegression: A simple linear regression model from the scikit-learn library
from sklearn.linear_model import LinearRegression

要将 MLForecast 用于时间序列预测,我们实例化一个新的 MLForecast 对象,并为其提供各种参数以根据我们的特定需求调整建模过程

  • models:此参数接受您希望用于预测的机器学习模型列表。您可以从 scikit-learn、lightgbm 和 xgboost 中导入您喜欢的模型。

  • freq:这是一个字符串,指示数据的频率(每小时、每日、每周等)。此字符串的具体格式应与 pandas 识别的频率字符串对齐。

  • target_transforms:这些是在模型训练之前和模型预测之后应用于目标变量的转换。这在处理可能受益于转换(例如对高度偏斜数据进行对数转换)的数据时非常有用。

  • lags:此参数接受要用作回归变量的特定滞后值。滞后表示在为模型创建特征时,您希望回溯多长时间。例如,如果您希望使用前一天的数据作为预测当天值的特征,您将指定滞后为 1。

  • lags_transforms:这些是每个滞后的特定转换。这允许您对滞后特征应用转换。

  • date_features:此参数指定要用作回归变量的日期相关特征。例如,您可能希望将星期几或月份包含在模型中作为特征。

  • num_threads:此参数控制用于并行化特征创建的线程数,有助于在处理大型数据集时加快此过程。

所有这些设置都传递给 MLForecast 构造函数。一旦使用这些设置初始化 MLForecast 对象,我们就会调用其 fit 方法并传递历史数据框作为参数。fit 方法在提供的历史数据上训练模型,为未来的预测任务做好准备。

# Instantiate the MLForecast object
mlf = MLForecast(
    models=[LGBMRegressor(verbosity=-1), XGBRegressor(), LinearRegression()],  # List of models for forecasting: LightGBM, XGBoost and Linear Regression
    freq='D',  # Frequency of the data - 'D' for daily frequency
    lags=list(range(1, 7)),  # Specific lags to use as regressors: 1 to 6 days
    lag_transforms = {
        1: [ExpandingMean()],  # Apply expanding mean transformation to the lag of 1 day
    },
    date_features=['year', 'month', 'day', 'dayofweek', 'quarter', 'week'],  # Date features to use as regressors
)

只需调用 fit 模型即可训练所选模型。在这种情况下,我们正在生成一致性预测区间。

# Start the timer to calculate the time taken for fitting the models
init = time()

# Fit the MLForecast models to the data
mlf.fit(Y_df)

# Calculate the end time after fitting the models
end = time()

# Print the time taken to fit the MLForecast models, in minutes
print(f'MLForecast Minutes: {(end - init) / 60}')
MLForecast Minutes: 0.5360581119855244

之后,只需调用 predict 即可生成预测。

fcst_mlf_df = mlf.predict(28)
fcst_mlf_df.head()
unique_iddsLGBMRegressorXGBRegressorLinearRegression
0FOODS_3_001_CA_12016-05-230.5495200.5601230.332693
1FOODS_3_001_CA_12016-05-240.5531960.3693370.055071
2FOODS_3_001_CA_12016-05-250.5996680.3743380.127144
3FOODS_3_001_CA_12016-05-260.6380970.3271760.101624
4FOODS_3_001_CA_12016-05-270.7633050.3316310.269863

NeuralForecast

NeuralForecast 是一个强大的神经网络预测模型集合,专注于可用性和性能。它包含各种模型架构,从多层感知机 (MLP) 和循环神经网络 (RNN) 等经典网络到 N-BEATS、N-HITS、时间融合 Transformer (TFT) 等新颖贡献,等等。

>NeuralForecast 的主要功能包括

  • 广泛的全局模型集合。开箱即用的 MLP、LSTM、RNN、TCN、DilatedRNN、NBEATS、NHITS、ESRNN、TFT、Informer、PatchTST 和 HINT 实现。
  • 简单直观的界面,只需几行代码即可训练、预测和回测各种模型。
  • 支持 GPU 加速,提高计算速度。

这台机器没有 GPU,但 Google Colabs 提供免费的 GPU。

使用 Colab 的 GPU 训练 NeuralForecast

# Read the results from Colab
fcst_nf_df = pd.read_parquet('https://m5-benchmarks.s3.amazonaws.com/data/forecast-nf.parquet')
fcst_nf_df.head()
unique_iddsAutoNHITSAutoNHITS-lo-90AutoNHITS-hi-90AutoTFTAutoTFT-lo-90AutoTFT-hi-90
0FOODS_3_001_CA_12016-05-230.00.02.00.00.02.0
1FOODS_3_001_CA_12016-05-240.00.02.00.00.02.0
2FOODS_3_001_CA_12016-05-250.00.02.00.00.01.0
3FOODS_3_001_CA_12016-05-260.00.02.00.00.02.0
4FOODS_3_001_CA_12016-05-270.00.02.00.00.02.0
# Merge the forecasts from StatsForecast and NeuralForecast
fcst_df = fcst_df.merge(fcst_nf_df, how='left', on=['unique_id', 'ds'])

# Merge the forecasts from MLForecast into the combined forecast dataframe
fcst_df = fcst_df.merge(fcst_mlf_df, how='left', on=['unique_id', 'ds'])
fcst_df.head()
unique_iddsSeasonalNaiveNaiveHistoricAverageCrostonOptimizedADIDAIMAPAAutoETSAutoNHITSAutoNHITS-lo-90AutoNHITS-hi-90AutoTFTAutoTFT-lo-90AutoTFT-hi-90LGBMRegressorXGBRegressorLinearRegression
0FOODS_3_001_CA_12016-05-231.02.00.4487380.3451920.3454770.3472490.3814140.00.02.00.00.02.00.5495200.5601230.332693
1FOODS_3_001_CA_12016-05-240.02.00.4487380.3451920.3454770.3472490.2869330.00.02.00.00.02.00.5531960.3693370.055071
2FOODS_3_001_CA_12016-05-250.02.00.4487380.3451920.3454770.3472490.3349870.00.02.00.00.01.00.5996680.3743380.127144
3FOODS_3_001_CA_12016-05-261.02.00.4487380.3451920.3454770.3472490.1868510.00.02.00.00.02.00.6380970.3271760.101624
4FOODS_3_001_CA_12016-05-270.02.00.4487380.3451920.3454770.3472490.3081120.00.02.00.00.02.00.7633050.3316310.269863

预测图

plot_series(Y_df, fcst_df, max_insample_length=28 * 3)

使用 plot 函数探索模型和 ID

plot_series(
    Y_df,
    fcst_df,
    max_insample_length=28 * 3, 
    models=['CrostonOptimized', 'AutoNHITS', 'SeasonalNaive', 'LGBMRegressor'],
)

验证模型性能

这三个库 - StatsForecastMLForecastNeuralForecast - 提供专门为时间序列设计的开箱即用交叉验证功能。这使我们能够使用历史数据评估模型性能,以获得对每个模型在新数据上可能表现如何的无偏评估。

StatsForecast 中的交叉验证

StatsForecast 类中的 cross_validation 方法接受以下参数

  • df:表示训练数据的 DataFrame。
  • h (int):预测范围,表示我们希望预测未来的步数。例如,如果我们预测每小时数据,h=24 将表示 24 小时的预测。
  • step_size (int):每个交叉验证窗口之间的步长。此参数确定我们希望运行预测过程的频率。
  • n_windows (int):用于交叉验证的窗口数。此参数定义了我们希望评估多少个过去的预测过程。

这些参数使我们能够控制交叉验证过程的范围和粒度。通过调整这些设置,我们可以在计算成本和交叉验证的彻底性之间取得平衡。

sf.verbose = False
init = time()
cv_df = sf.cross_validation(df=Y_df, h=horizon, n_windows=3, step_size=horizon)
end = time()
print(f'CV Minutes: {(end - init) / 60}')
CV Minutes: 10.829525109132131

crossvaldation_df 对象是一个新的数据框,其中包含以下列

  • unique_id 序列标识符
  • ds:日期戳或时间索引
  • cutoff:n_windows 的最后一个日期戳或时间索引。如果 n_windows=1,则有一个唯一的截止值;如果 n_windows=2,则有两个唯一的截止值。
  • y:真实值
  • "model":包含模型名称和拟合值的列。
cv_df.head()
unique_iddscutoffySeasonalNaiveNaiveHistoricAverageCrostonOptimizedADIDAIMAPAAutoETS
0FOODS_3_001_CA_12016-02-292016-02-280.02.00.00.4491110.6184720.6183750.6179980.655286
1FOODS_3_001_CA_12016-03-012016-02-281.00.00.00.4491110.6184720.6183750.6179980.568595
2FOODS_3_001_CA_12016-03-022016-02-281.00.00.00.4491110.6184720.6183750.6179980.618805
3FOODS_3_001_CA_12016-03-032016-02-280.01.00.00.4491110.6184720.6183750.6179980.455891
4FOODS_3_001_CA_12016-03-042016-02-280.01.00.00.4491110.6184720.6183750.6179980.591197

MLForecast

MLForecast 类中的 cross_validation 方法接受以下参数。

  • df:训练数据框
  • h (int):表示预测未来的步数。在此示例中,提前 24 小时。
  • step_size (int):每个窗口之间的步长。换句话说:您希望运行预测过程的频率。
  • n_windows (int):用于交叉验证的窗口数。换句话说:您希望评估过去多少个预测过程。
init = time()
cv_mlf_df = mlf.cross_validation(
    df=Y_df, 
    h=horizon,
    n_windows=3,
)
end = time()
print(f'CV Minutes: {(end - init) / 60}')
CV Minutes: 1.6215598344802857

crossvaldation_df 对象是一个新的数据框,其中包含以下列

  • unique_id 序列标识符
  • ds:日期戳或时间索引
  • cutoff:n_windows 的最后一个日期戳或时间索引。如果 n_windows=1,则有一个唯一的截止值;如果 n_windows=2,则有两个唯一的截止值。
  • y:真实值
  • "model":包含模型名称和拟合值的列。
cv_mlf_df.head()
unique_iddscutoffyLGBMRegressorXGBRegressorLinearRegression
0FOODS_3_001_CA_12016-02-292016-02-280.00.4356740.556261-0.353077
1FOODS_3_001_CA_12016-03-012016-02-281.00.6396760.625807-0.088985
2FOODS_3_001_CA_12016-03-022016-02-281.00.7929890.6596510.217697
3FOODS_3_001_CA_12016-03-032016-02-280.00.8068680.5351210.438713
4FOODS_3_001_CA_12016-03-042016-02-280.00.8291060.3133540.637066

NeuralForecast

这台机器没有 GPU,但 Google Colabs 提供免费的 GPU。

使用 Colab 的 GPU 训练 NeuralForecast

cv_nf_df = pd.read_parquet('https://m5-benchmarks.s3.amazonaws.com/data/cross-validation-nf.parquet')
cv_nf_df.head()
unique_iddscutoffAutoNHITSAutoNHITS-lo-90AutoNHITS-hi-90AutoTFTAutoTFT-lo-90AutoTFT-hi-90y
0FOODS_3_001_CA_12016-02-292016-02-280.00.02.01.00.02.00.0
1FOODS_3_001_CA_12016-03-012016-02-280.00.02.01.00.02.01.0
2FOODS_3_001_CA_12016-03-022016-02-280.00.02.01.00.02.01.0
3FOODS_3_001_CA_12016-03-032016-02-280.00.02.01.00.02.00.0
4FOODS_3_001_CA_12016-03-042016-02-280.00.02.01.00.02.00.0

合并交叉验证预测

cv_df = cv_df.merge(cv_nf_df.drop(columns=['y']), how='left', on=['unique_id', 'ds', 'cutoff'])
cv_df = cv_df.merge(cv_mlf_df.drop(columns=['y']), how='left', on=['unique_id', 'ds', 'cutoff'])

绘制交叉验证结果

cutoffs = cv_df['cutoff'].unique()
for cutoff in cutoffs:
    display(
        plot_series(
            Y_df, 
            cv_df.query('cutoff == @cutoff').drop(columns=['y', 'cutoff']), 
            max_insample_length=28 * 5, 
            ids=['FOODS_3_001_CA_1'],
        )
    )

总需求

agg_cv_df = cv_df.loc[:,~cv_df.columns.str.contains('hi|lo')].groupby(['ds', 'cutoff']).sum(numeric_only=True).reset_index()
agg_cv_df.insert(0, 'unique_id', 'agg_demand')
agg_Y_df = Y_df.groupby(['ds']).sum(numeric_only=True).reset_index()
agg_Y_df.insert(0, 'unique_id', 'agg_demand')
for cutoff in cutoffs:
    display(
        plot_series(
            agg_Y_df, 
            agg_cv_df.query('cutoff == @cutoff').drop(columns=['y', 'cutoff']),
            max_insample_length=28 * 5,
        )
    )

每个序列和交叉验证窗口的评估

在本节中,我们将评估每个模型在每个时间序列上的性能。

from utilsforecast.evaluation import evaluate
from utilsforecast.losses import mse, mae, smape
evaluation_df = evaluate(cv_df.drop(columns='cutoff'), metrics=[mse, mae, smape])
evaluation_df
unique_id指标SeasonalNaiveNaiveHistoricAverageCrostonOptimizedADIDAIMAPAAutoETSAutoNHITSAutoTFTLGBMRegressorXGBRegressorLinearRegression
0FOODS_3_001_CA_1mse1.2500000.8928570.4851820.5079570.5092990.5169880.4942350.6309520.5714290.6489620.5847220.529400
1FOODS_3_001_CA_2mse6.2738093.7738093.4773093.4125803.4322953.4740503.4264684.5505953.6071433.4236463.8564653.773264
2FOODS_3_001_CA_3mse5.8809524.3571435.0163964.1731544.1606454.1767334.1451484.0059524.3720244.9287646.9377925.317195
3FOODS_3_001_CA_4mse1.0714290.4761900.4029380.3825590.3807830.3808770.3808720.4761900.4761900.6642700.4240680.637221
4FOODS_3_001_TX_1mse0.0476190.0476190.2388240.2613560.0476190.0476190.0775750.0476190.0476190.7187960.0635640.187810
24685FOODS_3_827_TX_2smape0.0833330.0357140.9895400.9963620.9873950.9828470.9815370.3238100.3357140.9763560.9947020.985058
24686FOODS_3_827_TX_3smape0.7085320.6814950.6624900.6530570.6558100.6601610.6491800.6839470.7121210.6395180.8568660.686547
24687FOODS_3_827_WI_1smape0.6087220.6943280.4705700.4708460.4800320.4800320.4669560.4868520.4759800.4723360.4849060.492277
24688FOODS_3_827_WI_2smape0.5317770.3981560.4335770.3877180.3888270.3893710.3898880.3937740.3746400.4135590.4308930.399131
24689FOODS_3_827_WI_3smape0.6436890.6801780.5880310.5891430.5998200.6286730.5914370.5582010.5674600.5898700.6987980.627255
by_metric = evaluation_df.groupby('metric').mean(numeric_only=True)
by_metric
SeasonalNaiveNaiveHistoricAverageCrostonOptimizedADIDAIMAPAAutoETSAutoNHITSAutoTFTLGBMRegressorXGBRegressorLinearRegression
指标
mae1.7754152.0459061.7490801.6347911.5420971.5437451.5115451.4382501.4976471.6979471.5520611.592978
mse14.26577320.45332512.93813611.48423311.09019511.09444610.3519279.60691310.72125110.50228911.56591610.830894
smape0.4364140.4464300.6168840.6132190.6189100.6193130.6200840.4007700.4110180.5798560.6936150.641515

按指标划分的最佳模型

by_metric.idxmin(axis=1)
metric
mae      AutoNHITS
mse      AutoNHITS
smape    AutoNHITS
dtype: object

误差分布

!pip install seaborn
import matplotlib.pyplot as plt
import seaborn as sns
evaluation_df_long = pd.melt(evaluation_df, id_vars=['unique_id', 'metric'], var_name='model', value_name='error')

SMAPE

sns.violinplot(evaluation_df_long.query('metric=="smape"'), x='error', y='model');

为序列组选择模型

特征

  • 包含所有不同模型预测的统一数据框
  • 简单集成
  • 例如,平均预测
  • 或 MinMax(选择即是集成)
# Choose the best model for each time series, metric, and cross validation window
evaluation_df['best_model'] = evaluation_df.idxmin(axis=1, numeric_only=True)
# count how many times a model wins per metric and cross validation window
count_best_model = evaluation_df.groupby(['metric', 'best_model']).size().rename('n').to_frame().reset_index()
# plot results
sns.barplot(count_best_model, x='n', y='best_model', hue='metric')

Et pluribus unum:一个包容性的预测结果。

# For the mse, calculate how many times a model wins
eval_series_df = evaluation_df.query('metric == "mse"').groupby(['unique_id']).mean(numeric_only=True)
eval_series_df['best_model'] = eval_series_df.idxmin(axis=1)
counts_series = eval_series_df.value_counts('best_model')
plt.pie(counts_series, labels=counts_series.index, autopct='%.0f%%')
plt.show()

plot_series(
    Y_df,
    cv_df.drop(columns=['cutoff', 'y']), 
    max_insample_length=28 * 6, 
    models=['AutoNHITS'],
)

为不同序列组选择预测方法

# Merge the best model per time series dataframe
# and filter the forecasts based on that dataframe
# for each time series
fcst_df = pd.melt(fcst_df.set_index('unique_id'), id_vars=['ds'], var_name='model', value_name='forecast', ignore_index=False)
fcst_df = fcst_df.join(eval_series_df[['best_model']])
fcst_df[['model', 'pred-interval']] = fcst_df['model'].str.split('-', expand=True, n=1)
fcst_df = fcst_df.query('model == best_model')
fcst_df['name'] = [f'forecast-{x}' if x is not None else 'forecast' for x in fcst_df['pred-interval']]
fcst_df = pd.pivot_table(fcst_df, index=['unique_id', 'ds'], values=['forecast'], columns=['name']).droplevel(0, axis=1).reset_index()
plot_series(Y_df, fcst_df, max_insample_length=28 * 3)

更多资料