news 2026/9/15 15:40:34

Transformer+LSTM+TCN+XGBoost混合时间序列预测模板

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Transformer+LSTM+TCN+XGBoost混合时间序列预测模板

简介:一套基于Python的多模型融合时间序列预测源码与项目说明,面向有一定深度学习基础、希望提升时序预测精度的开发者与科研人员。方案同时整合Transformer、LSTM、TCN与XGBoost,兼顾序列长期依赖、局部特征与树模型非线性拟合能力,适合销量、股价、能耗等典型场景的预测任务。压缩包共5个文件,包含Python预测脚本、两份csv演示数据集、shell启动脚本及Markdown项目说明,体积仅12KB,短小精悍;脚本可直接运行,便于快速复现和二次改造。目前已有150人学习下载。资源提供的价值在于:一是给出多模型融合的完整代码流程,二是通过真实结构的演示数据展示数据预处理、训练与预测环节,三是附带的说明文档能帮读者理清各模型的组合方式与调参思路,尤其适合做算法对比实验或作为论文基线。

1. 一个把深度学习波动和树模型稳健性拼在一起的混合预测模板

用纯 Transformer 做时间序列预测,结果常常被一个只喂滞后特征的 XGBoost 按在地上摩擦。这不是段子,是很多人在 Kaggle 和业务基线里踩过的坑。这个标题里的融合思路,本质上是把深度学习抓时序模式、树模型抓非线性特征各自的长处拼到一起:Transformer 抓全局依赖,LSTM 处理中等尺度的记忆,TCN 用因果卷积稳定捕捉局部趋势,最后让 XGBoost 对前三者的预测结果做非线性校正。适合有 Python 基础、想搭一套能复现也能改的混合预测模板的工程师,也适合拿来做比赛基线和业务预演的初版方案。

2. 先把四个模型的分工想清楚:Transformer、LSTM、TCN、XGBoost 各管哪一段

2.1 三个深度模型的边界:全局依赖、顺序记忆、局部因果场

时间序列预测的核心矛盾是:序列里既有长期周期,又有短期惯性,还有突发噪声。单一模型很难同时处理好这三件事。Transformer 的优势在于注意力机制能直接建立任意两个时间步之间的关联,长周期依赖不需要像 RNN 那样一步步传递,这也是 transformer 架构近几年被引入时序领域的主要原因。但它的代价是位置编码对序列长度敏感,输入太长计算量上涨很快,而且对局部细节的刻画不如卷积类模型直接。

LSTM 则是经典的顺序建模思路,通过遗忘门、输入门、输出门控制信息流动,擅长捕捉中等长度的动态变化。它的短板在于训练是串行的,难以并行加速,而且面对超长序列时梯度传递仍然会衰减。这里有个常见的误解:LSTM 不是不能抓长期依赖,而是需要靠门控机制“有选择地记住”,它更适合那些依赖近期状态和缓慢趋势的信号。

TCN 的结构则是因果膨胀卷积,每一层只看到当前时刻及其之前的输入,不会引入未来信息。通过膨胀率指数增长,TCN 可以在层数很少的情况下扩大感受野,而且卷积天然支持并行,训练速度明显快于循环结构。它的缺点是感受野需要人为设计,窗口不够大时,周期超过感受野的信号就看不全,这在 tcn 模型结构里是必须处理的边界问题。

2.2 XGBoost 的定位:不学序列,只学预测残差与非线性校正

把三个深度模型的输出直接平均,是一种常见的 ensemble 做法,但不是这个标题里最合理的做法。既然加了 XGBoost,就应该让它承担更具体的工作:把三个深度模型的验证集预测结果当作特征,连同原始滞后值一起喂给 XGBoost,让它学习“真实值和深度模型预测值之间的差异”。

xgboost 本身是梯度提升树,擅长处理特征之间的非线性交互,对异常值和缺失值也有天然的稳健性。它不关心输入是否有时间顺序,所以不能单独拿来做序列预测,但用来做 stacking 的 meta learner 非常合适。树模型需要的是“有信息量的特征”,而三个深度模型的预测值恰好都是对目标的高度压缩表示,这种特征组合往往比直接拼原始序列效果更好。

2.3 融合方式:特征堆叠与两层架构

我一般会把整体流程拆成两层。第一层是三个深度模型,输入都是标准化后的滑窗序列,输出是单步预测值。第二层是 XGBoost,输入是三个模型在验证集上的预测值加上若干手工构造的滞后特征,输出是最终预测。

