拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

加权马尔可夫链修正ARIMA:让预测残差不再被当白噪声扔掉

加权马尔可夫链修正ARIMA:让预测残差不再被当白噪声扔掉

简介:这是一份《基于加权马尔可夫链修正的ARIMA预测模型的研究》期刊论文PDF,面向时间序列预测、设备状态监测与组合模型建模方向的算法研究人员和工程师。论文针对ARIMA模型在非线性、非平稳序列上偏差较大且不稳定的问题,引入加权马尔可夫链对残差序列进行分析,并采用状态特征值结合线性插值法将残差状态转化为具体修正值,最终得到更准确的预测结果。实验部分以船舶海水出口温度预测为例,与单一ARIMA模型的对比显示,修正后模型精度显著提高,具备可行性与工程应用价值。资源包仅含1个PDF文件,整体约767KB,内容完整覆盖摘要、引言、模型原理、实验设计与对比分析、结论及参考文献。目前已有513人学习使用,适合需要复现该组合预测方法或从事设备视情维修、轮机自动化、时间序列分析等工作的技术人员直接研读。

1. 加权马尔可夫链修正 ARIMA:当预测残差不再服从白噪声

做时间序列预测模型落地的人,大概率遇到过同一个尴尬:ARIMA 的拟合优度很好看,误差图却像心电图一样有规律地起伏——正误差连成片,负误差也连成片。这说明模型把该学的东西学完了,但残差里还残留着一种"状态惯性":这个月的误差偏正,下个月大概率还是偏正。这时候把残差当成白噪声处理,等于丢掉了最后一块可用的信息。加权马尔可夫链修正 ARIMA 的思路很直接:先用 ARIMA 把线性主趋势吃干榨净,再用加权马尔可夫链去刻画残差在不同状态之间的转移规律,最后把预测值补回来。这篇笔记就把这套方案从原理、参数到复现代码完整拆开,适合正在做短期预测、发现自己模型误差有规律而不是随机噪声的工程师。

2. ARIMA 的残差为什么值得修:先弄清楚哪里没学干净

2.1 ARIMA 能捕获什么:趋势、周期性和平稳性的边界

ARIMA 在处理单变量时间序列预测时,本质假设是:序列当前值可以由过去的自回归项、差分项和移动平均项线性组合解释。p 阶自回归捕捉序列自身的历史依赖,d 阶差分把非平稳趋势转化为平稳序列,q 阶移动平均吸收历史噪声的冲击。这套线性框架在趋势稳定、周期规整、波动同方差的序列上表现相当可靠,也是很多业务预测基线模型的首选。

但它的边界也很清楚:一旦序列存在明显的"状态切换"——比如设备运行模式切换、节假日前后行为突变、外部环境阶段性变化——ARIMA 只能在均值层面做一个折中拟合,无法表达"当前处于哪个状态、下一步更可能往哪个状态走"这类信息。换成更直白的话:ARIMA 是"平均主义",马尔可夫链是"状态主义"。

用 AIC 或 BIC 网格搜索选出来的 ARIMA 参数,描述的是整套训练数据上的最优线性依赖。拟合之后残差如果呈现系统性特征,就说明有一部分结构没有被线性模型消化掉。最典型的表现是残差序列的 Ljung-Box 检验 p 值远小于 0.05,拒绝了白噪声假设。很多团队在这个节点就急着换模型,其实换一个复杂度更高的模型未必划算,先把残差里的状态转移规律建模出来往往更轻量。

2.2 残差里的"惯性"长什么样:三个典型形态

我在实际项目里见过的残差惯性,大致可以归成三类。第一种是连续同号,比如连续 7 天的预测误差都为正,这不是巧合,而是某个外部因素(比如高温、促销、排产调整)让序列整体抬升,ARIMA 没跟上。第二种是区间往返,残差从正值区间跳到负值区间,再跳回来,形成类似窄带振荡的形态。第三种是方差分层,训练集前段残差小、后段残差大,说明序列的波动结构发生了改变。

