在预测中,我们经常需要较低和较高(时间)粒度的预测,例如产品需求预测,但也包括产品类别或产品部门预测。这些粒度可以通过使用层次结构来形式化。在分层预测中,我们创建与基础时间序列的预定义层次结构一致的预测。

使用 TimeGPT,我们可以创建多个时间序列的预测。随后,我们可以使用 HierarchicalForecast 的分层预测技术对这些预测进行后处理。

1. 导入软件包

首先,我们导入所需的软件包并初始化 Nixtla 客户端。

import pandas as pd
import numpy as np

from nixtla import NixtlaClient
nixtla_client = NixtlaClient(
    # defaults to os.environ.get("NIXTLA_API_KEY")
    api_key = 'my_api_key_provided_by_nixtla'
)

👍 使用 Azure AI 端点

要使用 Azure AI 端点,请设置 base_url 参数

nixtla_client = NixtlaClient(base_url="you azure ai endpoint", api_key="your api_key")

2. 加载数据

我们使用澳大利亚旅游数据集,该数据集来自 Forecasting, Principles and Practices,其中包含澳大利亚旅游数据。我们对澳大利亚的 7 个州、27 个区域和 76 个地区的预测感兴趣。这构成了一个层次结构,其中较低级别(例如悉尼、蓝山和亨特等地区)的预测应与较高级别(例如新南威尔士州)的预测一致。

该数据集仅包含最低级别的时间序列,因此我们需要为所有层次结构创建时间序列。

Y_df = pd.read_csv('https://raw.githubusercontent.com/Nixtla/transfer-learning-time-series/main/datasets/tourism.csv')
Y_df = Y_df.rename({'Trips': 'y', 'Quarter': 'ds'}, axis=1)
Y_df.insert(0, 'Country', 'Australia')
Y_df = Y_df[['Country', 'Region', 'State', 'Purpose', 'ds', 'y']]
Y_df['ds'] = Y_df['ds'].str.replace(r'(\d+) (Q\d)', r'\1-\2', regex=True)
Y_df['ds'] = pd.to_datetime(Y_df['ds'])

Y_df.head(10)
C:\Users\ospra\AppData\Local\Temp\ipykernel_16668\3753786659.py:6: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  Y_df['ds'] = pd.to_datetime(Y_df['ds'])
国家区域目的dsy
0澳大利亚阿德莱德南澳大利亚商务1998-01-01135.077690
1澳大利亚阿德莱德南澳大利亚商务1998-04-01109.987316
2澳大利亚阿德莱德南澳大利亚商务1998-07-01166.034687
3澳大利亚阿德莱德南澳大利亚商务1998-10-01127.160464
4澳大利亚阿德莱德南澳大利亚商务1999-01-01137.448533
5澳大利亚阿德莱德南澳大利亚商务1999-04-01199.912586
6澳大利亚阿德莱德南澳大利亚商务1999-07-01169.355090
7澳大利亚阿德莱德南澳大利亚商务1999-10-01134.357937
8澳大利亚阿德莱德南澳大利亚商务2000-01-01154.034398
9澳大利亚阿德莱德南澳大利亚商务2000-04-01168.776364

数据集可以按以下分层结构分组。

spec = [
    ['Country'],
    ['Country', 'State'], 
    ['Country', 'Purpose'], 
    ['Country', 'State', 'Region'], 
    ['Country', 'State', 'Purpose'], 
    ['Country', 'State', 'Region', 'Purpose']
]

使用 HierarchicalForecast 中的 aggregate 函数,我们可以获得完整的时间序列集。

注意

您可以使用 pip 安装 hierarchicalforecast

pip install hierarchicalforecast
from hierarchicalforecast.utils import aggregate
Y_df, S_df, tags = aggregate(Y_df, spec)

Y_df.head(10)
unique_iddsy
0澳大利亚1998-01-0123182.197269
1澳大利亚1998-04-0120323.380067
2澳大利亚1998-07-0119826.640511
3澳大利亚1998-10-0120830.129891
4澳大利亚1999-01-0122087.353380
5澳大利亚1999-04-0121458.373285
6澳大利亚1999-07-0119914.192508
7澳大利亚1999-10-0120027.925640
8澳大利亚2000-01-0122339.294779
9澳大利亚2000-04-0119941.063482

我们使用最后两年(8 个季度)作为测试集。

Y_test_df = Y_df.groupby('unique_id').tail(8)
Y_train_df = Y_df.drop(Y_test_df.index)

3. 使用 TimeGPT 进行分层预测

首先,我们使用 TimeGPT 为所有时间序列创建基本预测。请注意,我们设置了 add_history=True,因为我们将需要 TimeGPT 的样本内拟合值。

我们将预测 2 年(8 个季度),从 2016 年 01 月 01 日开始。

timegpt_fcst = nixtla_client.forecast(df=Y_train_df, h=8, freq='QS', add_history=True)
INFO:nixtla.nixtla_client:Validating inputs...
INFO:nixtla.nixtla_client:Preprocessing dataframes...
INFO:nixtla.nixtla_client:Querying model metadata...
INFO:nixtla.nixtla_client:Calling Forecast Endpoint...
INFO:nixtla.nixtla_client:Calling Historical Forecast Endpoint...