这里有一个必须注意的细节:XGBoost 训练用的特征,必须来自深度模型在验证集上的预测结果,而不是训练集上的。如果拿训练集的预测结果去喂 XGBoost,会产生严重的过拟合,因为深度模型已经记住了训练集的噪声,XGBoost 会把这些噪声当作规律学进去。验证集的预测才是模型真实泛化能力的体现。

3. 数据形态:把时间序列改造成监督学习样本

3.1 滑窗函数:从 pandas 到 PyTorch Tensor 的完整转换

深度时序模型输入的标准形态是(B, T, F),B 是批大小,T 是窗口长度,F 是特征维度。单变量序列就是(B, T, 1)。下面这个函数把一维序列切成滑窗样本,这一步是后面所有模型共同的数据前置。

import numpy as np import pandas as pd import torch from torch.utils.data import TensorDataset, DataLoader def make_windows(series, seq_len=32, horizon=1, step=1): """ 把一维时间序列切成 (样本, seq_len, 1) 的监督学习样本 series : 1D numpy array,原始序列 seq_len: 每个样本的输入窗口长度 horizon: 要预测的未来步数 step : 滑窗步长,越大样本量越小 """ X, y = [], [] for i in range(0, len(series) - seq_len - horizon + 1, step): X.append(series[i:i + seq_len]) y.append(series[i + seq_len + horizon - 1]) X = np.array(X, dtype=np.float32).reshape(-1, seq_len, 1) y = np.array(y, dtype=np.float32) return X, y

参数说明:seq_len取多少直接决定模型能看到的视野。经验上至少覆盖一个完整周期,月度数据取 12 或 24,日度业务数据取 30 到 60。horizon大于 1 时,上面这段代码预测的是窗口结束后的第horizon个点,不是逐步递归,这一点和后面的滚动预测不同。step控制样本重叠程度,训练数据量不够时可以设为 1,但要注意相邻样本高度相关,验证集指标会偏乐观。

3.2 按时间切分,禁止随机打乱

时间序列切分和普通机器学习最大的区别是:测试集必须在时间上晚于训练集。随机打乱会把未来的信息泄露到训练集里,导致验证指标虚高,上线后立刻现原形。

train_ratio, val_ratio = 0.7, 0.15 n = len(series) train_end = int(n * train_ratio) val_end = int(n * (train_ratio + val_ratio)) train_raw = series[:train_end] val_raw = series[train_end:val_end] test_raw = series[val_end:] X_train, y_train = make_windows(train_raw, seq_len=32) X_val, y_val = make_windows(val_raw, seq_len=32) X_test, y_test = make_windows(test_raw, seq_len=32)

3.3 归一化只 fit 训练集,反归一化放在最后

深度模型对输入尺度很敏感,LSTM 和 TCN 里的激活函数在输入过大或过小时梯度容易饱和。常见做法是用MinMaxScaler或者StandardScaler,但必须注意:scaler 只能在训练集上fit,然后在验证集和测试集上transform。如果在全量数据上 fit,相当于把未来统计量泄露给了训练过程。

from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() train_norm = scaler.fit_transform(train_raw.reshape(-1, 1)).flatten() val_norm = scaler.transform(val_raw.reshape(-1, 1)).flatten() test_norm = scaler.transform(test_raw.reshape(-1, 1)).flatten() X_train, y_train = make_windows(train_norm, seq_len=32) X_val, y_val = make_windows(val_norm, seq_len=32) X_test, y_test = make_windows(test_norm, seq_len=32) train_loader = DataLoader(TensorDataset( torch.tensor(X_train), torch.tensor(y_train)), batch_size=64, shuffle=True) val_loader = DataLoader(TensorDataset( torch.tensor(X_val), torch.tensor(y_val)), batch_size=256)

反归一化在计算评估指标之前做,把预测值和真实值都还原回原始尺度,再算 RMSE 或 MAPE,这样指标才是业务上能理解的意义。

4. 三个深度模型的 PyTorch 实现与关键参数

4.1 用 Transformer 编码器做短序列回归

Transformer 做时间序列不需要完整的 encoder-decoder 结构,单变量短序列预测用nn.TransformerEncoder就够。核心思路是用线性层把每个时间步的标量映射到d_model维,再经过多层自注意力提取全局依赖,最后取最后一个时间步的输出接一个回归头。