这三个形态对应一个共同点:残差序列的当前值对下一时刻取值有预测力。这正是马尔可夫链能建模的信息。举个例子,可穿戴设备里做健康指标的时序预测时,序列天然存在"偏低、正常、偏高"这类分级状态,残差的转移路径本身就对应生理状态的切换。这类数据在构建高血压预测模型的前置特征工程里很常见,残差修正后往往比直接加大模型复杂度更有效。

判断残差有没有修的价值,有一个很实用的先验指标:计算残差的一阶自相关系数。如果一阶自相关系数的绝对值大于 0.3,说明残差里确实还有结构,值得往下做。小于 0.1 就不必费劲了,那是真噪声。

2.3 修残差的三条路:为什么加权马尔可夫链更省事

处理非白噪声残差,常见有三条路。第一条是换非线性模型,比如把历史特征喂给 xgboost回归预测模型,直接做有监督回归。这条路的问题是特征工程成本高,需要构造滞后特征、窗口统计特征、外部变量,而且对样本量要求不低,数据短了就很容易过拟合。第二条是上 prophet时序预测模型,它对季节性和节假日效应建模好,但残差修正不是它的核心设计目标,处理不了那种状态依赖很强的残差。第三条就是加权马尔可夫链,把残差划分成几个状态区间,用转移概率描述状态之间的跳转,再加权体现近期样本的重要性。

三条路放在一起看,加权马尔可夫链的优点非常突出:参数少,只有状态数、权重衰减系数、滞后阶数三个核心参数;可解释性强,每一行转移概率矩阵都能翻译成业务语言,比如"误差从偏高状态跳回正常状态的概率是 0.6";对样本量不敏感,几百个残差点就能把转移矩阵估计出来。对比逻辑见下表。

方案特征工程成本可解释性样本量要求适用场景
xgboost回归预测模型高低高特征丰富、样本充足
prophet时序预测模型中中中周期强、节假日效应明显
加权马尔可夫链修正低高低残差有状态惯性、样本量中等

3. 加权马尔可夫链的原理与参数:两个"加权"分别加在哪里

3.1 从残差到状态:分位数划分与转移矩阵

把马尔可夫链用在残差修正上,第一步是把连续残差变成离散状态。做法一般是按残差的分位数切出 K 个区间,比如 K=5,对应"大幅偏低、偏低、正常、偏高、大幅偏高"五个状态。用分位数而不是等宽划分,是因为残差通常集中在 0 附近,等宽划分会导致极端状态的样本极少,转移概率估计不可靠。

状态序列构造好之后,统计状态之间的跳转频数,就得到转移矩阵。假设三个状态 A、B、C,转移矩阵第 i 行第 j 列的含义是:当前处于状态 i,下一步转移到状态 j 的概率。训练集上如果观察到"从 A 跳到 B 出现 8 次,从 A 跳到 A 出现 2 次",那 A 行就是 [0.2, 0.8, 0.0]。这个矩阵就是修正的核心依据。

预测时,看当前时刻残差落在哪个状态,从矩阵里取出对应行,把行概率与每个状态的代表值(通常取该状态区间内残差的均值)做加权平均,就得到下一时刻的残差期望修正量。最终预测值等于 ARIMA 预测值加上这个修正量。逻辑上是把"预测残差"当成一个独立的小模型来处理。

这里要特别注意一点:马尔可夫链的无后效性假设。标准马尔可夫链认为下一时刻状态只取决于当前状态,但真实残差序列往往不完全满足这一点。这也是为什么要引入"加权"——通过多步转移矩阵组合,把更久之前的状态信息也带进来。

3.2 加权一:近期样本权重,让转移矩阵更贴合当下

第一个加权维度在转移概率估计的阶段。常规做法是直接统计频数,但这样所有历史转移样本是等权的。实际场景里,残差的状态模式可能随业务环境缓慢漂移,半年前的转移规律未必适用于现在。

