机器学习多步时间序列预测解决方案

机器学习多步时间序列预测解决方案_第1张图片

机器学习多步时间序列预测解决方案_第2张图片

点击上方入口立即【自由构建 探索无限】

一起共赴年度科技盛宴!

1

简介

近年来,随着机器学习与深度学习的发展,以及 Amazon SageMaker

(https://aws.amazon.com/cn/sagemaker/)等机器学习平台的成熟,数据科学家们不再需要关心底层的基础设施及构建复杂的训练与推理环境,从而可以把主要的时间与精力放在数据与算法本身。在机器学习变得更容易的今天,越来越多的传统行业已经开始使用机器学习算法来解决现实中的问题,降低成本及提升效率。在能源、制造及零售快消等传统行业中,多步回归预测任务是较为常见的业务场景之一。

例如:您是一家超市的经理,想要预测未来几周的销售情况,并且已经获得了数百种产品的每日销售历史数据。您将面临的问题是什么样的?

时间序列预测(如 ARIMA, Exponential Smoothing 等)可能会很自然地出现在您的脑海中。使用此类算法,您可以将产品的销量作为单独的时间序列,并根据其历史销量来推算其未来的销售额。

然而,您可能会发现每个产品的销售并非独立的,它们之间或许存在着某种依赖性。例如,在夏天时,空调的销量会增加,同时羽绒服的销量会下降。当存在着多个变量时,我们就需要使用多变量时间序列预测(multi-variable time series forecasting)来处理变量间的相关性。

此外,您希望的是预测未来几周的销售情况,而非仅仅预测明天的销量,因此,需要的是预测未来的多个时间步骤。这种预测未来的多个时间步长的任务被称为多步时间序列预测(multi-step time series forecasting)。多步时间序列预测通常有四种主要的方法:

  • 多步递归预测(recursive multi-step forecasting)

  • 多步直接预测(direct multi-step forecasting)

  • 直接&递归融合预测(direct-recursive hybrid forecasting)

  • 多输出预测(multiple output forecasting)

下面我们会分别讲解这4种方法的原理,对比他们的性能,并在 SageMaker Notebook 中实现它们。可以在命令行执行下列命令,下载相关代码及数据集。

git clone https://github.com/aws-samples/multi-step-forecasting-blog-sample-code

左滑查看更多

2

多步递归预测

(recursive multi-step 

forecasting)

多步递归预测在单步预测的基础上,把下一步的预测值作为输入,来进行迭代预测。需要注意的是:在多变量的多步回归分析中,由于存在外部变量作为输入,需要同时预测外部变量。多步递归的过程如下图所示:

机器学习多步时间序列预测解决方案_第3张图片

优点:

  • 需要的模型数量固定

  • 计算量较低

  • 预测的偏差(bias)相对于多步直接预测(后面会讲到)较低

缺点:

  • 由于使用上一步的预测值作为输入,预测误差会随着时间传递扩大,预测的方差(variance)较高

机器学习多步时间序列预测解决方案_第4张图片

下面我们结合多变量回归分析,来实现多步递归预测。数据集

(https://aws.amazon.com/cn/blogs/china/machine-learning-multi-step-time-series-prediction-solution/)

使用 Wage Growth and the Inflation Process: An Empirical Approach 论文中的公开数据集,涵盖1959年 Q1 至1988年 Q4 的工资数据。数据集共有123行和8列,列的定义:

  1. rgnp : 预测目标,实际 GNP(gross national product,国民生产总值)

  2. pgnp : 潜在 GNP

  3. ulc : 单位劳动成本

  4. gdfco : 不包括食物和能源在内的个人消费支出的固定权重平减指数

  5. gdf : 固定重量 GNP 缩减指数

  6. gdfim : 定量进口减缩指数

  7. gdfcf : 个人消费支出中食品的固定重量平减指数

  8. gdfce : 个人消费支出中能量的固定权重平减指数

在进行建模前,我们先查看一下每个变量的走势,可以看到8个变量均为向上趋势,各个变量间有一定相关性。此外,可以看到数据没有季节性(seasonality),因此,我们在后续的建模过程中未加入季节性相关的处理。

机器学习多步时间序列预测解决方案_第5张图片

在编写代码前,首先需要在 Amazon console 中创建 SageMaker Notebook,并运行下列 cell 安装所需的库。

! pip install lightgbm

我们的预测目标是 rgnp,也就是实际的国民生产总值。我们希望预测未来一年,也就是12个月中,每个月的国民生产总值。接下来,让我们进入代码部分。我们的代码使用的基础模型是 LightGBM,您也可以根据自己的需求选择其他任何回归算法。

首先,加载数据集:

import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.metrics import mean_squared_error
from sklearn.multioutput import MultiOutputRegressor

df = pd.read_csv('Raotbl6.csv')

# 将date作为索引
df['date'] = pd.to_datetime(df['date'])
df.index = df['date']
df.drop('date', axis=1, inplace=True)
以 8:2 来分割训练集与测试集
target = 'rgnp'

X_train = df[: int(len(df) * 0.8)]
y_train = df[: int(len(df) * 0.8)]

X_test = df[int(len(df) * 0.8) :]
y_test = df[int(len(df) * 0.8) :]

左滑查看更多

构建模型,由于迭代预测需要知道全部变量未来的值,因此模型除了预测目标变量,还要预测每一个变量。

MultiOutputRegressor 使用相同的 feature 来预测多个目标,通过该对象,可以针对每个目标训练出一个回归器。

model = MultiOutputRegressor(lgb.LGBMRegressor(objective='regression')).fit(X_train, y_train)

左滑查看更多

迭代预测,每次预测全部变量的未来值,共预测12次(一年)。

results = []
data = X_test
for i in range(12):
    data = pd.DataFrame(model.predict(data), columns=data.columns, index=data.index)
    results.append(data)
接下来,我们看一下预测结果,我们以MAE(mean average error)作为评价标准。
# make future targets
for i in range(12):
    df['rgnp_{}'.format(i)] = df['rgnp'].shift(-i)

df.dropna(inplace=True)
df.drop('rgnp', axis=1, inplace=True)

targets = [item for item in df.columns if 'rgnp' in item]

X_train = df.drop(targets, axis=1)[: int(len(df) * 0.8)]
y_train = df[targets][: int(len(df) * 0.8)]

X_test = df.drop(targets, axis=1)[int(len(df) * 0.8) :]
y_test = df[targets][int(len(df) * 0.8) :]

(y_test - pred).abs().mean()

左滑查看更多

机器学习多步时间序列预测解决方案_第6张图片

我们可以看到,随着预测距离逐渐变远,我们的预测准确率会变得越来越低。这个结果也是合理的,因为预测误差会不断向后传递。

3

多步直接预测

(direct multi-step 

forecasting)

多步直接预测的逻辑是训练多个模型,每个模型负责预测未来一个时间步的值。

机器学习多步时间序列预测解决方案_第7张图片

优点:

  • 与递归预测相比,由于不会误差传递,预测方差(variance)更低

缺点:

  • 预测较多时间步时,计算效率低(需要预测多少时间步,就需要训练多少个模型)

  • 与递归预测相比,预测偏差(bias)较高,因为较远的目标无法获取与其相近的数据

机器学习多步时间序列预测解决方案_第8张图片

接下来,我们在同样的数据集上使用直接预测来进行多步时间序列分析。

使用同样的方法来加载数据:

import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.metrics import mean_squared_error
from sklearn.multioutput import MultiOutputRegressor

df = pd.read_csv('Raotbl6.csv')
df['date'] = pd.to_datetime(df['date'])
df.index = df['date']
df.drop('date', axis=1, inplace=True)
为每个未来的时间步创建一列
# make future targets
for i in range(12):
    df['rgnp_{}'.format(i)] = df['rgnp'].shift(-i-1)

df.dropna(inplace=True)

左滑查看更多

分割训练集与测试集:

targets = [item for item in df.columns if 'rgnp_' in item]

X_train = df.drop(targets, axis=1)[: int(len(df) * 0.8)]
y_train = df[targets][: int(len(df) * 0.8)]

X_test = df.drop(targets, axis=1)[int(len(df) * 0.8) :]
y_test = df[targets][int(len(df) * 0.8) :]

model = MultiOutputRegressor(lgb.LGBMRegressor(objective='regression')).fit(X_train, y_train)

左滑查看更多

查看预测结果:

pred = pd.DataFrame(model.predict(X_test), columns=targets)
pred.index = y_test.index
(y_test - pred).abs().mean()

左滑查看更多

我们可以看到,预测的误差也是随着时间步的拉远变大,但与递归预测相比,误差的损失变得更小了。这是直接预测算法的优越性,由于 variance 更低,预测远距离时间步会更准确。

机器学习多步时间序列预测解决方案_第9张图片

4

多步直接&递归融合预测

(direct-recursive hybrid 

forecasting)

多步递归预测与多步直接预测各有千秋,递归预测的 bias 低,但 variance 高。而直接预测则是 variance 低,但 bias 高。

那么是否有一种方法可以结合两者呢?答案是肯定的,这种方法叫做直接&递归融合预测。

直接&递归融合预测的具体实施方法有很多种,下面我们以其中一种方法举例:

1.创建递归模型

2.创建直接预测模型,但预测模型的输入还包括递归模型在先前时间步中所做的预测作为输入值

机器学习多步时间序列预测解决方案_第10张图片

优点:

  • 结合直接预测来在一定程度上避免误差传播,降低了预测方差(variance)

  • 结合递归预测,拥有较低的预测偏差(bias)

缺点:

  • 计算量比前面两种方法更大

  • 实现复杂度较高

机器学习多步时间序列预测解决方案_第11张图片

同样的,我们先加载数据集:

import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.metrics import mean_squared_error
from sklearn.multioutput import MultiOutputRegressor

df = pd.read_csv('Raotbl6.csv')
df['date'] = pd.to_datetime(df['date'])
df.index = df['date']
df.drop('date', axis=1, inplace=True)

左滑查看更多

训练递归模型:

target = 'rgnp'

X_train = df[: int(len(df) * 0.8)].iloc[:-1]
y_train = df[: int(len(df) * 0.8)].shift(-1).dropna()

X_test = df[int(len(df) * 0.8) :].iloc[:-1]
y_test = df[int(len(df) * 0.8) :].shift(-1).dropna()

model = MultiOutputRegressor(lgb.LGBMRegressor(objective='regression')).fit(X_train, y_train)

左滑查看更多

取得递归模型的预测结果:

results = []
data = X_test
for i in range(12):
    data = pd.DataFrame(model.predict(data), columns=data.columns, index=data.index)
    results.append(data)

左滑查看更多

为直接模型构建基础特征:

# make future targets 
for i in range(12):
    df['rgnp_{}'.format(i+1)] = df['rgnp'].shift(-i-1)

df.dropna(inplace=True)

targets = [item for item in df.columns if 'rgnp_' in item]

X_train = df.drop(targets, axis=1)[: int(len(df) * 0.8)]
y_train = df[targets][: int(len(df) * 0.8)]

X_test = df.drop(targets, axis=1)[int(len(df) * 0.8) :]
y_test = df[targets][int(len(df) * 0.8) :]

左滑查看更多

训练直接模型,每次把上一步的递归模型的预测值也作为特征来训练模型。

models = []
for i in range(1, 13):

    model = lgb.LGBMRegressor(objective='regression').fit(X_train, y_train['rgnp_{}'.format(i)])
    X_train['rgnp_{}'.format(i)] = y_train['rgnp_{}'.format(i)]
    models.append(model)

左滑查看更多

使用直接模型来做预测:

final_results = []

for i in range(12):
    final_results.append(models[i].predict(X_test))
    X_test['rgnp_{}'.format(i + 1)] = results[i]['rgnp']

左滑查看更多

机器学习多步时间序列预测解决方案_第12张图片

我们可以看到,递归&直接预测融合模型的误差要低于递归预测或直接预测的模型。当然,基于 no free lunch 原则,我们无法完全确定融合方法要好于单独方法。

5

多输出预测

(multiple output forecasting)

前面3种方法中,我们可以计算出输入间的相关性,但无法计算输出间的相关性。而多输出预测,则是使用一个神经网络模型预测整个预测序列,每个权重对每个输出都有影响,因此模型会同时学习到输入与输出的相关性。

优点:

  • 不会有误差传递风险

  • 同时学习多个输出,能够找到每个时间步的关联

  • 由于神经网络的特殊性,对特征工程能力的要求较低

缺点:

  • 计算量过大

  • 只能使用神经网络,不能使用 tree-based 模型

  • 会有过拟合的风险,需要较多数据

08ed6ea724c47a6fd23fa72aff9eb170.png

接下来以 PyTorch 为例,来进行预测。本案例使用 SageMaker Notebook 的 conda_pytorch_p38 kernel,无需自行安装 PyTorch 库。

机器学习多步时间序列预测解决方案_第13张图片

import pandas as pd

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import torch.optim as optim

import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.metrics import mean_squared_error

df = pd.read_csv('Raotbl6.csv')
df['date'] = pd.to_datetime(df['date'])
df.index = df['date']
df.drop('date', axis=1, inplace=True)

# make future targets
for i in range(12):
    df['rgnp_{}'.format(i)] = df['rgnp'].shift(-i-1)

df.dropna(inplace=True)

targets = [item for item in df.columns if 'rgnp_' in item]

X_train = df.drop(targets, axis=1)[: int(len(df) * 0.8)]
y_train = df[targets][: int(len(df) * 0.8)]

X_test = df.drop(targets, axis=1)[int(len(df) * 0.8) :]
y_test = df[targets][int(len(df) * 0.8) :]

左滑查看更多

构建数据集类,用于训练数据。

class Raotbl6Dataset(Dataset):
    """Face Landmarks dataset."""

    def __init__(self, X_train, y_train):
        self.X_train = X_train
        self.y_train = y_train


    def __len__(self):
        return len(self.X_train)

    def __getitem__(self, idx):

        if torch.is_tensor(idx):
            idx = idx.tolist()

        X = torch.Tensor(self.X_train.iloc[idx].values)
        y = torch.Tensor(self.y_train.iloc[idx].values)
        return X, y

左滑查看更多

构建模型类,由于数据集非常小,所以只使用一个隐含层的神经网络。

class Model(nn.Module):
    def __init__(self, in_feats, out_feats=12, hidden_units=32):
        super(Model, self).__init__()

        self.net = nn.Sequential(
            nn.Linear(in_feats, hidden_units),
            nn.ReLU(),
            nn.Linear(hidden_units, out_feats)
        )

    def forward(self, x):
        return self.net(x)

左滑查看更多

训练模型,由于数据集的数据量太少,因此训练500轮来尽量提升模型性能。

NUM_EPOCHS = 500
LEARNING_RATE = 2e-4
device = 'cuda' if torch.cuda.is_available() else 'cpu'



dataset = Raotbl6Dataset(X_train, y_train)
dataloader = DataLoader(dataset,batch_size=32, shuffle=True)
criterion = torch.nn. MSELoss()

model = Model(8).to(device)
opt = optim.Adam(model.parameters(), lr=LEARNING_RATE, betas=(0.5, 0.999))

for epoch in range(NUM_EPOCHS):
    model.train()
    for batch_idx, (X, y) in enumerate(dataloader):

        X, y = X.to(device), y.to(device)
        pred = model(X)
        loss = criterion(y, pred)

        model.zero_grad()
        loss.backward()
        opt.step()
    print(
        f"Epoch [{epoch}/{NUM_EPOCHS}] \
          ERROR: {loss:.4f}"
    )

pred = pd.DataFrame(model(torch.Tensor(X_test.values).to(device)), columns=y_test.columns, index=y_test.index
(y_test - pred).abs().mean()

左滑查看更多

机器学习多步时间序列预测解决方案_第14张图片

可以看到,由于神经网络足够简单,即使只有100条数据并训练了500轮,也并没有过拟合,它的性能也超过前面3种方法较多,这其中也与前面3种方法没有花费时间调参有关。

6

总结

本篇文章中,我们展示了4种基于机器学习进行多步时间序列预测的方法。下列表格中分别列出了各个算法的优缺点。

机器学习多步时间序列预测解决方案_第15张图片

多步时间序列预测问题在传统行业中尤为常见,很多亚马逊云科技的客户面临类似问题。在机器学习任务中,除了构建模型外,还需关注后续模型在实际使用中的表现,并定期更新及优化。Amazon SageMaker 提供了完整的模型训练、部署及监控工作流,可大幅度降低机器学习开发难度。此外,在一些常见的业务场景下,也可以利用 Amazon SageMaker Canvas Amazon Forecast 等工具来基于 UI 进行建模,不需要关心算法本身的同时还能得到不错的效果。

亚马逊云科技专业技术服务团队旨在为客户提供基于亚马逊云科技的专业咨询服务,种类包括业务创新、迁移、关键业务应用上云、云原生架构和微服务改造、大数据、机器学习、DevOps、容灾、安全等,以强大的亚马逊云科技云服务帮助企业客户完成云端迁移。其中,隶属于专业技术服务团队下的机器学习团队具有丰富的人工智能行业解决经验,可在为客户提供亚马逊云科技云上 AI 模型开发方面的咨询。

Amazon SageMaker Canvas:

https://aws.amazon.com/cn/sagemaker/canvas/

Amazon Forecast :

https://aws.amazon.com/cn/forecast/

专业技术服务团队:

https://aws.amazon.com/cn/professional-services/

本篇作者

机器学习多步时间序列预测解决方案_第16张图片

刘明

亚马逊云科技专业服务团队数据科学家。在机器学习、深度学习、数据挖掘及模型部署开发拥有扎实经验。深度参与过能源、医疗、教育、媒体等行业的机器学习应用搭建及数据平台建设。

机器学习多步时间序列预测解决方案_第17张图片

陈恒智

亚马逊云科技专业服务团队数据科学家。

机器学习多步时间序列预测解决方案_第18张图片

王畇

亚马逊云科技专业服务团队助理数据科学家。在传统机器学习、计算机视觉、强化学习以及数据分析领域拥有多年经验,深度参与过生物识别、智慧供热等多个不同行业的人工智能项目,拥有丰富的实战经验。

机器学习多步时间序列预测解决方案_第19张图片

点击上方【立即报名】

直通大咖云集的亚马逊云科技中国峰会!

机器学习多步时间序列预测解决方案_第20张图片

机器学习多步时间序列预测解决方案_第21张图片

听说,点完下面4个按钮

就不会碰到bug了!

机器学习多步时间序列预测解决方案_第22张图片

你可能感兴趣的:(算法,大数据,python,神经网络,机器学习)