时间序列分析|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函数参数的提示,由于参数太多了,我们先进行梳理,列出其含义和注意事项

    参数 含义 备注
    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的超参数进行一些不同的建模尝试

  • 首先是不带任何超参的模型,只有要拟合的时序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

    物联沃分享整理
    物联沃-IOTWORD物联网 » 时间序列分析|auto_arima调参

    发表评论