每一步一个模型

%load_ext autoreload
%autoreload 2

训练一个模型来预测预测范围内的每一步。

默认情况下,mlforecast 使用递归策略,即训练一个模型来预测下一个值。如果我们要预测多个值,就逐个进行,然后使用模型的预测作为新的目标,重新计算特征并预测下一步。

还有一种方法,如果我们想预测 10 步,我们可以训练 10 个不同的模型,其中每个模型都训练用来预测每个特定步的值,即一个模型预测下一个值,另一个预测两步后的值,依此类推。这可能非常耗时,但也可能提供更好的结果。如果您想使用这种方法,可以在 MLForecast.fit 中指定 max_horizon,这样将训练对应数量的模型,并且每个模型在您调用 MLForecast.predict 时将预测其相应的时间跨度。

设置

import random
import lightgbm as lgb
import pandas as pd
from datasetsforecast.m4 import M4, M4Info
from utilsforecast.evaluation import evaluate
from utilsforecast.losses import smape

from mlforecast import MLForecast
from mlforecast.lag_transforms import ExponentiallyWeightedMean, RollingMean
from mlforecast.target_transforms import Differences

数据

我们将使用来自M4数据集的四个随机序列。

group = 'Hourly'
await M4.async_download('data', group=group)
df, *_ = M4.load(directory='data', group=group)
df['ds'] = df['ds'].astype('int')
ids = df['unique_id'].unique()
random.seed(0)
sample_ids = random.choices(ids, k=4)
sample_df = df[df['unique_id'].isin(sample_ids)]
info = M4Info[group]
horizon = info.horizon
valid = sample_df.groupby('unique_id').tail(horizon)
train = sample_df.drop(valid.index)
def avg_smape(df):
    """按序列计算SMAPE,然后对所有序列取平均值。"""
    full = df.merge(valid)
    return (
        evaluate(full, metrics=[smape])
        .drop(columns='metric')
        .set_index('unique_id')
        .squeeze()
    )

模型

fcst = MLForecast(
    models=lgb.LGBMRegressor(random_state=0, verbosity=-1),
    freq=1,
    lags=[24 * (i+1) for i in range(7)],
    lag_transforms={
        1: [RollingMean(window_size=24)],
        24: [RollingMean(window_size=24)],
        48: [ExponentiallyWeightedMean(alpha=0.3)],
    },
    num_threads=1,
    target_transforms=[Differences([24])],
)
horizon = 24
# 以下将训练24个模型,每个模型对应一个预测时间范围。
individual_fcst = fcst.fit(train, max_horizon=horizon)
individual_preds = individual_fcst.predict(horizon)
avg_smape_individual = avg_smape(individual_preds).rename('individual')
# 以下将训练一个单一模型,并采用递归策略。
recursive_fcst = fcst.fit(train)
recursive_preds = recursive_fcst.predict(horizon)
avg_smape_recursive = avg_smape(recursive_preds).rename('recursive')
# 结果
print('Average SMAPE per method and serie')
avg_smape_individual.to_frame().join(avg_smape_recursive).applymap('{:.1%}'.format)
Average SMAPE per method and serie
individual recursive
unique_id
H196 0.3% 0.3%
H256 0.4% 0.3%
H381 20.9% 9.5%
H413 11.9% 13.6%
# 我们在第一个时间步得到了相同的预测结果。
pd.testing.assert_frame_equal(
    individual_preds.groupby('unique_id').head(1).astype({'ds': 'int64'}),
    recursive_preds.groupby('unique_id').head(1).astype({'ds': 'int64'}),    
)

Give us a ⭐ on Github