跳转至

04 - 时序模型与ARIMA

时序模型与ARIMA图

🕐 时间序列概述

什么是时间序列?

定义: 按时间顺序排列的一系列数据点

Text Only
示例:
- 每日股价: [100, 102, 98, 105, 110, ...]
- 每小时温度: [22, 23, 21, 20, 19, ...]
- 每月销售额: [1000, 1200, 1150, 1300, ...]

时间序列组成要素

Text Only
时间序列 = 趋势 + 季节性 + 周期性 + 随机性

趋势 (Trend):
长期上升或下降方向
示例: 股票长期上涨

季节性 (Seasonality):
固定周期的重复模式
示例: 夏天冰淇淋销量高,冬天低

周期性 (Cyclical):
不固定周期的波动
示例: 经济周期 (繁荣→衰退→复苏)

随机性/残差 (Residual):
无法预测的噪声
示例: 突发事件影响

可视化示例

Text Only
温度数据:

30℃ ────────────🔥🔥🔥───────
     │          🔥🔥🔥
20℃ ───────────🔥──🔥────────
     │        🔥────🔥
10℃ ────────🔥────────🔥─────
     │     🔥──────────🔥
 0℃ ────🔥─────────────🔥───
     春  夏  秋  冬  春  夏

趋势: 整体平稳
季节性: 夏高冬低

📈 ARIMA 模型

ARIMA 是什么?

全称: AutoRegressive Integrated Moving Average (自回归整合移动平均)

组成部分: - AR: 自回归 - 用过去的值预测现在 - I: 差分 - 使数据平稳 - MA: 移动平均 - 用过去的误差预测现在

记法: ARIMA(p, d, q) - p: AR项数 (自回归阶数) - d: 差分次数 - q: MA项数 (移动平均阶数)


AR (自回归) - AutoRegressive

核心思想

用过去的时间点值预测当前值

Text Only
AR(1) 模型:
x_t = c + φ₁·x_{t-1} + ε_t

AR(p) 模型:
x_t = c + φ₁·x_{t-1} + φ₂·x_{t-2} + ... + φ_p·x_{t-p} + ε_t

其中:
- x_t: 当前时刻的值
- x_{t-1}, x_{t-2}: 过去的值
- φ₁, φ₂: 自回归系数
- ε_t: 白噪声误差

直观理解

例子: 今日股价预测

Text Only
假设: AR(1)模型, c = 20
今日股价 = 20 + 0.8 × 昨日股价 + 噪声

如果昨天股价是100元:
预测今天 = 20 + 0.8 × 100 = 100元
(假设噪声为0,此模型均值为 c/(1-φ) = 20/0.2 = 100)

φ = 0.8 < 1: 模型平稳
φ > 1: 模型发散 (不现实)

判断AR阶数: PACF (偏自相关函数)

Text Only
PACF图:
    |
1.0 │  ●
    │   ●
0.5 │    ●  ●
    │        ●
0.0 │─────────
   -0.5

如果PACF在p阶后截尾 (突然接近0),
则AR阶数为p

示例: PACF在1阶后接近0 → AR(1)

MA (移动平均) - Moving Average

核心思想

用过去的预测误差预测当前值

Text Only
MA(1) 模型:
x_t = μ + θ₁·ε_{t-1} + ε_t

MA(q) 模型:
x_t = μ + θ₁·ε_{t-1} + θ₂·ε_{t-2} + ... + θ_q·ε_{t-q} + ε_t

其中:
- ε_{t-1}: 过去的预测误差
- θ₁, θ₂: 移动平均系数

直观理解

例子: 温度预测

Text Only
MA(1)模型:
今日温度 = 平均温度 + 0.5 × 昨日预测误差 + 随机扰动

如果昨天预测误差是+2度 (预测偏低2度):
今天预测会考虑这个误差,
倾向于调高预测

判断MA阶数: ACF (自相关函数)

Text Only
ACF图:
    |
1.0 │  ●
    │   ●
0.5 │    ●     ●
    │        ●  ●
0.0 │────────────
    │          ●

如果ACF在q阶后截尾,
则MA阶数为q

I (差分) - Integration

为什么需要差分?

平稳性: 时间序列的统计特性 (均值、方差) 不随时间变化

Text Only
非平稳数据 (有趋势):
100, 102, 105, 108, 110, ...
↑ 上升趋势,均值在变化

一阶差分:
+2, +3, +3, +2, ...
→ 变成平稳!

二阶差分:
+1, +0, -1, ...
→ 更平稳

差分公式

Text Only
一阶差分:
Δx_t = x_t - x_{t-1}

二阶差分:
Δ²x_t = Δx_t - Δx_{t-1} = (x_t - x_{t-1}) - (x_{t-1} - x_{t-2})
      = x_t - 2x_{t-1} + x_{t-2}

季节差分:
Δ_s x_t = x_t - x_{t-s}
(s: 季节周期,如12个月)

判断差分次数

Text Only
方法1: 观察法
- 有明显趋势 → 差分1次
- 差分后仍有趋势 → 差分2次
- 一般不超过2次