我一般会引入一个指数衰减权重:距今越近的转移事件,贡献越大。具体实现是,在统计从状态 i 转移到状态 j 的次数时,给每一条转移事件乘一个权重 w_t = λ^(T-t),其中 T 是残差序列末尾时刻,t 是该转移事件发生的时间位置,λ 是衰减系数。λ 越接近 1,历史信息保留得越多;λ 越小,模型对近期模式越敏感。

按我的使用经验,λ 落在 0.90 到 0.99 之间最稳。样本量超过 500 时可以激进一点取 0.90,让模型快速适应当前状态;样本量只有一两百时取 0.98 以上,避免某个近期异常值把矩阵拉偏。不建议低于 0.85,否则转移矩阵会退化成只看最近十几个点,稳定性明显变差。

3.3 加权二:滞后步长权重,组合多阶信息

第二个加权维度在预测阶段。为了弥补一阶马尔可夫链的短视,常见做法是同时构建滞后 1 步、2 步、L 步的转移矩阵,再给不同滞后赋予不同权重。滞后步长的权重也做衰减:β^(l-1),l 是滞后阶数。比如滞后 1 阶权重 1.0,滞后 2 阶权重 0.7,滞后 3 阶权重 0.49,做归一化后合成最终的修正期望。

这两个加权叠加起来,就是标题里"加权"二字的完整含义:既在估计阶段向近期样本倾斜,又在预测阶段组合多步转移信息。前者解决模式漂移问题,后者解决无后效性假设过强的问题。两者不是必选,只做其中一个也能工作,但组合起来效果最稳。

3.4 参数与稀疏矩阵处理:不加平滑会翻车

核心参数归结为四个:状态数 K、近端样本衰减系数 λ、滞后阶数 L、滞后步长权重系数 β。推荐范围如下表。

参数含义推荐范围经验说明
K残差状态数3~6超过 6 矩阵稀疏,低于 3 信息太少
λ样本时间衰减系数0.90~0.99样本量大取 0.90,量小取 0.98
L最大滞后阶数2~4超过 4 权重趋零,收益递减
β滞后衰减系数0.5~0.8常用 0.7,业务状态变化快就取 0.5

还有一个几乎每次都会撞上的问题:某个转移行可能完全没有样本。比如状态"大幅偏高"最近出现的次数本来就少,从它出发的转移记录可能为零,归一化时分母为 0。常规处理是拉普拉斯平滑:在频数矩阵上整体加 1,再做行归一化。这样确保每一行都是合法的概率分布,不会出现预测时取到 NaN。之前有同事没做这一步,上线第一个星期就遇到了输出空值,排查半天发现是转移矩阵某个行和为 0。

4. Python 复现:从 ARIMA 拟合到加权马尔可夫链修正的完整流程

4.1 数据准备与 ARIMA 超参选择

实现这套方案不需要特别重的依赖,statsmodels 加 numpy、pandas 就够。我习惯先把数据读进来,按时间排序,然后做 ARIMA 参数的网格搜索。用 AIC 作为选择标准,因为 AIC 在样本量中等时比 BIC 更适合预测导向的选模。

import numpy as np import pandas as pd from statsmodels.tsa.arima.model import ARIMA df = pd.read_csv("your_series.csv", parse_dates=["date"], index_col="date") df = df.sort_index() # 用AIC网格搜索ARIMA(p,d,q),范围按业务经验控制 best_aic = np.inf best_order = None for p in range(0, 4): for d in range(0, 2): for q in range(0, 4): try: model = ARIMA(df["value"], order=(p, d, q)).fit() if model.aic < best_aic: best_aic = model.aic best_order = (p, d, q) except Exception: continue print(f"best_order: {best_order}, AIC: {best_aic:.2f}")

p、d、q 的范围收敛在 0~3、0~1、0~3 是常规做法。d 取到 2 的情况很少,除非序列本身就是 I(2) 过程。搜索过程用 try 包住,是因为某些参数组合下模型可能收敛失败或矩阵奇异,直接跳过比中断主流程更省心。

