[机器学习从入门到入土] 自回归滑动平均ARMA
个人导航
知乎:https://www.zhihu.com/people/byzh_rc
CSDN:https://blog.csdn.net/qq_54636039
注:本文仅对所述内容做了框架性引导,具体细节可查询其余相关资料or源码
参考文章:各方资料
文章目录
- [[机器学习从入门到入土] 自回归滑动平均ARMA](#[机器学习从入门到入土] 自回归滑动平均ARMA)
- 个人导航
- 参考资料
- 背景
- 架构(公式)
-
-
-
- 1) ARMA(p,q) 定义 ARMA(p,q) 定义)
- 2) 滞后算子形式 滞后算子形式)
- 3) 平稳性 平稳性)
- 4) 可逆性 可逆性)
- 5) 自相关结构(ACF/PACF的经验判断) 自相关结构(ACF/PACF的经验判断))
- 6) 选阶与拟合(实战最常用) 选阶与拟合(实战最常用))
-
-
- 创新点
- ARMA到底有什么用
-
-
-
- 1) 作为强基线 作为强基线)
- 2) 拆结构与做残差白化 拆结构与做残差白化)
- 3) 小数据与低成本场景 小数据与低成本场景)
- 4) 特征工程与信号任务 特征工程与信号任务)
-
-
- [代码实现 - numpy(模拟为主)](#代码实现 - numpy(模拟为主))
参考资料
Box & Jenkins:时间序列分析经典框架
背景
很多序列同时存在两类朴素现象
- 一类是惯性与相关性:当前值和过去若干时刻的值强相关
- 另一类是冲击回响:随机扰动并非立刻消失 而会在短期内拖尾
-> AR 负责刻画值的记忆
-> MA 负责刻画噪声的记忆
ARMA 就是把两者合在一起的最经典统计基线之一
- 既用过去的观测值解释现在
- 也用过去的随机冲击解释现在
- 常作为 ARIMA 的基础模块
架构(公式)
1) ARMA(p,q) 定义
设序列为 { x t } \{x_t\} {xt} 白噪声为 ε t ∼ N ( 0 , σ 2 ) \varepsilon_t \sim \mathcal{N}(0,\sigma^2) εt∼N(0,σ2)
x t = c + ∑ i = 1 p ϕ i x t − i + ε t + ∑ j = 1 q θ j ε t − j x_t = c + \sum_{i=1}^{p}\phi_i x_{t-i} + \varepsilon_t + \sum_{j=1}^{q}\theta_j \varepsilon_{t-j} xt=c+i=1∑pϕixt−i+εt+j=1∑qθjεt−j
- c c c:常数项
- ϕ i \phi_i ϕi:AR 系数 表示第 i i i 个滞后观测对当前的贡献
- θ j \theta_j θj:MA 系数 表示第 j j j 个滞后冲击对当前的回响
- p p p:AR 阶数
- q q q:MA 阶数
直观理解
- AR 部分负责建模可预测的结构
- MA 部分负责建模误差的相关结构
2) 滞后算子形式
定义滞后算子 L L L 满足 L x t = x t − 1 L x_t = x_{t-1} Lxt=xt−1
定义 AR 多项式 Φ ( L ) \Phi(L) Φ(L) 和 MA 多项式 Θ ( L ) \Theta(L) Θ(L)
Φ ( L ) = 1 − ϕ 1 L − ϕ 2 L 2 − ⋯ − ϕ p L p Θ ( L ) = 1 + θ 1 L + θ 2 L 2 + ⋯ + θ q L q \Phi(L) = 1 - \phi_1 L - \phi_2 L^2 - \cdots - \phi_p L^p \\ \Theta(L) = 1 + \theta_1 L + \theta_2 L^2 + \cdots + \theta_q L^q Φ(L)=1−ϕ1L−ϕ2L2−⋯−ϕpLpΘ(L)=1+θ1L+θ2L2+⋯+θqLq
则 ARMA 可写成:
Φ ( L ) x t = c + Θ ( L ) ε t \Phi(L) x_t = c + \Theta(L)\varepsilon_t Φ(L)xt=c+Θ(L)εt
这个形式很适合讨论平稳性与可逆性
3) 平稳性
ARMA 通常要求序列平稳 否则参数会漂 预测会爆
平稳性主要由 AR 部分决定
条件是 AR 特征多项式 Φ ( z ) = 0 \Phi(z)=0 Φ(z)=0 的根都在单位圆外
记忆法
- 根在单位圆外
- 系统不爆炸
- 自相关会衰减
4) 可逆性
MA 部分参数要唯一且估计稳定 常要求可逆
条件是 MA 多项式 Θ ( z ) = 0 \Theta(z)=0 Θ(z)=0 的根都在单位圆外
记忆法
- AR 看平稳
- MA 看可逆
- 两者都是为了防止爆和不唯一
5) 自相关结构(ACF/PACF的经验判断)
AR§
- PACF 往往在 p p p 阶截尾
- ACF 往往拖尾
MA(q)
- ACF 往往在 q q q 阶截尾
- PACF 往往拖尾
ARMA(p,q)
- ACF 通常拖尾
- PACF 通常也拖尾
所以 ARMA 常需要信息准则去选阶
6) 选阶与拟合(实战最常用)
选阶常用 AIC/BIC 越小越好
- AIC 更偏向拟合
- BIC 更偏向简洁
ARMA 的拟合一般不用 OLS
因为 MA 部分涉及不可观测的 ε t − j \varepsilon_{t-j} εt−j
主流做法是 MLE 或数值优化
创新点
- 极简但强:同时建模值的记忆 与噪声的记忆
- 可解释: ϕ i \phi_i ϕi 对应惯性贡献 θ j \theta_j θj 对应冲击回响
- 强基线:Box-Jenkins 体系核心模块
- 低成本:参数少 拟合快 小数据友好
ARMA到底有什么用
1) 作为强基线
在预测任务里 ARMA 是经典强基线
当你上 LSTM Transformer 之前 先用 ARMA 能快速得到一个可解释且靠谱的参考误差
2) 拆结构与做残差白化
很多真实序列的误差不是白噪声 而是带短期相关的 colored noise
ARMA 可以把
- 观测值的惯性结构
- 噪声的拖尾结构
一起解释掉
剩下的残差更接近白噪声 方便后续做控制与检测
3) 小数据与低成本场景
ARMA 参数少 训练快 数据需求小
在样本不多或实时性强时很实用
4) 特征工程与信号任务
对 1D 信号分类任务常见用法
- 拟合 ARMA 后用 ϕ , θ , σ 2 \phi,\theta,\sigma^2 ϕ,θ,σ2 当统计特征
- 或者用 ARMA 先白化再做分类 提升对异常冲击的敏感度
代码实现 - numpy(模拟为主)
说明:ARMA 的严格拟合通常用 MLE statsmodels 更合适
numpy 这里重点给序列模拟与预测接口 便于理解机制
py
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# =========================
# 1) ARMA(p,q) 序列模拟
# =========================
def simulate_arma(
n: int = 400,
phi=(0.7, -0.25),
theta=(0.6,),
c=0.0,
noise_std=1.0,
burn_in=200,
seed=0
):
"""
x_t = c + sum_i phi_i x_{t-i} + eps_t + sum_j theta_j eps_{t-j}
"""
rng = np.random.default_rng(seed)
phi = np.asarray(phi, dtype=float)
theta = np.asarray(theta, dtype=float)
p = len(phi)
q = len(theta)
# 预留足够长度用于 burn-in 与滞后
T = n + burn_in + max(p, q) + 1
eps = rng.normal(0.0, noise_std, size=T)
x = np.zeros(T, dtype=float)
# 初始化一些随机值避免全 0 起步
for t in range(max(p, q)):
x[t] = rng.normal(0.0, noise_std)
for t in range(max(p, q), T):
if p > 0:
# 取 x[t-p],...,x[t-1] 再反转成 [x_{t-1},...,x_{t-p}]
ar_part = float(phi @ x[t - p:t][::-1])
else:
ar_part = 0.0
if q > 0:
# 取 eps[t-q],...,eps[t-1] 再反转成 [eps_{t-1},...,eps_{t-q}]
ma_part = float(theta @ eps[t - q:t][::-1])
else:
ma_part = 0.0
x[t] = c + ar_part + eps[t] + ma_part
return x[-n:]
# =========================
# 2) 给定参数的递推预测(多步)
# =========================
def forecast_arma_multi_step(history: np.ndarray, phi: np.ndarray, theta: np.ndarray, c: float, steps: int):
"""
用已知参数做多步递推
注意:严格 ARMA 预测需要对未来 eps 做条件期望
这里给一个教学版近似:未来 eps 取 0 仅保留 AR 递推与已知历史 eps 项
"""
history = list(np.asarray(history, dtype=float))
phi = np.asarray(phi, dtype=float)
theta = np.asarray(theta, dtype=float)
p = len(phi)
q = len(theta)
# 近似处理:仅使用历史残差缓冲 未来残差取 0
eps_buf = [0.0] * q
preds = []
for _ in range(steps):
last_x = np.array(history[-p:][::-1]) if p > 0 else np.array([])
last_eps = np.array(eps_buf[::-1]) if q > 0 else np.array([])
ar_part = float(phi @ last_x) if p > 0 else 0.0
ma_part = float(theta @ last_eps) if q > 0 else 0.0
yhat = c + ar_part + ma_part
preds.append(yhat)
history.append(yhat)
if q > 0:
eps_buf = eps_buf[1:] + [0.0]
return np.array(preds, dtype=float)
# =========================
# 3) Demo:模拟 + 可视化
# =========================
def demo():
x = simulate_arma(
n=600,
phi=(0.75, -0.35),
theta=(0.55,),
c=0.2,
noise_std=0.8,
burn_in=400,
seed=42
)
n_train = 450
train = x[:n_train]
test = x[n_train:]
# 教学演示:假设我们已知真实参数
phi = np.array([0.75, -0.35], dtype=float)
theta = np.array([0.55], dtype=float)
c = 0.2
pred_multi = forecast_arma_multi_step(train, phi, theta, c, steps=len(test))
mse_multi = float(np.mean((pred_multi - test) ** 2))
t_train = np.arange(len(train))
t_test = np.arange(len(train), len(train) + len(test))
plt.figure(figsize=(10, 5))
plt.plot(t_train, train, label="train")
plt.plot(t_test, test, label="test (true)")
plt.plot(t_test, pred_multi, label="ARMA多步递推预测(教学近似)")
plt.axvline(len(train) - 1)
plt.title(f"ARMA(p=2,q=1) - 多步递推预测 | MSE={mse_multi:.3f}")
plt.xlabel("时间 t")
plt.ylabel("$x_t$")
plt.legend()
plt.tight_layout()
plt.show()
if __name__ == "__main__":
demo()