时间序列预测实战:ARIMA、Prophet 与 LSTM

系统覆盖时间序列预测:平稳性检验、ACF/PACF 分析、经典方法(移动平均/指数平滑/ARIMA)、Prophet 自动预测、LSTM 深度学习时序、特征工程与评估(RMSE/MAE)、实战对比。

引言

时间序列预测是「用过去推未来」:销量、流量、温度、股价。与普通回归不同,时序数据有自相关(今天影响明天)、趋势、季节性——需要专门的工具链。本文按「由易到难」推进:先讲平稳性与 ACF/PACF 诊断(所有方法的地基),再给经典统计法(指数平滑/ARIMA)、Facebook Prophet(自动处理趋势/节假日)、LSTM(深度学习),最后对比选型与评估。

前置:/ml-supervised-regression/(回归基础)、/ml-deep-learning-advanced/(LSTM)。评估见 /ml-model-evaluation/。


目录


1. 时序数据的三大结构

一条时间序列 = 趋势 + 季节 + 残差:

y(t) = 趋势 T(t) + 季节 S(t) + 循环 + 残差 ε(t)
      (长期方向)(固定周期)      (随机噪声)

三种分解(加性/乘性):

import pandas as pd
from statsmodels.tsa.seasonal import seasonal_decompose

decomp = seasonal_decompose(series, model='additive', period=12)
decomp.trend.plot(); decomp.seasonal.plot(); decomp.resid.plot()
结构含义例子
趋势长期上升/下降用户增长
季节固定周期波动周内/年内
循环无固定周期波动经济周期
残差不可解释噪声随机扰动

心智:先分解看清「有什么」,再选模型——有强趋势/季节用 Prophet/差分,纯随机噪声就别预测。


2. 平稳性与 ADF 检验

平稳性:统计性质(均值/方差)不随时间变化——ARIMA 要求平稳。

from statsmodels.tsa.stattools import adfuller

adf, p, *_ = adfuller(series)
print(f'ADF p-value: {p}')
# p < 0.05 → 平稳(拒绝非平稳假设)
# p >= 0.05 → 非平稳,需差分

处理非平稳:

方法做法例子
一阶差分y(t) - y(t-1)去线性趋势
对数变换log(y)方差随水平变大
季节性差分减去上一年同季去周期
series_diff = series.diff().dropna()   # 一阶差分

记忆:ADF 检验先验平稳性,非平稳就差分——ARIMA 的「I」就是差分的阶数 d。


3. 自相关分析:ACF 与 PACF

ACF(自相关函数):序列与其滞后版本的相关性——看出「依赖多远」。

PACF(偏自相关):去掉中间滞后影响后的净相关——帮 ARIMA 定阶。

from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

plot_acf(series_diff)    # ACF:拖尾或截尾?
plot_pacf(series_diff)   # PACF:定 AR 阶数 p

定阶规则(Box-Jenkins):

观察AR(p) 阶数MA(q) 阶数
PACF 在 lag p 后截尾p = 截尾处—
ACF 在 lag q 后截尾—q = 截尾处
两者都拖尾结合 AIC/BIC 搜索

记忆:PACF 截尾定 AR 阶,ACF 截尾定 MA 阶;都拖尾就用信息准则搜(见下节)。


4. 经典平滑法:移动平均与指数平滑

简单指数平滑(SES):越近的观测权重越大:

from statsmodels.tsa.holtwinters import ExponentialSmoothing

# Holt-Winters:趋势 + 季节
model = ExponentialSmoothing(
    series, trend='add', seasonal='add', seasonal_periods=12
).fit()
pred = model.forecast(12)
方法适合公式思想
移动平均平滑噪声近 k 期平均
SES无趋势季节ŷ = αy + (1-α)ŷ
Holt有趋势水平 + 趋势
Holt-Winters有趋势+季节水平+趋势+季节

指数平滑的优点:简单、稳、无需平稳化——适合「先跑通拿基线」。

记忆:平滑法 = 给「时间远近」分配权重——它常是时序项目的最佳基线。


5. ARIMA:自回归与差分

ARIMA(p, d, q) 三个参数:

p = AR 阶(自回归,用过去值预测)
d = 差分阶(去趋势,见第 2 节)
q = MA 阶(误差移动平均)
from statsmodels.tsa.arima.model import ARIMA
import itertools

# 网格搜索最佳 (p, d, q),用 AIC 最小
best_aic, best_cfg = float('inf'), None
for p, d, q in itertools.product(range(3), range(2), range(3)):
    try:
        model = ARIMA(series, order=(p, d, q)).fit()
        if model.aic < best_aic:
            best_aic, best_cfg = model.aic, (p, d, q)
    except Exception:
        pass

model = ARIMA(series, order=best_cfg).fit()
pred = model.forecast(12)
print('最佳阶数:', best_cfg, 'AIC:', best_aic)

SARIMA 扩展:加季节分量 (P, D, Q, m),处理周期数据。

模型适用
ARIMA平稳化后的一般序列
SARIMA有季节周期
局限性线性、非平稳要差分、难处理多变量

记忆:ARIMA 是「差分的线性自回归」——经典可靠,但对非线性/多变量力不从心。


6. Prophet:自动处理趋势与节假日

Prophet(Meta):把序列分解成趋势 + 季节 + 节假日,自动处理缺失值/异常点:

from prophet import Prophet
import pandas as pd

