4、经典预测模型:ARIMA模型原理、模型定阶(AIC/BIC准则)、Python实现与残差检验
说到时间序列预测,ARIMA模型绝对是个绕不开的老朋友。我记得刚入行那会儿,带我的老工程师就说:「先把ARIMA玩明白,再谈什么深度学习。」当时我不太服气,后来在好几个能源负荷预测项目里碰了壁,才明白这句话的分量。
说白了,ARIMA就是一套「让非平稳数据变平稳,然后找规律」的成熟方法论。咱们今天就把它的原理、定阶、代码实现和检验,一次性讲透。
4.1 ARIMA模型原理:三个字母的拆解
ARIMA的全称是 AutoRegressive Integrated Moving Average,也就是自回归积分滑动平均模型。它由三部分组成:
- AR(自回归):用过去时刻的值预测当前值。比如今天的负荷,跟昨天、前天的负荷有关系。
- I(差分):让非平稳序列变平稳。说白了就是「减一减」,把趋势去掉。
- MA(滑动平均):用过去的预测误差来修正当前预测。相当于给模型加了个「纠错机制」。
数学表达式长这样:
ARIMA(p,d,q) 模型:
(1 - φ₁B - φ₂B² - ... - φₚBᵖ)(1-B)ᵈ yₜ = (1 + θ₁B + θ₂B² + ... + θₙBⁿ) εₜ
其中 B 是滞后算子,p 是自回归阶数,d 是差分阶数,q 是滑动平均阶数。
核心理解:ARIMA(p,d,q) 的本质,就是先用 d 次差分把数据变平稳,然后用 p 阶自回归和 q 阶滑动平均来拟合这个平稳序列。
4.2 模型定阶:AIC与BIC准则
定阶是ARIMA里最让人头疼的一步。p、d、q到底取多少?我见过有人靠「玄学」猜,结果模型过拟合得一塌糊涂。
我个人习惯用两步走:
- 先定 d:通过单位根检验(ADF检验)确定差分阶数。一般 d=0 或 d=1 就够用,能源负荷数据很少需要 d>1。
- 再定 p 和 q:看自相关图(ACF)和偏自相关图(PACF),然后结合 AIC/BIC 准则选最优组合。
这里重点说说 AIC(赤池信息准则) 和 BIC(贝叶斯信息准则):
| 准则 | 公式 | 特点 |
|---|---|---|
| AIC | AIC = 2k - 2ln(L) | 倾向于选择更复杂的模型,k为参数个数 |
| BIC | BIC = k·ln(n) - 2ln(L) | 对复杂模型惩罚更大,n为样本量 |
我在项目中遇到过这样的情况:AIC选出来的模型参数偏多,预测时抖动很大;BIC选出来的更简洁,泛化能力反而更好。所以我的建议是——优先看BIC,尤其是样本量大的时候。
避坑指南:我曾经在一个光伏预测项目里,AIC选出了 p=7,q=5 的模型,结果测试集上误差爆炸。后来用BIC选了 p=2,q=2,效果反而稳定。记住:复杂不等于好。
4.3 Python实现:从数据到模型
咱们直接上代码。假设你手头有一份某区域过去1000天的日负荷数据,存在 load_data.csv 里。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.arima.model import ARIMA
import warnings
warnings.filterwarnings('ignore')
# 1. 加载数据
df = pd.read_csv('load_data.csv', parse_dates=['date'], index_col='date')
y = df['load']
# 2. 平稳性检验(ADF检验)
def adf_test(series):
result = adfuller(series.dropna())
print(f'ADF Statistic: {result[0]:.4f}')
print(f'p-value: {result[1]:.4f}')
return result[1] < 0.05 # p<0.05 表示平稳
# 3. 差分处理
d = 0
y_diff = y.copy()
while not adf_test(y_diff):
d += 1
y_diff = y.diff(d).dropna()
print(f'进行了 {d} 次差分')
print(f'最终确定 d = {d}')
# 4. 画ACF和PACF图,辅助定p和q
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))
plot_acf(y_diff, lags=30, ax=ax1)
plot_pacf(y_diff, lags=30, ax=ax2)
plt.show()
嗯,这里要注意:ACF图看拖尾还是截尾,PACF图也是。如果ACF在q阶后突然截尾,那q就取那个值;如果PACF在p阶后截尾,p就取那个值。但说实话,实际数据很少这么理想,所以咱们还是用AIC/BIC来定。
# 5. 网格搜索最优(p,q)
import itertools
best_aic = np.inf
best_bic = np.inf
best_order_aic = None
best_order_bic = None
# 设定搜索范围(根据ACF/PACF初步判断)
p_range = range(0, 5)
q_range = range(0, 5)
for p, q in itertools.product(p_range, q_range):
try:
model = ARIMA(y, order=(p, d, q))
result = model.fit()
aic = result.aic
bic = result.bic
if aic < best_aic:
best_aic = aic
best_order_aic = (p, d, q)
if bic < best_bic:
best_bic = bic
best_order_bic = (p, d, q)
except:
continue
print(f'AIC最优阶数: {best_order_aic}, AIC={best_aic:.2f}')
print(f'BIC最优阶数: {best_order_bic}, BIC={best_bic:.2f}')
# 6. 训练最终模型(用BIC选出来的)
final_order = best_order_bic
model = ARIMA(y, order=final_order)
result = model.fit()
print(result.summary())
注意:网格搜索时一定要加 try-except,因为某些(p,q)组合会导致模型无法收敛。我刚开始写代码时没加这个,程序跑一半就崩了,排查了半天才发现是参数组合的问题。
4.4 残差检验:模型好不好,残差说了算
模型训练完了,别急着用。你想想看,如果残差里还有明显的模式,说明模型没把信息提取干净。
残差检验主要看三点:
- 残差是否白噪声:用Ljung-Box检验,p值大于0.05说明残差是白噪声。
- 残差是否正态分布:画Q-Q图或做Shapiro-Wilk检验。
- 残差是否独立:看残差的自相关图,不应该有明显的自相关。
# 7. 残差检验
residuals = result.resid
# 7.1 Ljung-Box检验
from statsmodels.stats.diagnostic import acorr_ljungbox
lb_test = acorr_ljungbox(residuals, lags=[10, 20, 30], return_df=True)
print('Ljung-Box检验结果:')
print(lb_test)
# 7.2 画残差图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
# 残差时序图
axes[0, 0].plot(residuals)
axes[0, 0].set_title('残差时序图')
axes[0, 0].axhline(y=0, color='r', linestyle='--')
# 残差直方图
axes[0, 1].hist(residuals, bins=30, edgecolor='black', alpha=0.7)
axes[0, 1].set_title('残差分布直方图')
# 残差ACF图
plot_acf(residuals, lags=30, ax=axes[1, 0])
axes[1, 0].set_title('残差自相关图')
# Q-Q图
from scipy import stats
stats.probplot(residuals, dist="norm", plot=axes[1, 1])
axes[1, 1].set_title('残差Q-Q图')
plt.tight_layout()
plt.show()
# 7.3 判断
lb_pvalue = lb_test['lb_pvalue'].values
if all(p > 0.05 for p in lb_pvalue):
print('✅ 残差通过白噪声检验,模型拟合良好')
else:
print('⚠️ 残差未通过白噪声检验,建议调整模型参数')
我在一个风电预测项目里遇到过这种情况:模型AIC很低,但残差检验死活过不了。后来发现是数据里有个异常点没处理,去掉之后模型就正常了。所以记住:残差检验是模型质量的最后一道防线,别偷懒。
总结一下ARIMA建模流程:
- 数据预处理 → 平稳性检验 → 差分定d
- ACF/PACF辅助判断 → 网格搜索定p,q(优先BIC)
- 模型训练 → 残差检验(Ljung-Box + 可视化)
- 通过则用,不通过则调整参数或数据
好了,ARIMA模型的核心内容就这些。从原理到定阶,从代码实现到残差检验,每一步都有坑,但也都有解法。你把这个流程跑通一遍,以后遇到时间序列预测问题,心里就有底了。