如何使用滞后预测时间序列

如何使用滞后预测时间序列

原文链接

如何使用滞后预测时间序列

.以下是如何利用它们来为你带来优势

作者截图

作者截图

时间序列模型的特点是过去值往往会影响未来值。当你的数据有任何形式的季节性(换句话说,你的数据遵循每小时、每天、每周、每月或每年的周期)时,这种关系甚至更加强烈。

通过特征如小时、星期几、月份等来捕捉这种关系,但你也可以添加滞后,这可以迅速将你的模型提升到下一个层次。

什么是滞后?

滞后值简单来说就是:在某个时间点或另一个时间点,先于你的当前值。

假设你有一个时间序列数据集,具有以下值:[5,10,15,20,25]。

25,作为你最新的值,是时间 t 的值。

20 是 t-1 的值,15 是 t-2 的值,以此类推,直到数据集的开始。

这在直观上是有意义的,因为“滞后”这个词暗示着某物“落后”于另一物。

当我们使用滞后特征训练模型时,我们可以训练它识别与先前值如何影响当前和未来值相关的模式。

为什么使用滞后?(+ 如何实现)

为了展示滞后如何使你的模型受益,我将通过一个例子来指导你,使用一个每小时能源消耗数据集 (CC0 1.0 许可)。

这里是这个数据集大约四周的样本,这样你可以感受到它的样子:

作者截图

作者截图

如你所见,周末(用红色圆圈表示)的利用率在这四周切片中较低,存在一些周季节性。还有明显的日季节性,因为一天的高峰通常在 17:00 到 19:00(下午 5 点到 7 点)之间。

当你放大查看时,你还可以看到不同月份的年使用模式不同(尤其是夏季与冬季月份)。

作者截图

作者截图

如果我要训练一个常规的时间序列模型,我会关注以下特征:

  • 一天中的小时

  • 星期几

  • 年份中的月份

我将用一个例子来展示如何使用NIXTLA mlforecast 库,因为它不仅使时间序列预测变得非常简单,而且还能轻松地将滞后特征添加到你的时间序列模型中。

首先,我使用我列出的特征训练了一个常规模型。首先,我将数据集加载进来,并为其准备 NIXTLA 库:

import pandas as pd
from mlforecast import MLForecast
from sklearn.ensemble import RandomForestRegressor

# Load in data and basic data cleaning
df = pd.read_csv('AEP_hourly.csv')
df['Datetime']=pd.to_datetime(df['Datetime'])
# Sort by date
df.set_index('Datetime',inplace=True)
df.sort_index(inplace=True)
df.reset_index(inplace=True)
# This dataset is huge with over 100,000 rows
# Get only the last 10,000 rows of hourly data (a little over a year of data)
df = df.tail(10000)

# NIXTLA requires that your date/timestamp column be named "ds"
# and your target variable be named y
df.rename(columns={'Datetime':'ds','AEP_MW':'y'},inplace=True)
# NIXTLA requires a "unique_id" column in case you are training
# more than one model using different datasets, but if you're only
# training with 1 dataset, I just create a dummy constant variable
# column with a value of 1
df['unique_id']=1

然后,我通过将数据分为训练集和测试集来准备我的建模数据:

# Split into train/test sets. For this problem, 
# I don't want a huge test set, since when using lags, you'll have to
# predict using predictions after the first hour forecast.
# (More on this later)
# So my test set will be only 48 hours long (48 rows)
train_size = df.shape[0] - 48

df_train = df.iloc[:train_size]
df_test = df.iloc[train_size:]

接下来,我使用 NIXTLA 训练了一个随机森林模型:

# NIXTLA allows you to train multiple models, so it requires
# a list as an input. For this exercise, I only trained 1 model.
models = [
    RandomForestRegressor(random_state=0)
]

# Instantiate an MLForecast object and pass in:
# - models: list of models for training
# - freq: timestamp frequency (in this case it is hourly data "H")
# - lags: list of lag features (blank for now)
# - date_features: list of time series date features like hour, month, day
fcst = MLForecast(
    models=models,
    freq='H',
    lags=[],
    date_features=['hour','month','dayofweek']
)

# Fit to train set
fcst.fit(df_train)

最后,我在测试集上进行了预测(使用预测对象预测接下来的 48 小时,并将其与测试集值进行比较),以及运行了 3 个窗口的交叉验证,每个窗口预测 24 小时块:

from sklearn.metrics import mean_squared_error
from utilsforecast.evaluation import evaluate
from utilsforecast.losses import rmse

# Predict 
predictions = fcst.predict(48)

# Compare to test set. This returns a result of 689.9 RMSE
print(mean_squared_error(df_test.y.values,predictions.RandomForestRegressor.values,squared=False))

# Run cross validation with train df
cv_df = fcst.cross_validation(
    df=df_train,
    h=24,
    n_windows=3,
)

# Get CV RMSE metric
cv_rmse = evaluate(
    cv_df.drop(columns='cutoff'),
    metrics=[rmse], 
    agg_fn='mean'
)

# Prints out 1264.1
print(f"RMSE using cross-validation: {cv_rmse['RandomForestRegressor'].item():.1f}")

