一、任务背景

        时间序列预测是人工智能领域的一个重要任务,常见的股票价格预测、耗电量预测等都属于时间序列预测的范畴。时间序列预测可以是单变量的,基于前面长度为N的时间窗口中的数值来预测当前的取值,例如根据一只股票历史3个月的价格来预测未来3天的价格。这意味这我们无法较好地使用传统的回归方法来处理时间序列预测问题,因为回归模型一般需要额外的特征进行建模才能取得更好的效果。当然,如果我们把时刻t之前的N条时序数据作为特征,当前t时刻的取值作为标签,也可以构造一个用于回归的数据集。这里我们使用来自Kaggle的飞机乘客数据集来训练一个时序预测模型,预测未来N个时间节点的股价。

二、时间序列预测模型

        时间序列预测任务有专门的一类模型,与常见的机器学习和深度学习模型不同,这些时间序列预测模型有着自己的一套逻辑,这里我们着重介绍一下最经典的时间序列预测模型——ARIMA,并在后面的建模任务中使用该模型。

        ARIMA模型,即自回归差分移动平均模型(AutoRegressive Integrated Moving Average),是一种用于分析和预测时间序列数据的统计模型。它结合了自回归(AR)、差分(I)和移动平均(MA)三个部分,尤其适用于带有趋势或季节性的数据。

1、AR自回归

        AR自回归部分利用当前观测和前一时期观测之间的依赖关系,来计算时间序列自身过去的值对其当前值的影响。如果一个时间序列的当前值与其过去的值之间存在线性关系,那么这个时间序列就具有自回归特性。AR部分可以表示为:

eq?Y_%7Bt%7D%20%3D%20c+%5Cphi_%7B1%7DY_%7Bt-1%7D+%5Cphi_%7B2%7DY_%7Bt-2%7D+...+%5Cphi_%7Bp%7DY_%7Bt-p%7D+%5Cepsilon%20_%7Bt%7D

        其中,eq?Y_%7Bt%7D是时间序列在时间t的取值;c是常数项;eq?%5Cphi_%7B1%7D%2C%5Cphi_%7B2%7D%2C...%2C%5Cphi_%7Bp%7D是自回归系数;eq?Y_%7Bt-1%7D%2CY_%7Bt-2%7D%2C...%2CY_%7Bt-p%7D是时间序列在过去的p个时间点的取值,eq?%5Cepsilon_%7Bt%7D是白噪声误差项。

2、MA移动平均

        移动平均部分关注的是时间序列的误差项对当前值的影响。如果时间序列的当前值受到过去误差项的影响,那么这个时间序列就具有移动平均特性。移动平均分量将模型的误差描述为先前误差项的组合,MA部分可以表示为:

eq?Y_%7Bt%7D%20%3D%20%5Cmu%20+%20%5Cepsilon%20_%7Bt%7D+%5Ctheta%20_%7B1%7D%5Cepsilon_%7Bt-1%7D+%5Ctheta_%7B2%7D%5Cepsilon_%7Bt-2%7D+...+%5Ctheta_%7Bq%7D%5Cepsilon_%7Bt-q%7D

        其中,eq?Y_%7Bt%7D是时间序列在时间t的取值;eq?%5Cmu是时间序列的均值;eq?%5Ctheta_%7B1%7D%2C%5Ctheta_%7B2%7D%2C...%2C%5Ctheta_%7Bq%7D是移动平均系数;eq?%5Cepsilon_%7Bt%7D%2C%5Cepsilon_%7Bt-1%7D%2C...%2C%5Cepsilon_%7Bt-q%7D是过去q个时间点的误差项。

3、I差分

        差分部分用于处理非平稳时间序列,通过差分操作使序列变得平稳是时间序列建模的必要操作。差分需要制定阶数,例如一阶差分是指当前值与前一个值之间的差,二阶差分是指连续两次差分之间的差。一阶差分可以表示为:

eq?%5Ctriangledown%20Y_%7Bt%7D%20%3D%20Y_%7Bt%7D-Y_%7Bt-1%7D

        其中,eq?%5Ctriangledown%20Y_%7Bt%7D是时间序列在时间t的一阶差分值。

4、模型表达式

        有了上面的几个部分,我们就可以组合成最终的ARIMA模型了:

eq?%281-%5Cphi_%7B1%7DB-%5Cphi_%7B2%7DB%5E%7B2%7D-...-%5Cphi_%7Bp%7DB%5E%7Bp%7D%29%281-B%29%5E%7Bd%7DY_%7Bt%7D%20%3D%20%281+%5Ctheta_%7B1%7DB+%5Ctheta_%7B2%7DB%5E%7B2%7D+...+%5Ctheta_%7Bq%7DB%5E%7Bq%7D%29%5Cepsilon_%7Bt%7D

        其中,B是后退因子,有eq?BY_%7Bt%7D%20%3D%20Y_%7Bt-1%7Dd是差分的阶数;p是自回归项的阶数;q是移动平均项的阶数。在实际应用中,ARIMA模型的参数p、d、q需要通过统计检验来确定。一旦确定了参数,就可以使用最大似然估计或其他优化方法来估计模型的参数,并进行时间序列的预测。

三、任务建模

        ARIMA模型的建模步骤如下:

  • 对序列进行平稳性检验,观察序列是否为平稳,对于非平稳时间序列要先进行d阶差分,转化为平稳序列;
  • 经过第一步处理,已经得到平稳时间序列。对平稳时间序列分别求得其自身相关系数ACF,和偏自相关系数PACF,通过对自相关图和偏自相关图的分析或通过AIC/BIC搜索,得到最佳的阶数p、q。

1、数据可视化

        我们打印一下数据长度和可用的特征列:

df = pd.read_csv('./data/AirPassengers.csv', index_col='Month', parse_dates=['Month'])
print(df.head())
print(df.info())

d6006b5f65a748cea46636c3ef476c13.png

        这个任务中,我们使用前124个值作为训练集,ARIMA预测最后的20个值。先看一下分布:

plt.figure(figsize=(15, 6))
df['#Passengers'].plot()
plt.show()

 

f5b69892ec3346d08897e169f7bf3dad.png

可以看到,数据有着明显的周期性特征。

2、参数探索

(1)阶数

        我们看一下一阶差分能否去除趋势:

# 进行一阶差分以去除趋势
data_diff = df.diff(1).dropna()
data_diff['#Passengers'].plot()
plt.show()

3f6cf4fddfe044f88efa8f634725fa59.png

        可以看到,整体向上的趋势已经被我们去掉了,现在更多的是周期性,阶数d暂定为1。周期性我们可以通过季节性差分进行消除,这里每两个数据点时间相隔1个月,那我们取12个点(靠经验和数据含义确定),也就是一年:

# 进行季节性差分以去除季节性成分
data_seasonal_diff = data_diff.diff(12).dropna()
data_seasonal_diff['#Passengers'].plot()
plt.show()

a1514d33ab4a4dd7ba5eee1a143eca9d.png

(2)q值

        我们看看q值的选取,可以使用statsmodels提供的ACF(Autocorrelation Function,自相关函数)来确定,自相关函数测量的是时间序列在不同时间滞后下的值之间的相关性:

from statsmodels.graphics.tsaplots import plot_acf

# 绘制自相关图
plot_acf(data_seasonal_diff, lags=100, alpha=1)
plt.show()

bb3b64a253174d749e2fa81510b0fec8.png

        这里我们主要判断在哪一届出现了拖尾或者截尾。其中,拖尾指序列以指数率单调递减或震荡衰减,而截尾指序列从某个时点变得非常小,这里我们看到第二阶就出现了截尾,那么阶数定义为第二阶之前的一阶,也就是q=1。

(3)p值

        接着看看p的取值,我们使用PACF(Partical Autocorrelation Coefficient,偏自相关系数)来寻找p值。PACF在滞后k时的值表示在已知时间序列在t−1, t−2,⋯, t−k+1的值的情况下,时间点t和t−k的值之间的相关性:

from statsmodels.graphics.tsaplots import plot_pacf

# 绘制偏自相关图
plot_pacf(data_seasonal_diff, lags=50, alpha=1)
plt.show()

6574b34329054b20949dccf37fccd436.png

        同样,拖尾指序列以指数率单调递减或震荡衰减,而截尾指序列从某个时点变得非常小。这里在第二阶出现了截尾,那阶数p=1。

3、建模

        确定了p,d,q接下来我们就可以建模了。这里我们使用了ARIMA的拓展版——季节性自回归综合移动平均外生回归模型(Seasonal Autoregressive Integrated Moving Average with eXogenous regressors,SARIMAX,在ARIMA的基础上增加了季节性和外生因素的处理能力:

# 划分数据集
train = df['#Passengers'][:-20]
test = df['#Passengers'][-20:]

# 创建ARIMA模型实例
# 这里我们使用(1,1,1)模型,即1阶自回归,1阶差分,1阶移动平均
# 同时,我们加入季节性差分,设为12
model = SARIMAX(train, seasonal_order=(1, 1, 1, 12))

# 拟合模型
model_fit = model.fit()

# 进行预测
forecast = model_fit.forecast(steps=len(test))

# 可视化结果
plt.figure(figsize=(10, 6))
plt.plot(train.index, train, label='Train')
plt.plot(test.index, test, label='Test')
plt.plot(test.index, forecast, label='Forecast')
plt.legend()
plt.show()

f8987c7b29094065bcbc6b5755940a36.png

        可以看到,ARIMA对当前的数据有较好的拟合能力。

四、完整代码

import pandas as pd
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.statespace.sarimax import SARIMAX
import matplotlib.pyplot as plt
from statsmodels.graphics.tsaplots import plot_pacf
from statsmodels.graphics.tsaplots import plot_acf


df = pd.read_csv('./data/AirPassengers.csv', index_col='Month', parse_dates=['Month'])
print(df.head())
print(df.info())
plt.figure(figsize=(15, 6))
df['#Passengers'].plot()
plt.show()

# 进行一阶差分以去除趋势
data_diff = df.diff(1).dropna()
# 进行季节性差分以去除季节性成分
data_seasonal_diff = data_diff.diff(12).dropna()
data_seasonal_diff['#Passengers'].plot()
plt.show()


# 划分训练数据和测试数据
train = df['#Passengers'][:-20]
test = df['#Passengers'][-20:]
# print(train.head())

# 绘制自相关图
plot_acf(data_seasonal_diff, lags=100, alpha=1)
plt.show()

# 绘制偏自相关图
plot_pacf(data_seasonal_diff, lags=50, alpha=1)
plt.show()

# 创建ARIMA模型实例
model = SARIMAX(train, seasonal_order=(1, 1, 1, 12))

# 拟合模型
model_fit = model.fit()
# 进行预测
forecast = model_fit.forecast(steps=len(test))
# 可视化结果
plt.figure(figsize=(10, 6))
plt.plot(train.index, train, label='Train')
plt.plot(test.index, test, label='Test')
plt.plot(test.index, forecast, label='Forecast')
plt.legend()
plt.show()

# 打印模型的摘要信息
print(model_fit.summary())

五、总结

        本文,我们学习了一类新的模型——时间序列预测模型。有别于常见的分类或者回归模型,时间序列预测模型尤其在时序数据处理任务上的独特能力,在诸多任务中发挥着重要作用。

 

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