4.2 拟合 ARIMA 并提取样本内残差

选定参数后,在训练集上拟合,提取样本内残差。注意差分阶数 d 大于 0 时,fittedvalues 靠前的位置会出现 NaN,因为差分后的初始值无法被模型复现,需要剔除。

train = df.iloc[: int(len(df) * 0.8)] test = df.iloc[int(len(df) * 0.8):] model = ARIMA(train["value"], order=best_order).fit() fitted = model.fittedvalues resid = train["value"] - fitted resid = resid.dropna() print(f"训练集样本数: {len(train)}, 有效残差数: {len(resid)}")

残差的构造方式需要想清楚:这里用的是样本内拟合值与真实值的差,代表 ARIMA 在训练集上的未解释部分。后面预测阶段产生的残差也会逐步加入状态序列,保持状态序列的持续更新。

4.3 分位数状态编码

用分位数把残差切成 K 个状态。K 的选择我会先看残差分布:如果残差基本对称,K=5 通常够用;如果希望捕捉极端情况,可以加到 6,但不要超过 6。K 越大,转移矩阵的每行样本越少,统计可靠性下降。

n_states = 5 quantiles = np.quantile(resid, np.linspace(0, 1, n_states + 1)) quantiles[0] = -np.inf quantiles[-1] = np.inf states = np.digitize(resid, bins=quantiles[1:-1]) state_means = [resid[states == i].mean() for i in range(n_states)] print("分位边界:", quantiles[1:-1]) print("各状态残差均值:", state_means)

state_means 是每个状态的代表值。修正时,转移概率行向量与 state_means 做点积,得到的是残差的期望估计。用区间均值而不是区间中点,是因为残差在区间内往往不是均匀分布。

4.4 构建加权转移矩阵

下面这段是核心逻辑。统计转移频数时加入指数衰减权重,近期样本权重更高,再用拉普拉斯平滑兜底。

def build_weighted_transition_matrix(states, alpha=0.95): n = len(states) k = states.max() + 1 transition = np.zeros((k, k)) for t in range(n - 1): w = alpha ** (n - 1 - t) # 越靠近末尾,权重越大 i = states[t] j = states[t + 1] transition[i, j] += w # 拉普拉斯平滑,避免零行导致的NaN transition = transition + 1.0 P = transition / transition.sum(axis=1, keepdims=True) return P

这里的关键细节是权重计算的方向。设 T = n - 1 为最后一个转移事件的位置,第 t 个转移事件的权重是 α^(T-t),距离末尾越近指数越小,权重越接近 1。尾部样本在矩阵中的话语权就越大。alpha 取 0.95 时,20 个时间步之前的事件权重约为 0.36,100 个时间步之前只剩约 0.006。

4.5 多阶转移矩阵与最终修正

单阶转移矩阵的信息量有限,我会再构建滞后 2、3 阶的矩阵,并用 β 衰减合成期望修正值。

def multi_step_expectation(states, max_lag=3, alpha=0.95, beta=0.7): n = len(states) k = states.max() + 1 P_list = [] for lag in range(1, max_lag + 1): trans = np.zeros((k, k)) for t in range(n - lag): w = alpha ** (n - lag - t) trans[states[t], states[t + lag]] += w trans = trans + 1.0 P = trans / trans.sum(axis=1, keepdims=True) P_list.append(P) # 滞后步长权重,短滞后优先 lag_w = np.array([beta ** (l - 1) for l in range(1, max_lag + 1)]) lag_w = lag_w / lag_w.sum() cur_state = states[-1] expectation = 0.0 for lag in range(max_lag): expectation += lag_w[lag] * np.dot(P_list[lag][cur_state], state_means) return expectation, P_list

这个函数返回两个东西:修正期望值和多阶转移矩阵列表。修正期望就是下一时刻残差的预测值,最终预测等于 ARIMA 预测加上这个修正期望。

4.6 滚动预测评估

