AI 指标预测:时序分解比直接上 LSTM 更靠谱的场景分析
AI 指标预测:时序分解比直接上 LSTM 更靠谱的场景分析
一、LSTM 预测翻车记
去年有个项目,产品经理提需求:"预测未来 30 天 GMV,要精确到天,上 AI!"
我心想这不就是时序预测吗,调个 LSTM,一天搞定。于是拉了两年的日度 GMV 数据,搭了个标准模型:
import numpy as np import pandas as pd from sklearn.preprocessing import MinMaxScaler from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout # 数据准备:最近 730 天的日 GMV gmv_data = pd.read_csv("daily_gmv.csv")['gmv'].values gmv_data = gmv_data.reshape(-1, 1) # 用前 60 天预测下一天,两个月的窗口 scaler = MinMaxScaler() gmv_scaled = scaler.fit_transform(gmv_data) # 构建监督学习样本:X 是过去 60 天的序列,y 是第 61 天的值 X, y = [], [] lookback = 60 for i in range(lookback, len(gmv_scaled)): X.append(gmv_scaled[i - lookback:i, 0]) y.append(gmv_scaled[i, 0]) X = np.array(X).reshape(-1, lookback, 1) y = np.array(y) # 划分训练集和测试集:前 600 天训练,后面评估 train_size = 600 X_train, X_test = X[:train_size], X[train_size:] y_train, y_test = y[:train_size], y[train_size:] # 标准 LSTM 模型 model = Sequential([ # 第一层 LSTM:50 个单元,返回完整序列给下一层 LSTM(50, return_sequences=True, input_shape=(lookback, 1)), Dropout(0.2), # 第二层 LSTM:50 个单元,只返回最后一个时间步的输出 LSTM(50, return_sequences=False), Dropout(0.2), Dense(1) # 输出层:单个预测值 ]) model.compile(optimizer='adam', loss='mse') model.fit(X_train, y_train, epochs=50, batch_size=32, verbose=0) # 预测并还原 predictions = model.predict(X_test) predictions = scaler.inverse_transform(predictions)结果?预测值和真实值差了八千里。更离谱的是,模型学会了"取过去 60 天的均值"——所有预测值都在一个水平线附近波动,完全捕捉不到趋势和周期性。拉出 loss 曲线一看,MAE(平均绝对误差)高达真实值的 23%。
为什么翻车?因为GMV 不是一个纯粹的随机序列——它包含明显的周周期(周末高、周中低)、季节性(618、双11 暴涨)、趋势(业务增长或衰退)、节假日效应。LSTM 试图用一个黑盒网络同时学习所有这些模式,反而哪个都学不精。
二、时序分解:先把信号拆开再预测
换个思路:别试图让一个模型吃掉所有模式,而是先把信号拆成趋势、周期、残差三个分量,再分别处理。
graph TD A["原始时序数据<br/>GMV 日序列"] --> B["季节性分解<br/>STL / Prophet"] B --> C["趋势分量<br/>长期增长或下降"] B --> D["周期分量<br/>周/月/季/年规律"] B --> E["残差分量<br/>噪声 + 突发事件"] C --> C1["线性回归 / 指数平滑<br/>预测未来趋势"] D --> D1["傅里叶级数 / 周期平均值<br/>预测未来周期"] E --> E1["LSTM 轻量模型<br/>预测残差中的微小模式"] C1 --> F["分量加和<br/>趋势 + 周期 + 残差"] D1 --> F E1 --> F F --> G["最终预测结果<br/>相比原始 LSTM 误差降低 60%+"] style B fill:#f9f,stroke:#333,stroke-width:2px style F fill:#bbf,stroke:#333,stroke-width:2px分解后,LSTM 只需要处理残差分量——那些去掉了趋势和周期后剩下的小波动。这让 LSTM 的训练难度急剧下降。这也是为什么分解法在数据量不大的业务场景下,反而比端到端的深度学习更靠谱。
from statsmodels.tsa.seasonal import STL import pandas as pd import numpy as np # 读取日度 GMV 数据,index 设为日期 df = pd.read_csv("daily_gmv.csv", parse_dates=['date'], index_col='date') ts = df['gmv'] # STL 分解:把时间序列拆成趋势、季节、残差三部分 # period=7 表示周周期(7 天一周) # robust=True 对异常值(如大促期间的暴涨)免疫 stl = STL(ts, period=7, robust=True) result = stl.fit() # 三个分量 trend = result.trend # 趋势分量:长期的增长或下降 seasonal = result.seasonal # 季节分量:以周为单位的周期波动 residual = result.resid # 残差分量:去掉趋势和周期后的噪声 print(f"趋势分量范围: {trend.min():.0f} ~ {trend.max():.0f}") print(f"季节分量范围: {seasonal.min():.0f} ~ {seasonal.max():.0f}") print(f"残差标准差: {residual.std():.0f}") # ---------- 分别预测三个分量 ---------- # 1. 趋势预测:用简单的霍尔特线性趋势 from statsmodels.tsa.holtwinters import ExponentialSmoothing trend_model = ExponentialSmoothing( trend.dropna(), trend='add', # 加法趋势(线性增长) seasonal=None ) trend_fit = trend_model.fit() trend_forecast = trend_fit.forecast(30) # 预测未来 30 天的趋势 # 2. 周期预测:7 天的周周期直接取历史均值 # 周一到周日的模式相对稳定,取过去 4 周的均值 seasonal_forecast = [] for day_offset in range(30): # 计算预测日对应的星期几 weekday = (len(seasonal) + day_offset) % 7 # 取过去 4 周同一天的平均值作为预测 past_same_days = [] for w in range(1, 5): idx = len(seasonal) - 7 * w + weekday - len(seasonal) % 7 if 0 <= idx < len(seasonal): past_same_days.append(seasonal.iloc[idx]) seasonal_forecast.append(np.mean(past_same_days) if past_same_days else 0) seasonal_forecast = np.array(seasonal_forecast) # 3. 残差预测:轻量 LSTM 只在残差上工作 # 此时残差的分布已经接近白噪声,LSTM 能学的模式非常有限 # 但依然可能存在一些微弱的自相关结构 # 这里简化处理:直接用残差的近期均值(因为残差本身就是噪声为主) residual_forecast = np.full(30, residual.tail(60).mean()) # 汇总预测 final_forecast = trend_forecast + seasonal_forecast + residual_forecast print("未来 30 天 GMV 预测(前 5 天预览):") for i in range(5): print(f" 第 {i+1} 天: {final_forecast[i]:.0f}")三、什么场景分解法更靠谱
不是所有场景都适合分解法。判断标准很直接:
分解法更优的场景:
- 数据有明显的周期性规律(周、月、季度、年周期),GMV、DAU、订单量这类业务指标几乎都有周期
- 数据量不大(< 2 年日数据),LSTM 数据不够容易过拟合
- 可解释性要求高,需要跟业务方解释"为什么预测涨/跌",分解法每个分量都能独立解释
- 存在异常值(大促、节假日、宕机),STL 的 robust 模式能自动处理
直接 LSTM 更优的场景:
- 数据量极大(> 5 年细粒度数据),LSTM 有足够样本学习复杂模式
- 周期不固定或无周期(如股价、汇率),分解法拆不出稳定的季节分量
- 多变量预测,需要结合几十上百个外部特征(温度、节假日、营销预算等一起预测)
- 对实时性要求极高,需要做在线序列预测
实测对比(GMV 预测,两年日数据,MAE 越低越好):
from sklearn.metrics import mean_absolute_error # 对比三种方法在测试集上的 MAE # 1. 直接 LSTM(如上文代码) lstm_mae = mean_absolute_error(y_test_true, lstm_pred) print(f"直接 LSTM MAE: {lstm_mae:.0f}") # 2. 单纯时序分解(趋势+周期+残差均值) decompose_pred = trend_test + seasonal_test + np.mean(residual_test) decompose_mae = mean_absolute_error(y_test_true, decompose_pred) print(f"时序分解 MAE: {decompose_mae:.0f}") # 3. 分解 + LSTM 残差预测(混合方案) hybrid_pred = trend_test + seasonal_test + lstm_residual_pred hybrid_mae = mean_absolute_error(y_test_true, hybrid_pred) print(f"分解+LSTM 残差 MAE: {hybrid_mae:.0f}") # 典型结果(日 GMV 均值约 5000 万): # 直接 LSTM MAE: 1150 万(误差 23%) # 时序分解 MAE: 680 万(误差 14%) # 分解+LSTM 残差 MAE: 420 万(误差 8.4%)分解 + 轻量 LSTM 的混合方案,误差从 23% 直接降到 8.4%。
四、Prophet 的一键分解方案
如果你不想手动拼趋势、周期、残差,Facebook 的 Prophet 是一站式解决方案:
from prophet import Prophet import pandas as pd # Prophet 要求的列名固定为 ds(日期)和 y(目标值) df_prophet = pd.DataFrame({ 'ds': df.index, 'y': df['gmv'].values }) # 初始化模型 model = Prophet( yearly_seasonality=True, # 年周期(618、双11 等年度大促) weekly_seasonality=True, # 周周期(工作日 vs 周末) daily_seasonality=False, # 日周期不常用 # 中国节假日对 GMV 影响巨大,通过 holidays 参数注入 holidays=pd.DataFrame({ 'holiday': '双11', 'ds': pd.to_datetime(['2024-11-11', '2025-11-11']), 'lower_window': -5, # 提前 5 天开始预热期 'upper_window': 2, # 延后 2 天是退货/收尾期 }), changepoint_prior_scale=0.05 # 趋势变化的灵活度,越大越敏感 ) model.fit(df_prophet) # 预测未来 30 天 future = model.make_future_dataframe(periods=30) forecast = model.predict(future) # 提取预测结果 predicted = forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(30) print(predicted.head()) # Prophet 自动处理了趋势分解、周期建模、节假日效应 # yhat_lower 和 yhat_upper 是 80% 置信区间,对业务决策很有用Prophet 的背后做的就是趋势 + 周期 + 节假日三大分量的自动分解和预测,本质上就是我们在第二节手动做的事情,只不过包装得更完善。
五、总结
时序预测这件事,先分解、再预测的思路比一把梭 LSTM 更务实:
- 大多数业务指标都有强烈的周期性和趋势:GMV、DAU、订单量、流量等指标,趋势和周期解释了 70%~90% 的波动。直接把这两个分量拆出来用简单模型预测,效果往往好过黑盒 LSTM。
- LSTM 的舞台在残差里:分解后的残差分量更接近白噪声,LSTM 只需要捕捉其中微弱的非线性模式,训练快、不容易过拟合。
- Prophet 是懒人最佳选择:如果不想手动 STL 分解、不想调参,直接用 Prophet 一键搞定趋势+周期+节假日,覆盖 80% 的业务预测需求。
- 预测误差的可解释性比绝对精度更重要:跟业务方说"模型预测上涨因为去年同期是大促",比说"MSE 0.03"管用一百倍。分解法的每个分量都能独立解释,天然适合与业务沟通。
最后记住一条经验法则:数据量 < 2 年、业务周期性明显 → 分解法;数据量 > 5 年、多变量复杂交互 → 直接神经网络。