📘 Azure AI 中的可用模型

如果您使用 Azure AI 端点,请务必设置 model="azureai"

nixtla_client.forecast(..., model="azureai")

对于公共 API,我们支持两种模型:timegpt-1timegpt-1-long-horizon

默认情况下,使用 timegpt-1。关于如何以及何时使用 timegpt-1-long-horizon,请参阅 本教程

timegpt_fcst_insample = timegpt_fcst.query("ds < '2016-01-01'")
timegpt_fcst_outsample = timegpt_fcst.query("ds >= '2016-01-01'")

让我们绘制一些预测图,从最高聚合级别 (Australia) 到最低级别 (Australia/Queensland/Brisbane/Holiday)。我们可以看到这些预测仍有改进的空间。

nixtla_client.plot(
    Y_df, 
    timegpt_fcst_outsample, 
    max_insample_length=4 * 12, 
    unique_ids=['Australia', 'Australia/Queensland','Australia/Queensland/Brisbane', 'Australia/Queensland/Brisbane/Holiday']
)

我们可以通过使用 NeuralForecast 中的 HierarchicalReconciliation 方法,使这些预测与指定的层次结构一致。我们将使用 MinTrace 方法。

from hierarchicalforecast.methods import MinTrace
from hierarchicalforecast.core import HierarchicalReconciliation
reconcilers = [
    MinTrace(method='ols'),
    MinTrace(method='mint_shrink'),
]
hrec = HierarchicalReconciliation(reconcilers=reconcilers)

Y_df_with_insample_fcsts = Y_df.copy()
Y_df_with_insample_fcsts = timegpt_fcst_insample.merge(Y_df_with_insample_fcsts)

Y_rec_df = hrec.reconcile(Y_hat_df=timegpt_fcst_outsample, Y_df=Y_df_with_insample_fcsts, S=S_df, tags=tags)
Y_rec_df
unique_iddsTimeGPTTimeGPT/MinTrace_method-olsTimeGPT/MinTrace_method-mint_shrink
0澳大利亚2016-01-0124967.1910025044.40863425394.406211
1澳大利亚2016-04-0124528.8830024503.08981024327.212355
2澳大利亚2016-07-0124221.7750024083.10781223813.826553
3澳大利亚2016-10-0124559.4400024548.03879724174.894203
4澳大利亚2017-01-0125570.3380025669.24828125560.277473
3395Australia/Western Australia/Experience Perth/V…2016-10-01427.81146435.423617434.047102
3396Australia/Western Australia/Experience Perth/V…2017-01-01450.71786453.434056459.954598
3397Australia/Western Australia/Experience Perth/V…2017-04-01452.17923460.197847470.009789
3398Australia/Western Australia/Experience Perth/V…2017-07-01450.68683463.034888482.645932
3399Australia/Western Australia/Experience Perth/V…2017-10-01443.31050451.754435474.403379

再次,我们绘制一些预测图。我们可以看到预测之间存在一些(主要是微小的)差异。

nixtla_client.plot(
    Y_df, 
    Y_rec_df, 
    max_insample_length=4 * 12, 
    unique_ids=['Australia', 'Australia/Queensland','Australia/Queensland/Brisbane', 'Australia/Queensland/Brisbane/Holiday']
)

让我们从数值上验证在不应用后处理步骤的情况下的预测。我们可以为此使用 HierarchicalEvaluation

from hierarchicalforecast.evaluation import evaluate
from utilsforecast.losses import rmse
eval_tags = {}
eval_tags['Total'] = tags['Country']
eval_tags['Purpose'] = tags['Country/Purpose']
eval_tags['State'] = tags['Country/State']
eval_tags['Regions'] = tags['Country/State/Region']
eval_tags['Bottom'] = tags['Country/State/Region/Purpose']

evaluation = evaluate(
        df=Y_rec_df.merge(Y_test_df, on=['unique_id', 'ds']),
        tags=eval_tags, 
        train_df=Y_train_df,
        metrics=[rmse],
)       
numeric_cols = evaluation.select_dtypes(np.number).columns
evaluation[numeric_cols] = evaluation[numeric_cols].map('{:.2f}'.format)
evaluation
级别指标TimeGPTTimeGPT/MinTrace_method-olsTimeGPT/MinTrace_method-mint_shrink
0总计rmse1433.071436.071627.43
1目的rmse482.09475.64507.50
2rmse275.85278.39294.28
3区域rmse49.4047.9147.99
4底部rmse19.3219.1118.86
5总体rmse38.6638.2139.16

通过使用 MinTrace(ols) 对预测进行协调,我们使总体 RMSE 略有改进;而使用 MinTrace(mint_shrink) 则使预测略微变差,这表明基础预测本身已经相对较强。

然而,我们现在也获得了协调一致的预测——因此,我们不仅实现了(小幅)精度改进,还通过协调步骤获得了与层次结构的一致性。

参考文献