验证阶段采用单步滚动预测。每一步用截至当前时刻的数据重新拟合 ARIMA,再叠加修正值,然后与真实值对比。

from sklearn.metrics import mean_absolute_error history = train["value"].tolist() test_values = test["value"].tolist() pred_arima_all = [] pred_final_all = [] states_history = states.copy() for i in range(len(test_values)): model_refit = ARIMA(history, order=best_order).fit() pred_arima = model_refit.forecast(1).iloc[0] correction, _ = multi_step_expectation(states_history) pred_final = pred_arima + correction pred_arima_all.append(pred_arima) pred_final_all.append(pred_final) actual = test_values[i] new_resid = actual - pred_arima new_state = np.digitize(new_resid, bins=quantiles[1:-1]) states_history = np.append(states_history, new_state) history.append(actual) mae_arima = mean_absolute_error(test_values, pred_arima_all) mae_final = mean_absolute_error(test_values, pred_final_all) print(f"ARIMA MAE: {mae_arima:.4f}") print(f"ARIMA+马尔可夫修正 MAE: {mae_final:.4f}")

每一步都重新拟合 ARIMA 在数据量大时比较慢。生产环境里常见的优化是:每 10 步或 20 步重估一次 ARIMA 参数,中间步数直接使用模型外推,只有状态序列逐步更新。这种做法的精度损失通常很小,但速度提升明显。

5. 避坑与常见问题:四个踩过才会懂的参数陷阱

5.1 差分阶数过高,转移矩阵退化成单位矩阵

现象:修正后的预测结果几乎等于纯 ARIMA 预测,MAE 没有改善,打印转移矩阵发现对角线元素接近 1。

原因:d 阶差分把残差序列的短期结构磨平了。差分本身是强滤波,d 越大,序列的波动细节丢失越多。残差状态序列变成缓慢爬行的形态,相邻状态基本保持不变,转移矩阵自然收敛到单位矩阵。

解决:把 d 的搜索范围限制在 0 和 1,不要轻易尝试 d=2。如果业务序列本身有很强的趋势和季节性,优先考虑 SARIMA 而不是加高差分阶数。另一个做法是检查残差序列的一阶自相关系数,如果差分后残差自相关接近 0,说明马尔可夫链没有信息可用,直接放弃修正。

5.2 状态数设太多,转移矩阵稀疏导致预测值抖动

现象:K 取到 8 或 10 以后,预测曲线出现很多毛刺,修正值在相邻两步之间大幅跳变。

原因:残差样本量不变,状态区间变多,每个状态对应的转移样本变少。某些状态行只有两三个样本,转移概率估计受单个样本影响过大,行概率向量不光滑。

解决:K 控制在 3~6 之间。如果业务场景需要区分更多状态级别,至少保证每个状态区间内样本量不低于 30。样本量只有 200 时,K=5 已经是上限。还有一个辅助手段:对转移矩阵做一次列方向的平滑,让相邻状态的概率过渡更自然。

5.3 验证集残差方差大于训练集,状态偏移

现象:训练集上修正效果明显,验证集上一测试就失效,甚至 MAE 比纯 ARIMA 还差。

原因:样本内拟合残差的方差通常小于一步预测残差的方差。ARIMA 在训练集上的拟合值用的是全部历史信息,预测值只用了截至当前时刻的信息,预测误差天然更大。按训练集分位数划出的状态边界,在验证集上会导致大量残差落在"偏高"和"大幅偏高"这两个状态里,转移矩阵缺乏对这些状态的有力过渡。

解决:在构建状态序列时,给训练集残差乘以一个方差异常系数,近似一步预测误差的分布。常见做法是,先用前 20% 的训练集做一次模拟滚动预测,得到一步预测残差,用这个残差的方差重新缩放状态边界。代价是要多跑一次滚动预测,但状态边界贴合真实预测场景,修正效果稳定很多。

5.4 多步预测时修正失效,误差累积

