时间序列分析|auto_arima调参

目录

  • 前言
  • auto_arima参数列表
  • 实践案例
  • 结论

前言

在对时间序列进行ARIMA(p,d,q)建模的时候,一个比较头疼的事就是确定其中超参数p, d, q, 常规做法是先用平稳性检验来确定d,然后通过ACF图和PACF图来观察p和q,这种通过机器和人工相结合的方法一套走下来往往比较长时间,而且还容易出错,如果只对一个时间序列进行分析还好,如果要对很多个时间序列,如300家连锁便利店每天的营销额进行批量建模的话,时间成本就太高了,然而,现在越来越多的朋友在进行时间序列分析的时候直接调用auto_arima也能够取得较好的效果,而且能够节省很多时间,总的来看auto_arima有几大好处

(1) 自动寻参

(2) 自动拟合

(3) 自动预测

auto_arima参数列表

下图是pmdarima 1.8.5版本的auto_arima函数参数的提示,由于参数太多了,我们先进行梳理,列出其含义和注意事项
时间序列分析|auto_arima调参_第1张图片

参数 含义 备注
y 要拟合的时间序列 必要,array-like or iterable, shape=(n_samples,),⼀维的浮点型数组,不能包含空值和或者无穷’
X 外置变量 非必要,给定额外的特征来帮助预测,需要注意的是,对于预测未来的时序数据的时候,也要提供未来的特征数据
start_p 参数p的下界 int, 默认为2
d ⾮周期的差分阶数 int, 默认None,如果是None,则⾃动选择,此时,运⾏时间会显著增加。
start_q 参数q时的下界 int, 默认为2
max_p 参数p的上界 int, 默认为5,max_p>=start_p
max_d ⾮周期的差分阶数d的上界 int, 默认2,max_d>=d
max_q 参数q时的上界 int, 默认5,max_q>=start_q
start_P 周期参数P的下界 int,默认1
D 周期差分的阶数 int,默认None,如果是None,则⾃动选择
start_Q 周期参数Q的下界 int, 默认1
max_P 周期参数P的上界 int,默认2
max_D 周期的差分阶数的上界 int, 默认1, max_D>=D
max_Q 周期参数Q的上界 int,默认2
max_order p+q+P+Q 组合最大值 int, 默认5,如果p+q≥max_order,该组合对应的模型将不会被拟合,如果是None的话,对最大阶没有限制
m 周期数 int, 默认1,例如年数据 m=1,季度数据m=4,月度数据m=12,周数据52;如果m=1, 则seasonal会被设置为False
seasonal 是否进⾏周期性ARIMA拟合 bool, 默认True,如果seasonal=True同时m=1,seasonal会被设置为False
stationary 标志该序列是否是平稳序列 bool, 默认False
information_criterion 模型评价指标 str, 默认’aic’,可选‘aic’, ‘bic’, ‘hqic’,'oob’
alpha test的显著性⽔平 float,默认0.05
test 单位根检验的类型 str, 默认’kpss’,当非平稳且d=None才会进⾏检验, 当出现奇异值分解错误可选adf
seasonal_test 周期单位根检验⽅法的标志 str, 默认‘ocsb’,可选’ch’
stepwise 是否采用stepwise 算法 bool, 默认True,可以更快速的找到最佳模型和防止过拟合,但存在不是最佳模型的风险,这样课可以设置成False,模型搜寻范围扩大,耗时也会增加
n_jobs 并行拟合模型的数目 int,默认1,如果为-1,则尽可能多的并行,提速用
start_params ARMA(p,q)的起始参数 array-like, 默认None
trend 多项式趋势的多项式的系数 str or iterable,‘n’, ‘c’, ‘t’ , ‘ct’ 选择 ,其中n表示没有,c表示常数,t:线性,ct:常数+线性
method 似然函数的类型 str, 默认lbfgs, 可选{‘css-mle’,‘mle’,‘css’}之⼀
maxiter 求解最大迭代次数 int, 默认50
transparams 是否检验平稳性和可逆性 bool,默认True,如果为True,则进⾏变换确保平稳性,如果为False,不检验平稳性和可逆性
suppress_warnings 是否过滤掉警告 bool, 默认True
error_action 是否跟踪错误提示 str, 默认 ‘trace’,如果由于某种原因无法匹配ARIMA,则可以控制错误处理行为。(warn,raise,ignore,trace)
trace 是否跟踪拟合过程 bool, 默认False
random 是否随机搜索,而不是超参数空间全搜索或者stepwise搜索, bool, 默认False
with_intercept 是否需要截距 ,均值漂移 str, 默认auto
disp 收敛信息的打印控制 int, 默认0,disp<0表⽰不打印任何信息

实践案例

我们以air_passenger数据集为对象进行auto_arima调参,先来看一下原数据长什么样子并对其进行可视化

import datetime
import time
import tsod
import pandas as pd
import pmdarima as pm
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller
from sklearn.metrics import mean_absolute_error 

