Transformer+LSTM+TCN与XGBoost融合的时间序列预测实战
2026/9/15 4:17:45 网站建设 项目流程

简介:一套基于Python的混合模型时间序列预测源码包,面向中高级Python学习者、算法工程师与数据挖掘开发者,旨在弥补单一模型在长短期依赖捕捉和非线性拟合上的不足,通过Transformer、LSTM、TCN与XGBoost四种算法融合提升预测稳定性,适用于价格、销量、流量等连续值回归场景。压缩包共5个文件,包含核心Python预测脚本、Shell一键运行脚本、两个CSV演示数据集及Markdown项目说明,整体仅12KB,轻量易用,下载后可直接解压调试。已有150人学习下载。读者可借助演示数据快速跑通训练与预测全流程,结合说明文档理解多模型融合的构建思路,替换数据或调整参数后即可迁移到自身业务;可靠的代码组织与精简结构,也使其成为课程设计或小型预测项目的良好参考。资源虽小,但覆盖了数据加载、模型训练、结果输出与使用说明等完整链路,适合快速上手和二次开发。

1. 为什么 XGBoost 要放在 Transformer、LSTM、TCN 后面

单一模型做时间序列预测,总会在某个环节露怯:Transformer 擅长抓长程依赖却对局部波动不敏感,LSTM 能记住序列顺序却训得慢,TCN 用膨胀卷积并行计算但感受野设计稍有不慎就会失效。这个项目把三者当作特征提取器,把隐层输出拼接后交给 XGBoost 做最终回归,等于先用深度网络把时序结构拆开,再用树模型去拟合残差中的非线性关系。我拆过不少时序源码,大部分所谓融合模型只是简单加权重平均,而这个结构明显是 stacking 思路,特征拼接之后树模型对尺度不敏感,能直接消化不同网络的隐层向量。项目自带price_data_演示.csvahead_data_演示.csv,跑通run_hybrid_forecast.py就能看到完整的训练、预测、指标输出,适合做过单模型预测、想往多模型融合方向走的 Python 工程师。

2. 三种时序网络的互补结构与 TCN 膨胀卷积感受野计算

2.1 Transformer 和 LSTM 在序列建模上的边界

2.1.1 自注意力复杂度带来的序列长度上限

Transformer 的优势是 self-attention 能直接建立任意两个时间步之间的依赖,路径长度为 1,理论上不存在 LSTM 那样的长程衰减问题。但它的计算复杂度是 O(n²) 的,序列长度翻倍,显存占用接近四倍。实际做单变量价格序列预测时,窗口长度超过 128 步,训练速度就会明显下降,而且注意力矩阵里大量权重会分散在无关的时间步上。所以在这个融合结构里,Transformer 的输入窗口不需要贪大,一般取 24 到 48 步就够用,它负责捕捉周期性的长期模式,比如日波动或者周趋势。

LSTM 的价值在于门控机制。遗忘门、输入门、输出门让网络可以决定哪些历史信息要保留、哪些要丢弃,这种顺序建模方式对短中期依赖特别友好。但 LSTM 是按时间步逐个展开的,无法并行,训练效率天然低于 CNN。把 LSTM 和 Transformer 并列放在同一个融合模型里,不是让它们互相替代,而是让它们从不同角度提取特征:LSTM 提供隐状态序列中的渐变信息,Transformer 提供全局关联权重。

2.2 TCN 的因果卷积与感受野

2.2.1 感受野公式决定层数设计

TCN 的核心是因果膨胀卷积。因果意味着输出 t 时刻只依赖 t 及之前的数据,不会泄漏未来信息;膨胀意味着卷积核通过间隔采样扩大覆盖范围。感受野计算公式是:

receptive_field = 1 + (kernel_size - 1) * sum(dilation_rates)

