在对时间序列进行ARIMA(p,d,q)建模的时候,一个比较头疼的事就是确定其中超参数p, d, q, 常规做法是先用平稳性检验来确定d,然后通过ACF图和PACF图来观察p和q,这种通过机器和人工相结合的方法一套走下来往往比较长时间,而且还容易出错,如果只对一个时间序列进行分析还好,如果要对很多个时间序列,如300家连锁便利店每天的营销额进行批量建模的话,时间成本就太高了,然而,现在越来越多的朋友在进行时间序列分析的时候直接调用auto_arima也能够取得较好的效果,而且能够节省很多时间,总的来看auto_arima有几大好处
(1) 自动寻参
(2) 自动拟合
(3) 自动预测
下图是pmdarima 1.8.5版本的auto_arima函数参数的提示,由于参数太多了,我们先进行梳理,列出其含义和注意事项
参数 | 含义 | 备注 |
---|---|---|
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
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的超参数进行一些不同的建模尝试
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基准,进行多种超参的增删改查调试。
很多算法工程师用 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,这个并行执行可以大大提升速度。
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参数
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超参,这里选个’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=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
从输出结果来看,速度似乎没有之前来得快,然而在测试集上的表现要比之前好很多。
从上面的几个试验可以看到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