Kaggle时间序列(Time Series)教程 6-使用机器学习预测(forecasting-with-machine-learning)

引言

在第2课和第3课中,我们将预测视为一个简单的回归问题,我们的所有特征都来自单个输入,即时间索引。 只需生成我们想要的趋势和季节性特征,我们就可以轻松地创建未来任何时间的预测。

然而,当我们在第4课中添加滞后特征时,问题的性质发生了变化。 滞后特征要求在预测时已知滞后目标值。 一个lag_1将时间序列向前移动1步,这意味着您可以预测未来1步,但不能预测2步。

在第4课中,我们只是假设我们总是可以产生延迟到我们想要预测的时期(换句话说,每个预测都只是向前迈出一步)。 现实世界的预测通常需要更多的东西,因此在本课中,我们将学习如何针对各种情况进行预测。

定义预测任务

在设计预测模型之前,需要确定两件事:

  • 预测时可获得哪些信息(特征),以及,
  • 您需要预测的时间段。

**预测起点(forecast origin)**是您开始预测的时间点。 实际上,您可能会认为预测起点是您在预测时拥有的最后一条训练数据。 直到起点的一切都可以用来创建特征。

**预测范围(forecast horizon)**是您要预测的时间范围。 我们经常通过时间步长的长度来描述预测范围:例如,“1 步”预测或“5 步”预测。 预测范围描述的是目标值。

在这里插入图片描述

使用四个滞后特征。具有两步的前置时间(lead time)和三步预测范围 该图表示单行训练数据 -- 换句话说,用于单个预测的数据。

在起点和范围之间的时间叫预测的前置时间(lead time)(有时也叫 延迟(latency))。 预测的前置时间(lead time)指的是从起点到范围的步数:例如“向前1步”或“向前3步”预测。 在实践中,由于数据采集或处理的延迟,预测可能需要向前多个步骤再开始。

为预测准备数据

为了使用机器学习算法预测时间序列,我们需要将序列转换为可以与这些算法一起使用的dataframe。 (除非您只使用趋势和季节性等确定性特征。)

我们在第4课中看到了这个过程的前半部分,当时我们创建了一个滞后的特征集。 后半部分是准备目标值。 我们如何做到这一点取决于预测任务。

dataframe中的每一行代表一个预测。 该行的时间索引是预测范围内的第一个时间,但我们将整个范围的值安排在同一行中。 对于多步预测,这意味着我们需要一个模型来产生多个输出,每步输出一个。


import numpy as np
import pandas as pd

N = 20
ts = pd.Series(
    np.arange(N),
    index=pd.period_range(start='2010', freq='A', periods=N, name='Year'),
    dtype=pd.Int8Dtype,
)

# Lag features
X = pd.DataFrame({
    'y_lag_2': ts.shift(2),
    'y_lag_3': ts.shift(3),
    'y_lag_4': ts.shift(4),
    'y_lag_5': ts.shift(5),
    'y_lag_6': ts.shift(6),    
})

# Multistep targets
y = pd.DataFrame({
    'y_step_3': ts.shift(-2),
    'y_step_2': ts.shift(-1),
    'y_step_1': ts,
})

data = pd.concat({'Targets': y, 'Features': X}, axis=1)

data.head(10).style.set_properties(['Targets'], **{'background-color': 'LavenderBlush'}) \
                   .set_properties(['Features'], **{'background-color': 'Lavender'})

在这里插入图片描述
上面说明了如何通过类似于定义预测图来准备数据集:使用五个滞后特征的一个具有2步长度的前置时间的3步预测长度的预测任务。起始时间序列是y_step_1。 我们可以填写或删除缺失值。

多步预测策略

有许多策略可以生成预测所需的多个目标步骤。 我们将概述四种常见的策略,每种策略都有优点和缺点。

多输出模型

使用能够产生多个输出的模型。 线性回归和神经网络都可以产生多个输出。 这种策略简单而有效,但不可能适用于您可能想要使用的每种算法。 例如,XGBoost 无法做到这一点。
在这里插入图片描述

直接策略

为预测范围的每一步训练一个单独的模型:一个模型预测向前1步,另一个预测向前2步,依此类推。 向前1步预测与向前2步(等等)是不同的问题,因此它可以让不同的模型对每一步进行预测。 缺点是训练大量模型的计算成本可能很高。
在这里插入图片描述

递归策略

训练一个单步模型并使用其预测值来更新下一步的滞后特征。 使用递归方法,我们将模型的1步预测反馈回同一模型,以用作下一个预测步骤的滞后特征。 这样我们只需要训练一个模型,但由于错误会一步一步地传播,因此长期预测可能不准确。
在这里插入图片描述

DirRec策略

直接策略和递归策略的组合:为每个步骤训练一个模型,并使用来自先前步骤的预测作为新的滞后特征。 逐步地,每个模型都会获得一个额外的滞后输入。 由于每个模型总是有一组最新的滞后特征,DirRec 策略可以比直接策略更好地捕获序列依赖,但它也可能遭受像递归策略这样的错误传播。
在这里插入图片描述

示例 - 流感趋势

在此示例中,我们将对第4课中的流感趋势数据应用多输出和直接策略,对训练期之后的多个星期进行真实预测。

我们将我们的预测任务定义为8周的时间范围和1周的前置时间。 换句话说,我们将从下周开始预测接下来八周的流感病例数。

下面设置示例并定义辅助函数 plot_multistep


from pathlib import Path
from warnings import simplefilter

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
from xgboost import XGBRegressor

simplefilter("ignore")