假设 kernel_size = 3,dilation_rates 按 1、2、4、8 递增,四层感受野就是1 + 2 × (1+2+4+8) = 31,也就是输入长度为 32 时最后一层每个输出点都看过全部历史。这个公式在调参时直接决定网络搭几层,不要盲目堆深度,层数超过感受野需求后收益很小。

模型建模方式主要优势主要限制在融合模型中的角色
Transformer全局自注意力长程依赖、并行度高O(n²) 计算量、局部噪声敏感提取周期性长程特征
LSTM递推门控顺序记忆、适合渐变过程无法并行、存在梯度衰减提取短期时序状态
TCN因果膨胀卷积感受野可控、训练并行感受野设计依赖公式计算提取局部多尺度特征

三个网络的隐层输出维度在拼接时要保持一致或者显式记录长度。我一般的处理方式是让 LSTM 取最后一步的 hidden state,输出维度是batch_size × hidden_size;TCN 取最后一个时间步的输出,维度也是batch_size × channels;Transformer 则取编码器输出的均值池化或者最后一个 token 的向量。三种特征语义不同、维度接近,交给 XGBoost 以后才能公平竞争。

3. 融合 pipeline:隐层特征拼接与 XGBoost 非线性回归

3.1 数据流与窗口划分约定

3.1.1 训练集、验证集、测试集如何切分

整个 pipeline 的数据流是单向的:原始时序 → 滑窗切片 → 三个深度网络各自前向传播 → 隐层特征拼接 → XGBoost 回归 → 输出预测值。这里最容易犯的错是直接拿原始序列训练 XGBoost,树模型没有时序位置概念,输入顺序打乱后结果完全不变,说明它根本学不到时间结构。必须让深度网络先处理时序,XGBoost 只吃压缩后的隐层特征。

划分方式建议按 8:1:1 切训练、验证、测试,并且严格按时间顺序切,不能乱序抽样。深度网络用训练集拟合、验证集做早停,测试集从头到尾不参与训练。XGBoost 阶段也要单独划分,不能直接把三个网络在训练集上的隐层输出拿来训练然后又在同样的输出上评估,那样分数虚高。

import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler def make_windows(data, window_size=24, horizon=1): X, y = [], [] for i in range(len(data) - window_size - horizon + 1): X.append(data[i:i + window_size]) y.append(data[i + window_size + horizon - 1]) return np.array(X), np.array(y) # 演示数据按时间排序后切分 df = pd.read_csv("price_data_演示.csv", parse_dates=["date"]) values = df["price"].values.reshape(-1, 1) train_size = int(len(values) * 0.8) val_size = int(len(values) * 0.1) scaler = StandardScaler() # 注意:fit 只用在训练段上 scaler.fit(values[:train_size]) values_scaled = scaler.transform(values) X_all, y_all = make_windows(values_scaled, window_size=24, horizon=1) X_train, y_train = X_all[:train_size], y_all[:train_size] X_val, y_val = X_all[train_size:train_size + val_size], y_all[train_size:train_size + val_size] X_test, y_test = X_all[train_size + val_size:], y_all[train_size + val_size:]

窗口大小window_size决定每个样本能看到多长的历史,horizon 是预测步长。归一化只在训练段上执行fit,验证和测试段的缩放参数从训练段继承,避免验证集信息提前进入模型。price_data_演示.csv里的字段一般包含时间列和价格列,实际使用时要先确认时间排序无缺失。

3.2 三个网络提取隐层特征

3.2.1 隐层维度的选择逻辑

每个深度网络单独训练到收敛后,冻结权重,然后对训练、验证、测试三组数据分别执行前向传播,取出隐层向量。LSTM 的 hidden_size 一般取 32 或 64,TCN 的 channels 取 32,Transformer 的 d_model 取 32。三个向量拼接后是 96 维,维度不高,XGBoost 处理起来很快,也不容易过拟合。