air_passenger = pd.read_csv(r"D:\项目\时间序列\air_passenger.csv") #读取数据
air_passenger['month'] = pd.to_datetime(air_passenger['month']) #datetime格式化

air_passenger.set_index("month", inplace = True) #设置time列为索引列
ts = air_passenger['passenger'] #乘客时序
print(ts.head(5)) #查看头5行
print(ts.tail(5)) #查看尾5行

fig = plt.figure(figsize = (6,4)) #新建6*4的画布
ts.plot(label = 'origin ts') #原时序图
plt.xticks(rotation =45) #横坐标逆时针倾斜45度
plt.legend() 
plt.show()

n_periods = 5 #取n_periods为测试集
train_ts = ts[0:-n_periods] #训练数据
test_ts = ts[-n_periods:]  #测试数据
print(train_ts)
print(test_ts)

输出的内容

month
1949-01-01    112
1949-02-01    118
1949-03-01    132
1949-04-01    129
1949-05-01    121
Name: passenger, dtype: int64
month
1960-08-01    606
1960-09-01    508
1960-10-01    461
1960-11-01    390
1960-12-01    432

时间序列分析|auto_arima调参_第2张图片

month
1949-01-01    112
1949-02-01    118
1949-03-01    132
1949-04-01    129
1949-05-01    121
             ... 
1960-03-01    419
1960-04-01    461
1960-05-01    472
1960-06-01    535
1960-07-01    622
Name: passenger, Length: 139, dtype: int64
month
1960-08-01    606
1960-09-01    508
1960-10-01    461
1960-11-01    390
1960-12-01    432
Name: passenger, dtype: int64

把数据划分为训练集和测试集,就可以拿训练集来训练模型,拿测试集来评估训练模型的好坏,接下来我们来结合auto_arima的超参数进行一些不同的建模尝试

  • 首先是不带任何超参的模型,只有要拟合的时序train_ts,此时所有的超参都是默认设置,希望给出一个baseline。
start_time = time.time() #开始时间
model = pm.auto_arima(train_ts,     
                    ) #模型初始化,什么都默认
print("best model:", model) #拟合出来最佳模型
y_pred = model.predict(n_periods) #预测未来n_periods期
mae = mean_absolute_error(test_ts.values, y_pred)
print("mean_absolute_error: ", mae)
end_time = time.time() #结束时间
print("cost time: ", end_time - start_time) #耗时

对应的输出结果

best model:  ARIMA(4,1,3)(0,0,0)[0]          
mean_absolute_error:  53.99503219911581
cost time:  3.915421724319458

从输出的结果我们看到拟合最好的模型是 ARIMA(4,1,3)(0,0,0)[0],在测试集上算的平均绝对误差是 53.99503219911581,耗时为3.9秒, 我们把这个作为一个baseline基准,进行多种超参的增删改查调试。

  • stepwise算法

很多算法工程师用 auto_arima主要还是图他的简便高效,故时效是一个重要的考虑因素,原auto_arima默认是stepwies = True,我们加入一个stepwies = False的超参数看一看效果

start_time = time.time() #开始时间
model = pm.auto_arima(train_ts,
                    stepwise=False 
                    ) #模型初始化
print("best model:", model) #拟合出来最佳模型
y_pred = model.predict(n_periods) #预测未来n_periods期
mae = mean_absolute_error(test_ts.values, y_pred)
print("mean_absolute_error: ", mae)
end_time = time.time() #结束时间
print("cost time: ", end_time - start_time) #耗时

输出结果

best model:  ARIMA(0,1,4)(0,0,0)[1] intercept
mean_absolute_error:  76.7801771043133
cost time:  3.0566318035125732

在设置stepwise=False之后,最佳拟合的ARIMA变成了有了季节因子的SARIMA,耗时更少,平均绝对误差变大了,这与我们预想的好像不太一样,时间反而变少了。

  • n_jobs

还有一个可以提升速度的参数那就是n_jobs,这个并行执行可以大大提升速度。

start_time = time.time() #开始时间
model = pm.auto_arima(train_ts,
                    stepwise=False,
                    n_jobs = -1
                    ) #模型初始化
print("best model:", model) #拟合出来最佳模型
y_pred = model.predict(n_periods) #预测未来n_periods期
mae = mean_absolute_error(test_ts.values, y_pred)
print("mean_absolute_error: ", mae)
end_time = time.time() #结束时间
print("cost time: ", end_time - start_time) #耗时

输出结果

best model:  ARIMA(4,1,1)(0,0,0)[1] intercept
mean_absolute_error:  131.43970823928404
cost time:  0.7644350528717041

果不其然,耗时不到1秒就结束了,速度是提升了很多,然而平均绝对误差又变大了。

  • season

从原时序图可以很明显的观察到该时序是周期性的,那么我们就往里面加入season参数

start_time = time.time() #开始时间
model = pm.auto_arima(train_ts,
                       seasonal = True, 
                       seasonal_test= 'ocsb',
                       stepwise=False,
                       n_jobs = -1 
                        ) #模型初始化