import torch import torch.nn as nn class TransformerBlock(nn.Module): def __init__(self, d_model=64, nhead=4, num_layers=2, dropout=0.1): super().__init__() self.input_proj = nn.Linear(1, d_model) encoder_layer = nn.TransformerEncoderLayer( d_model=d_model, nhead=nhead, dim_feedforward=128, dropout=dropout, batch_first=True ) self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=num_layers) self.reg_head = nn.Sequential( nn.Linear(d_model, 32), nn.ReLU(), nn.Linear(32, 1) ) def forward(self, x): # x: (B, T, 1) h = self.input_proj(x) # (B, T, d_model) h = self.encoder(h) # (B, T, d_model) return self.reg_head(h[:, -1, :]) # 取最后时间步

参数说明:d_model是注意力机制的向量维度,不用追求过大,64 到 128 在短序列上足够;nhead必须能整除d_model,4 或 8 比较常见;num_layers堆到 4 层以上收益很小,反而增加训练时间。还有一个容易踩的坑:nn.TransformerEncoder默认会对输入做 layer norm,如果数据本身已经做了归一化,dropout 要设小一点,防止叠加后把信号磨平。

4.2 LSTM 的输入形状与隐藏层设置

LSTM 的输入形状同样是(B, T, 1),但它的输出序列里每个时间步都携带了从序列开头到当前时刻的信息,取最后一个时间步的输出作为整段序列的压缩表示是标准做法。

class LSTMBlock(nn.Module): def __init__(self, input_size=1, hidden_size=64, num_layers=2, dropout=0.1): super().__init__() self.lstm = nn.LSTM( input_size, hidden_size, num_layers, batch_first=True, dropout=dropout ) self.reg_head = nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Linear(32, 1) ) def forward(self, x): # x: (B, T, 1) out, _ = self.lstm(x) # out: (B, T, hidden_size) return self.reg_head(out[:, -1, :])

参数说明:hidden_size决定 LSTM 的记忆容量,太小记不住波动模式,太大容易过拟合,64 到 128 是安全区间。num_layers为 2 层时效果比 1 层明显好,但到 3 层以上收益递减且训练时间翻倍。batch_first=True必须带上,否则输入输出维度对不上。LSTM 对梯度爆炸比较敏感,训练时建议配合梯度裁剪clip_grad_norm_,这是很多 lstm 时间序列预测 python 实战里最终救场的操作。

4.3 TCN 的因果卷积与膨胀感受野

TCN 的构建分两步:先实现因果卷积,保证未来信息不会泄漏到过去;再按 1、2、4、8 的膨胀率堆叠,让感受野指数级扩大。

import torch.nn.functional as F class CausalConv1d(nn.Module): def __init__(self, in_channels, out_channels, kernel_size, dilation): super().__init__() self.padding = (kernel_size - 1) * dilation self.conv = nn.Conv1d(in_channels, out_channels, kernel_size, dilation=dilation) def forward(self, x): # 左侧补零,保证输出长度不变,且只看过去 x = F.pad(x, (self.padding, 0)) return self.conv(x) class TCNBlock(nn.Module): def __init__(self, input_size=1, channels=32, levels=4, kernel_size=3, dropout=0.1): super().__init__() self.project = nn.Conv1d(input_size, channels, 1) layers = [] for i in range(levels): dilation = 2 ** i layers.append(CausalConv1d(channels, channels, kernel_size, dilation)) layers.append(nn.ReLU()) layers.append(nn.Dropout(dropout)) self.tower = nn.Sequential(*layers) def forward(self, x): # x: (B, T, 1) -> 转成 (B, 1, T) 进 Conv1d x = x.permute(0, 2, 1) h = self.project(x) h = self.tower(h) return h[:, :, -1] # 取最后一个位置

参数说明:kernel_sizelevels共同决定感受野,计算公式是1 + sum((kernel_size - 1) * 2^i),3 和 4 的组合对应感受野 31,刚好覆盖 32 的窗口长度。如果窗口加长到了 64,levels要相应增加到 5 或 6。TCN 里 dropout 建议保留 0.1 左右,因为卷积层之间没有类似 LSTM 的隐式正则。