import torch import torch.nn as nn import xgboost as xgb # 假设 lstm_model, tcn_model, transformer_model 已经训练好并处于 eval 模式 # 取验证集作为示例,训练集和测试集同样处理 def extract_features(model, X, device): model.eval() features = [] with torch.no_grad(): for i in range(0, len(X), 64): batch = torch.FloatTensor(X[i:i+64]).to(device) feat = model.get_hidden(batch) # 返回 (batch, hidden_dim) features.append(feat.cpu().numpy()) return np.concatenate(features, axis=0) lstm_feat = extract_features(lstm_model, X_val, device) tcn_feat = extract_features(tcn_model, X_val, device) transformer_feat = extract_features(transformer_model, X_val, device) X_val_stack = np.concatenate([lstm_feat, tcn_feat, transformer_feat], axis=1) xgb_model = xgb.XGBRegressor( n_estimators=400, max_depth=5, learning_rate=0.02, subsample=0.8, colsample_bytree=0.8, early_stopping_rounds=50, random_state=42 ) xgb_model.fit( X_val_stack, y_val, eval_set=[(X_val_stack, y_val)], verbose=False )

extract_features按 batch 前向传播,避免一次把全部数据塞进显存。每个网络需要实现get_hidden方法,返回的是最后一层输出的隐层向量而不是最终预测值。X_val_stack的列顺序是 LSTM、TCN、Transformer 各自特征的顺序拼接,这个顺序只影响特征名,不影响树模型结果。XGBoost 设置subsamplecolsample_bytree来做样本列双重随机,降低三个网络特征之间的相关性带来的过拟合风险。

XGBoost 的价值在于它对非线性特征变换的拟合能力。深度网络输出的特征有明确的数值尺度,树模型用分裂阈值自动处理不同量纲,省去再次标准化。n_estimatorsearly_stopping_rounds是常规操作,训练到验证集误差不再下降就停,避免树模型把训练集隐层特征背下来。

4. 运行 run_hybrid_forecast.py 的完整流程与数据泄漏排查

4.1 项目文件结构与启动方式

4.1.1 start.sh 的实际内容

项目压缩包解开后是这几个文件:run_hybrid_forecast.py是主入口,README.md是项目说明,start.sh是启动脚本,price_data_演示.csvahead_data_演示.csv是两份演示数据。ahead_data_演示.csv从文件名看是用于向前预测的输入序列,也就是模型训练完成后,要预测的那段时间之前的已知数据。这对应的是滚动预测场景:先训练,再读取ahead_data生成未来若干步的预测结果。

start.sh常见写法是创建虚拟环境、安装依赖、拉起主脚本,在项目目录里直接执行即可:

#!/bin/bash # start.sh 的常见写法:安装依赖并运行主程序 pip install -r requirements.txt python run_hybrid_forecast.py \ --train_data price_data_演示.csv \ --ahead_data ahead_data_演示.csv \ --window_size 24 \ --horizon 1 \ --epochs 80 \ --batch_size 64 \ --hidden_size 64 \ --tcn_channels 32 \ --d_model 32 \ --lr 0.001

参数--horizon控制预测未来几步,--window_size控制历史窗口,--hidden_size是 LSTM 隐层维度,--tcn_channels是 TCN 卷积通道数,--d_model是 Transformer 向量维度。--epochs设 80 配合早停机制,实际训练会在验证集误差连续多轮不降时提前终止。如果显存不够,优先调小--batch_size而不是调小窗口,窗口变小会直接损失历史信息量。

4.2 演示 CSV 的字段约定与超参数表

4.2.1 参数配置速查

price_data_演示.csv用于训练,一般包含两列:时间戳和目标数值。ahead_data_演示.csv不含目标值,只有过去一段时间的观测,模型用它对未来做递推预测。程序读取时默认第一列是时间、第二列是数值,如果自己的数据格式不同,需要在脚本里改pd.read_csv的列名映射。