因此,使用这个时间序列模型——使用小时、星期几和月份——平均交叉验证 RMSE 为 1264.1,测试集 RMSE 为 689.9。

让我们比较一下基于滞后的模型。

# Pass in lags as a list argument - I'm tracking lags for 24 hours
# since the goal of our model is to forecast 24 hours at a time
fcst_lags = MLForecast(
    models=models,
    freq='H', 
    lags=range(1,25), 
    date_features=[] 
)

fcst_lags.fit(df_train)

# Predict 24 hours twice for the test set (48 hours)
predictions_lags = fcst_lags.predict(48)

# RMSE test score w/ lags: 421.86
print(mean_squared_error(df_test.y.values,predictions_lags.RandomForestRegressor.values,squared=False))

# Cross validation:
cv_df_lags = fcst_lags.cross_validation(
    df=df_train,
    h=24,
    n_windows=3,
)

cv_rmse_lags = evaluate(
    cv_df_lags.drop(columns='cutoff'),
    metrics=[rmse], 
    agg_fn='mean'
)

# RMSE for CV w/ lags: 1038.7
print(f"RMSE using cross-validation: {cv_rmse_lags['RandomForestRegressor'].item():.1f}")

让我们并排比较一下指标:

  • 无滞后模型 + 时间序列特征(小时、星期几、月份)测试 RMSE: 689.9

  • 无滞后模型 + 时间序列特征 CV RMSE: 1264.1

  • 具有滞后模型的测试 RMSE: 421.86

  • 具有滞后模型的 CV RMSE: 1038.7

备注:在两种情况下,CV RMSE 都远高于测试 RMSE。这是因为 CV 评估的数据与测试集不同。

CV 在以下 3 天进行评估:7 月 29 日至 31 日,每次预测 24 小时,并取平均值。保留测试集在 8 月 1 日至 2 日进行评估,每次预测 48 小时。

为了进一步调查这个问题,我绘制了交叉验证预测值与实际值的对比图,并得到了每个分割窗口(共有 3 个——每天一个)的 RMSE。

在这两种情况下,模型对 7 月 30 日(RMSE 为 1445)的预测都存在显著的低估,对 7 月 31 日(RMSE 为 888)的预测略有高估。这导致了 CV 平均值上升。

滞后预测模型交叉验证预测值与实际值。图片由作者提供

滞后预测模型交叉验证预测值与实际值。图片由作者提供

因此,可能由于某些原因(可能是我们没有考虑的其他变量,例如天气),在两种情况下,CV 验证集的表现都不太好。

当你的指标看起来有点不正常时,总是很重要去调查发生了什么。

在一个真实的机器学习项目案例中,我会深入调查为什么这些天特别难以预测,但为了本文的目的,我不会这样做——只是指出,这样做总是个好主意。

如果我取平均值:

无滞后模型的模型: 997

具有滞后模型的模型: 730.28

然而,无论平均数如何,具有滞后的模型在 CV 和保留测试集上都优于无滞后的模型。

一点注意事项

滞后可以为你的模型提供有用的信息并提高其性能。它们也相对容易实现,尤其是在像 NIXTLA(注意:我不是 NIXTLA 的赞助商)这样的良好构建的时间序列库的帮助下。

但过度依赖滞后可能会成为一个问题,尤其是如果你的目标是预测更长时间的范围。

基本上,关于滞后的处理是这样的:在某个时刻,模型不再有实际值作为特征来使用,因此它必须依赖预测。这给模型引入了一些误差。随着你做出越来越多的预测,误差会累积。

例如,假设你只使用了一个滞后列,并且你的数据集如下所示:[1,2,3,4,5]。你在这个数据集上训练了你的模型,现在你需要预测接下来的 5 行。

在第一次遍历中,我们来到了超过 5 个时间步之后,让我们称它为时间 t。t-1 的滞后特征是 5。模型预测下一个值将是 6。为了预测 t+1 的下一个值,我们需要使用我们的预测:6。这引入了不确定性,因为 6 是一个预测值,而不是一个真实的滞后值。

如果模型在 t+1 时预测 8,那么下一个预测,在 t+2 时必须将 8(由于使用了 6 作为特征而具有额外不确定性的预测)作为下一个滞后特征。

以此类推,每次预测的不确定性都会增加。

因此,你可以看到这可能会在更长的预测范围内导致更差的表现。

结论

滞后项对于预测较短的时间范围非常有用,例如 1 到 48 行。之后你需要仔细观察你的误差。使用预测区间来衡量不确定性。

除了滞后项之外,优先考虑其他数值和分类特征也很重要。标记是否是假日、周末还是工作日,或者季节,也可以改善你的模型,以及包括外部变量,如温度。

然而,一切取决于你的具体数据集,所以总是要尝试多个特征和组合进行实验。

你可以在这里找到完整的源代码和数据集。

感谢阅读

  • LinkedIn上与我联系。

  • 我现在在Topmate提供 1 对 1 的数据科学辅导、职业指导/辅导、写作建议、简历审查等服务!

每当 Haden Pelletier 发布时,都会收到电子邮件

posted @ 2026-03-28 09:34  绝不原创的飞龙  阅读(3)  评论(0)    收藏  举报