这三个模型在源码里通常各自独立成文件,训练时也分别保存权重,方便后面调 XGBoost 集成时灵活替换个别模型。模型之间没有任何共享参数,耦合点只在特征层面。

5. 训练、验证与 Stacking 集成流程

5.1 一个通用的深度模型训练循环

三个模型的训练逻辑几乎一样,只差模型实例,因此可以直接用一个函数覆盖。

import numpy as np def fit_deep_model(model, train_loader, val_loader, epochs=80, lr=1e-3, patience=10): opt = torch.optim.Adam(model.parameters(), lr=lr) sched = torch.optim.lr_scheduler.ReduceLROnPlateau( opt, mode="min", factor=0.5, patience=5) loss_fn = nn.MSELoss() best_loss = float("inf") bad_epochs = 0 for epoch in range(epochs): model.train() for xb, yb in train_loader: opt.zero_grad() loss = loss_fn(model(xb).squeeze(), yb) loss.backward() # LSTM 梯度裁剪,Transformer 和 TCN 可省略 torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0) opt.step() model.eval() val_losses = [] with torch.no_grad(): for xb, yb in val_loader: val_losses.append(loss_fn(model(xb).squeeze(), yb).item()) avg_val = float(np.mean(val_losses)) sched.step(avg_val) if avg_val < best_loss: best_loss = avg_val bad_epochs = 0 torch.save(model.state_dict(), f"{model.__class__.__name__}.pt") else: bad_epochs += 1 if bad_epochs >= patience: break return best_loss

逻辑说明:每个 epoch 先跑训练集更新权重,再跑验证集计算损失;ReduceLROnPlateau在验证损失连续 5 个 epoch 不下降时把学习率减半,patience是早停阈值,避免在验证集上过拟合。三个模型用同一套循环分别训练即可,训练顺序没有要求。

5.2 用验证集预测结果组装 XGBoost 训练特征

三个模型训练完成后,冻结权重,在验证集上各自输出预测值,再用这些预测值构造 XGBoost 的训练特征。

import xgboost as xgb def stack_features(models, X_window, raw_last): """ models : [transformer_model, lstm_model, tcn_model] X_window : 归一化后的滑窗输入 (N, T, 1) raw_last : 原始尺度下窗口最后一个值 (N,) """ feats = [] for m in models: m.eval() with torch.no_grad(): pred = m(X_window).cpu().numpy().flatten() feats.append(pred) # 三个深度模型的预测 feats = np.column_stack(feats) feats = np.column_stack([ feats, raw_last.reshape(-1, 1), # 当前最新值 ]) return feats X_val_tensor = torch.tensor(X_val) feats_val = stack_features( [transformer_model, lstm_model, tcn_model], X_val_tensor, val_raw[31:-1]) dtrain = xgb.DMatrix(feats_train, label=y_train) dval = xgb.DMatrix(feats_val, label=y_val) params = { "objective": "reg:squarederror", "max_depth": 4, "eta": 0.05, "subsample": 0.8, "colsample_bytree": 0.8, "min_child_weight": 4, } bst = xgb.train( params, dtrain, num_boost_round=500, evals=[(dval, "val")], early_stopping_rounds=30, verbose_eval=50 )

参数说明:max_depth=4控制树复杂度,混合预测场景下特征只有 4 到 5 个,树太深只会记住验证集的噪声。eta=0.05是学习率,小学习率配合 500 棵树是比较稳的组合。subsample=0.8colsample_bytree=0.8是随机采样比例,降低树与树之间的相关性。early_stopping_rounds=30表示验证集损失连续 30 轮不下降就停止训练,省时间也防止过拟合。

5.3 超参速查表与调参顺序

下面这张表是四个模型的核心超参起点,后续调参按“先深度模型后 XGBoost”的顺序进行,不要一开始就同时调所有参数。

模型参数推荐初值调整方向
Transformerd_model64序列变长时加大到 128
Transformernhead4跟随 d_model 同步调整
LSTMhidden_size64数据波动复杂时加大到 128
LSTMnum_layers2数据量充足时可试 3
TCNlevels4感受野要超过主周期
XGBoostmax_depth4特征增多时可试 5 或 6
XGBoosteta0.05欠拟合时先加树,不减学习率

调参顺序的常见做法是:先用默认参数训练三个深度模型,保证验证集上各自不出现明显欠拟合;然后固定深度模型权重,集中调 XGBoost 的max_depthmin_child_weight;最后如果验证集 RMSE 还有明显下降空间,再回头微调seq_len和 TCN 的膨胀层数。调参过程中始终以验证集为准,测试集只在最终评估时碰一次。