超参数建议取值范围说明
window_size12 ~ 48历史窗口长度,受 TCN 感受野约束
horizon1 ~ 7预测步长,越大越难训
hidden_size32 ~ 128LSTM 隐层维度和 TCN 特征通道上下限
tcn_channels16 ~ 64TCN 每层通道数
d_model16 ~ 64Transformer 嵌入维度
num_heads2 ~ 8注意力头数,d_model 需要能被它整除
dropout0.1 ~ 0.3深度网络通用正则手段
lr0.0005 ~ 0.005低于 0.0001 训练过慢,高于 0.01 容易震荡
n_estimators200 ~ 800XGBoost 树数量,配早停使用
max_depth3 ~ 7树深度,超过 7 容易过拟合隐层特征

4.3 三处常见数据泄漏

4.3.1 归一化边界泄漏

归一化是数据泄漏最隐蔽的地方。用整段数据的均值和标准差做缩放,验证集和测试集的信息就渗透到了训练过程里。正确做法是只对训练段fit,验证集和测试集直接transform。上面代码里scaler.fit(values[:train_size])就是关键一行,很多项目会在这里顺手写成缩放全量数据,模型效果看起来好,上线后马上变差。

4.3.2 窗口重叠泄漏

滑窗切片天然会产生样本重叠:第 i 个样本是t0~t23,第 i+1 个样本是t1~t24,两个样本共享 23 个时间步。深度网络训练时这种重叠问题不大,因为 batch 内随机打乱,模型不会按时间顺序记忆。但如果把重叠样本同时放进 XGBoost 的训练集和验证集,树模型会记住特征与目标之间的直接映射,验证集误差被严重低估。所以三个网络的隐层特征提取完成后,XGBoost 阶段要按时间块划分,或者用下面第 5 章的 walk-forward 方式逐折验证。

4.3.3 转发预测时的标签泄漏

ahead_data_演示.csv做未来预测时要注意一个细节:如果预测 horizon 大于 1,递推预测的第一轮用了真实观测值,第一轮之后的输入里会混入上一轮的预测值。这是允许的,但评估指标只能对比第一步的误差,多步预测误差会累积放大,这不是模型 bug,而是序列预测的固有属性。看输出指标时,重点看第一步的 MAPE。

5. walk-forward 回测与残差驱动调参

5.1 滑窗验证的代码骨架

固定训练测试集的单次划分容易高估模型效果。我一般用 walk-forward 做时间序列回测:每次用前 80% 的数据训练,预测后 20% 中一个固定长度的区间,然后窗口整体向后滑动,重复预言,最后把每次的预测结果拼在一起评估。这样每一条测试样本都是模型在训练阶段从未见过的未来数据,最接近上线后的真实行为。

def walk_forward_predict(data, window_size, horizon, step): predictions, actuals = [], [] train_ratio = 0.8 split = int(len(data) * train_ratio) while split + horizon <= len(data): train_data = data[:split] test_data = data[split:split + horizon] # 进入主流程:训练深度网络 + 提取特征 + XGBoost 回归 pred = run_fusion_model(train_data, test_data, window_size, horizon) predictions.extend(pred) actuals.extend(test_data[window_size:window_size + horizon]) split += step return np.array(predictions), np.array(actuals)

step决定窗口滑动的幅度,step=1每次只往前走一步,样本量最大但训练成本高;step=horizon可以维持窗口互不重叠,评估更保守。回测结束后计算 MAPE 和 RMSE,MAPE 能直观反映预测误差占比,如果数据里有接近零的值要改用 RMSE。

5.2 残差驱动调参

模型训练完不要只看指标。把测试集上的残差actual - predicted按时间画出来,如果残差序列表现出明显的正负交替,说明模型滞后于趋势,优先把 LSTM 的 hidden_size 调大或增加 TCN 层数。如果残差和滞后一期的残差相关性很高,说明输入窗口信息不足,应该增大 window_size 而非堆模型参数。XGBoost 的特征重要性也要看:三个网络的隐层特征里,如果某一列重要性长期趋近于零,说明对应的网络没有提取到有效信息,问题可能出在那个网络没收敛,而不是融合逻辑错了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询