# 数据格式:ds(日期)+ y(值)
df = pd.DataFrame({'ds': dates, 'y': values})

model = Prophet(
    yearly_seasonality=True,
    weekly_seasonality=True,
    daily_seasonality=False,
    changepoint_prior_scale=0.05,   # 趋势突变灵敏度
)
# 加节假日(电商大促等)
model.add_country_holidays(country_name='CN')

model.fit(df)
future = model.make_future_dataframe(periods=30)   # 未来 30 天
forecast = model.predict(future)

Prophet 优点:

特性说明
自动趋势+季节无需手动平稳化
节假日内置国家节假日
鲁棒处理缺失/异常
可解释组件可视化

适用:业务指标(销量/流量)带趋势与节假日——比 ARIMA 好用得多。

记忆:Prophet = 傻瓜式自动分解——趋势、季节、节假日都帮你拆好,先试它。


7. LSTM:深度学习时序

LSTM 处理时序:把「过去窗口」当序列输入,预测未来(用 /ml-deep-learning-advanced/ 的 LSTM 结构):

import torch, torch.nn as nn
import numpy as np

# 构造滑窗样本:用前 W 步预测下一步
def make_windows(series, window=12):
    X, y = [], []
    for i in range(len(series) - window):
        X.append(series[i:i+window])
        y.append(series[i+window])
    return np.array(X, dtype=np.float32), np.array(y, dtype=np.float32)

X, y = make_windows(values, window=12)

class TimeLSTM(nn.Module):
    def __init__(self, window=12, hidden=32):
        super().__init__()
        self.lstm = nn.LSTM(1, hidden, batch_first=True)
        self.fc = nn.Linear(hidden, 1)
    def forward(self, x):            # x: (B, W, 1)
        out, _ = self.lstm(x)
        return self.fc(out[:, -1])   # 取最后一步 → 预测

model = TimeLSTM()
# 训练循环同标准流程(归一化、损失 MSE、AdamW)

LSTM 时序要点:

要点说明
归一化输入输出都归一化
滑窗窗口大小 = 依赖长度
预测方式递归(喂回预测值)或多步直接
数据量需足够样本(LSTM 数据饥渴)
对比数据少/线性 → 统计法可能更好

记忆:LSTM 强在非线性与长依赖,弱在数据饥渴——样本 <1k 先用统计法。


8. 时序特征工程与训练切分

时序「特征工程」(把日期变特征):

df['year'] = df['ds'].dt.year
df['month'] = df['ds'].dt.month
df['dayofweek'] = df['ds'].dt.dayofweek
df['is_holiday'] = df['ds'].isin(holidays)
df['lag_1'] = df['y'].shift(1)          # 过去值
df['rolling_7'] = df['y'].rolling(7).mean()   # 滚动均值

训练切分(时序不能随机打乱!):

# 随机切分会数据泄露(未来信息进训练)→ 必须按时间切
train = df[df['ds'] < '2025-01-01']
test  = df[df['ds'] >= '2025-01-01']

# 滚动验证(walk-forward):更真实的时序评估
# 不断前移训练窗口预测下一段,累计误差
切分方式说明
固定时间切分简单,一次评估
滚动/扩展窗模拟真实迭代预测,最稳
交叉验证(普通)❌ 时序禁用!

铁律:时序数据禁止随机打乱切分——未来的信息绝不能泄漏进训练。


9. 评估指标与模型对比

评估指标:

from sklearn.metrics import mean_absolute_error, mean_squared_error
import numpy as np

mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
# MAPE(百分比误差,业务可读)
mape = np.mean(np.abs((y_test - y_pred) / y_test)) * 100
指标特点
MAE直观,平均绝对误差
RMSE放大离群惩罚
MAPE百分比,业务友好(y=0 时失效)
MASE与朴素基线比(推荐对比)

模型对比表:

方法数据量趋势/季节非线性使用难度
指数平滑少✅❌低
ARIMA中靠差分❌中
Prophet中✅ 自动部分低
LSTM多✅✅高

选型建议:先 Prophet 拿基线 → 不够再 LSTM;线性小数据用 ARIMA。对比用统一指标 + 统一滚动窗口。


10. 速查表

需求做法
先看结构seasonal_decompose 分解
平稳性ADF 检验 + 差分
定阶ACF/PACF + AIC 搜索
快速基线指数平滑 / Prophet
强趋势+节假日Prophet
非线性大数据LSTM
特征工程日期/滞后/滚动特征
切分按时间切,禁随机打乱
评估MAE/RMSE/MAPE + 滚动验证
多步预测递归或直接多输出

一句话记忆:先分解看趋势季节,ADF 验平稳、差分去趋势;基线用平滑/Prophet,进阶用 LSTM;时序切分按时间、禁打乱;MAE/RMSE 评估、滚动窗口验证。


延伸阅读

  • /ml-deep-learning-advanced/ — LSTM 结构细节
  • /ml-supervised-regression/ — 回归评估与特征缩放
  • /ml-model-deployment/ — 时序模型的批处理部署
  • [[statistics]] — 统计检验基础
  • [[database]] — 时序数据存储(TSDB)

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 集成学习实战:Bagging、随机森林、梯度提升与 Stacking
  2. 迁移学习实战:预训练模型、特征提取与微调全流程
  3. 计算机视觉入门实战:图像处理与 CNN 图像分类