精华内容
下载资源
问答
  • 2021-12-28 20:40:11

    时间序列数据一般有以下几种特点:1.趋势(Trend) 2. 季节性(Seasonality)。
    趋势描述的是时间序列的整体走势,比如总体上升或者总体下降。下图所示的时间序列是总体上升的:
    在这里插入图片描述
    季节性描述的是数据的周期性波动,比如以年或者周为周期,如下图:
    在这里插入图片描述

    ARMA

    自回归滑动平均模型(ARMA 模型,Auto-Regressive and Moving Average Model)是研究时间序列的重要方法,由自回归模型(简称AR模型)与滑动平均模型(简称MA模型)为基础“混合”构成。在市场研究中常用于长期追踪资料的研究,如:Panel研究中,用于消费行为模式变迁研究;在零售研究中,用于具有季节变动特征的销售量、市场规模的预测等。
    AR:
    当前值只是过去值的加权求和。
    在这里插入图片描述
    MA:
    过去的白噪音的移动平均。
    在这里插入图片描述
    ARMA:
    AR和MA的综合
    在这里插入图片描述

    ARIMA

    ARIMA模型运用的基本流程有几下几步:

    1. 数据可视化,识别平稳性。
    2. 对非平稳的时间序列数据,做差分,得到平稳序列。
    3. 建立合适的模型。
      平稳化处理后,若偏自相关函数是截尾的,而自相关函数是拖尾的,则建立AR模型;
      若偏自相关函数是拖尾的,而自相关函数是截尾的,则建立MA模型;
      若偏自相关函数和自相关函数均是拖尾的,则序列适合ARMA模型。
    4. 模型的阶数在确定之后,对ARMA模型进行参数估计,比较常用是最小二乘法进行参数估计。
    5. 假设检验,判断(诊断)残差序列是否为白噪声序列。
    6. 利用已通过检验的模型进行预测。

    Prophet

    • Facebook 时间序列预测框架
    • 其中对于节假日,year,month,week等预测有良好的适用性
    • 组成部分(增长趋势,周期性,节日数据)如何拟合周期性数据

    指数平滑 Exponential Smoothing, ES

    一次指数平滑法
    二次指数平滑法
    三次指数平滑法

    上面说到第一期的F1的值一般取前三期的实际观察值的平均数,这只是一般情况,接下来讨论一下这个F1的取值。
    一般分为两种情况,当样本为大样本时(n>42),F1一般以第一期的观察值代替;当样本为小样本时(n<42),F1一般取前几期的平均值代替。

    但一次指数平滑法只适合于具有水平发展趋势的时间序列分析,只能对近期进行预测。如果碰到时间序列具有上升或下降趋势时,在这个上升或下降的过程中,预测偏差会比较大,这时最好用二次指数平滑法进行预测

    参考
    https://www.jianshu.com/p/6fb0408b3f54
    http://www.statr.cn/?p=14
    http://www.cnblogs.com/kemaswill/archive/2013/04/01/2993583.html
    https://blog.csdn.net/alanconstantinelau/article/details/70173561
    http://www.cnblogs.com/TTyb/p/5716125.html
    http://blog.sina.com.cn/s/blog_4b9acb5201016nkd.html

    GARCH模型

    广义自回归条件异方差模型

    statsmodels

    在python中对时间序列的建模通常使用statsmodel库,该库在计算库排名中长期处于垫底状态,因为早期文档实在匮乏,不过近来似有好转倾向。

    pyflux

    An open source time series library for the Python Programming Language

    灰度模型

    更多相关内容
  • 基于ARMA模型和BP网络的时间序列预测模型,杨杰,孙旭东,摘要:本文主要介绍了时间序列和BP网络基础理论以及其预测的应用,并以2006年上证指数的开盘数据为例建立了基于这两种方法的预测模
  • 提出一种高阶直觉模糊时间序列预测模型。模型首先应用模糊聚类算法实现论域的非等分划分;然后,针对直觉模糊时间序列的数据特性,提出一种更具客观性的直觉模糊集隶属度和非隶属度函数的确定方法;最后,利用直觉...
  • 在分析经典模糊时间序列预测模型的基础上, 指出了传统的模型不能处理多因素的情形; 然后分析并 改进了证据理论中关于证据合成的方法, 提出了基于证据理论的多因素模糊时间序列预测模型; 最后用1997 年∼ ...
  • 针对现有直觉模糊时间序列模型中直觉模糊关系组和确定性转换规则过度依赖训练数据规模的问题,提出一种基于动态时间弯曲(DTW,dynamic time warping)距离的长期直觉模糊时间序列预测模型。通过直觉模糊C均值(IFCM,...
  • 该文档包括混沌时间序列预测模型研究硕士论文及预测模型的原型系统(Matlab编程),论文部分详细阐述了预测模型构建等方面研究。
  • 时间序列定义: 时间序列是按照一定的时间间隔排列的一组数据,其时间间隔可以...对于时间序列预测,由于很难确定它与其他变量之间的关系,这时我们就不能用回归去预测,而应使用时间序列方法进行预测。 采用时间序列

    时间序列系列文章:

    时间序列(一):时间序列数据与时间序列预测模型
    时间序列(二):时间序列平稳性检测
    时间序列(三):ARIMA模型实战

    时间序列及其预测是日常工作中建模,分析,预测的重要组成部分。本系列我们将从0开始介绍时间序列的含义,模型及其分析。本篇为第一部分,我们主要介绍时间序列,与其常用的预测模型。

    时间序列定义:

    时间序列是按照一定的时间间隔排列的一组数据,其时间间隔可以是任意的时间单位,如小时、日、周月等。比如,每天某产品的用户数量,每个月的销售额,这些数据形成了以一定时间间隔的数据。

    通过对这些时间序列的分析,从中发现和揭示现象发展变化的规律,并将这些知识和信息用于预测。比如销售量是上升还是下降,销售量是否与季节有关,是否可以通过现有的数据预测未来一年的销售额是多少等。

    对于时间序列的预测,由于很难确定它与其他变量之间的关系,这时我们就不能用回归去预测,而应使用时间序列方法进行预测。

    采用时间序列分析进行预测时需要一系列的模型,这种模型称为时间序列模型。

    时间序列预测模型与方法

    注:本部分只关注相关模型与分析的方法,模型的选择,调参与优化会放在后续文章中详细讲解

    原始数据

    本文所使用原始数据与代码,可以在公众号:Smilecoc的杂货铺 中回复“时间序列”获取。可直接扫描文末二维码关注!

    朴素法

    朴素法就是预测值等于实际观察到的最后一个值。它假设数据是平稳且没有趋势性与季节性的。通俗来说就是以后的预测值都等于最后的值。

    这种方法很明显适用情况极少,所以我们重点通过这个方法来熟悉一下数据可视化与模型的评价及其相关代码。

    #朴素法
    dd = np.asarray(train['Count'])#训练组数据
    y_hat = test.copy()#测试组数据
    y_hat['naive'] = dd[len(dd) - 1]#预测组数据
    
    #数据可视化
    plt.figure(figsize=(12, 8))
    plt.plot(train.index, train['Count'], label='Train')
    plt.plot(test.index, test['Count'], label='Test')
    plt.plot(y_hat.index, y_hat['naive'], label='Naive Forecast')
    plt.legend(loc='best')
    plt.title("Naive Forecast")
    plt.show()
    

    得到结果:
    在这里插入图片描述
    我们通过计算均方根误差,检查模型在测试数据集上的准确率。
    其中均方根误差(RMSE)是各数据偏离真实值的距离平方和的平均数的开方

    #计算均方根误差RMSE
    from sklearn.metrics import mean_squared_error
    from math import sqrt
    
    # mean_squared_error求均方误差
    rmse = sqrt(mean_squared_error(test['Count'], y_hat['naive']))
    print(rmse)
    

    得到均方根误差为1053

    简单平均法

    简单平均法就是预测的值为之前过去所有值的平均.当然这不会很准确,但这种预测方法在某些情况下效果是最好的。

    #简单平均法
    y_hat_avg = test.copy()
    y_hat_avg['avg_forecast'] = train['Count'].mean()
    

    其后续可视化与模型效果评估方法与上述一致,这里不再赘述,需要详细代码可以查看相关源码。得到RMSE值为2637

    移动平均法

    我们经常会遇到这种数据集,比如价格或销售额某段时间大幅上升或下降。如果我们这时用之前的简单平均法,就得使用所有先前数据的平均值,但在这里使用之前的所有数据是说不通的,因为用开始阶段的价格值会大幅影响接下来日期的预测值。因此,我们只取最近几个时期的价格平均值。很明显这里的逻辑是只有最近的值最要紧。这种用某些窗口期计算平均值的预测方法就叫移动平均法。

    #移动平均法
    y_hat_avg = test.copy()
    #利用时间窗函数rolling求平均值u
    y_hat_avg['moving_avg_forecast'] = train['Count'].rolling(60).mean().iloc[-1]
    

    其后续可视化与模型效果评估方法与上述一致,这里不再赘述,需要详细代码可以查看相关源码。得到RMSE值为1121

    指数平滑法

    在做时序预测时,一个显然的思路是:认为离着预测点越近的点,作用越大。比如我这个月体重100斤,去年某个月120斤,显然对于预测下个月体重而言,这个月的数据影响力更大些。假设随着时间变化权重以指数方式下降——最近为0.8,然后0.8**2,0.8**3…,最终年代久远的数据权重将接近于0。将权重按照指数级进行衰减,这就是指数平滑法的基本思想。

    指数平滑法有几种不同形式:一次指数平滑法针对没有趋势和季节性的序列,二次指数平滑法针对有趋势但没有季节性的序列,三次指数平滑法针对有趋势也有季节性的序列。“

    所有的指数平滑法都要更新上一时间步长的计算结果,并使用当前时间步长的数据中包含的新信息。它们通过”混合“新信息和旧信息来实现,而相关的新旧信息的权重由一个可调整的参数来控制。

    一次指数平滑

    一次指数平滑法的递推关系如下:

    s i = α x i + ( 1 − α ) s i − 1 , 其 中 0 ≤ α ≤ 1 s_{i}=\alpha x_{i}+(1-\alpha)s_{i-1},其中 0 \leq \alpha \leq 1 si=αxi+(1α)si10α1

    其中, s i s_{i} si是时间步长i(理解为第i个时间点)上经过平滑后的值, x i x_{i} xi 是这个时间步长上的实际数据。 α \alpha α可以是0和1之间的任意值,它控制着新旧信息之间的平衡:当 α \alpha α 接近1,就只保留当前数据点;当 α \alpha α 接近0时,就只保留前面的平滑值(整个曲线都是平的)。我们展开它的递推关系式:

    我们展开它的递推关系式:
    s i = α x i + ( 1 − α ) s i − 1 = α x i + ( 1 − α ) [ α x i − 1 + ( 1 − α ) s i − 2 ] = α x i + ( 1 − α ) [ α x i − 1 + ( 1 − α ) [ α x i − 2 + ( 1 − α ) s i − 3 ] ] = α [ x i + ( 1 − α ) x i − 1 + ( 1 − α ) 2 x i − 2 + ( 1 − α ) 3 s i − 3 ] = . . . = α ∑ j = 0 i ( 1 − α ) j x i − j \begin{aligned} s_{i}&=\alpha x_{i}+(1-\alpha)s_{i-1} \\ &=\alpha x_{i}+(1-\alpha)[\alpha x_{i-1}+(1-\alpha)s_{i-2}]\\ &=\alpha x_{i}+(1-\alpha)[\alpha x_{i-1}+(1-\alpha)[\alpha x_{i-2}+(1-\alpha)s_{i-3}]]\\ &=\alpha[x_{i}+(1-\alpha)x_{i-1}+(1-\alpha)^{2}x_{i-2}+(1-\alpha)^{3}s_{i-3}]\\ &=... \\ &=\alpha\sum_{j=0}^{i}(1-\alpha)^{j}x_{i-j} \end{aligned} si=αxi+(1α)si1=αxi+(1α)[αxi1+(1α)si2]=αxi+(1α)[αxi1+(1α)[αxi2+(1α)si3]]=α[xi+(1α)xi1+(1α)2xi2+(1α)3si3]=...=αj=0i(1α)jxij

    可以看出,在指数平滑法中,所有先前的观测值都对当前的平滑值产生了影响,但它们所起的作用随着参数 α \alpha α 的幂的增大而逐渐减小。那些相对较早的观测值所起的作用相对较小。同时,称α为记忆衰减因子可能更合适——因为α的值越大,模型对历史数据“遗忘”的就越快。从某种程度来说,指数平滑法就像是拥有无限记忆(平滑窗口足够大)且权值呈指数级递减的移动平均法。一次指数平滑所得的计算结果可以在数据集及范围之外进行扩展,因此也就可以用来进行预测。预测方式为:

    x i + h = s i x_{i+h}=s_{i} xi+h=si

    s i s_{i} si是最后一个已经算出来的值。h等于1代表预测的下一个值。

    我们可以通过statsmodels中的时间序列模型进行指数平滑建模。官方文档地址为:
    https://www.statsmodels.org/stable/generated/statsmodels.tsa.holtwinters.SimpleExpSmoothing.html
    具体代码如下:

    #一次指数平滑
    from statsmodels.tsa.api import SimpleExpSmoothing
     
    y_hat_avg = test.copy()
    fit = SimpleExpSmoothing(np.asarray(train['Count'])).fit(smoothing_level=0.6, optimized=False)
    y_hat_avg['SES'] = fit.forecast(len(test))
    

    之后同样进行数据可视化并查看模型效果

    plt.figure(figsize=(16, 8))
    plt.plot(train['Count'], label='Train')
    plt.plot(test['Count'], label='Test')
    plt.plot(y_hat_avg['SES'], label='SES')
    plt.legend(loc='best')
    plt.show()
    

    可视化结果为:
    在这里插入图片描述
    RMSE结果为1040

    二次指数平滑

    在介绍二次指数平滑前介绍一下趋势的概念。

    趋势,或者说斜率的定义很简单: b = Δ y / Δ x b=Δy/Δx b=Δy/Δx,其中 Δ x Δx Δx为两点在x坐标轴的变化值,所以对于一个序列而言,相邻两个点的 Δ x = 1 Δx=1 Δx=1,因此 b = Δ y = y ( x ) − y ( x − 1 ) b=Δy=y(x)-y(x-1) b=Δy=y(x)y(x1)。 除了用点的增长量表示,也可以用二者的比值表示趋势。比如可以说一个物品比另一个贵20块钱,等价地也可以说贵了5%,前者称为可加的(addtive),后者称为可乘的(multiplicative)。在实际应用中,可乘的模型预测稳定性更佳,但是为了便于理解,我们在这以可加的模型为例进行推导。
    指数平滑考虑的是数据的baseline,二次指数平滑在此基础上将趋势作为一个额外考量,保留了趋势的详细信息。即我们保留并更新两个量的状态:平滑后的信号和平滑后的趋势。公式如下:
    基准等式
    s i = α x i + ( 1 − α ) ( s i − 1 + t i − 1 ) s_{i}=\alpha x_{i}+(1-\alpha)(s_{i-1}+t_{i-1}) si=αxi+(1α)(si1+ti1)
    趋势等式
    t i = β ( s i − s i − 1 ) + ( 1 − β ) t i − 1 t_{i}=\beta (s_{i}-s_{i-1})+(1-\beta)t_{i-1} ti=β(sisi1)+(1β)ti1

    第二个等式描述了平滑后的趋势。当前趋势的未平滑“值”( t i t_{i} ti )是当前平滑值( s i s_{i} si )和上一个平滑值( s i − 1 s_{i-1} si1)的差;也就是说,当前趋势告诉我们在上一个时间步长里平滑信号改变了多少。要想使趋势平滑,我们用一次指数平滑法对趋势进行处理,并使用参数 β \beta β (理解:对 t i t_{i} ti 的处理类似于一次平滑指数法中的 s i s_{i} si ,即对趋势也需要做一个平滑,临近的趋势权重大)。

    为获得平滑信号,我们像上次那样进行一次混合,但要同时考虑到上一个平滑信号及趋势。假设单个步长时间内保持着上一个趋势,那么第一个等式的最后那项就可以对当前平滑信号进行估计。

    若要利用该计算结果进行预测,就取最后那个平滑值,然后每增加一个时间步长就在该平滑值上增加一次最后那个平滑趋势:

    x i + h = s i + h t i x_{i+h}=s_{i}+ht_{i} xi+h=si+hti

    在这里插入图片描述
    之后使用二次指数平滑进行预测:

    from statsmodels.tsa.api import Holt
     
    y_hat_avg = test.copy()
     
    fit = Holt(np.asarray(train['Count'])).fit(smoothing_level=0.3, smoothing_slope=0.1)
    y_hat_avg['Holt_linear'] = fit.forecast(len(test))
    

    结果如图:
    在这里插入图片描述
    得到对应的RMSE为1033

    三次指数平滑

    在应用这种算法前,我们先介绍一个新术语。假如有家酒店坐落在半山腰上,夏季的时候生意很好,顾客很多,但每年其余时间顾客很少。因此,每年夏季的收入会远高于其它季节,而且每年都是这样,那么这种重复现象叫做“季节性”(Seasonality)。如果数据集在一定时间段内的固定区间内呈现相似的模式,那么该数据集就具有季节性。
    在这里插入图片描述
    二次指数平滑考虑了序列的基数和趋势,三次就是在此基础上增加了一个季节分量。类似于趋势分量,对季节分量也要做指数平滑。比如预测下一个季节第3个点的季节分量时,需要指数平滑地考虑当前季节第3个点的季节分量、上个季节第3个点的季节分量…等等。详细的有下述公式(累加法):

    s i = α ( x i − p i − k ) + ( 1 − α ) ( s i − 1 + t i − 1 ) t i = β ( s i − s i − 1 ) + ( 1 − β ) t i − 1 p i = γ ( x i − s i ) + ( 1 − γ ) p i − k \begin{aligned} s_{i}&=\alpha (x_{i}-p_{i-k})+(1-\alpha)(s_{i-1}+t_{i-1}) \\ t_{i} &=\beta (s_{i}-s_{i-1})+(1-\beta)t_{i-1}\\ p_{i}&=\gamma (x_{i}-s_{i})+(1-\gamma)p_{i-k} \end{aligned} sitipi=α(xipik)+(1α)(si1+ti1)=β(sisi1)+(1β)ti1=γ(xisi)+(1γ)pik

    其中, p i p_{i} pi 是指“周期性”部分。预测公式如下:

    x i + h = s i + h t i + p i − k + h x_{i+h}=s_{i}+ht_{i}+p_{i-k+h} xi+h=si+hti+pik+h

    k 是这个周期的长度。

    在使用二次平滑模型与三次平滑模型前,我们可以使用sm.tsa.seasonal_decompose分解时间序列,可以得到以下分解图形——从上到下依次是原始数据、趋势数据、周期性数据、随机变量(残差值)

    在这里插入图片描述
    根据分析图形和数据可以确定对应的季节参数

    具体代码为:

    #三次指数平滑
    from statsmodels.tsa.api import ExponentialSmoothing
     
    y_hat_avg = test.copy()
    fit1 = ExponentialSmoothing(np.asarray(train['Count']), seasonal_periods=7, trend='add', seasonal='add', ).fit()
    y_hat_avg['Holt_Winter'] = fit1.forecast(len(test))
    

    在这里插入图片描述
    得到的RMSE为575。我们可以看到趋势和季节性的预测准确度都很高。你可以试着调整参数来优化这个模型。

    AR模型

    AR(Auto Regressive Model)自回归模型是线性时间序列分析模型中最简单的模型。通过自身前面部分的数据与后面部分的数据之间的相关关系(自相关)来建立回归方程,从而可以进行预测或者分析。服从p阶的自回归方程表达式如下:

    x t = ϕ 1 x t − 1 + ϕ 2 x t − 2 + ⋯ + ϕ p x t − p + μ t x_{t}=\phi_{1}x_{t-1}+\phi_{2}x_{t-2}+\cdots+\phi_{p}x_{t-p}+\mu_{t} xt=ϕ1xt1+ϕ2xt2++ϕpxtp+μt

    表示为 A R ( p ) AR(p) AR(p),。其中, μ t \mu_{t} μt表示白噪声,是时间序列中的数值的随机波动,但是这些波动会相互抵消,最终是0。 ϕ \phi ϕ表示自回归系数。

    所以当只有一个时间记录点时,称为一阶自回归过程,即AR(1)。其表达式为:
    x t = ϕ 1 x t − 1 + μ t x_{t}=\phi_{1}x_{t-1}+\mu_{t} xt=ϕ1xt1+μt

    利用Python建立AR模型一般会用到我们之后会说到的ARIMA模型(AR模型中的p是ARIMA模型中的参数之一,只要将其他的参数设置为0即为AR模型)。您可以先阅读后续ARIMA模型的内容并参考文件中的代码查看具体的内容

    MA模型

    MA(Moving Average Model)移动平均模型通过将一段时间序列中白噪声(误差)进行加权和,可以得到移动平均方程。如下模型为q阶移动平均过程,表示为MA(q)。

    x t = μ + μ t + θ 1 μ t − 1 + θ 2 μ t − 2 + ⋯ + θ q μ t − q x_{t}=\mu+\mu_{t}+\theta_{1}\mu_{t-1}+\theta_{2}\mu_{t-2}+\cdots+\theta_{q}\mu_{t-q} xt=μ+μt+θ1μt1+θ2μt2++θqμtq

    其中 x t x_{t} xt表示t期的值,当期的值由前q期的误差值来决定, μ μ μ值是常数项,相当于普通回归中的截距项, μ t \mu_{t} μt是当期的随机误差。MA模型的核心思想是每一期的随机误差都会影响当期值,把前q期的所有误差加起来就是对t期值的影响。

    同样,利用Python建立MA模型一般会用到我们之后会说到的ARIMA模型,您可以先阅读后续ARIMA模型的内容并参考文件中的代码查看具体的内容

    ARMA模型

    ARMA(Auto Regressive and Moving Average Model)自回归移动平均模型是与自回归和移动平均模型两部分组成。所以可以表示为ARMA(p, q)。p是自回归阶数,q是移动平均阶数。

    x t = ϕ 1 x t − 1 + ϕ 2 x t − 2 + ⋯ + ϕ p x t − p + μ t + θ 1 μ t − 1 + θ 2 μ t − 2 + ⋯ + θ q μ t − q x_{t}=\phi_{1}x_{t-1}+\phi_{2}x_{t-2}+\cdots+\phi_{p}x_{t-p}+\mu_{t}+\theta_{1}\mu_{t-1}+\theta_{2}\mu_{t-2}+\cdots+\theta_{q}\mu_{t-q} xt=ϕ1xt1+ϕ2xt2++ϕpxtp+μt+θ1μt1+θ2μt2++θqμtq

    从式子中就可以看出,自回归模型结合了两个模型的特点,其中,AR可以解决当前数据与后期数据之间的关系,MA则可以解决随机变动也就是噪声的问题。

    ARIMA模型

    ARIMA(Auto Regressive Integrate Moving Average Model)差分自回归移动平均模型是在ARMA模型的基础上进行改造的,ARMA模型是针对t期值进行建模的,而ARIMA是针对t期与t-d期之间差值进行建模,我们把这种不同期之间做差称为差分,这里的d是几就是几阶差分。ARIMA模型也是基于平稳的时间序列的或者差分化后是稳定的,另外前面的几种模型都可以看作ARIMA的某种特殊形式。表示为ARIMA(p, d, q)。p为自回归阶数,q为移动平均阶数,d为时间成为平稳时所做的差分次数,也就是Integrate单词的在这里的意思。

    具体步骤如下:

    x t = ϕ 1 w t − 1 + ϕ 2 w t − 2 + ⋯ + ϕ p w t − p + μ t + θ 1 μ t − 1 + θ 2 μ t − 2 + ⋯ + θ q μ t − q x_{t}=\phi_{1}w_{t-1}+\phi_{2}w_{t-2}+\cdots+\phi_{p}w_{t-p}+\mu_{t}+\theta_{1}\mu_{t-1}+\theta_{2}\mu_{t-2}+\cdots+\theta_{q}\mu_{t-q} xt=ϕ1wt1+ϕ2wt2++ϕpwtp+μt+θ1μt1+θ2μt2++θqμtq

    上面公式中的 w t w_{t} wt表示t期经过d阶差分以后的结果。我们可以看到ARIMA模型的形式基本与ARMA的形式是一致的,只不过把 X X X换成了 w w w

    使用ARIMA进行预测代码如下:

    from statsmodels.tsa.arima_model import ARIMA
     
    ts_ARIMA= train['Count'].astype(float)
    fit1 = ARIMA(ts_ARIMA, order=(7, 1, 4)).fit()
    y_hat_ARIMA = fit1.predict(start="2013-11-1", end="2013-12-31", dynamic=True)
    

    并画出预测值与实际值图形:

    plt.figure(figsize=(16, 8))
    plt.plot(train['Count'], label='Train')
    plt.plot(test['Count'], label='Test')
    plt.plot(y_hat_ARIMA, label='ARIMA')
    plt.legend(loc='best')
    plt.show()
    

    在这里插入图片描述
    并计算RMSE:

    from sklearn.metrics import mean_squared_error
    from math import sqrt
      
    rmse = sqrt(mean_squared_error(test['Count'],y_hat_ARIMA.to_frame()))
    print(rmse)
    

    得到对应的RMSE为3723

    SARIMA模型

    SARIMA季节性自回归移动平均模型模型在ARIMA模型的基础上添加了季节性的影响,结构参数有七个:SARIMA(p,d,q)(P,D,Q,s)
    其中p,d,q分别为之前ARIMA模型中我们所说的p:趋势的自回归阶数。d:趋势差分阶数。q:趋势的移动平均阶数。
    P:季节性自回归阶数。
    D:季节性差分阶数。
    Q:季节性移动平均阶数。
    s:单个季节性周期的时间步长数。

    import statsmodels.api as sm
    y_hat_avg = test.copy()
    fit1 = sm.tsa.statespace.SARIMAX(train.Count, order=(2, 1, 4), seasonal_order=(0, 1, 1, 7)).fit()
    y_hat_avg['SARIMA'] = fit1.predict(start="2013-11-1", end="2013-12-31", dynamic=True)
    

    得到实际值与预测值如下:

    plt.figure(figsize=(16, 8))
    plt.plot(train['Count'], label='Train')
    plt.plot(test['Count'], label='Test')
    plt.plot(y_hat_avg['SARIMA'], label='SARIMA')
    plt.legend(loc='best')
    plt.show()
    

    在这里插入图片描述
    并计算RMSE:

    from sklearn.metrics import mean_squared_error
    from math import sqrt
    rmse = sqrt(mean_squared_error(test['Count'], y_hat_avg['SARIMA']))
    print(rmse)
    

    结果为933

    其他时间序列预测的模型还有SARIMAX模型(在ARIMA模型上加了季节性的因素),Prophet模型,ARCH模型,LSTM神经网络模型等。限于篇幅,感兴趣的同学可以自行查看相关模型资料

    在后续的文章中我们将讲解如何确定数据的平稳性与数据预处理,为后续时间序列的建模做准备

    参考文章:
    https://www.analyticsvidhya.com/blog/2018/02/time-series-forecasting-methods/
    https://blog.csdn.net/anshuai_aw1/article/details/82499095

    相关代码与数据可关注公众号并回复:时间序列获取
    在这里插入图片描述

    展开全文
  • 文章目录前言适用于多时间步预测的CNN...如何为单变量数据开发多步时间序列预测的CNN模型; 如何为多变量数据开发多通道多步时间序列预测的CNN模型; 如何为多变量数据开发多头多步时间序列预测的CNN模型。 本文介绍
  • 首先,采用二进正交小波变换对原始时间序列分解和重构,分离出原始序列中的高频部分和低频部分;然后对低频部分构建过程神经网络模型,对低频部分采用自回归模型;最后将两种模型预测值叠加,得到原序列的预测值。
  • 卷积神经网络模型(CNN)可以应用于时间序列预测。有许多类型的CNN模型可用于每种特定类型的时间序列预测问题。在本介绍了在以TF2.1为后端的Keras中如何开发用于时间序列预测的不同的CNN模型。这些模型是在比较小的...

    【时间序列预测/分类】 全系列60篇由浅入深的博文汇总:传送门


    卷积神经网络模型(CNN)可以应用于时间序列预测。有许多类型的CNN模型可用于每种特定类型的时间序列预测问题。在本介绍了在以TF2.1为后端的Keras中如何开发用于时间序列预测的不同的CNN模型。这些模型是在比较小的人为构造的时间序列问题上演示的,模型配置也是任意的,并没有进行调参优化,这些内容会在以后的文章中介绍。

    先看一下思维导图,本文讲解了开发CNN时间序列预测模型中的前两个知识点:单变量CNN模型和多变量CNN模型(思维导图中的1、2):
    在这里插入图片描述



    1. 单变量CNN模型(Univariate CNN Models)

    尽管传统上是针对二维图像数据开发的,但CNNs可以用来对单变量时间序列预测问题进行建模。单变量时间序列是由具有时间顺序的单个观测序列组成的数据集,需要一个模型从过去的观测序列中学习以预测序列中的下一个值。本节分为两部分:

    • Data Preparation
    • CNN Model

    1.1 数据准备

    假设有如下序列数据:

    [10, 20, 30, 40, 50, 60, 70, 80, 90]
    

    我们可以将序列分成多个输入/输出模式,称为样本(samples),其中三个时间步(time steps)作为输入,一个时间步作为输出,并据此来预测一个时间步的输出值y。

    X,  		 y
    10, 20, 30,  40
    20, 30, 40,  50
    30, 40, 50,  60
    ...
    

    我们可以通过一个 split_sequence() 函数来实现上述操作,该函数可以将给定的单变量序列拆分为多个样本,其中每个样本具有指定数量的时间步,并且输出是单个时间步。因为这些函数在之前的文章已经介绍过,为了增加文章的可读性,此处就不逐一讲解了,会在每章结束之后,给出完整代码,如果有不懂的地方,可以查看之前的一篇文章。经过此函数处理之后,单变量序列变为:

    [10 20 30] 40
    [20 30 40] 50
    [30 40 50] 60
    [40 50 60] 70
    [50 60 70] 80
    [60 70 80] 90
    

    其中,每一行作为一个样本,其中三个时间步长值是样本数据,每一行中的最后一个单值是输出(y),因为是单变量,所以特征数(features)为1。


    1.2 CNN 模型

    1D CNN是一个CNN模型,它有一个卷积隐藏层,在一维序列上工作。在某些情况下,这之后可能是第二卷积层,例如非常长的输入序列,然后是池化层,其任务是将卷积层的输出提取到最显著的元素。卷积层和池化层之后是全连接层,用于解释模型卷积部分提取的特征。在卷积层和全连接层之间使用展平层(Flatten)将特征映射简化为一个一维向量。代码实现:

    model = Sequential()
    model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps,
    n_features)))
    model.add(MaxPooling1D(pool_size=2))
    model.add(Flatten())
    model.add(Dense(50, activation='relu'))
    model.add(Dense(1))
    model.compile(optimizer='adam', loss='mse')
    

    模型的关键是输入的形状 input_shape 参数;这是模型在时间步数和特征数方面期望作为每个样本的输入。我们使用的是一个单变量序列,因此特征数为1。时间步数是在划分数据集时 split_sequence() 函数的参数中定义的。

    每个样本的输入形状在第一个隐藏层定义的输入形状参数中指定。因为有多个样本,因此,模型期望训练数据的输入维度或形状为:[样本,时间步,特征]([samples, timesteps, features])split_sequence() 函数输出的训练数据 X 的形状为 [samples,timesteps],因此应该对 X 重塑形状,增加一个特征维度,以满足CNN模型的输入要求。代码实现:

    n_features = 1
    X = X.reshape((X.shape[0], X.shape[1], n_features))
    

    CNN实际上并不认为数据具有时间步,而是将其视为可以执行卷积读取操作的序列,如一维图像。上例中,我们定义了一个卷积层,它有64个filter,kernel大小为2。接下来是一个最大池化层和一个全连接层(Dense)来解释输入特性。最后,输出层预测单个数值。该模型利用有效的随机梯度下降Adam进行拟合,利用均方误差(mse)损失函数进行优化。处理好训练数据和定义完模型之后,接下来开始训练,代码实现:

    model.fit(X, y, epochs=1000, verbose=0)
    

    在模型拟合后,可以利用它进行预测。假设输入[70,80,90]来预测序列中的下一个值,并期望模型能预测类似于[100]的数据。该CNN模型期望输入形状是三维的,形状为 [样本、时间步长、特征] ,因此,在进行预测之前,必须重塑单个输入样本为三维形状。代码实现:

    x_input = array([70, 80, 90])
    x_input = x_input.reshape((1, n_steps, n_features))
    yhat = model.predict(x_input, verbose=0)
    

    完整代码:

    import numpy as np
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Dense, Flatten, Conv1D, MaxPooling1D
    
    # 该该函数将序列数据分割成样本
    def split_sequence(sequence, sw_width, n_features):
        '''
        这个简单的示例,通过for循环实现有重叠截取数据,滑动步长为1,滑动窗口宽度为sw_width。
        以后的文章,会介绍使用yield方法来实现特定滑动步长的滑动窗口的实例。
        '''
        X, y = [], []
        
        for i in range(len(sequence)):
            # 获取单个样本中最后一个元素的索引,因为python切片前闭后开,索引从0开始,所以不需要-1
            end_element_index = i + sw_width
            # 如果样本最后一个元素的索引超过了序列索引的最大长度,说明不满足样本元素个数,则这个样本丢弃
            if end_element_index > len(sequence) - 1:
                break
            # 通过切片实现步长为1的滑动窗口截取数据组成样本的效果
            seq_x, seq_y = sequence[i:end_element_index], sequence[end_element_index]
            
            X.append(seq_x)
            y.append(seq_y)
            
            process_X, process_y = np.array(X), np.array(y)
            process_X = process_X.reshape((process_X.shape[0], process_X.shape[1], n_features))
        
        print('split_sequence:\nX:\n{}\ny:\n{}\n'.format(np.array(X), np.array(y)))
        print('X_shape:{},y_shape:{}\n'.format(np.array(X).shape, np.array(y).shape))
        print('train_X:\n{}\ntrain_y:\n{}\n'.format(process_X, process_y))
        print('train_X.shape:{},trian_y.shape:{}\n'.format(process_X.shape, process_y.shape))
        return process_X, process_y
    
        
    def oned_cnn_model(sw_width, n_features, X, y, test_X, epoch_num, verbose_set):
        model = Sequential()
        
        # 对于一维卷积来说,data_format='channels_last'是默认配置,该API的规则如下:
        # 输入形状为:(batch, steps, channels);输出形状为:(batch, new_steps, filters),padding和strides的变化会导致new_steps变化
        # 如果设置为data_format = 'channels_first',则要求输入形状为: (batch, channels, steps).
        model.add(Conv1D(filters=64, kernel_size=2, activation='relu',
                         strides=1, padding='valid', data_format='channels_last',
                         input_shape=(sw_width, n_features)))
        
        # 对于一维池化层来说,data_format='channels_last'是默认配置,该API的规则如下:
        # 3D 张量的输入形状为: (batch_size, steps, features);输出3D张量的形状为:(batch_size, downsampled_steps, features)
        # 如果设置为data_format = 'channels_first',则要求输入形状为:(batch_size, features, steps)
        model.add(MaxPooling1D(pool_size=2, strides=None, padding='valid', 
                               data_format='channels_last')) 
        
        # data_format参数的作用是在将模型从一种数据格式切换到另一种数据格式时保留权重顺序。默认为channels_last。
        # 如果设置为channels_last,那么数据输入形状应为:(batch,…,channels);如果设置为channels_first,那么数据输入形状应该为(batch,channels,…)
        # 输出为(batch, 之后参数尺寸的乘积)
        model.add(Flatten())
        
        # Dense执行以下操作:output=activation(dot(input,kernel)+bias),
        # 其中,activation是激活函数,kernel是由层创建的权重矩阵,bias是由层创建的偏移向量(仅当use_bias为True时适用)。
        # 2D 输入:(batch_size, input_dim);对应 2D 输出:(batch_size, units)
        model.add(Dense(units=50, activation='relu',
                    use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros',))
        
        # 因为要预测下一个时间步的值,因此units设置为1
        model.add(Dense(units=1))
        
        # 配置模型
        model.compile(optimizer='adam', loss='mse',
                     metrics=['accuracy'], loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)
        
        print('\n',model.summary())
        # X为输入数据,y为数据标签;batch_size:每次梯度更新的样本数,默认为32。
        # verbose: 0,1,2. 0=训练过程无输出,1=显示训练过程进度条,2=每训练一个epoch打印一次信息
        
        history = model.fit(X, y, batch_size=32, epochs=epoch_num, verbose=verbose_set)
        
        
        yhat = model.predict(test_X, verbose=0)
        print('\nyhat:', yhat)
        
        return model, history
    
    if __name__ == '__main__':
        
        train_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
        sw_width = 3
        n_features = 1
        epoch_num = 1000
        verbose_set = 0
        
        train_X, train_y = split_sequence(train_seq, sw_width, n_features)
    
        # 预测
        x_input = np.array([70, 80, 90])
        x_input = x_input.reshape((1, sw_width, n_features))
        
        model, history = oned_cnn_model(sw_width, n_features, train_X, train_y, x_input, epoch_num, verbose_set)
        
        print('\ntrain_acc:%s'%np.mean(history.history['accuracy']), '\ntrain_loss:%s'%np.mean(history.history['loss']))
    

    输出:

    split_sequence:
    X:
    [[10 20 30]
     [20 30 40]
     [30 40 50]
     [40 50 60]
     [50 60 70]
     [60 70 80]]
    y:
    [40 50 60 70 80 90]
    
    X_shape:(6, 3),y_shape:(6,)
    
    train_X:
    [[[10]
      [20]
      [30]]
    
     [[20]
      [30]
      [40]]
    
     [[30]
      [40]
      [50]]
    
     [[40]
      [50]
      [60]]
    
     [[50]
      [60]
      [70]]
    
     [[60]
      [70]
      [80]]]
    train_y:
    [40 50 60 70 80 90]
    
    train_X.shape:(6, 3, 1),trian_y.shape:(6,)
    
    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    conv1d (Conv1D)              (None, 2, 64)             192       
    _________________________________________________________________
    max_pooling1d (MaxPooling1D) (None, 1, 64)             0         
    _________________________________________________________________
    flatten (Flatten)            (None, 64)                0         
    _________________________________________________________________
    dense (Dense)                (None, 50)                3250      
    _________________________________________________________________
    dense_1 (Dense)              (None, 1)                 51        
    =================================================================
    Total params: 3,493
    Trainable params: 3,493
    Non-trainable params: 0
    _________________________________________________________________
    
     None
    
    yhat: [[101.824524]]
    
    train_acc:0.0 
    train_loss:83.09048912930488
    

    2. 多变量 CNN 模型 (Multivariate CNN Models)

    多变量(多元)时间序列数据是指每一时间步有多个观测值的数据。对于多变量时间序列数据,有两种主要模型:

    • 多输入序列;
    • 多并行序列

    2.1 多输入序列

    一个问题可能有两个或多个并行输入时间序列和一个依赖于输入时间序列的输出时间序列。输入时间序列是并行的,因为每个序列在同一时间步上都有观测值。我们可以通过两个并行输入时间序列的简单示例来演示这一点,其中输出序列是输入序列的简单相加。代码实现:

    in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
    in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
    out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
    

    我们可以将这三个数据数组重塑为单个数据集,其中每一行是一个时间步,每一列是一个单独的时间序列。这是在CSV文件中存储并行时间序列的标准方法。代码实现:

    in_seq1 = in_seq1.reshape((len(in_seq1), 1)) 
    in_seq2 = in_seq2.reshape((len(in_seq2), 1))
    out_seq = out_seq.reshape((len(out_seq), 1))
    # 对于二维数组,hstack方法沿第二维堆叠,即沿着列堆叠,列数增加。
    dataset = hstack((in_seq1, in_seq2, out_seq)
    

    最后结果:

    [[ 10 15 25]
    [ 20 25 45]
    [ 30 35 65]
    [ 40 45 85]
    [ 50 55 105]
    [ 60 65 125]
    [ 70 75 145]
    [ 80 85 165]
    [ 90 95 185]]
    

    与单变量时间序列一样,我们必须将这些数据构造成具有输入和输出样本的样本。一维CNN模型需要足够的上下文信息来学习从输入序列到输出值的映射。CNNs可以支持并行输入时间序列作为独立的通道,可以类比图像的红色、绿色和蓝色分量。因此,我们需要将数据分成样本,保持两个输入序列的观测顺序。如果我们选择三个输入时间步骤,那么第一个示例将如下所示:

    输入:

    10, 15
    20, 25
    30, 35
    

    输出:

    65
    

    这里可能会看不明白,其实之前的文章已经介绍过了,这里再说一遍。看上边的9×3数组,也就是说,将每个并行序列(列)的前三个时间步(行)的值(3×2,三行两列)作为输入样本提供给模型,并且把第三个时间步(列)的值(本例中为65),作为样本标签提供给模型。还要注意,在将时间序列转换为输入/输出样本以训练模型时,不得不放弃输出时间序列中的一些值(前两行的第三列25和45没有使用),因为在先前的时间步,输入时间序列中没有值,所以没法预测。这个操作可以通过类似上节的oned_split_sequence函数来实现,为了增加文章的可读性,这里只放出结果,相关代码在完整代码部分中。划分结果:

    [[10 15]
    [20 25]
    [30 35]] 65
    [[20 25]
    [30 35]
    [40 45]] 85
    [[30 35]
    [40 45]
    [50 55]] 105
    [[40 45]
    [50 55]
    [60 65]] 125
    [[50 55]
    [60 65]
    [70 75]] 145
    [[60 65]
    [70 75]
    [80 85]] 165
    [[70 75]
    [80 85]
    [90 95]] 185
    

    看到这里,其实就很好理解了。每个样本中,三行相当于滑动窗口的宽度为3,上述划分是以滑动步长为1来取值的,每个样本之间的值是有重叠的,两列相当于有两个特征,然后用这些数据来预测下一步的输出,这个输出是单值的。这种方式可以用于时间序列分类任务,之后的文章中会介绍,比如人类行为识别。

    之前也在滑动窗口取值卡了一段时间,经过这段时间的学习,想明白了,画个图吧,方便填坑。这个图一看就明白了,还是拿上边的数据,来分析,如下图:
    在这里插入图片描述
    如果要做时间序列分类任务,只需要把最后一列数据换为标签就行了,每个采样点一个标签,然后取滑动窗口宽度结束索引的行的标签为样本标签;如果想要增加特征,增加列数就可以了。比如做降雨量预测,每一列数据就代表一个特征的采样数据,例如温度、湿度、风速等等,每一行就表示在不同时间戳上这些特征的采样值。


    2.1.1 CNN Model

    完整代码:

    import numpy as np
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Dense, Flatten, Conv1D, MaxPooling1D
    
    def split_sequences(first_seq, secend_seq, sw_width):
        '''
        该函数将序列数据分割成样本
        '''
        input_seq1 = np.array(first_seq).reshape(len(first_seq), 1)
        input_seq2 = np.array(secend_seq).reshape(len(secend_seq), 1)
        out_seq = np.array([first_seq[i]+secend_seq[i] for i in range(len(first_seq))])
        out_seq = out_seq.reshape(len(out_seq), 1)
        
        dataset = np.hstack((input_seq1, input_seq2, out_seq))
        print('dataset:\n',dataset)
        
        X, y = [], []
        
        for i in range(len(dataset)):
            # 切片索引从0开始,区间为前闭后开,所以不用减去1
            end_element_index = i + sw_width
            # 同样的道理,这里考虑最后一个样本正好取到最后一行的数据,那么索引不能减1,如果减去1的话最后一个样本就取不到了。
            if end_element_index > len(dataset):
                break
            
            # 该语句实现步长为1的滑动窗口截取数据功能;
            # 以下切片中,:-1 表示取除最后一列的其他列数据;-1表示取最后一列的数据
            seq_x, seq_y = dataset[i:end_element_index, :-1], dataset[end_element_index-1, -1]
            
            X.append(seq_x)
            y.append(seq_y)
            
            process_X, process_y = np.array(X), np.array(y)
        
        n_features = process_X.shape[2]
        print('train_X:\n{}\ntrain_y:\n{}\n'.format(process_X, process_y))
        print('train_X.shape:{},trian_y.shape:{}\n'.format(process_X.shape, process_y.shape))
        print('n_features:',n_features)
        return process_X, process_y, n_features
    
        
    def oned_cnn_model(sw_width, n_features, X, y, test_X, epoch_num, verbose_set):
        model = Sequential()
        
        model.add(Conv1D(filters=64, kernel_size=2, activation='relu',
                         strides=1, padding='valid', data_format='channels_last',
                         input_shape=(sw_width, n_features)))
        
        model.add(MaxPooling1D(pool_size=2, strides=None, padding='valid', 
                               data_format='channels_last')) 
        
        model.add(Flatten())
        
        model.add(Dense(units=50, activation='relu',
                    use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros',))
        
        model.add(Dense(units=1))
        
        model.compile(optimizer='adam', loss='mse',
                     metrics=['accuracy'], loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)
        
        print('\n',model.summary())
        
        history = model.fit(X, y, batch_size=32, epochs=epoch_num, verbose=verbose_set)
        
        
        yhat = model.predict(test_X, verbose=0)
        print('\nyhat:', yhat)
        
        return model, history
    
    if __name__ == '__main__':
        
        train_seq1 = [10, 20, 30, 40, 50, 60, 70, 80, 90]
        train_seq2 = [15, 25, 35, 45, 55, 65, 75, 85, 95]
        sw_width = 3
    
        epoch_num = 1000
        verbose_set = 0
        
        train_X, train_y, n_features = split_sequences(train_seq1, train_seq2, sw_width)
    
        # 预测
        x_input = np.array([[80, 85], [90, 95], [100, 105]])
        x_input = x_input.reshape((1, sw_width, n_features))
        
        model, history = oned_cnn_model(sw_width, n_features, train_X, train_y, x_input, epoch_num, verbose_set)
        
        print('\ntrain_acc:%s'%np.mean(history.history['accuracy']), '\ntrain_loss:%s'%np.mean(history.history['loss']))
    

    输出:

    dataset:
     [[ 10  15  25]
     [ 20  25  45]
     [ 30  35  65]
     [ 40  45  85]
     [ 50  55 105]
     [ 60  65 125]
     [ 70  75 145]
     [ 80  85 165]
     [ 90  95 185]]
    train_X:
    [[[10 15]
      [20 25]
      [30 35]]
    
     [[20 25]
      [30 35]
      [40 45]]
    
     [[30 35]
      [40 45]
      [50 55]]
    
     [[40 45]
      [50 55]
      [60 65]]
    
     [[50 55]
      [60 65]
      [70 75]]
    
     [[60 65]
      [70 75]
      [80 85]]
    
     [[70 75]
      [80 85]
      [90 95]]]
    train_y:
    [ 65  85 105 125 145 165 185]
    
    train_X.shape:(7, 3, 2),trian_y.shape:(7,)
    
    n_features: 2
    Model: "sequential_2"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    conv1d_2 (Conv1D)            (None, 2, 64)             320       
    _________________________________________________________________
    max_pooling1d_2 (MaxPooling1 (None, 1, 64)             0         
    _________________________________________________________________
    flatten_2 (Flatten)          (None, 64)                0         
    _________________________________________________________________
    dense_4 (Dense)              (None, 50)                3250      
    _________________________________________________________________
    dense_5 (Dense)              (None, 1)                 51        
    =================================================================
    Total params: 3,621
    Trainable params: 3,621
    Non-trainable params: 0
    _________________________________________________________________
    
     None
    
    yhat: [[205.84216]]
    
    train_acc:0.0 
    train_loss:290.3450777206163
    

    2.1.2 Multi-headed CNN Model

    有另一种更精细的方法来解决多变量输入序列问题。每个输入序列可以由单独的CNN处理,并且在对输出序列进行预测之前,可以组合这些子模型中的每个的输出。我们可以称之为 Multi-headed CNN模型。它可能提供更多的灵活性或更好的性能,这取决于正在建模的问题的具体情况。例如,它允许每个输入序列配置不同的子模型,例如过滤器映射的数量和内核大小。这种类模型可以用Keras API定义。首先,可以将第一个输入模型定义为一个一维CNN,其输入层要求输入为n个步骤和1个特征。代码实现:

    visible1 = Input(shape=(n_steps, n_features))
    cnn1 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible1)
    cnn1 = MaxPooling1D(pool_size=2)(cnn1)
    cnn1 = Flatten()(cnn1)
    
    visible2 = Input(shape=(n_steps, n_features))
    cnn2 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible2)
    cnn2 = MaxPooling1D(pool_size=2)(cnn2)
    cnn2 = Flatten()(cnn2)
    

    定义好两个输入子模型后,可以将每个模型的输出合并为一个长向量,在对输出序列进行预测之前可以对其进行解释。最后,将输入和输出绑定在一起。代码实现:

    merge = concatenate([cnn1, cnn2])
    dense = Dense(50, activation='relu')(merge)
    output = Dense(1)(dense)
    
    model = Model(inputs=[visible1, visible2], outputs=output)
    

    此模型要求将输入作为两个元素的列表提供,其中列表中的每个元素都包含其中一个子模型的数据。为了实现这一点,我们可以将3D输入数据分割成两个独立的输入数据数组,即从一个形状为 [7,3,2] 的数组分割成两个形状为 [7,3,1] 的3D数组。代码实现:

    n_features = 1
    # separate input data
    X1 = X[:, :, 0].reshape(X.shape[0], X.shape[1], n_features)
    X2 = X[:, :, 1].reshape(X.shape[0], X.shape[1], n_features)
    

    开始训练:

    model.fit([X1, X2], y, epochs=1000, verbose=0)
    

    完整代码:

    import numpy as np
    from tensorflow.keras.models import Sequential, Model
    from tensorflow.keras.layers import Dense, Flatten, Conv1D, MaxPooling1D, Input, concatenate
    from tensorflow.keras.utils import plot_model
    
    def split_sequences2(first_seq, secend_seq, sw_width, n_features):
        '''
        该函数将序列数据分割成样本
        '''
        input_seq1 = np.array(first_seq).reshape(len(first_seq), 1)
        input_seq2 = np.array(secend_seq).reshape(len(secend_seq), 1)
        out_seq = np.array([first_seq[i]+secend_seq[i] for i in range(len(first_seq))])
        out_seq = out_seq.reshape(len(out_seq), 1)
        
        dataset = np.hstack((input_seq1, input_seq2, out_seq))
        print('dataset:\n',dataset)
        
        X, y = [], []
        
        for i in range(len(dataset)):
            # 切片索引从0开始,区间为前闭后开,所以不用减去1
            end_element_index = i + sw_width
            # 同样的道理,这里考虑最后一个样本正好取到最后一行的数据,那么索引不能减1,如果减去1的话最后一个样本就取不到了。
            if end_element_index > len(dataset):
                break
            
            # 该语句实现步长为1的滑动窗口截取数据功能;
            # 以下切片中,:-1 表示取除最后一列的其他列数据;-1表示取最后一列的数据
            seq_x, seq_y = dataset[i:end_element_index, :-1], dataset[end_element_index-1, -1]
            
            X.append(seq_x)
            y.append(seq_y)
            
            process_X, process_y = np.array(X), np.array(y)
        
        # [:,:,0]表示三维数组前两个维度的数据全取,第三个维度取第一个数据,可以想象成一摞饼干,取了一块。
        # 本例中 process_X的shape为(7,3,2),所以下式就很好理解了,
        X1 = process_X[:,:,0].reshape(process_X.shape[0], process_X.shape[1], n_features)
        X2 = process_X[:,:,1].reshape(process_X.shape[0], process_X.shape[1], n_features)
    
        print('train_X:\n{}\ntrain_y:\n{}\n'.format(process_X, process_y))
        print('train_X.shape:{},trian_y.shape:{}\n'.format(process_X.shape, process_y.shape))
        print('X1.shape:{},X2.shape:{}\n'.format(X1.shape, X2.shape))
        
        return X1, X2, process_y
    
        
    def oned_cnn_model(n_steps, n_features, X_1, X_2, y, x1, x2, epoch_num, verbose_set):
        
        visible1 = Input(shape=(n_steps, n_features))
        cnn1 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible1)
        cnn1 = MaxPooling1D(pool_size=2)(cnn1)
        cnn1 = Flatten()(cnn1)
    
        visible2 = Input(shape=(n_steps, n_features))
        cnn2 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible2)
        cnn2 = MaxPooling1D(pool_size=2)(cnn2)
        cnn2 = Flatten()(cnn2)
        
        merge = concatenate([cnn1, cnn2])
        dense = Dense(50, activation='relu')(merge)
        output = Dense(1)(dense)
    
        model = Model(inputs=[visible1, visible2], outputs=output)
    
        model.compile(optimizer='adam', loss='mse',
                     metrics=['accuracy'], loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)
        
        print('\n',model.summary())
        plot_model(model, to_file='multi_head_cnn_model.png', show_shapes=True, show_layer_names=True, rankdir='TB', dpi=200)
        
        history = model.fit([X_1, X_2], y, batch_size=32, epochs=epoch_num, verbose=verbose_set)
    
        yhat = model.predict([x1,x2], verbose=0)
        print('\nyhat:', yhat)
        
        return model, history
    
    if __name__ == '__main__':
        
        train_seq1 = [10, 20, 30, 40, 50, 60, 70, 80, 90]
        train_seq2 = [15, 25, 35, 45, 55, 65, 75, 85, 95]
        sw_width = 3
        n_features = 1
        epoch_num = 1000
        verbose_set = 0
        
        train_X1, train_X2, train_y = split_sequences2(train_seq1, train_seq2, sw_width, n_features)
    
        # 预测
        x_input = np.array([[80, 85], [90, 95], [100, 105]])
        x_1 = x_input[:, 0].reshape((1, sw_width, n_features))
        x_2 = x_input[:, 1].reshape((1, sw_width, n_features))
        
        model, history = oned_cnn_model(sw_width, n_features, train_X1, train_X2, train_y, x_1, x_2, epoch_num, verbose_set)
    
        print('\ntrain_acc:%s'%np.mean(history.history['accuracy']), '\ntrain_loss:%s'%np.mean(history.history['loss']))
    

    输出:

    dataset:
     [[ 10  15  25]
     [ 20  25  45]
     [ 30  35  65]
     [ 40  45  85]
     [ 50  55 105]
     [ 60  65 125]
     [ 70  75 145]
     [ 80  85 165]
     [ 90  95 185]]
    train_X:
    [[[10 15]
      [20 25]
      [30 35]]
    
     [[20 25]
      [30 35]
      [40 45]]
    
     [[30 35]
      [40 45]
      [50 55]]
    
     [[40 45]
      [50 55]
      [60 65]]
    
     [[50 55]
      [60 65]
      [70 75]]
    
     [[60 65]
      [70 75]
      [80 85]]
    
     [[70 75]
      [80 85]
      [90 95]]]
    train_y:
    [ 65  85 105 125 145 165 185]
    
    train_X.shape:(7, 3, 2),trian_y.shape:(7,)
    
    X1.shape:(7, 3, 1),X2.shape:(7, 3, 1)
    
    Model: "model_8"
    __________________________________________________________________________________________________
    Layer (type)                    Output Shape         Param #     Connected to                     
    ==================================================================================================
    input_17 (InputLayer)           [(None, 3, 1)]       0                                            
    __________________________________________________________________________________________________
    input_18 (InputLayer)           [(None, 3, 1)]       0                                            
    __________________________________________________________________________________________________
    conv1d_16 (Conv1D)              (None, 2, 64)        192         input_17[0][0]                   
    __________________________________________________________________________________________________
    conv1d_17 (Conv1D)              (None, 2, 64)        192         input_18[0][0]                   
    __________________________________________________________________________________________________
    max_pooling1d_16 (MaxPooling1D) (None, 1, 64)        0           conv1d_16[0][0]                  
    __________________________________________________________________________________________________
    max_pooling1d_17 (MaxPooling1D) (None, 1, 64)        0           conv1d_17[0][0]                  
    __________________________________________________________________________________________________
    flatten_16 (Flatten)            (None, 64)           0           max_pooling1d_16[0][0]           
    __________________________________________________________________________________________________
    flatten_17 (Flatten)            (None, 64)           0           max_pooling1d_17[0][0]           
    __________________________________________________________________________________________________
    concatenate_8 (Concatenate)     (None, 128)          0           flatten_16[0][0]                 
                                                                     flatten_17[0][0]                 
    __________________________________________________________________________________________________
    dense_16 (Dense)                (None, 50)           6450        concatenate_8[0][0]              
    __________________________________________________________________________________________________
    dense_17 (Dense)                (None, 1)            51          dense_16[0][0]                   
    ==================================================================================================
    Total params: 6,885
    Trainable params: 6,885
    Non-trainable params: 0
    __________________________________________________________________________________________________
    
     None
    
    yhat: [[205.66142]]
    
    train_acc:0.0 
    train_loss:201.12432714579907
    

    保存的网络结构图:
    在这里插入图片描述


    2.2 多并行序列

    假设有如下序列:

    [[ 10 15 25]
    [ 20 25 45]
    [ 30 35 65]
    [ 40 45 85]
    [ 50 55 105]
    [ 60 65 125]
    [ 70 75 145]
    [ 80 85 165]
    [ 90 95 185]]
    

    输入:

    10, 15, 25
    20, 25, 45
    30, 35, 65
    

    输出:

    40, 45, 85
    

    对于整个数据集:

    [[10 15 25]
    [20 25 45]
    [30 35 65]] [40 45 85]
    [[20 25 45]
    [30 35 65]
    [40 45 85]] [ 50 55 105]
    [[ 30 35 65]
    [ 40 45 85]
    [ 50 55 105]] [ 60 65 125]
    [[ 40 45 85]
    [ 50 55 105]
    [ 60 65 125]] [ 70 75 145]
    [[ 50 55 105]
    [ 60 65 125]
    [ 70 75 145]] [ 80 85 165]
    [[ 60 65 125]
    [ 70 75 145]
    [ 80 85 165]] [ 90 95 185]
    

    2.2.1 Vector-Output CNN Model

    要在该数据集上建立一个一维CNN模型。在该模型中,通过input_shape参数为输入层指定时间步数和并行序列(特征)。代码实现:

    model = Sequential()
    model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps,
    n_features)))
    model.add(MaxPooling1D(pool_size=2))
    model.add(Flatten())
    model.add(Dense(50, activation='relu'))
    model.add(Dense(n_features))
    model.compile(optimizer='adam', loss='mse')
    

    完整代码:

    import numpy as np
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Dense, Flatten, Conv1D, MaxPooling1D
    
    def split_sequences(first_seq, secend_seq, sw_width):
        '''
        该函数将序列数据分割成样本
        '''
        input_seq1 = np.array(first_seq).reshape(len(first_seq), 1)
        input_seq2 = np.array(secend_seq).reshape(len(secend_seq), 1)
        out_seq = np.array([first_seq[i]+secend_seq[i] for i in range(len(first_seq))])
        out_seq = out_seq.reshape(len(out_seq), 1)
        
        dataset = np.hstack((input_seq1, input_seq2, out_seq))
        print('dataset:\n',dataset)
        
        X, y = [], []
        
        for i in range(len(dataset)):
            end_element_index = i + sw_width
            if end_element_index > len(dataset) - 1:
                break
            
            # 该语句实现步长为1的滑动窗口截取数据功能;
            seq_x, seq_y = dataset[i:end_element_index, :], dataset[end_element_index, :]
            
            X.append(seq_x)
            y.append(seq_y)
            
            process_X, process_y = np.array(X), np.array(y)
        
        n_features = process_X.shape[2]
        print('train_X:\n{}\ntrain_y:\n{}\n'.format(process_X, process_y))
        print('train_X.shape:{},trian_y.shape:{}\n'.format(process_X.shape, process_y.shape))
        print('n_features:',n_features)
        return process_X, process_y, n_features
    
        
    def oned_cnn_model(sw_width, n_features, X, y, test_X, epoch_num, verbose_set):
        model = Sequential()
        
        model.add(Conv1D(filters=64, kernel_size=2, activation='relu',
                         strides=1, padding='valid', data_format='channels_last',
                         input_shape=(sw_width, n_features)))
        
        model.add(MaxPooling1D(pool_size=2, strides=None, padding='valid', 
                               data_format='channels_last')) 
        
        model.add(Flatten())
        
        model.add(Dense(units=50, activation='relu',
                    use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros',))
        
        model.add(Dense(units=n_features))
        
        model.compile(optimizer='adam', loss='mse',
                     metrics=['accuracy'], loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)
        
        print('\n',model.summary())
        
        history = model.fit(X, y, batch_size=32, epochs=epoch_num, verbose=verbose_set)
        
        
        yhat = model.predict(test_X, verbose=0)
        print('\nyhat:', yhat)
        
        return model, history
    
    if __name__ == '__main__':
        
        train_seq1 = [10, 20, 30, 40, 50, 60, 70, 80, 90]
        train_seq2 = [15, 25, 35, 45, 55, 65, 75, 85, 95]
        sw_width = 3
    
        epoch_num = 3000
        verbose_set = 0
        
        train_X, train_y, n_features = split_sequences(train_seq1, train_seq2, sw_width)
    
        # 预测
        x_input = np.array([[70,75,145], [80,85,165], [90,95,185]])
        x_input = x_input.reshape((1, sw_width, n_features))
        
        model, history = oned_cnn_model(sw_width, n_features, train_X, train_y, x_input, epoch_num, verbose_set)
        
        print('\ntrain_acc:%s'%np.mean(history.history['accuracy']), '\ntrain_loss:%s'%np.mean(history.history['loss']))
    

    输出:

    dataset:
     [[ 10  15  25]
     [ 20  25  45]
     [ 30  35  65]
     [ 40  45  85]
     [ 50  55 105]
     [ 60  65 125]
     [ 70  75 145]
     [ 80  85 165]
     [ 90  95 185]]
    train_X:
    [[[ 10  15  25]
      [ 20  25  45]
      [ 30  35  65]]
    
     [[ 20  25  45]
      [ 30  35  65]
      [ 40  45  85]]
    
     [[ 30  35  65]
      [ 40  45  85]
      [ 50  55 105]]
    
     [[ 40  45  85]
      [ 50  55 105]
      [ 60  65 125]]
    
     [[ 50  55 105]
      [ 60  65 125]
      [ 70  75 145]]
    
     [[ 60  65 125]
      [ 70  75 145]
      [ 80  85 165]]]
    train_y:
    [[ 40  45  85]
     [ 50  55 105]
     [ 60  65 125]
     [ 70  75 145]
     [ 80  85 165]
     [ 90  95 185]]
    
    train_X.shape:(6, 3, 3),trian_y.shape:(6, 3)
    
    n_features: 3
    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    conv1d (Conv1D)              (None, 2, 64)             448       
    _________________________________________________________________
    max_pooling1d (MaxPooling1D) (None, 1, 64)             0         
    _________________________________________________________________
    flatten (Flatten)            (None, 64)                0         
    _________________________________________________________________
    dense (Dense)                (None, 50)                3250      
    _________________________________________________________________
    dense_1 (Dense)              (None, 3)                 153       
    =================================================================
    Total params: 3,851
    Trainable params: 3,851
    Non-trainable params: 0
    _________________________________________________________________
    
     None
    
    yhat: [[100.58862 106.20969 207.11055]]
    
    train_acc:0.9979444 
    train_loss:36.841995810692595
    

    2.2.2 Multi-output CNN Model

    与多个输入序列一样,还有另一种更精细的方法来建模问题。每个输出序列可以由单独的输出CNN模型处理。我们可以称之为多输出CNN模型。它可能提供更多的灵活性或更好的性能,这取决于正在建模的问题的具体情况。代码实现:

    visible = Input(shape=(n_steps, n_features))
    cnn = Conv1D(filters=64, kernel_size=2, activation='relu')(visible)
    cnn = MaxPooling1D(pool_size=2)(cnn)
    cnn = Flatten()(cnn)
    cnn = Dense(50, activation='relu')(cnn)
    

    然后,我们可以为希望预测的三个序列中的每一个定义一个输出层,其中每个输出子模型将预测一个时间步。

    output1 = Dense(1)(cnn)
    output2 = Dense(1)(cnn)
    output3 = Dense(1)(cnn)
    

    绑定模型,编译模型:

    model = Model(inputs=visible, outputs=[output1, output2, output3])
    model.compile(optimizer='adam', loss='mse')
    

    在训练模型时,每个样本需要三个独立的输出数组。可以通过将具有形状[7,3]的输出训练数据转换为具有形状[7,1]的三个数组来实现这一点。

    y1 = y[:, 0].reshape((y.shape[0], 1))
    y2 = y[:, 1].reshape((y.shape[0], 1))
    y3 = y[:, 2].reshape((y.shape[0], 1))
    

    开始训练:

    model.fit(X, [y1,y2,y3], epochs=2000, verbose=0)
    

    完整代码:

    import numpy as np
    from tensorflow.keras.models import Sequential, Model
    from tensorflow.keras.layers import Dense, Flatten, Conv1D, MaxPooling1D, Input, concatenate
    from tensorflow.keras.utils import plot_model
    
    def split_sequences(first_seq, secend_seq, sw_width):
        '''
        该函数将序列数据分割成样本
        '''
        input_seq1 = np.array(first_seq).reshape(len(first_seq), 1)
        input_seq2 = np.array(secend_seq).reshape(len(secend_seq), 1)
        out_seq = np.array([first_seq[i]+secend_seq[i] for i in range(len(first_seq))])
        out_seq = out_seq.reshape(len(out_seq), 1)
        
        dataset = np.hstack((input_seq1, input_seq2, out_seq))
        print('dataset:\n',dataset)
        
        X, y = [], []
        
        for i in range(len(dataset)):
            end_element_index = i + sw_width
            if end_element_index > len(dataset) - 1:
                break
            
            # 该语句实现步长为1的滑动窗口截取数据功能;
            seq_x, seq_y = dataset[i:end_element_index, :], dataset[end_element_index, :]
            
            X.append(seq_x)
            y.append(seq_y)
            
            process_X, process_y = np.array(X), np.array(y)
        
        n_features = process_X.shape[2]
        y1 = process_y[:, 0].reshape((process_y.shape[0], 1))
        y2 = process_y[:, 1].reshape((process_y.shape[0], 1))
        y3 = process_y[:, 2].reshape((process_y.shape[0], 1))
        
        print('train_X:\n{}\ntrain_y:\n{}\n'.format(process_X, process_y))
        print('train_X.shape:{},trian_y.shape:{}\n'.format(process_X.shape, process_y.shape))
        print('n_features:',n_features)
        return process_X, process_y, n_features, y1, y2, y3
    
        
    def oned_cnn_model(n_steps, n_features, X, y, test_X, epoch_num, verbose_set):
        visible = Input(shape=(n_steps, n_features))
        cnn = Conv1D(filters=64, kernel_size=2, activation='relu')(visible)
        cnn = MaxPooling1D(pool_size=2)(cnn)
        cnn = Flatten()(cnn)
        cnn = Dense(50, activation='relu')(cnn)
        
        output1 = Dense(1)(cnn)
        output2 = Dense(1)(cnn)
        output3 = Dense(1)(cnn)
        
        model = Model(inputs=visible, outputs=[output1, output2, output3])
        
        model.compile(optimizer='adam', loss='mse',
                     metrics=['accuracy'], loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)
        
        print('\n',model.summary())
        plot_model(model, to_file='vector_output_cnn_model.png', show_shapes=True, show_layer_names=True, rankdir='TB', dpi=200)
    
        model.fit(X, y, batch_size=32, epochs=epoch_num, verbose=verbose_set)
        
        
        yhat = model.predict(test_X, verbose=0)
        print('\nyhat:', yhat)
        
        return model
    
    if __name__ == '__main__':
        
        train_seq1 = [10, 20, 30, 40, 50, 60, 70, 80, 90]
        train_seq2 = [15, 25, 35, 45, 55, 65, 75, 85, 95]
        sw_width = 3
    
        epoch_num = 2000
        verbose_set = 0
        
        train_X, train_y, n_features, y1, y2, y3 = split_sequences(train_seq1, train_seq2, sw_width)
    
        # 预测
        x_input = np.array([[70,75,145], [80,85,165], [90,95,185]])
        x_input = x_input.reshape((1, sw_width, n_features))
        
        model = oned_cnn_model(sw_width, n_features, train_X, [y1, y2, y3], x_input, epoch_num, verbose_set)
        
    

    输出:

    dataset:
     [[ 10  15  25]
     [ 20  25  45]
     [ 30  35  65]
     [ 40  45  85]
     [ 50  55 105]
     [ 60  65 125]
     [ 70  75 145]
     [ 80  85 165]
     [ 90  95 185]]
    train_X:
    [[[ 10  15  25]
      [ 20  25  45]
      [ 30  35  65]]
    
     [[ 20  25  45]
      [ 30  35  65]
      [ 40  45  85]]
    
     [[ 30  35  65]
      [ 40  45  85]
      [ 50  55 105]]
    
     [[ 40  45  85]
      [ 50  55 105]
      [ 60  65 125]]
    
     [[ 50  55 105]
      [ 60  65 125]
      [ 70  75 145]]
    
     [[ 60  65 125]
      [ 70  75 145]
      [ 80  85 165]]]
    train_y:
    [[ 40  45  85]
     [ 50  55 105]
     [ 60  65 125]
     [ 70  75 145]
     [ 80  85 165]
     [ 90  95 185]]
    
    train_X.shape:(6, 3, 3),trian_y.shape:(6, 3)
    
    n_features: 3
    Model: "model_2"
    __________________________________________________________________________________________________
    Layer (type)                    Output Shape         Param #     Connected to                     
    ==================================================================================================
    input_4 (InputLayer)            [(None, 3, 3)]       0                                            
    __________________________________________________________________________________________________
    conv1d_4 (Conv1D)               (None, 2, 64)        448         input_4[0][0]                    
    __________________________________________________________________________________________________
    max_pooling1d_4 (MaxPooling1D)  (None, 1, 64)        0           conv1d_4[0][0]                   
    __________________________________________________________________________________________________
    flatten_4 (Flatten)             (None, 64)           0           max_pooling1d_4[0][0]            
    __________________________________________________________________________________________________
    dense_14 (Dense)                (None, 50)           3250        flatten_4[0][0]                  
    __________________________________________________________________________________________________
    dense_15 (Dense)                (None, 1)            51          dense_14[0][0]                   
    __________________________________________________________________________________________________
    dense_16 (Dense)                (None, 1)            51          dense_14[0][0]                   
    __________________________________________________________________________________________________
    dense_17 (Dense)                (None, 1)            51          dense_14[0][0]                   
    ==================================================================================================
    Total params: 3,851
    Trainable params: 3,851
    Non-trainable params: 0
    __________________________________________________________________________________________________
    
     None
    
    yhat: [array([[101.1264]], dtype=float32), array([[106.274635]], dtype=float32), array([[207.64928]], dtype=float32)]
    

    网络结构图:
    在这里插入图片描述


    由于字数过多(已经28000多字)页面变卡,下篇继续将剩下的两类模型。
    在这里插入图片描述


    参考:
    https://machinelearningmastery.com/how-to-develop-convolutional-neural-network-models-for-time-series-forecasting/

    展开全文
  • 一个成功的时间序列预测模型应该捕获这两种重复模式以进行准确的预测。 另一个例子,基于不同位置的大型传感器测量的太阳辐射来预测太阳能发电厂的输出的任务。长期模式反映白天与黑夜、夏季与冬季等之间的差异,...

    论文名称:Modeling Long- and Short-Term Temporal Patterns with Deep Neural Networks
    论文下载:https://dl.acm.org/doi/abs/10.1145/3209978.3210006
    论文年份:SIGIR 2018
    论文被引:594(2022/04/21)
    论文代码:https://github.com/laiguokun/LSTNet
    论文数据:https://github.com/laiguokun/multivariate-time-series-data

    ABSTRACT

    Multivariate time series forecasting is an important machine learning problem across many domains, including predictions of solar plant energy output, electricity consumption, and traffic jam situation. Temporal data arise in these real-world applications often involves a mixture of long-term and short-term patterns, for which traditional approaches such as Autoregressive models and Gaussian Process may fail. In this paper, we proposed a novel deep learning framework, namely Long- and Short-term Time-series network (LSTNet), to address this open challenge. LSTNet uses the Convolution Neural Network (CNN) and the Recurrent Neural Network (RNN) to extract short-term local dependency patterns among variables and to discover long-term patterns for time series trends. Furthermore, we leverage traditional autoregressive model to tackle the scale insensitive problem of the neural network model. In our evaluation on real-world data with complex mixtures of repetitive patterns, LSTNet achieved significant performance improvements over that of several state-of-the-art baseline methods. All the data and experiment codes are available online.

    【现实意义】

    多变量时间序列预测是跨多个领域的重要机器学习问题,包括太阳能发电厂能量输出、电力消耗和交通拥堵情况的预测。

    【存在问题】

    在这些实际应用中出现的时间数据通常涉及长期和短期模式的混合,对于这些模式,自回归模型和高斯过程等传统方法可能会失败。

    【解决方案】

    在本文中,我们提出了一种新颖的深度学习框架,即长期和短期时间序列网络 (Long- and Short-term Time-series network, LSTNet),以应对这一开放挑战。 LSTNet 使用卷积神经网络 (CNN) 和循环神经网络 (RNN) 来提取变量之间的短期局部依赖模式,并发现时间序列趋势的长期模式。此外,我们利用传统的自回归模型来解决神经网络模型的尺度不敏感问题

    【实验结果】

    在我们对具有重复模式的复杂混合的现实世界数据的评估中,LSTNet 比几种最先进的基线方法实现了显着的性能改进。所有数据和实验代码均可在线获取。

    1 INTRODUCTION

    【现实意义】

    多元时间序列数据在我们的日常生活中无处不在,从股票市场的价格、高速公路上的交通流量、太阳能发电厂的输出、不同城市的温度等等,不一而足。在此类应用中,用户通常对基于对时间序列信号的历史观察预测新趋势或潜在危险事件感兴趣。例如,可以根据提前几个小时预测的交通拥堵模式制定更好的路线计划,通过对近期股市的预测获得更大的利润。

    【存在问题】

    多元时间序列预测经常面临一个重大的研究挑战,即如何捕捉和利用多个变量之间的动态依赖关系。具体来说,现实世界的应用程序通常需要混合使用短期和长期重复模式

    【举例说明】
    在这里插入图片描述

    • 如图 1 所示,该图绘制了高速公路的每小时占用率。显然,有两种重复模式,每天和每周前者描绘了早高峰与晚高峰,而后者则反映了工作日和周末的模式。一个成功的时间序列预测模型应该捕获这两种重复模式以进行准确的预测
    • 另一个例子,基于不同位置的大型传感器测量的太阳辐射来预测太阳能发电厂的输出的任务长期模式反映白天与黑夜、夏季与冬季等之间的差异短期模式反映云运动、风向变化等的影响。同样,没有考虑这两种循环模式,准确的时间序列预测是不可能的

    【已有研究的局限性】

    然而,传统方法,如自回归方法 [2, 12, 22, 32, 35] 中的大量工作在这方面存在不足,因为它们中的大多数没有区分这两种模式,也没有明确和动态地模拟它们的交互。解决现有方法在时间序列预测中的这些局限性是本文的主要重点,为此我们提出了一个新的框架,该框架利用了深度学习研究的最新发展。

    【从粗粒度上阐述与本文相关的工作,本文是RNN和CNN】

    深度神经网络已在相关领域得到深入研究,并对广泛问题的解决产生了非凡的影响。例如,递归神经网络 (RNN) 模型 [9] 在最近的自然语言处理 (NLP) 研究中变得最流行。特别是 RNN 的两个变体,即长短期记忆 (LSTM) [15] 和门控循环单元 (GRU) [6],显着提高了机器翻译、语音识别和其他 NLP 任务,因为它们可以根据输入文档中单词之间的长期和短期依赖关系有效地捕捉单词的含义 [1, 14, 19]。在计算机视觉领域,再举一个例子,卷积神经网络(CNN) 模型 [19, 21] 通过从输入图像中成功提取各种粒度级别的局部和移位不变特征(有时称为“shapelets”),显示出出色的性能。

    【进一步阐述RNN和CNN应用于时间序列领域(分类,回归)的研究】

    深度神经网络在时间序列分析中也受到越来越多的关注。先前工作的很大一部分都集中在时间序列分类上,即自动将类标签分配给时间序列输入的任务。例如,RNN 架构已被研究用于从医疗保健序列数据中提取信息模式 [5, 23],并根据诊断类别对数据进行分类。 RNN 也被应用于移动数据,用于根据动作或活动对输入序列进行分类 [13]。 CNN 模型也被用于动作/活动识别 [13, 20, 31],用于从输入序列中提取移位不变的局部模式作为分类模型的特征

    深度神经网络也被研究用于时间序列预测 [8, 33],即使用过去观察到的时间序列来预测前瞻视野中的未知时间序列的任务——视野越大,越难预测。在这个方向上的努力范围从早期使用朴素 RNN 模型 [7] 和结合使用 ARIMA [3] 和多层感知器 (MLP) 的混合模型 [16, 34, 35] 到最近将 vanilla RNN 和时间序列预测中的动态玻尔兹曼机[8]。

    【最后引出本文的解决方案及实验结果】
    在这里插入图片描述

    在本文中,我们提出了一个为多元时间序列预测设计的深度学习框架,即长期和短期时间序列网络 (LSTNet),如图 2 所示。

    • 利用两个卷积层的优势来发现局部多维输入变量和循环层之间的依赖模式,以捕获复杂的长期依赖关系
    • 一种新颖的循环结构 Recurrent-skip,旨在捕获非常长期的依赖模式,并使优化更容易,因为它利用了输入时间序列信号的周期性属性
    • 最后,LSTNet 将传统的自回归线性模型与非线性神经网络部分并行合并,使得非线性深度学习模型对于违反尺度变化的时间序列更加鲁棒

    在真实世界季节性时间序列数据集的实验中,我们的模型始终优于传统的线性模型和 GRU 递归神经网络。

    【本文剩余部分内容的组织】

    本文的其余部分安排如下。第 2 节概述了相关背景,包括具有代表性的自回归方法和高斯过程模型。第 3 节描述了我们提出的 LSTNet。第 4 节报告了我们模型的评估结果,并与真实数据集上的强基线进行了比较。最后,我们在第 5 节总结我们的发现。

    2 RELATED BACKGROUND

    【ARIMA模型家族的优缺点】

    自回归综合移动平均 (autoregressive integrated moving average , ARIMA) 模型:最突出的单变量时间序列模型之一。

    优点:其统计特性以及模型选择过程中著名的 Box-Jenkins 方法 [2]。 ARIMA 模型不仅适用于各种指数平滑技术 [25],而且足够灵活,可以包含其他类型的时间序列模型,包括:

    • 自回归 (autoregression, AR)
    • 移动平均 (moving average, MA)
    • 自回归移动平均 (Autoregressive Moving Average, ARMA)

    缺点:ARIMA 模型,包括它们用于建模长期时间依赖关系的变体 [2],由于其高计算成本而很少用于高维多元时间序列预测


    【VAR模型家族的优缺点】

    向量自回归 (vector autoregression, VAR):由于其简单性,是多元时间序列 [2, 12, 24] 中使用最广泛的模型。 VAR 模型自然地将 AR 模型扩展到多变量设置,它忽略了输出变量之间的依赖关系

    近年来,各种 VAR 模型取得了重大进展,包括:

    • elliptical VAR model [27]:用于长尾时间序列
    • structured VAR model [26]:用于更好地解释高维变量之间依赖关系

    然而,VAR 的模型容量在时间窗口大小上呈线性增长,在变量数量上呈二次方增长。这意味着,在处理长期时间模式时,继承的大模型容易过拟合为了缓解这个问题,[32] 提出将原始高维信号减少为低维隐藏表示,然后应用 VAR 进行预测,并选择多种正则化


    【线性回归+正则化的模型的优缺点】

    时间序列预测问题也可以被视为具有时变参数的标准回归问题。因此,可以很自然地将具有不同损失函数和正则化项的各种回归模型应用于时间序列预测任务。例如:

    • 线性支持向量回归 (SVR) [4, 17] 基于回归损失学习最大边距超平面,其中超参数 ϵ 控制预测误差的阈值。
    • 岭回归(Ridge regression)可以通过将 ϵ 设置为零来从 SVR 模型中恢复。
    • [22] 应用 LASSO 模型来鼓励模型参数的稀疏性,以便可以显示不同输入信号之间的有趣模式。

    由于机器学习社区中有高质量的现成求解器,这些线性方法实际上对于多变量时间序列预测更有效。尽管如此,与 VAR 一样,这些线性模型可能无法捕捉多元信号的复杂非线性关系,从而以牺牲效率为代价导致性能下降


    【高斯过程的优缺点】

    高斯过程 (GP) 是一种用于对函数连续域上的分布进行建模的非参数方法。这与由诸如 VAR 和 SVR 等参数化函数类定义的模型形成对比。

    • [28] 中GP 可以应用于多元时间序列预测任务,并且可以用作贝叶斯推理中函数空间的先验。
    • [10] 提出了一种具有 GP 先验的完全贝叶斯方法,用于非线性状态空间模型,能够捕捉复杂的动态现象。

    然而,高斯过程的性能以高计算复杂度为代价由于核矩阵的矩阵求逆,多变量时间序列预测的高斯过程的直接实现在观测数上具有三次复杂度

    3 FRAMEWORK

    在本节中,我们首先制定时间序列预测问题,然后在下一部分讨论所提出的 LSTNet 架构(图 2)的细节。最后,我们介绍了目标函数和优化策略。

    3.1 Problem Formulation

    在本文中,我们对多元时间序列预测的任务感兴趣。更正式地说,给定一系列完全观察到的时间序列信号 Y = y 1 , y 2 , . . . , y T Y = {y_1,y_2, ...,y_T } Y=y1,y2,...,yT 其中 y t ∈ R n y_t ∈ \R^n ytRn n n n 是变量维度,我们旨在以滚动预测方式预测一系列未来信号。话虽如此,为了预测 y T + h y_{T +h} yT+h,其中 h h h 是当前时间戳之前的理想范围,我们假设 { y 1 , y 2 , . . . , y T } \{y_1,y_2, . . . ,y_T\} {y1,y2,...,yT} 可用。同样,为了预测下一个时间戳 y T + h + 1 y_{T+h+1} yT+h+1 的值,我们假设 { y 1 , y 2 , . . . , y T , y T + 1 } \{y_1,y_2, . . . ,y_T ,y_{T +1}\} {y1,y2,...,yT,yT+1} 可用。因此,我们将时间戳 T T T 处的输入矩阵表示为 X T = { y 1 , y 2 , . . . , y T } ∈ R n × T X_T = \{y_1,y_2, . . . ,y_T \} ∈ \R^{n×T} XT={y1,y2,...,yT}Rn×T

    在大多数情况下,预测任务的范围是根据环境设置的要求来选择的,例如对于流量使用,感兴趣的范围从几小时到一天不等;对于股市数据,即使是提前几秒/分钟的预测对于产生回报也很有意义。

    图 2 概述了建议的 LSTnet 架构。 LSTNet 是一个深度学习框架,专为混合长期和短期模式的多变量时间序列预测任务而设计。在接下来的部分中,我们将详细介绍 LSTNet 的构建块。

    3.2 Convolutional Component

    LSTNet 第一层是一个没有池化的卷积网络,旨在提取时间维度上的短期模式以及变量之间的局部依赖关系卷积层由多个宽度为 ω 和高度为 n 的滤波器组成(高度设置为与变量个数相同)。第 k 个滤波器扫描输入矩阵 X 并产生
    在这里插入图片描述

    其中 * 表示卷积运算,输出 hk 将是一个向量,RELU 函数为 RELU (x) = max(0, x)。我们通过在输入矩阵 X 的左侧进行零填充来使每个向量 hk 的长度为 T 。卷积层的输出矩阵大小为 dc × T,其中 dc 表示滤波器的数量。

    3.3 Recurrent Component

    卷积层的输出同时被馈送到 Recurrent 组件和 Recurrent-skip 组件(将在 3.4 小节中描述)。循环组件是一个带有门控循环单元 (GRU) [6] 的循环层,并使用 RELU 函数作为隐藏更新激活函数。在时间 t 的循环单元的隐藏状态计算为:
    在这里插入图片描述
    其中 ⊙ 是元素乘积(element-wise product),σ 是 sigmoid 函数,xt 是该层在时间 t 的输入。该层的输出是每个时间戳的隐藏状态。虽然研究人员习惯于使用 tanh 函数作为隐藏更新激活函数,但我们凭经验发现 RELU 导致更可靠的性能,通过它梯度更容易反向传播。

    3.4 Recurrent-skip Component

    带有 GRU [6] 和 LSTM [15] 单元的循环层经过精心设计,可以记住历史信息,从而了解相对长期的依赖关系。然而,由于梯度消失,GRU 和 LSTM 在实践中通常无法捕捉到非常长期的相关性。我们建议通过一种新颖的循环跳过组件来缓解这个问题,该组件利用现实世界集中的周期性模式。例如,每天的用电量和交通使用量都呈现出明显的规律。如果我们想预测今天 2 点的用电量,季节性预测模型中的一个经典技巧是利用历史日期中的 2 点记录,以及最近的记录。由于一个周期(24 小时)的长度极长以及随后的优化问题,这种类型的依赖关系很难被现成的循环单元捕获。受此技巧有效性的启发,我们开发了一种具有时间跳跃连接的循环结构,以扩展信息流的时间跨度,从而简化优化过程。具体来说,在当前隐藏单元和相邻周期中相同阶段的隐藏单元之间添加跳跃链接。更新过程可以表述为:
    在这里插入图片描述

    其中该层的输入是卷积层的输出p 是跳过的隐藏单元的数量。对于具有明确周期性模式的数据集(例如,对于每小时用电量和交通使用数据集,p = 24),p 的值可以很容易地确定,并且必须进行调整。在我们的实验中,我们凭经验发现,即使在后一种情况下,经过良好调整的 p 也可以显着提高模型性能。此外,LSTNet 可以很容易地扩展为包含跳跃长度 p 的变体。

    我们使用全连接层来组合 Recurrent 和 Recurrent-skip 组件的输出。全连接层的输入包括:

    • Recurrent 组件在时间戳 t 的隐藏状态,用 h t R h^R_t htR 表示,

    • 从时间戳 t − p + 1 t - p + 1 tp+1 t t t 的 Recurrent-skip 组件的 p 个隐藏状态,用 h t − p + 1 S , h t − p + 2 S , . . . , h t S h^S_{t-p+1},h^S_{t-p+2},. . . ,h^S_t htp+1S,htp+2S,...,htS 表示 。

    全连接层的输出计算为:
    在这里插入图片描述

    其中 h t D h^D_t htD 是图 2 中神经网络(上)部分在时间戳 t t t 处的预测结果。

    3.5 Temporal Attention Layer

    然而,Recurrent-skip 层需要一个预定义的超参数 p,这在非季节性时间序列预测中是不利的,或者其周期长度随时间变化。为了缓解这个问题,我们考虑了另一种方法,注意力机制[1],它在输入矩阵的每个窗口位置学习隐藏表示的加权组合。具体来说,当前时间戳 t 处的注意力权重 α t ∈ R q α_t ∈ \R^q αtRq 计算为

    α t = A t t n S c o r e ( H t R , h t − 1 R ) α_t = AttnScore(H^R_t ,h^R_{t−1}) αt=AttnScore(HtR,ht1R)

    其中 H t R = [ h t − q R , . . . , h t − 1 R ] H^R_t = [h^R_{t−q}, . . . ,h^R_{t−1}] HtR=[htqR,...,ht1R] 是按列堆叠 RNN 的隐藏表示的矩阵,AttnScore 是一些相似函数,例如点积、余弦或由简单的多层感知器参数化

    时间注意层的最终输出是加权上下文向量 c t = H t α t c_t = H_t α_t ct=Htαt 和最后一个窗口隐藏表示 h t − 1 R h^R_{t-1} ht1R 的连接,以及线性投影操作

    h t D = W [ c t ; h t − 1 R ] + b . h^D_t = W [c_t ;h^R_{t−1}] + b. htD=W[ct;ht1R]+b.

    3.6 Autoregressive Component

    由于卷积和循环组件的非线性特性,神经网络模型的一个主要缺点是输出的规模对输入的规模不敏感。不幸的是,在特定的真实数据集中,输入信号的规模以非周期性的方式不断变化,这大大降低了神经网络模型的预测精度。 4.6 节给出了这种失败的具体例子。为了解决这个缺陷,在类似于 highway network [29],我们将 LSTNet 的最终预测分解为线性部分,主要关注局部缩放问题,加上包含重复模式的非线性部分。在 LSTNet 架构中,我们采用经典的自回归 (AR) 模型作为线性组件。将 AR 分量的预测结果记为 h t L ∈ R n h^L_t ∈ \R^n htLRn,将 AR 模型的系数记为 W a r ∈ R q a r W^{ar} ∈ \R^{q^{ar}} WarRqar b a r ∈ R b^{ar}∈ \R barR,其中 q a r q^{ar} qar 是输入矩阵上输入窗口的大小。请注意,在我们的模型中,所有维度共享同一组线性参数。 AR 模型公式如下,
    在这里插入图片描述

    然后通过整合神经网络部分和 AR 组件的输出来获得 LSTNet 的最终预测:
    在这里插入图片描述

    其中 Y ^ t \hat{Y}_t Y^t 表示模型在时间戳 t t t 处的最终预测。

    3.7 Objective function

    平方误差是许多预测任务的默认损失函数,相应的优化目标表示为,
    在这里插入图片描述

    其中 Θ 表示模型的参数集,ΩTrain 是用于训练的时间戳集,|| · ||F 是 Frobenius 范数,h 是 3.1 节中提到的范围。传统的带有平方损失函数的线性回归模型被命名为 Linear Ridge,相当于带有岭正则化的向量自回归模型。然而,实验表明,线性支持向量回归(线性 SVR)[30] 在某些数据集中优于线性岭模型线性 SVR 和线性岭之间的唯一区别是目标函数。线性 SVR 的目标函数是,
    在这里插入图片描述
    其中 C 和 ϵ 是超参数。受线性 SVR 模型卓越性能的启发,我们将其目标函数纳入 LSTNet 模型作为平方损失的替代方案。为简单起见,我们假设 ϵ = 01,上面的目标函数简化为绝对损失(L1-loss)函数如下:
    在这里插入图片描述

    绝对损失函数的优点是它对实时序列数据中的异常具有更强的鲁棒性。在实验部分,我们使用验证集来决定使用哪个目标函数,平方损失 Eq.7 或绝对损失 Eq.9。

    3.8 Optimization Strategy

    在本文中,我们的优化策略与传统时间序列预测模型中的优化策略相同。假设输入时间序列为 Yt = {y1,y2, . . . ,yt },我们定义了一个可调窗口大小 q,并将时间戳 t 处的输入重新表述为 Xt = {yt−q+1,yt−q+2, . . . ,yt }。然后,该问题成为具有一组特征值对 {Xt ,Yt+h } 的回归任务,并且可以通过随机梯度体下降 (SGD) 或其变体(例如 Adam [18])来解决。

    4 EVALUATION

    我们在 4 个基准数据集上使用 9 种方法(包括我们的新方法)进行了广泛的实验,用于时间序列预测任务。所有数据和实验代码均可在线获取。

    4.1 Methods for Comparison

    我们比较评估的方法如下。

    • AR 代表自回归模型,相当于一维VAR模型。
    • LRidge 是具有L2 正则化的向量自回归 (VAR) 模型,它在多元时间序列预测中最为流行。
    • LSVR 是具有支持向量回归目标函数的向量自回归 (VAR) 模型 [30]。
    • TRMF 是使用[32] 的时间正则化矩阵分解的自回归模型。
    • GP 是用于时间序列建模的高斯过程。 [11, 28]
    • VAR-MLP 是[35] 中提出的模型,它结合了多层感知(MLP)和自回归模型。
    • RNN-GRU 是使用 GRU 单元的循环神经网络模型。
    • LSTNet-skip 是我们提出的带有skip-RNN 层的LSTNet 模型。
    • LSTNet-Attn 是我们提出的具有时间注意层的 LSTNet 模型。

    对于上面的 AR、LRidge、LSVR 和 GP 等单输出方法,我们只是独立训练了 n 个模型,即 n 个输出变量中的每一个都训练一个模型

    4.2 Metrics

    我们使用了三个传统的评估指标,定义为:
    在这里插入图片描述
    其中 Y , Y ^ ∈ R n × T Y , \hat{Y} ∈ \R^{n×T} Y,Y^Rn×T 分别是真实信号和系统预测信号。RSE 是广泛使用的均方根误差 (RMSE) 的缩放版本,旨在使评估更具可读性,无论数据规模如何RSE 值越低越好,而 CORR 值越高越好

    4.3 Data

    我们使用了四个公开可用的基准数据集。表 1 总结了语料库的统计数据。
    在这里插入图片描述

    Traffic:加州交通部 48 个月(2015-2016)每小时数据的集合。该数据描述了旧金山湾区高速公路上不同传感器测量的道路占用率(介于 0 和 1 之间)。

    Solar-Energy:2006 年的太阳能发电记录,每10 分钟从阿拉巴马州的137 个光伏电站采样一次。

    Electricity:从2012 年到2014 年,每15 分钟记录一次电力消耗,单位为千瓦时,n = 321 个客户。我们转换数据以反映每小时消耗量;

    Exchange-Rate:澳大利亚、英国、加拿大、瑞士、中国、日本、新西兰、新加坡等8个国家在1990年至2016年的每日汇率汇总。

    所有数据集按时间顺序分为训练集(60%)、验证集(20%)和测试集(20%)。为了促进多元时间序列预测的未来研究,我们在网站上公开了所有原始数据集和预处理后的数据集。

    为了检查时间序列数据中长期和/或短期重复模式的存在,在图 3 中,从四个数据集中随机选择一些变量绘制了自相关图。自相关,也称为序列相关,是一个信号,其自身的延迟副本是下面定义的延迟函数
    在这里插入图片描述

    其中 Xt 是时间序列信号,μ 是均值,σ2 是方差。在实践中,我们考虑经验无偏估计量来计算自相关
    在这里插入图片描述

    我们可以在图 3 的图表 (a)、(b) 、© 和 (d) 中看到:

    • 在交通、太阳能和电力数据集中存在具有高自相关性的重复模式,但在汇率数据集中没有。
    • 在 Traffic 和 Electricity 数据集的图中,观察到短期每日模式(每 24 小时)和长期每周模式(每 7 天),这完美反映了高速公路交通情况的预期规律性和电力消耗。
    • 在汇率数据集的图 (d) 中,几乎看不到任何重复的长期模式,预计会有一些短期的局部连续性。

    这些观察对于我们以后分析不同方法的实证结果很重要。也就是说,对于能够正确建模并成功利用数据中短期和长期重复模式的方法,当数据包含此类重复模式(如电力、交通和太阳能)时,它们应该表现出色。另一方面,如果数据集不包含此类模式(如汇率),则这些方法的优势可能不会比其他功能较弱的方法带来更好的性能。我们将在第 4.7 节中用经验论证重新讨论这一点。

    4.4 Experimental Details

    我们对每个方法和数据集的保留验证集上的所有可调超参数进行网格搜索。具体来说,所有方法共享相同的窗口大小 q 的网格搜索范围,范围从 {20, 21, . . . , 29} 如果适用。对于 LRidge 和 LSVR,正则化系数 λ 选自 {2−10, 2−8, . . . , 28, 210}。对于 GP,RBF 内核带宽 σ 和噪声水平 α 选自 {2−10, 2−8, . . . , 28, 210}。对于 TRMF,隐藏维度选自 {22, . . . , 26} 和正则化系数 λ 从 {0.1, 1, 10} 中选择。对于 LST-Skip 和 LST-Attn,我们采用了第 3.8 节中描述的训练策略。循环和卷积层的隐藏维度从 {50, 100, 200} 和 {20, 50, 100} 中选择用于循环跳过层。对于交通和电力数据集,循环跳跃层的跳跃长度 p 设置为 24,对于太阳能和汇率数据集,调整范围从 21 到 26。 AR 分量的正则化系数从 {0.1, 1, 10} 中选择以达到最佳性能。我们在每一层之后执行 dropout,除了输入和输出层,并且速率通常设置为 0.1 或 0.2Adam[18] 算法用于优化模型的参数

    4.5 Main Results

    表 2 总结了所有方法(8)在所有指标(3)中对所有测试集(4)的评估结果。我们分别设置horizon = {3, 6, 12, 24},这意味着对于电力和交通数据的预测,范围设置为3到24小时,对于太阳能数据的预测设置为30到240分钟,对于汇率数据从 3 到 24 天。视野越大,预测任务就越难。每个(数据、指标)对的最佳结果在此表中以粗体突出显示。 LSTNet-Skip(提出的 LSTNet 的一个版本)的粗体结果总数为 17,LSTNet-Attn(我们的 LSTNet 的另一个版本)为 7,其余方法的总数为 0 到 3。
    在这里插入图片描述

    • 显然,LSTNet-skip 和 LSTNet-Attn 这两个提出的模型在具有周期性模式的数据集上持续增强了现有技术,尤其是在大视野的设置中
    • 此外,当预测范围为 24 时,LSTNet 在太阳能、交通和电力数据集的 RSE 指标上分别优于基线 RNN-GRU 9.2%、11.7%、22.2%,证明了框架设计对复杂重复模式的有效性。
    • 更重要的是,当周期性模式 q 从应用程序中不清楚时,用户可以考虑 LSTNet-attn 作为 LSTNet-skip 的替代方案,因为前者仍然比基线产生了相当大的改进。但提议的 LSTNet 在 Exchange-Rate 数据集上比 AR 和 LRidge 稍差。为什么?回想一下,在第 4.3 节和图 3 中,我们使用这些数据集的自相关曲线来显示 SolarEnergy、Traffic 和 Electricity 数据集中存在重复模式,但 Exchange-Rate 中没有。
    • 当前的结果为 LSTNet 模型在数据中确实出现长期和短期依赖模式时成功建模提供了经验证据。否则,LSTNet 的表现与代表性基线中较好的(AR 和 LRidge)相当。

    将单变量 AR 的结果与多变量基线方法(LRidge、LSVR 和 RNN)的结果进行比较,我们看到在某些数据集中,即 Solar-Energy 和 Traffic,多变量方法更强,但在其他方面更弱,这意味着更丰富输入信息会导致传统多变量方法的过度拟合。相比之下,LSTNet 在不同情况下具有强大的性能,部分原因在于它的自回归组件,我们将在 4.6 节进一步讨论。

    4.6 Ablation Study

    在这里插入图片描述

    为了证明我们的框架设计的效率,我们进行了仔细的消融研究。具体来说,我们在 LSTNet 框架中一次删除每个组件。首先,我们将没有不同组件的 LSTNet 命名如下。

    • LSTw/oskip:没有 Recurrent-skip 组件和注意力组件的 LSTNet 模型。
    • LSTw/oCNN:没有卷积组件的 LSTNet-skip 模型。
    • LSTw/oAR:没有AR 组件的 LSTNet-skip 模型。

    对于不同的基线,我们调整模型的隐藏维度,使它们具有与完整的 LSTNet 模型相似数量的模型参数,消除了由模型复杂性引起的性能增益

    使用 RSE 和 CORR 测量的测试结果如图 5 6 所示。这些结果的几个观察结果值得强调:

    • 每个数据集的最佳结果是使用 LSTSkip 或 LST-Attn 获得的。
    • 从完整模型中删除 AR 组件(在 LSTw/oAR 中)导致大多数数据集的性能下降最为显着,总体上显示了 AR 组件的关键作用。
    • 删除(LSTw/oCNN 或 LSTw/oskip)中的 Skip 和 CNN 组件会导致某些数据集的性能大幅下降,但并非全部。LSTNet 的所有组件共同导致我们的方法在所有数据集上的稳健性能。

    结论是,我们的架构设计在所有实验设置中都是最稳健的,尤其是在大的预测范围时。至于为什么 AR 组件会发挥如此重要的作用,我们的解释是 AR 通常对数据的规模变化具有鲁棒性
    在这里插入图片描述

    为了从经验上验证这种直觉,我们在图 6 中绘制了电力消耗数据集中 1 到 5000 小时持续时间的一维(一个变量)时间序列信号,其中蓝色曲线是真实数据,红色曲线是系统- 预测信号。我们可以看到,真正的消耗在第 1000 小时左右突然增加,LSTNet-Skip 成功捕捉到了这种突然的变化,但 LSTw/oAR 未能正确反应

    为了更好地验证这个假设,我们进行了模拟实验。首先,我们通过以下步骤随机生成一个尺度变化的自回归过程。首先,我们随机采样一个向量 w ∼ N (0, I),w ∈ Rp,其中 p 是给定的窗口大小。那么生成的自回归过程 xt 可以描述为
    在这里插入图片描述
    其中 ϵ ∼ N (µ, 1)。为了注入尺度变化,我们将高斯噪声的平均值每 T 个时间戳增加 µ0。则时间序列 xt 的高斯噪声可写为
    在这里插入图片描述
    其中⌊·⌋表示底函数。
    在这里插入图片描述

    我们将时间序列拆分为训练集并按时间顺序进行测试,并测试 RNN-GRU 和 LSTNet 模型。结果如图 4 所示。RNN-GRU 和 LSTNet 都可以记住训练集中的模式(左侧)。但是,RNN-GRU 模型无法遵循测试集中的尺度变化模式(右侧)。相反,LSTNet 模型更适合测试集。换句话说,普通的 RNN 模块,或者说 LSTNet 中的神经网络组件,可能对数据中违反的尺度波动不够敏感(这在电力数据中很典型,可能是由于公共假期的随机事件或温度波动等),而简单的线性AR模型可以在预测中做出适当的调整

    总之,这项消融研究清楚地证明了我们架构设计的效率。所有组件都为 LSTNet 的出色而强大的性能做出了贡献。

    4.7 Mixture of long- and short-term patterns

    在这里插入图片描述

    为了说明 LSTNet 在对时间序列数据中短期和长期重复模式混合建模方面的成功,图 7 比较了数据集 LSTNet 和 VAR 在 Traffic 中特定时间序列(输出变量之一)上的性能。如第 4.3 节所述,流量数据表现出两种重复模式,即每日和每周。我们可以在图 7 中看到,

    • 周五和周六的真实交通占用模式(蓝色)非常不同,而周日和周一则不同。
    • 图 7 是交通流量监控传感器的 VAR 模型((a)部分)和 LSTNet((b)部分)的预测结果,其中它们的超参数是根据验证集上的 RMSE 结果选择的。该图表明,VAR 模型只能处理短期模式VAR 模型的预测结果模式只取决于预测的前一天。我们可以清楚地看到它在星期六(第 2 和第 9 个峰值)和星期一(第 4 和第 11 个峰值)的结果与 ground truth 不同,其中星期一(工作日)的 ground truth 有两个峰值,一个峰值为星期六(周末)。
    • 相反,我们提出的 LSTNet 模型分别在工作日和周末执行两种模式。这个例子证明了 LSTNet 模型能够同时记忆短期和长期重复模式的能力,这是传统预测模型所不具备的,在现实世界时间序列信号的预测任务中至关重要。

    5 CONCLUSION

    在本文中,我们提出了一种用于多变量时间序列预测任务的新型深度学习框架 (LSTNet)。通过结合卷积和递归神经网络的优势以及自回归组件,所提出的方法显着改善了在多个基准数据集上进行时间序列预测的最新结果。通过深入的分析和经验证据,我们展示了 LSTNet 模型架构的效率,它确实成功地捕获了数据中的短期和长期重复模式,并结合了线性和非线性模型来进行稳健的预测

    对于未来的研究,扩展工作有几个有希望的方向。首先,跳跃循环层的跳跃长度 p 是一个关键的超参数。目前,我们根据验证数据集手动调整它。如何根据数据自动选择p是一个有趣的问题。其次,在卷积层中我们平等对待每个可变维度,但在现实世界的数据集中,我们通常拥有丰富的属性信息。将它们集成到 LSTNet 模型中是另一个具有挑战性的问题。

    展开全文
  • 基于GM-LSSVR的机场噪声时间序列预测模型
  • 利用相空间重构和预测模型参数的互相关系,提出一种混沌时间序列预测模型参数同步优化方法。首先采用均匀设计方法对影响模型预测精度的参数进行均匀设计,然后采用自调用最小二乘支持向量机进行参数同步优化,得到...
  • 来源:机器学习研习院大家好,我是阳哥!时间序列预测是机器学习中一个经常被忽视的重要领域。时间序列在观察之间添加了显式的顺序依赖性:时间维度。这个额外的维度既是一个约束,也是一个提供额外信息...
  • 运用混沌理论加权一阶局域预测方法,建立了混沌时间序列瓦斯异常涌出预测模型;并利用平煤十矿己15-24080掘进工作面31d的瓦斯实际浓度数据对该模型进行了预测效果检验。结果表明:时间序列的最大Lyapunov指数大于零,...
  • 时间序列预测步骤I am a strong believer in “learning by doing” philosophy. 我坚信“做中学”的哲学。 Data science is an applied field, so you need to get your feet wet to learn something. One can ...
  • 为公交规划科学合理的进行,建立公交客运量时间序列预测模型.通过对公交客运量影响因素进行分析,选取市区人口数、从业人员数、在校学生数、工业生产总值、职工年平均工资、公交车辆数、运营线路数等7个指标自变量,...
  • lstm时间序列预测模型 时间序列-LSTM模型 (Time Series - LSTM Model) Advertisements 广告 Previous Page 上一页 Next Page 下一页 Now, we are familiar with statistical modelling on time series...
  • 长时时间序列预测(LSTF)要求模型具有较强的预测能力,即能够有效捕捉输出和输入之间的精确长程相关耦合。近期的研究表明,Transformer具有提高预测能力的潜力。然而,Transformer存在一些严重的问题,使其无法直接...
  • 时间序列预测是机器学习中一个经常被忽视的重要领域。时间序列在观察之间添加了显式的顺序依赖性:时间维度。这个额外的维度既是一个约束,也是一个提供额外信息来源的结构。时间序列时间序列分析使用经...
  • 混沌时间序列预测模型研究
  • 时间序列预测问题中,建立LSTM模型,采用python语言代码实现
  • 如何利用Transformer建立时间序列预测模型

    万次阅读 多人点赞 2021-05-16 09:54:52
    我最近读了一篇非常有趣的论文,叫做 Deep Transformer Models for Time Series Forecasting: The...预测流感流行病例:时间序列预测的深度变形模型:流感流行病例 能源生产预测:能源消耗预测使用堆叠非参数贝叶斯方法 天
  • MATLAB在时间序列建模预测及程序代码,时间序列预测模型matlab代码源码.rar
  • MATLAB在时间序列建模预测及程序代码,时间序列预测模型matlab代码源码.zip
  • import pandas # 读取数据,指定日期为索引... # 6 建立模型预测 model = ARIMA(data, (p,1,q)).fit() #给出一份模型报告 model.summary2() #作为期5天的预测,返回预测结果、标准误差、置信区间。 model.forecast(5)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 84,219
精华内容 33,687
关键字:

时间序列预测模型

友情链接: GSO算法matlab代码.rar