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

news2024/11/17 6:01:15

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

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

时间序列预测(如 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/multi-step-forecasting-blog-sample-code

技术交流

技术要学会分享、交流,不建议闭门造车。一个人可以走的很快、一堆人可以走的更远。

好的文章离不开粉丝的分享、推荐,文章源码、数据、技术交流提升,均可加交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

方式①、添加微信号:dkl88194,备注:来自CSDN + 多步时间序列预
方式②、微信搜索公众号:Python学习与数据挖掘,后台回复:多步时间序列预

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

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

图片

优点:
  • 需要的模型数量固定

  • 计算量较低

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

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

图片

下面我们结合多变量回归分析,来实现多步递归预测。数据集涵盖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),因此,我们在后续的建模过程中未加入季节性相关的处理。

图片

在编写代码前,首先需要安装所需的库。

! 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)82 来分割训练集与测试集
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()

图片

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

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

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

图片

优点:
  • 与递归预测相比,由于不会误差传递,预测方差(variance)更低
缺点:
  • 预测较多时间步时,计算效率低(需要预测多少时间步,就需要训练多少个模型)

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

图片

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

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

# 数据获取:关注公众号:Python学习与数据挖掘 后台回复 多步时间序列预测
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 更低,预测远距离时间步会更准确。

图片

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

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

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

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

  1. 创建递归模型

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

图片

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

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

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

  • 实现复杂度较高

图片

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

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']

图片

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

5 多输出预测 (multiple output forecasting)

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

优点:
  • 不会有误差传递风险

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

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

缺点:
  • 计算量过大

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

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

图片

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

图片

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()

图片

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

6 总结

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

图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/676630.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HAL库——STM32CubeMX中断相关配置(中断反转LED状态)

STM32CubeMX中断相关配置 文章目录 STM32CubeMX中断相关配置1. 选择你要用的芯片(双击打开)2. 设置串口写入3. 配置时钟树,外部时钟为系统时钟(PLL倍频时钟)4. 查看原理图,找到可以中断控制的器件,或者外接小灯来达到中…

中国版chatGPT【文心一言】

文心一言是一款基于人工智能技术的中文自然语言处理工具,它可以用于文本生成、情感分析、关键词提取等多种应用场景。相比于GPT等其他自然语言处理模型,文心一言有着更多的优势。 首先,文心一言具有更高的准确率和可靠性。它采用了最新的深度…

Redis之RDB和AOF持久化原理解析

Redis之RDB和AOF持久化原理解析 一 RDB持久化原理及缺点 说明: 主进程是没法读取物理内存的,所以会在主进程有一个页表来读取物理内存中的数据子进程共享主进程的数据,会复制页表,写入磁盘中写操作是会拷贝一份在进行写操作 二…

【MySql】MySql事务隔离级别与一致性

文章目录 理解隔离性隔离级别查看与设置隔离性读未提交Read Uncommitted读提交Read Committed可重复读 Repeatable Read串行化serializable一致性(Consistency) 理解隔离性 MySQL服务可能会同时被多个客户端进程(线程)访问,访问的方式以事务方式进行 一个事务可能由…

计算机网络——自顶向下方法(第四章学习记录)

本章学习网络层:数据平面 前一章中我们学习了运输层依赖于网络层的主机到主机的通信服务,提供了各种形式的进程到进程的通信。在本章中我们将看到与运输层和应用层不同的是,在网络中的每一台主机和路由器中都有一个网络层部分。 网络层能够…

【FreeRTOS】FreeRTOS学习笔记 ---- 堆和栈,第1个FreeRTOS程序,创建任务函数及任务管理

🍀作者:阿润菜菜 目录 一、通过故事介绍FreeRTOS1.什么是FreeRTOS?2.FreeRTOS能做什么? 二、如何使用FreeRTOS? --- 第1个FreeRTOS程序三、FreeRTOS的堆和栈1.堆和栈的概念2.堆和栈的分配方式3.堆和栈的溢出检测 四、创…

【创建一个螺旋状的相机轨迹并可视化该轨迹以及每个点的姿态】

文章目录 焦点的作用static const Eigen::Vector3d b_cam_z(0, 0, 1);static const Eigen::Matrix3d R_w_base = (Eigen::Matrix3d() << 0, 0, -1, 1, 0, 0, 0, -1, 0).finished()import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import …

canvas详解03-绘制图像和视频

canvas 更有意思的一项特性就是图像操作能力。可以用于动态的图像合成或者作为图形的背景,以及游戏界面(Sprites)等等。浏览器支持的任意格式的外部图片都可以使用,比如 PNG、GIF 或者 JPEG。你甚至可以将同一个页面中其他 canvas 元素生成的图片作为图片源。 引入图像到 …