方法2: ADF检验 (Augmented Dickey-Fuller)
- H0: 数据非平稳
- H1: 数据平稳
- p < 0.05: 拒绝H0,数据平稳

ARIMA 完整建模流程

步骤1: 可视化和平稳性检验

Python
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller

# 加载数据
df = pd.read_csv('data.csv', parse_dates=['date'], index_col='date')

# 绘制时序图
plt.figure(figsize=(12, 6))
plt.plot(df['value'])
plt.title('时间序列可视化')
plt.show()

# ADF检验
result = adfuller(df['value'])
print(f'ADF统计量: {result[0]:.4f}')
print(f'p值: {result[1]:.4f}')
if result[1] < 0.05:
    print('数据平稳')
else:
    print('数据非平稳,需要差分')

步骤2: 确定差分阶数 d

Python
# 绘制ACF和PACF
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
plot_acf(df['value'], lags=20, ax=ax1)
plot_pacf(df['value'], lags=20, ax=ax2)
plt.show()

# 如果ACF缓慢衰减 → 需要差分
# 差分后再看ACF/PACF
df_diff = df['value'].diff().dropna()
plot_acf(df_diff, lags=20)
plot_pacf(df_diff, lags=20)

步骤3: 确定 p 和 q

Text Only
ACF (自相关函数):
- 在q阶后截尾 → MA(q)
- 缓慢衰减 → AR或ARMA

PACF (偏自相关函数):
- 在p阶后截尾 → AR(p)
- 缓慢衰减 → AR或ARMA

示例:
ACF在1阶后截尾, PACF缓慢衰减 → MA(1)
ACF缓慢衰减, PACF在2阶后截尾 → AR(2)
两者都缓慢衰减 → ARMA(p,q)

步骤4: 拟合ARIMA模型

Python
from statsmodels.tsa.arima.model import ARIMA

# ARIMA(p,d,q)
model = ARIMA(df['value'], order=(2, 1, 1))  # AR(2), 差分1次, MA(1)
results = model.fit()

# 查看摘要
print(results.summary())

# 关键信息:
# - AIC/BIC: 越小越好 (用于模型选择)
# - 系数显著性: p < 0.05
# - 残差检验: 应该是白噪声

步骤5: 模型诊断

Python
# 残差分析
residuals = results.resid

# 绘制残差
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
ax1.plot(residuals)
ax1.set_title('残差图')

# 残差ACF (应该都是0,无显著相关)
plot_acf(residuals, lags=20, ax=ax2)
plt.show()

# Ljung-Box检验 (残差是否白噪声)
from statsmodels.stats.diagnostic import acorr_ljungbox
lb_test = acorr_ljungbox(residuals, lags=10)
print(lb_test)

# p值 > 0.05: 残差是白噪声 (好!)

步骤6: 预测

Python
# 预测未来10个时间点
forecast = results.forecast(steps=10)

# 绘制预测
plt.figure(figsize=(12, 6))
plt.plot(df['value'], label='历史数据')
plt.plot(forecast, label='预测', color='red')
plt.legend()
plt.title('ARIMA预测')
plt.show()

# 置信区间
forecast_ci = results.get_forecast(steps=10).conf_int()
plt.fill_between(forecast_ci.index,
                 forecast_ci.iloc[:, 0],
                 forecast_ci.iloc[:, 1],
                 alpha=0.3)

SARIMA (季节性ARIMA)

当数据有季节性时使用

记法: SARIMA(p, d, q)(P, D, Q, s) - (p, d, q): 非季节部分 - (P, D, Q, s): 季节部分 - P: 季节AR阶数 - D: 季节差分次数 - Q: 季节MA阶数 - s: 季节周期 (12=月度, 4=季度)

Python
from statsmodels.tsa.statespace.sarimax import SARIMAX

# 月度数据,年度季节性
model = SARIMAX(df['value'],
                order=(1, 1, 1),           # (p,d,q)
                seasonal_order=(1, 1, 1, 12))  # (P,D,Q,s)
results = model.fit()

forecast = results.forecast(steps=12)

🧠 神经网络时序模型

RNN (循环神经网络, Recurrent Neural Network)

核心思想

通过循环连接处理序列数据,隐藏状态传递历史信息

Text Only
传统NN:
x₁ → [NN] → y₁
x₂ → [NN] → y₂  (每个样本独立处理)

RNN:
x₁ → [RNN] → h₁ → y₁
      ↑  ↓
x₂ → [RNN] → h₂ → y₂
      ↑  ↓
x₃ → [RNN] → h₃ → y₃

h_t: 隐藏状态 (记忆)
h_t = f(h_{t-1}, x_t)

数学公式

Text Only
h_t = tanh(W_hh · h_{t-1} + W_xh · x_t + b_h)
y_t = W_hy · h_t + b_y

其中:
- h_t: 当前隐藏状态
- h_{t-1}: 上一时刻隐藏状态
- x_t: 当前输入
- y_t: 当前输出

代码实现 (PyTorch)

Python
import torch
import torch.nn as nn