现象:预测未来 5 步、10 步时,修正模型的结果反而不如纯 ARIMA,且步数越多差距越大。

原因:马尔可夫链修正的是残差的期望,一步预测时残差状态已知,修正可靠。多步预测时,ARIMA 的预测值本身在滚动更新,残差状态只能依赖前面步数的预测残差,误差层层传递,修正量本身就带噪声。

解决:这个方案建议只做单步或最多三步以内的短期预测。业务上确实需要多步预测时,可以对修正量加一个衰减因子,比如第 h 步修正量乘以 ρ^h,ρ 取 0.5~0.8,让修正的置信度随步长递减。别指望马尔可夫链能解决长期预测问题,那不是它的适用边界。

5.5 权重衰减系数取太大,修正过于激进

现象:修正模型在大多数时段表现好,但遇到个别异常值后,连续好几个步数的预测都被拉偏。

原因:λ 取 0.99 时,近期样本权重占比过高,一个极端的近期残差事件会在转移矩阵里留下很大痕迹,后续几步修正都受影响。

解决:λ 不要一刀切。样本量大且序列波动稳定时取 0.90~0.93,让单个异常事件的影响快速衰减;样本量小或业务状态切换缓慢时取 0.97~0.98。判断标准是:如果修正后的 MAE 在正常时段下降了,但每遇到异常值就要震荡五六步才恢复,说明权重衰减过慢,把 λ 往 0.90 的方向调。

6. 验证修正是否有效:三种对比方法和一个进阶技巧

6.1 前置检验:先确认残差不是白噪声

修正前先跑一次 Ljung-Box 检验,用 p 值判断有没有修正空间。

from statsmodels.stats.diagnostic import acorr_ljungbox lb = acorr_ljungbox(resid, lags=[10], return_df=True) print(lb)

p 值大于 0.05 时,残差已经接近白噪声,加马尔可夫链修正属于硬加噪声,只会让模型变差。p 值小于 0.01 时修正价值明显。这是一个成本几乎为零的前置筛选,值得养成习惯。

6.2 基线对比:纯 ARIMA、修正模型、直接换非线性模型

评估阶段至少要保留两条基线:纯 ARIMA 和修正模型。如果条件允许,再加一条 xgboost回归预测模型 的基线作为成本参考。三者的对比不只看 MAE,还要看误差分布。修正模型的优势往往体现在误差分位数上:P90 误差下降明显,说明那些原本偏得离谱的样本被拉了回来,而不是只降了均值。

我习惯把误差图按时间顺序画出来,观察修正模型是否把原来连片的同号误差打断了。如果曲线从"长条形同色块"变成"短促交替",说明状态转移机制确实捕捉到了残差惯性。

6.3 进阶技巧:把修正状态从残差扩展到业务状态

马尔可夫链修正不一定只作用于残差。如果业务本身有明确的状态定义,比如设备运行模式、库存周转级别、血压监测数据的风险等级,可以直接把业务状态序列和残差状态序列拼接成一个复合状态。这样修正时不仅看残差往哪走,还看业务状态有没有切换。复合状态矩阵的维度会变成两个状态数的乘积,K 的取值要相应缩小,否则矩阵稀疏问题会立刻回来。

我在一个用电负荷预测项目里用过这个技巧,复合状态修正比单纯残差修正的 MAPE 额外下降了大约 8%。代价是状态序列的维护逻辑复杂一些,但产出回报是值得的。这个方向做透以后,你会发现预测模型调参的精力不需要都花在 ARIMA 上,把残差和状态的交互吃透,往往才是预测精度真正上台阶的地方。

希望这篇笔记能帮你把加权马尔可夫链修正 ARIMA 从论文概念变成自己手里的可用工具。我自己的习惯是:任何改动的第一步先跑 Ljung-Box 看残差,第二步跑一次滚动验证看增益,从不跳过。这两个动作做完了,模型效果好不好、值不值得上线,大概率已经有了答案。

本文还有配套的精品资源,点击获取

返回列表