# Set Matplotlib defaults
plt.style.use("seaborn-whitegrid")
plt.rc("figure", autolayout=True, figsize=(11, 4))
plt.rc(
    "axes",
    labelweight="bold",
    labelsize="large",
    titleweight="bold",
    titlesize=16,
    titlepad=10,
)
plot_params = dict(
    color="0.75",
    style=".-",
    markeredgecolor="0.25",
    markerfacecolor="0.25",
)
%config InlineBackend.figure_format = 'retina'


def plot_multistep(y, every=1, ax=None, palette_kwargs=None):
    palette_kwargs_ = dict(palette='husl', n_colors=16, desat=None)
    if palette_kwargs is not None:
        palette_kwargs_.update(palette_kwargs)
    palette = sns.color_palette(**palette_kwargs_)
    if ax is None:
        fig, ax = plt.subplots()
    ax.set_prop_cycle(plt.cycler('color', palette))
    for date, preds in y[::every].iterrows():
        preds.index = pd.period_range(start=date, periods=len(preds))
        preds.plot(ax=ax)
    return ax


data_dir = Path("./ts-course-data")
flu_trends = pd.read_csv(data_dir / "flu-trends.csv")
flu_trends.set_index(
    pd.PeriodIndex(flu_trends.Week, freq="W"),
    inplace=True,
)
flu_trends.drop("Week", axis=1, inplace=True)

首先,我们将准备我们的目标系列(每周流感的访问)以进行多步预测。 一旦完成,训练和预测将非常简单。

def make_lags(ts, lags, lead_time=1):
    return pd.concat(
        {
            f'y_lag_{i}': ts.shift(i)
            for i in range(lead_time, lags + lead_time)
        },
        axis=1)


# Four weeks of lag features
y = flu_trends.FluVisits.copy()
X = make_lags(y, lags=4).fillna(0.0)


def make_multistep_target(ts, steps):
    return pd.concat(
        {f'y_step_{i + 1}': ts.shift(-i)
         for i in range(steps)},
        axis=1)


# Eight-week forecast
y = make_multistep_target(y, steps=8).dropna()

# Shifting has created indexes that don't match. Only keep times for
# which we have both targets and features.
y, X = y.align(X, join='inner', axis=0)

多输出模型

我们将使用线性回归作为多输出策略。 一旦我们为多个输出准备好数据,训练和预测就和往常一样了。


# Create splits
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, shuffle=False)

model = LinearRegression()
model.fit(X_train, y_train)

y_fit = pd.DataFrame(model.predict(X_train), index=X_train.index, columns=y.columns)
y_pred = pd.DataFrame(model.predict(X_test), index=X_test.index, columns=y.columns)

请记住,多步模型将为用作输入的每个实例生成完整的预测。 训练集中有 269 周,测试集中有 90 周,我们现在对这些周中的每一周都有一个 8 周的预测。


train_rmse = mean_squared_error(y_train, y_fit, squared=False)
test_rmse = mean_squared_error(y_test, y_pred, squared=False)
print((f"Train RMSE: {train_rmse:.2f}\n" f"Test RMSE: {test_rmse:.2f}"))

palette = dict(palette='husl', n_colors=64)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(11, 6))
ax1 = flu_trends.FluVisits[y_fit.index].plot(**plot_params, ax=ax1)
ax1 = plot_multistep(y_fit, ax=ax1, palette_kwargs=palette)
_ = ax1.legend(['FluVisits (train)', 'Forecast'])
ax2 = flu_trends.FluVisits[y_pred.index].plot(**plot_params, ax=ax2)
ax2 = plot_multistep(y_pred, ax=ax2, palette_kwargs=palette)
_ = ax2.legend(['FluVisits (test)', 'Forecast'])

在这里插入图片描述

直接策略

XGBoost 不能为回归任务生成多个输出。 但是通过简单的方式,我们仍然可以使用它来生成多步预测。 这就是使用 scikit-learn 的 MultiOutputRegressor 来包装它。

from sklearn.multioutput import MultiOutputRegressor

model = MultiOutputRegressor(XGBRegressor())
model.fit(X_train, y_train)

y_fit = pd.DataFrame(model.predict(X_train), index=X_train.index, columns=y.columns)
y_pred = pd.DataFrame(model.predict(X_test), index=X_test.index, columns=y.columns)

这里的 XGBoost 显然在训练集上过度拟合。 但在测试集上,它似乎能够比线性回归模型更好地捕捉到流感季节的一些动态。 通过一些超参数调整它可能会做得更好。


train_rmse = mean_squared_error(y_train, y_fit, squared=False)
test_rmse = mean_squared_error(y_test, y_pred, squared=False)
print((f"Train RMSE: {train_rmse:.2f}\n" f"Test RMSE: {test_rmse:.2f}"))

palette = dict(palette='husl', n_colors=64)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(11, 6))
ax1 = flu_trends.FluVisits[y_fit.index].plot(**plot_params, ax=ax1)
ax1 = plot_multistep(y_fit, ax=ax1, palette_kwargs=palette)
_ = ax1.legend(['FluVisits (train)', 'Forecast'])
ax2 = flu_trends.FluVisits[y_pred.index].plot(**plot_params, ax=ax2)
ax2 = plot_multistep(y_pred, ax=ax2, palette_kwargs=palette)
_ = ax2.legend(['FluVisits (test)', 'Forecast'])

在这里插入图片描述
要使用 DirRec 策略,您只需将 MultiOutputRegressor 替换为另一个 scikit-learn 包装器 RegressorChain。 递归策略就需要我们需要自己编写代码来实现。

轮到你了

商店销售额创建预测数据集 并应用 DirRec 策略。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值