canvas详解04-绘制文字

绘制文本 canvas 提供了两种方法来渲染文本: fillText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。 strokeText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置绘制文本边框,绘制的最大宽度是可选的。 #一个填充文本的示例…

Vue电商项目--购物车操作

购物车动态展示数据 但是这个数据的格式不完美 一层套一层 重新对vuex进行存储处理 这里接口写错 这一块&#xff0c;我们通过计算属性加工一下&#xff0c;重新渲染到页面上 在这里我们考虑一个问题&#xff0c;那就是将计算出来的总值计算到页面上 这里还有一个问题&#x…

qt读写xml文件

Qt使用XML模块&#xff0c;在.pro文件中添加 QT xml Qt 提供了三种读写 XML 文档的方法&#xff1a; QXmlStreamReader/QXmlStreamWriter&#xff1a; 一种快速的基于流的方式访问良格式 XML 文档&#xff0c;特别适合于实现一次解析器&#xff08;所谓“一次解析器”&…

前端vue入门(纯代码)13

【13.Vue的消息订阅与发布】 备注&#xff1a;全局事件总线用的更多些&#xff0c;消息订阅与发布只需了解即可。【注意点1】&#xff1a;由于“消息订阅与发布”可依赖的第三方库太多了&#xff0c;这里使用pubsub-js 问题&#xff1a;“全局事件总线”和“消息订阅与发布”都…

看完这篇 教你玩转渗透测试靶机vulnhub—Emplre: Breakout

Vulnhub靶机Emplre: Breakout渗透测试详解 Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;Vulnhub靶机漏洞详解&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;登入后台&#xff1a;③&#xff1a;GetShell&#xff1a;④…

oracle操作xml格式数据

新建一张用来测试的表 -- Create table create table XMLTEST (id NUMBER,content VARCHAR2(4000) );往表中插入数据 insert into XMLTEST (id, content) values (1, <root><app><id>1</id><name>张三</name><age>18</age…

《网络安全0-100》经典访问控制策略

1经典访问控制策略 1.1自主访问控制 允许用户自己对客体将已有的权限赋予给其他主体&#xff0c;也可以撤销自己赋予给其他主体的权限。 矩阵结构分为三个主要的表&#xff1a; 访问控制矩阵 访问控制列表 权能表 矩阵的局限性&#xff1a; 大小为主体数量客体数量&…

【FPGA】Verilog:时序电路设计 | 自循环移位寄存器 | 环形计数 | 扭环计数 | 约翰逊计数器

前言&#xff1a;本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例&#xff1a;计数器 ​​ 功能特性&#xff1a; 采用 Xilinx Artix-7 XC7A35T芯片 配置方式&#xff1a;USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度 存储器&#xff1a;2Mb…

Bresenham直线算法

文章目录 1.Bresenham直线算法1.1 算法流程1.2 Bresenham算法实现1.3matlab中应用1.4 算法优势1.5 对比以往方法的改进和优化1.6 算法改进和缺陷 2.国内外研究现状3.个人感想及算法改进 1.Bresenham直线算法 Bresenham直线算法是一种用于将两点之间的线段绘制在屏幕上的算法。…

什么是MLOps?为什么要使用MLOps进行机器学习实践

随着数字化和计算能力的发展&#xff0c;机器学习&#xff08;Machine Learning&#xff09;技术在提高企业生产力方面所涌现的潜力越来越被大家所重视&#xff0c;然而很多机器学习的模型及应用在实际的生产环境并未达到预期&#xff0c;大量的ML项目被证明是失败的。从机器学…

【Red Hat7.9安装Oracle11g】---调用图形化界面的几种方式

【Red Hat7.9安装Oracle11g】---调用图形化界面的几种方式 &#x1f53b; 一、续上一篇[【Red Hat 7.9---详细安装Oracle 11g---图形化界面方式】](https://blog.csdn.net/qq_41840843/article/details/131198718?spm1001.2014.3001.5501)⛳ 1.1 前言⛳ 1.2 方式一、使用Xmana…

MIT 6.S081 Lab Five

MIT 6.S081 Lab Five 引言xv6 lazy page allocationEliminate allocation from sbrk() (easy)代码解析 Lazy allocation (moderate)代码解析 Lazytests and Usertests (moderate)代码解析 可选的挑战练习 引言 本文为 MIT 6.S081 2020 操作系统 实验五解析。 MIT 6.S081课程前…