6. 落地技巧:滚动预测、残差修正与模型导出

6.1 递归多步预测的误差累积

上面所有设计都是单步预测。要做多步预测,最直接的办法是递归式预测:把上一步的预测值补到窗口末尾,丢掉窗口第一个值,继续预测下一步。这个方法实现简单,但误差会累积,业务上步数越长越偏。下面是一个带残差修正的版本。

def recursive_forecast(models, xgb_model, history, horizon, scaler): """ history : 归一化后的历史窗口,长度 seq_len horizon : 要预测的步数 """ preds = [] for _ in range(horizon): win = history[-seq_len:].reshape(1, seq_len, 1) win_tensor = torch.tensor(win, dtype=torch.float32) feats = stack_features(models, win_tensor, win[:, -1, 0]) d = xgb.DMatrix(feats) pred_norm = xgb_model.predict(d)[0] # 残差修正:加一个单调递减的校正项 residual_adj = 0.0 if len(preds) > 0: residual_adj = -0.05 * float(preds[-1] - win[:, -1, 0]) preds.append(pred_norm + residual_adj) history = np.append(history, pred_norm) preds_raw = scaler.inverse_transform( np.array(preds).reshape(-1, 1)).flatten() return preds_raw

残差修正的思路是:如果上一步预测偏高,说明模型当前有正向偏差,下一步适度回调一点,这里用的是固定系数 0.05,实际使用时可以在验证集上调这个系数。它不是严格意义上的统计校正,但在业务预演场景里能明显改善多步预测的漂移问题。

6.2 模型导出与加载

深度模型用torch.save存权重,XGBoost 用原生接口存模型文件。加载时注意两个坑:一是深度模型必须先实例化再加载state_dict,结构要和保存时完全一致;二是MinMaxScaler也要保存,否则线上预测时无法做归一化和反归一化。

# 保存 bst.save_model("xgb_model.json") torch.save(transformer_model.state_dict(), "transformer.pt") joblib.dump(scaler, "scaler.pkl") # 加载 bst = xgb.Booster() bst.load_model("xgb_model.json") transformer_model = TransformerBlock(d_model=64, nhead=4) transformer_model.load_state_dict(torch.load("transformer.pt")) transformer_model.eval()

验证加载结果是否正确,最简单的方法是拿测试集第一个样本跑一次 predict,对比保存前的输出。数值完全一致说明链路正常,有一点小误差也正常,因为 dropout 在 eval 模式下已经关闭。

6.3 一个验证技巧:在测试集上分别看单模型和集成的误差

集成模型最大的风险是过度依赖某个单一模型,导致 XGBoost 形同虚设。验证方法很简单:在测试集上分别计算 Transformer、LSTM、TCN、XGBoost 集成四者的 RMSE。正常情况是集成结果最好,前三者各有千秋。如果集成结果和某个深度模型几乎一样,说明 XGBoost 的特征贡献不够,检查一下是否只用了三个模型的预测值而没加原始滞后特征。最后一招是直接用xgb.plot_importance画出特征重要性,三个特征重要性得分应该相对均匀,如果某一个占了 80% 以上,就值得回看那个模型的训练过程是不是出了问题。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 15:37:57

深圳全网站建设公司速查手册:域名服务器选型避坑

深圳全网站建设公司速查手册:域名服务器选型避坑 域名和服务器,这俩词儿是不是让你头大?很多老板找深圳全网站建设公司时,一听到“云主机”、“CDN”、“DNS解析”就懵圈。别慌,这篇速查手册就是为你写的。咱们不整虚的,直接上干货。在珠三角做业务,网络环境的稳定性直接决定客户体验。如果你还在纠结是买阿里…

作者头像 李华
网站建设 2026/9/15 15:33:42

如何用 MNN qwen3_tts_demo 运行 Qwen3-TTS 文本转语音?

如何用 MNN qwen3_tts_demo 运行 Qwen3-TTS 文本转语音&#xff1f; 【免费下载链接】MNN MNN: A blazing-fast, lightweight inference engine battle-tested by Alibaba, powering high-performance on-device LLMs and Edge AI. 项目地址: https://gitcode.com/GitHub_Tre…

作者头像 李华