print("best model:", model) #拟合出来最佳模型
y_pred = model.predict(n_periods) #预测未来n_periods期
mae = mean_absolute_error(test_ts.values, y_pred)
print("mean_absolute_error: ", mae)
end_time = time.time() #结束时间
print("cost time: ", end_time - start_time) #耗时

输出结果

best model:  ARIMA(4,1,1)(0,0,0)[1] intercept
mean_absolute_error:  131.43970823928404
cost time:  4.542391300201416

从输出结果来看,虽然设置了season = True和seasonal_test= ‘ocsb’,但是拟合的最佳模型季节阶还是0,明显没寻出季节性参数,难道auto_arima对季节性时序失效?还是我们设置的姿势不对?

  • trend

从原时序图我们还看到了乘客人数有整体上升的趋势,于是可以加入trend超参,这里选个’C’

start_time = time.time() #开始时间
model = pm.auto_arima(train_ts,               
                       trend = 'c',
                       seasonal = True, 
                       seasonal_test= 'ocsb',
                       stepwise=False,
                       n_jobs = -1 
                        ) #模型初始化
print("best model:", model) #拟合出来最佳模型
y_pred = model.predict(n_periods) #预测未来n_periods期
mae = mean_absolute_error(test_ts.values, y_pred)
print("mean_absolute_error: ", mae)
end_time = time.time() #结束时间
print("cost time: ", end_time - start_time) #耗时

输出结果

best model:  ARIMA(4,1,1)(0,0,0)[1] intercept
mean_absolute_error:  131.43970823928404
cost time:  0.7941315174102783

从输出结果来看,除了速度提升了,在测试集上面的表现似乎没有好转,寻思是不是要预设的一个季节因子呢?

  • m

由前面的试验猜测可能季节因子m需要预先设定,不如加入m=12试一试

start_time = time.time() #开始时间
model = pm.auto_arima(train_ts, 
                       m = 12,
                       trend = 'c',
                       seasonal = True, 
                       seasonal_test= 'ocsb',
                       stepwise=False,
                       n_jobs = -1 
                        ) #模型初始化
print("best model:", model) #拟合出来最佳模型
y_pred = model.predict(n_periods) #预测未来n_periods期
mae = mean_absolute_error(test_ts.values, y_pred)
print("mean_absolute_error: ", mae)
end_time = time.time() #结束时间
print("cost time: ", end_time - start_time) #耗时

输出结果

best model:  ARIMA(0,1,1)(1,1,2)[12]          
mean_absolute_error:  20.186482740916176
cost time:  8.20675253868103

从输出结果来看,速度似乎没有之前来得快,然而在测试集上的表现要比之前好很多。

  • all in one

从上面的几个试验可以看到auto_arima有其一定的优势,但是也有一些鸡肋的地方,尤其是在季节性表现上很糟糕,现在我们尽可能多加入一些超参试一试

start_time = time.time() #开始时间
model = pm.auto_arima(train_ts,
                        start_p = 1, d= None, start_q = 1,          
                        max_p = 5, max_d = 2, max_q = 5, 
                        start_P = 1, D = None, start_Q = 1,
                        max_P = 2, max_D = 1, max_Q= 2,
                        max_order = None,
                        test = 'kpss',
                        m =12,
                        seasonal = True, 
                        seasonal_test= 'ocsb',
                        trend = 'c',
                        information_criterion ='aic',
                        trace = False,
                        with_intercept = 'auto', 
                        error_action = 'ignore',  
                        suppress_warnings = True,
                        maxiter= 50,
                        stepwise=False,
                        n_jobs = -1
                        ) #模型初始化
print("best model:", model) #拟合出来最佳模型
y_pred = model.predict(n_periods) #预测未来n_periods期
mae = mean_absolute_error(test_ts.values, y_pred)
print("mean_absolute_error: ", mae)
end_time = time.time() #结束时间
print("cost time: ", end_time - start_time) #耗时

输出效果

best model:  ARIMA(0,1,1)(1,1,2)[12]          
mean_absolute_error:  20.186482740916176
cost time:  77.62792158126831

从输出效果来看,除了时间加长了不少好像没别的优势了【苦笑】。

结论

不要轻易的认为auto_arima就是一种一劳永逸的省时省力的好方法(silver bullet),然后一招打遍天下无敌手,如果分析的时序不多的话还是按照经典时序分析步骤一步一步的来为好,在仔细权衡时效性和精准性两方面再决定用不用auto_arima。

参考文献
1,https://wenku.baidu.com/view/e393289efe0a79563c1ec5da50e2524de518d027.html
2,https://stats.stackexchange.com/questions/120806/frequency-value-for-seconds-minutes-intervals-data-in-r
3,http://alkaline-ml.com/pmdarima/modules/generated/pmdarima.arima.auto_arima.html?highlight=auto_arima
时间序列分析|auto_arima调参_第3张图片

你可能感兴趣的:(时间序列分析,python,时间序列分析,auto_arima)