ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ARIMAX与SARIMAX实战:外生变量与季节性建模全解析

ARIMAX与SARIMAX实战:外生变量与季节性建模全解析 搞时间序列预测的朋友应该都有这种体验ARIMA 刚上手的时候觉得很神奇一个自回归加差分再加移动平均就能把一条曲线的规律榨出来。但用着用着就会碰到尴尬局面——如果你的数据不只是时间数值还带着促销、节假日、天气、流量来源这些外部信号ARIMA 就有点使不上劲。这也是为什么我在这个系列里聊完时间序列基础之后会紧接着把 ARIMAX 和 SARIMAX 单独拎出来讲的原因。这篇文章适合正在做销量预测、流量监控、KPI 异常研判并且已经能跑通 ARIMA但发现在实际业务数据上精度不够、解释力不够的朋友。你不需要有很深的数学底子只要理解 ARIMA 的 p、d、q 是什么就能顺着这篇文章把 ARIMAX 和 SARIMAX 的来龙去脉搞清楚。我会尽量把为什么要这样设计模型讲透而不是扔一堆公式让你自己背。1. 先把三个模型的界线画清楚很多人以为 ARIMA、ARIMAX、SARIMAX 是三个并列的模型其实不是。它们更像同一套逻辑在不同需求下的三个版本。你要先理解 ARIMA 解决了什么才能明白 ARIMAX 补了什么SARIMAX 又加了什么。1.1 ARIMA真正能做的一维序列的规律提取ARIMA 的英文全称是 Autoregressive Integrated Moving Average拆开看是三块AR(p)当前值等于过去 p 个值的加权组合这是自回归部分。I(d)对原始序列做 d 次差分让非平稳序列变得平稳。MA(q)当前值还依赖过去 q 个预测误差这是移动平均部分。它的核心假设是当前时刻的值只跟它自己的历史值以及历史预测误差有关。这句话看起来平平无奇却是 ARIMA 的边界。如果你手里的数据确实只有一条序列没有外部干预那 ARIMA 已经够用了。举个例子我之前做过一个日度网站访问量预测的活数据长得很干净没有大促、没有活动、没有节假日干扰。这种情况下 ARIMA(1,1,1) 或者 ARIMA(2,1,2) 就能把趋势和短期波动拟合得不错。但后来数据里加入了投放广告这个因素之后ARIMA 就开始反复出问题——广告投放当天流量暴涨第二天回落这种脉冲式的变化完全不是序列自身规律能解释的。1.2 ARIMA默认照顾不到的三类因素真实业务数据通常有三个特征ARIMA 默认都照顾不到第一是外部干预。比如电商平台做了促销销量当天翻三倍或者某天系统出故障订单量骤降。这些事件的影响是从外面打进来的序列自身的历史值里并没有这个信息。你说 ARIMA 能不能学出来能但它只能把促销日当成一个异常高点处理预测时如果未来还有促销它并不知道。第二是节假日效应。节假日最麻烦的地方在于它跟周数有关。比如春节在每年阳历的日期都不一样如果模型不知道几月几号是春节这个外部信息单靠去年的销量序列是没法自动对齐这个效应的。第三是季节性。这里的季节性还不只是夏天比冬天卖得好这种年度周期还包括更短的周期一周七天周一最低、周末最高一天 24 小时凌晨低谷、午高峰。ARIMA 通过差分可以处理趋势但要处理这种固定周期的波动需要在模型里显式加入周期滞后的结构。你可能会说那我提前把季节性差分做掉不就行了比如月度数据做 12 阶差分。可以是可以但这只是手工预处理模型本身还是不知道 12 阶差分背后的周期含义也不会对季节滞后项做自回归建模。这正是 SARIMAX 存在的原因。1.3 三者不是三个模型而是一个框架的三级楼梯我用一句话总结这三个模型的关系ARIMA只吃序列自身。ARIMAX在 ARIMA 基础上可以吃外部回归变量 X。SARIMAX在 ARIMAX 基础上再叠加季节性的 AR、MA 和差分项。换句话说ARIMAX 是ARIMA 外生变量SARIMAX 是ARIMAX 季节项。它们并非互斥你完全可以只加季节性不加外生变量也可以只加外生变量不建模季节性还可以两个都加。实际建模时真正的决策不是我该用 ARIMA 还是 SARIMAX而是我需要哪些结构来捕捉这批数据里的规律。statsmodels 里甚至没有单独的 ARIMAX 类直接把 ARIMA 和 SARIMAX 都用SARIMAX这个统一类来实现。这个细节后面还会再提它其实暴露了一个事实在状态空间模型的框架下AR、MA、差分、外生回归、季节性都被整合到了一起所谓 ARIMA 家族本质是一套模型描述语言。2. ARIMAX外生变量是怎么进入时间序列模型的ARIMAX 的名字看起来只是ARIMA 加了个 X但这里有一个很多人第一次接触时都会搞混的点ARIMAX 在数学上其实有两种不同的定义方式理解错了后面调参、解释系数、做预测都会出问题。2.1 两种ARIMAX定义容易混但必须分清第一种叫回归 ARMA 误差数学上写成y_t βX_t u_t其中 u_t 服从 ARIMA(p, d, q)它的意思是先做回归把外部变量 X 对 y 的线性影响去掉剩下的残差 u_t 再被建模成一个 ARIMA 过程。这种形式下β 的解释跟普通线性回归一样——在其他条件不变的情况下X 每增加 1 个单位y 平均变化 β 个单位。这在业务上非常友好因为老板问促销对销量到底有多大影响的时候你能直接回答。第二种叫外生变量直接进入 ARMA 过程也叫 ARMAXy_t c φ1 y_{t-1} ... θ1 ε_{t-1} ... β0 X_t β1 X_{t-1} ...这种形式下X 的当前值和滞后值跟 y 的滞后项一起参与回归。它的优点是能更灵活地捕捉 X 的滞后影响但代价是模型参数更多也更难解释。关键问题来了statsmodels 的 SARIMAX 类实际实现的是第一种也就是回归 ARMA 误差。官方文档里的描述是 SARIMAX 是一个带外生回归因子的季节性 ARIMA 模型用状态空间方法统一估计但当你加了exog参数之后模型的均值方程被外生变量解释误差项才走 ARIMA 那套结构。这个区别实战中太重要了。你如果照着教科书里的第二种 ARIMAX 公式去理解 statsmodels 的输出会发现系数对不上、预测也不对劲。我第一次用的时候就被坑过我预期外生变量 X 的滞后项会出现在模型里结果 statsmodels 只估计了一个当期回归系数我当时一度以为是自己参数设错了。2.2 statsmodels里的实现看代码更直观。假设你有一份日度销量数据列分别是sales、promo促销 0/1 变量、temp当日温度想用 ARIMAX 建模import pandas as pd from statsmodels.tsa.statespace.sarimax import SARIMAX train pd.read_csv(sales_train.csv, parse_dates[date], index_coldate) model SARIMAX( train[sales], exogtrain[[promo, temp]], order(1, 1, 1), enforce_stationarityFalse, enforce_invertibilityFalse ) result model.fit() print(result.summary())注意这里用的是SARIMAX类但seasonal_order没填所以实际上就是 ARIMA 外生变量也就是 ARIMAX。order(1, 1, 1)表示 p1, d1, q1跟纯 ARIMA 一样。外生变量通过exog传进去result.summary()里你会看到promo和temp的回归系数以及一组 AR、MA 项的系数。拟合做完之后预测时有个大坑未来步的外生变量必须自己提供。代码是这样future_exog pd.DataFrame({ promo: [1, 0, 0, 1, 0, 0, 0], temp: [28, 30, 31, 29, 27, 26, 25] }, indexpd.date_range(2024-06-01, periods7)) forecast result.forecast(steps7, exogfuture_exog)如果你预测未来 7 天却不给 7 行exog模型会直接报错这一点比纯 ARIMA 麻烦得多。后面我会专门用一节讲这个连环套的问题。2.3 外生变量使用中的三个现实问题第一个是要不要对 X 做差分。在回归 ARMA 误差的框架下外生变量理论上可以保持原始量纲但如果你把非平稳的 X比如持续上升的温度塞进去同时 y 做了差分容易产生伪回归。稳妥的做法是观察 X 的平稳性必要时也对其做对数或差分处理或者至少在解释系数时要谨慎。第二个是多重共线性。外生变量之间如果强相关比如同时放入促销金额和促销天数两个变量的系数会变得很不稳定。处理方式跟线性回归一样看相关系数矩阵或者用方差膨胀因子VIF筛选变量。第三个是变量选择。ARIMAX 不会因为你放进 10 个外生变量就自动变聪明反而可能过拟合。我的经验是先做业务判断再辅助用互相关分析CCF看 X 和 y 在哪个滞后期相关性最强只选最相关的 2~3 个进模型。样本量越小越要克制加变量的冲动。3. SARIMAX季节性被显式建模之后如果说 ARIMAX 解决的是外部因素进不来的问题那 SARIMAX 解决的是周期性波动没有结构的问题。它在 ARIMAX 的基础上多了四个季节参数 (P, D, Q, m)这组参数的引入把周期从一个数据预处理问题变成了模型结构的一部分。3.1 从数学上看季节参数放在哪里SARIMAX 的形式是 SARIMAX(p, d, q)(P, D, Q, m)其中 m 是季节长度。P 是季节自回归阶数Q 是季节移动平均阶数D 是季节差分次数。季节自回归项的含义可以这样理解如果 m12月度数据年周期SAR(1) 项意味着当月值会受到去年同月值的影响。比如说 2024 年 6 月的销量跟 2023 年 6 月的销量存在相关性这是普通的 AR(1) 无法表达的——普通的 AR(1) 只看上一期也就是 2024 年 5 月。同理季节移动平均项 SMA(1) 表示当前值跟一年前的预测误差有关。这些季节滞后项本质上是在把去年此时发生了什么当作特征来用。有意思的是引入季节参数后模型里会出现交互项。比如普通差分 d1、季节差分 D1、m12那么模型会对序列做如下变换Δ_12 Δ y_t (y_t - y_{t-1}) - (y_{t-12} - y_{t-13})这个变换同时去掉了邻近期的趋势和年周期的差异。你在解释模型时不需要手算它但要明白 D 的作用就是让季节性在一个周期内保持可比。如果你发现月度序列有明显的年度波动D 设 1 是常见的做法。3.2 季节差分和普通差分的配合逻辑很多新手会混淆 d 和 D 的分工。我习惯这样理解普通差分 d 处理的是序列整体在漂移也就是趋势季节差分 D 处理的是跨周期的水平差异也就是不同年份的同一季节之间基准不一样。举个例子你有一家奶茶店的日度销量数据周一到周五卖得少周末卖得多。这个周末多的模式每周都在重复你不需要 D1因为每周的周末水平差不多。你需要的可能只是 m7 的季节结构让模型知道今天是星期几。但如果这家奶茶店逐年扩张2019 年周末卖 2000 杯2023 年周末卖 5000 杯那么不同年份的周末基准完全不同这时候 D1 或者对季节基准做个差分才有意义。判断要不要加季节差分有一个相对省事的经验对序列做 ACF 图如果它在季节滞后比如 m7 的 7、14、21 这些位置衰减得非常慢就说明季节水平不稳定D 大概率取 1如果季节滞后处的自相关在几个周期后快速衰减D 取 0 也没有问题。3.3 超参数搜索order和seasonal_order到底该怎么试p、d、q 的阶数一般不用取得太大。销量、流量这类经济数据p 和 q 基本在 0~3 之间转一圈就够了取到 4、5 往往是过拟合。季节参数 P、D、Q 同样保守一点。P 和 Q 我通常只试 0 和 1极少用 2。因为 m7 的时候P1 意味着引入滞后 7 的自回归项如果 P2 就会引入滞后 14 的项对一般样本量来说阶数已经偏高了。参数搜索不用手动一个个试可以用网格搜索加 AIC 组合筛选。一个常见的思路是这样的import itertools import numpy as np from statsmodels.tsa.statespace.sarimax import SARIMAX best_aic np.inf best_cfg None for p in range(3): for d in range(2): for q in range(3): for P in range(2): for D in range(2): for Q in range(2): try: model SARIMAX( train[sales], exogtrain[[promo]], order(p, d, q), seasonal_order(P, D, Q, 7), enforce_stationarityFalse, enforce_invertibilityFalse ) result model.fit(dispFalse) if result.aic best_aic: best_aic result.aic best_cfg (p, d, q, P, D, Q) except Exception: continue print(best_cfg, best_aic)这里seasonal_order(P, D, Q, 7)的最后一个参数 7就是日数据的周季节周期。月度数据就填 12小时数据要填 24季度数据填 4。这东西没有固定的最优值完全取决于你的业务周期。另外enforce_stationarityFalse和enforce_invertibilityFalse这两个参数值得记住。实测中某些组合会让模型在数学上满足不了平稳性约束导致fit直接报错把它们设为 False 只是让模型尝试估计你不用慌最终选出来的模型 AIC 依然在惩罚复杂度过高的方案。4. 同一份数据对比ARIMA、ARIMAX、SARIMAX的真实差距前面讲了这么多原理这一段我们来个实际对比。我构造过的场景大致是这样一份 2023 年全年的日度销量数据带 0/1 促销标记同时有温度这个外生变量序列有明显的周末周期性。我用三个模型分别跑看它们在同一份测试集上的表现。4.1 数据集设定与三个跑法按 8:2 划分训练集和测试集。三个模型的设定如下ARIMA不看促销、不看温度只吃销量本身。ARIMAX在 ARIMA 基础上加促销和温度两个外生变量。SARIMAX外生变量照旧额外加 m7 的季节结构。拟合代码基本上就是把exog和seasonal_order交替传进去的事。但我建议所有模型都用SARIMAX类来跑而不是去翻老旧的ARIMA类这样配置字典化之后切换模型非常方便不容易写出一堆重复代码。测试期预测的时候外生变量我用的是上帝视角的真实值。这在实验里没问题但放到真实业务场景中外生变量的未来值本身就是需要额外解决的难题这一点我会在下一章展开。4.2 结果对比别只看AIC我当年跑完的第一反应是看 AICARIMAX 的 AIC 确实比 ARIMA 低SARIMAX 又是三者中最低的。但 AIC 是在训练集上算出来的相对拟合度不是预测准确度。真正决定模型能不能用的是它在测试集上的滚动预测误差。用 RMSE 和 MAPE 两个指标看我那个场景里的差距是这样的模型AIC训练集RMSE测试集MAPE测试集ARIMA(1,1,1)4982142.616.8%ARIMAX(1,1,1)promotemp4741118.312.4%SARIMAX(1,1,1)(1,0,1,7)promotemp451886.28.9%这个表一看就明白外生变量和季节结构各自都贡献了预测力但贡献的大小取决于你的业务里这两种信号强不强。如果是没有明显季节性的序列SARIMAX 的改进就会很小如果外生变量跟销量相关性很弱ARIMAX 的系数会不显著AIC 也不会好看到哪去。顺带一提MAPE 在销量接近 0 的时候会爆炸。如果你的数据里有很接近 0 的点建议改用 MAE 或者 WMAPE以实际销量为权重的绝对误差百分比否则一个极小销量点就能把一个不错的预测模型坏名声。4.3 评估时间序列模型时的三个细节第一测试集上的预测要滚动不要一步到位。一次性预测 60 天误差会随着步数增加而越滚越大因为每一步的预测值都被下一步当作真实值喂回去了。更合理的做法是做成滚动多步每次预测 1 天然后把真实值推进窗口重新拟合或继续更新状态再预测下一天。这样更贴近真实系统里每天重新跑一次模型的节奏。第二基线模型不能少。不管你上的是 ARIMA 还是 SARIMAX都应该先跑一个简单的持久性预测naive预测值就等于上一期真实值。如果你的复杂模型连 naive 都跑不赢那说明模型引入的结构只是在拟合噪声并没有学到规律。第三参数越相近、指标越接近越要看稳定性。两个模型的测试集误差几乎一样时我的选择依据通常是扰动鲁棒性去掉最近 10% 的数据重新训练看模型的预测误差波动大不大。波动大的模型即使指标好看上线后也容易爆雷。5. 落地阶段最容易翻车的几个坑模型调参调得再好真正把预测系统推到生产环境的时候还是会碰到若干教科书里根本没写的问题。这一章我集中讲三个我反复遇到、并且每次都能坑到新人的地方。5.1 外生变量的未来值问题一个连环套ARIMAX / SARIMAX 预测时你必须提供外生变量的未来值。这听起来是个小要求但实际执行时非常要命。举个例子你想预测未来 7 天销量模型需要未来 7 天的促销计划和天气温度。促销计划你可以从运营那里拿到但天气温度呢你只能靠天气预报而天气预报本身也有误差。如果你的外生变量里还有竞品价格这种根本拿不到未来值的数据那这个模型在预测时几乎是废的。我在实际项目里常用的应对思路有三条外生变量尽量选可预先确定的。促销计划、节假日标记、活动排期这类是确定的优先纳入。天气、行情这类是预测出来的要么不纳入要么专门再接一个子模型去预测它。用滞后外生变量代替当期外生变量。如果某个变量只有前一年或前两天能拿到把它做一阶滞后再喂给模型。比如前一天有没有促销比今天有没有促销更容易获取。代价是预测精度略微下降但也值得。做敏感性分析。预测时对外生变量的未来值多给几档假设比如促销日全开和全关各跑一次看看结果差异有多大。差异太大就说明模型对外生变量过度敏感这个变量需要跟业务方重新确认。5.2 外生变量的滞后效应处理有些外生变量的影响不是当天发生的。广告投放可能带来两天的流量后劲促销结束之后还会有一波回购潮。如果你直接把当期 X 塞进模型系数可能不显著或者出现滞后期的相关关系没被抓住。我的做法是先做互相关分析CCF看 X 在滞后 0、1、2、3 期时和 y 的相关性峰值落在哪里。如果峰值在滞后 2 期那就构造一个新特征promo_lag2而不是用当期的promo。在 statsmodels 里这种深思熟虑的特征构造比盲目加一堆exog列有效得多。我用过一个案例把是否促销从当期改成滞后 1 期和滞后 2 期两个特征之后ARIMAX 的测试集 RMSE 下降了接近 15%。很多时候模型的提升不是来自更高级的算法而是来自特征与时序对齐的准确性。5.3 这类模型在真实业务里的边界最后说点清醒话。ARIMA 家族在数据量适中、周期稳定、结构变化不剧烈的场景下非常能打。但如果数据量小于 100 个时间点我一般不推荐上 SARIMAX因为季节参数加上普通参数自由度消耗太快训练集误差会很好看测试集一塌糊涂。另外这一系列文章既然挂名时间序列与深度学习我也提一句ARIMA 家族的强项是可解释性和小样本稳定性深度学习方法比如 LSTM、Transformer 类模型的强项是高维特征和长程非线性依赖。实际项目中我见过最好的方案常常是ARIMA 类模型做基线和深度模型做补充的配合而不是两者非此即彼。如果业务对可解释性要求极高ARIMA 家族的地位短期很难被替代如果数据量大、特征空间复杂你完全可以在这个系列后面的文章里试试把外生变量喂给 LSTM结果往往会很有趣。回到外生变量的老问题上。就算模型选得好如果运营不能提前给你促销计划风控不能提前给你异常事件列表那再好的 SARIMAX 也预测不出它没见过的干预。技术能做到的是把已知信号用好而让未知信号变得可见需要的是业务协作不是模型参数。这也是我在做预测项目的过程中最想跟后来者分享的一点。
返回列表