class SimpleRNN(nn.Module):  # 继承nn.Module定义神经网络层
    def __init__(self, input_size, hidden_size, output_size):  # __init__构造方法,创建对象时自动调用
        super().__init__()  # super()调用父类方法
        self.hidden_size = hidden_size
        self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # x: (batch, seq_len, input_size)
        out, h_n = self.rnn(x)
        # out: (batch, seq_len, hidden_size)
        # h_n: (1, batch, hidden_size)

        # 取最后一个时间步
        out = out[:, -1, :]
        out = self.fc(out)
        return out

# 使用
model = SimpleRNN(input_size=1, hidden_size=32, output_size=1)

RNN的问题: 梯度消失/爆炸

Text Only
问题:
长序列时,梯度在反向传播中不断乘以权重矩阵
- 权重 < 1: 梯度指数衰减 → 梯度消失
- 权重 > 1: 梯度指数增长 → 梯度爆炸

结果:
无法学习长期依赖 (RNN的记忆只有短期)

LSTM (长短期记忆网络, Long Short-Term Memory)

核心思想

通过门控机制选择性记忆和遗忘

Text Only
LSTM细胞结构:

输入 x_t ─┬─→ 遗忘门 ─┬─→ 细胞状态 C_{t-1} ──┐
         │          ↓                      ↓
         └─→ 输入门  → [候选记忆] → 更新 C_t  │
         输出门 ←────────────────────────────┘
         隐藏状态 h_t

三大门

1. 遗忘门 (Forget Gate): 决定丢弃什么信息

Text Only
f_t = σ(W_f · [h_{t-1}, x_t] + b_f)
输出: 0-1之间的值
0: 完全遗忘
1: 完全保留

2. 输入门 (Input Gate): 决定存储什么信息

Text Only
i_t = σ(W_i · [h_{t-1}, x_t] + b_i)  (门控)
C̃_t = tanh(W_C · [h_{t-1}, x_t] + b_C)  (候选值)

3. 输出门 (Output Gate): 决定输出什么信息

Text Only
o_t = σ(W_o · [h_{t-1}, x_t] + b_o)
h_t = o_t ⊙ tanh(C_t)

细胞状态更新:

Text Only
C_t = f_t ⊙ C_{t-1} + i_t ⊙ C̃_t
⊙: 逐元素相乘

代码实现

Python
class LSTMModel(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, output_size):
        super().__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers,
                           batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # x: (batch, seq_len, input_size)
        out, (h_n, c_n) = self.lstm(x)
        # out: (batch, seq_len, hidden_size)
        # h_n: (num_layers, batch, hidden_size)
        # c_n: (num_layers, batch, hidden_size)

        # 取最后一个时间步
        out = out[:, -1, :]
        out = self.fc(out)
        return out

# 使用
model = LSTMModel(input_size=1, hidden_size=64,
                 num_layers=2, output_size=1)

GRU (门控循环单元, Gated Recurrent Unit)

LSTM的简化版

Text Only
区别:
- GRU只有2个门 (重置门、更新门)
- LSTM有3个门 (遗忘门、输入门、输出门)
- GRU不单独维护细胞状态

公式:
重置门: r_t = σ(W_r · [h_{t-1}, x_t])
更新门: z_t = σ(W_z · [h_{t-1}, x_t])
候选隐藏: h̃_t = tanh(W · [r_t ⊙ h_{t-1}, x_t])
最终隐藏: h_t = (1 - z_t) ⊙ h_{t-1} + z_t ⊙ h̃_t

LSTM vs GRU

特性 LSTM GRU
门数量 3 2
细胞状态
参数量 更多 更少
训练速度 更慢 更快
表达能力 更强 略弱
应用 长序列 中等序列

📊 模型对比

ARIMA vs 神经网络

Text Only
ARIMA:
优点:
- 数学原理清晰
- 可解释性强
- 适合小数据集
- 训练快

缺点:
- 假设线性关系
- 难建模长期依赖
- 需要手动确定p,d,q
- 不适合多变量

LSTM:
优点:
- 能建模非线性关系
- 自动学习特征
- 能处理长期依赖
- 支持多变量

缺点:
- 需要大量数据
- 训练慢
- 黑盒 (难解释)
- 需要调参

选择建议

Text Only
使用ARIMA:
- 数据量小 (< 1000个点)
- 单变量时间序列
- 需要可解释性
- 线性关系

使用LSTM/GRU:
- 数据量大 (> 1000个点)
- 多变量或非线性
- 不需要可解释性
- 长期依赖

🎯 实践建议

时序预测通用流程

Text Only
1. 数据探索:
   - 可视化趋势、季节性
   - 检查平稳性
   - 异常值处理

2. 特征工程:
   - 滞后特征 (lag)
   - 滚动统计 (均值、标准差)
   - 时间特征 (星期、月份)

3. 模型选择:
   - 简单基线: 移动平均、ARIMA
   - 复杂模型: LSTM、Transformer

4. 评估:
   - 时间序列交叉验证
   - 指标: MAE, RMSE, MAPE

5. 部署:
   - 滚动预测
   - 定期重训练

下一步

继续阅读 05-深度学习.md,学习CNN和更多深度学习知识!