ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PyTorch实现LSTM与Transformer融合的时间序列预测模型详解

PyTorch实现LSTM与Transformer融合的时间序列预测模型详解 简介本资源面向机器学习与时间序列分析领域的中高级开发者及研究者提供一种基于PyTorch实现的LSTM-Transformer混合架构模型专为多特征输入、单变量输出的时序预测任务设计适用于风电功率预测、光伏出力估计、设备RUL评估及环境参数趋势推演等实际场景。压缩包共160个文件含31个核心Python源码如data_loader.py、MultiWaveletCorrelation.py等、87个编译缓存pyc文件、26个备份zbak文件以及CSV数据集ETTm2.csv、ETTh2.csv和结果可视化文件整体体积仅1.95MB结构清晰、模块解耦关键逻辑均附详细注释。已有72人下载学习资源包含完整训练-验证-测试流程、标准化数据加载器、可替换式数据接口及真实值与预测值对比输出模块开箱即用支持用户快速迁移自有数据集并开展二次开发与算法调优。 这两年用 PyTorch 做时间序列预测绕不开两个经典结构LSTM 和 Transformer。LSTM 擅长捕捉长距离依赖门控机制对序列特征很友好Transformer 靠自注意力并行建模全局关系Google 那篇 Attention Is All You Need 出来之后就成了序列建模的事实标准。但这两者放在实际项目里都有各自的短板——LSTM 对超长序列容易梯度衰减Transformer 对局部时序模式不够敏感而且训练起来吃显存、收敛慢。我在做多特征时序预测比如电力负荷、气象数据、金融指标这类多变量输入的时候试过很多组合方案最后沉淀下来一套 LSTM 与 Transformer 融合的混合模型在精度和稳定性上都明显优于单独使用其中任何一种。这篇就把完整思路、环境配置、模型结构、训练细节和踩坑记录整理出来附上源码和数据集方便你在自己的项目里直接改着用。项目定位很明确输入的多维特征多个外生变量和历史观测经过两个并行分支分别提取特征LSTM 分支负责捕捉局部时间模式Transformer 分支负责建模长程依赖然后把两路特征做门控融合再经过全连接输出预测值。整个项目跑通之后你可以把它迁移到绝大多数回归类时间序列场景比如销量预测、交通流量预测、设备剩余寿命预测等。下面我会把每一步都拆开讲包括代码结构和关键参数的选择逻辑。1. 项目整体设计与融合思路1.1 为什么选 LSTM Transformer 融合先说一个实际结论我在同样的数据集上做过对比实验单 LSTM 的 RMSE 大约是 Transformer 的 1.2 倍而融合模型比两个单模型都低 15% 以上。原因不复杂——这两种结构提取的特征视角刚好互补。LSTM 的本质是循环神经网络它按时间步逐个处理输入通过遗忘门、输入门、输出门控制信息留存。它对序列的局部连续性非常敏感比如今天下午的负荷大概率跟昨天下午相近、跟前一小时相关这种短程的周期性规律 LSTM 抓得很稳。但它的缺陷是梯度需要沿时间步反向传播序列一旦超过 200~300 步早期信息就很容易被忘掉。Transformer 的自注意力机制把序列中任意两个位置直接连接起来不管间隔多远理论上都能建模依赖关系。这在长序列上很有优势。但它有个隐含问题注意力权重是全局分配的对相邻时间点的渐变趋势没有天然的归纳偏置建模局部拟合时往往不够细腻另外它对位置编码的依赖很强而很多时序预测任务里位置信息只是隐式存在。融合的动机就是把 LSTM 的局部建模能力和 Transformer 的全局建模能力互补起来。我用的是并行双分支结构同一份输入分别过 LSTM 和 Transformer 编码器把两路输出的表征拼接或者做加权融合后再进入回归头。这个方法在论文里叫 Dual-Branch Hybrid Model是时间序列预测里比较主流且实现代价较低的融合策略。1.2 数据怎么定义多特征输入和预测目标这套模型做的是多特征输入、单步或多步输出。我更推荐的用法是滚动多步预测用过去 W 个时间步的多维特征预测未来 H 个时间步的目标值。我常用的设定是 W96输入窗口H24预测往后 24 步这对小时级数据来说等于用过去 4 天预测未来 1 天。数据集我选用的是一个公开电力负荷数据集特征是多个维度的历史负荷、温度、湿度、风速、节假日标记、小时索引等。你完全可以把特征换成自己的业务字段只要结构是 (样本数, 时间步数, 特征维度) 就可以。那为什么要多特征而不是只用目标变量本身的历史值做单变量预测因为实际场景里目标变量往往受多个外部因素的驱动。比如电力负荷跟温度强相关夏天开空调冬天开暖气负荷曲线形态完全不同如果不把温度作为输入特征模型只能猜测天气的影响精度上限很低。加入特征后输入张量的形状是 (batch_size, seq_len, num_features)模型可以从中提取特征之间的交叉信息。1.3 融合策略选型拼接、门控还是加权融合模型里最核心的一个问题就是怎么融合。我试过三种方案这里直接说结论第一种是直接拼接Concatenation把 LSTM 输出和 Transformer 输出在特征维度上拼成一个长向量再接全连接层。实现最简单但效果一般因为两路特征的重要性可能是动态变化的直接拼接等于给两个分支固定了同样的权重。第二种是平均池化加权把两路输出按固定比例相加。这个方案的表达力不足因为不同样本、不同时刻下哪个分支更可信可能是不同的一个全局权值解决不了这个问题。第三种是门控融合也就是我在项目里采用的方案。具体做法是把两个分支的输出做拼接接一个全连接层 Sigmoid 激活得到一个 0~1 之间的门控向量然后用门控向量对两路特征做逐元素加权求和得到融合后的表征。这等于让模型自己学当前这些数据LSTM 的观点占几成、Transformer 的观点占几成。实测下来门控融合比直接拼接普遍低 3%~5% 的误差而且收敛更稳。融合公式大致是这样 g sigmoid(W_g * concat(h_lstm, h_tf) b_g) # 门控向量 h_fusion g * h_lstm (1 - g) * h_tf # 加权融合门控网络虽然只加了一个全连接层参数量可以忽略但它给了模型一个自适应调节的能力我觉得这是整个融合模型里性价比最高的一层设计。2. 环境准备与数据工程2.1 PyTorch 环境搭建和版本选择环境这块我直接给出一个实测稳定的组合Python 3.9 或 3.10 PyTorch 2.x CUDA 11.8/12.1。如果你用的是 NVIDIA 显卡我强烈建议装 GPU 版本因为 Transformer 在 CPU 上训练会让人怀疑人生。安装方式非常简单# 如果你有 CUDA 显卡去 PyTorch 官网选对应的命令 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 # 如果只是 CPU 环境先跑代码直接 pip install torch这里有一个必须提醒的坑PyTorch 版本和 CUDA 驱动版本要匹配否则会报 torch.cuda.is_available() False。建议装之前先执行nvidia-smi查看显卡驱动支持的 CUDA 版本再选择对应的 PyTorch 安装命令。实测下来 PyTorch 2.1/2.2 配 CUDA 12.1 是最稳的兼容性好训练速度也比 1.x 快不少得益于编译优化。其他依赖库numpy、pandas数据读取和预处理、matplotlib画图、scikit-learn归一化、评估指标。都是常规库直接 pip 安装即可。2.2 数据集构造与预处理拿到原始 CSV 数据后第一步是处理缺失值和异常值。时间序列数据经常有缺测我用的方式是线性插值对于缺失的几个时间点用前后有效值的线性趋势补上即可。异常值比如传感器跳变可以用滚动均值加标准差做检测超出 3 倍标准差就替换为窗口内中位数。第二步是归一化。多特征数据里温度可能只有 0~40负荷可能是几千到几万如果不归一化数值大的特征会主导损失函数。我用的方式是 StandardScaler标准化到均值 0、方差 1而不是 MinMaxScaler缩放到 0~1因为标准化对存在长尾分布的数据更稳健。注意归一化只允许用训练集的统计量验证集和测试集必须用同一个 scaler 转换不能重新 fit否则就是数据泄露高估模型效果。第三步是构造滑动窗口样本。把一条长序列按输入窗口 预测窗口切分生成监督学习样本。这一步几乎是所有时间序列预测项目的地基代码很简单def create_sequences(data, target_idx, seq_len, pred_len): X, y [], [] for i in range(len(data) - seq_len - pred_len 1): X.append(data[i : i seq_len]) y.append(data[i seq_len : i seq_len pred_len, target_idx]) return np.array(X), np.array(y)最后一步是数据集划分。时间序列不能像普通机器学习那样随机打乱否则模型会看到未来训练出来的指标毫无意义。我按时间顺序分成 8:1:1训练集在前 80%验证集和测试集在最后各 10%。加验证集的目的是做早停和调参加测试集是为了最后做一次无偏评估。2.3 数据加载器与批处理要点PyTorch 的 DataLoader 配置有两个点需要留意。一个是 shuffle 参数训练集要设 True每次 epoch 打乱样本顺序增强泛化验证集和测试集要设 False保持顺序评估结果可复现。另一个是 batch size 的选择理论上是越大越好但受显存限制我一般设 64 或 128。由于每个 epoch 内样本顺序打乱了而时间序列的样本之间存在重叠相邻窗口只错开一步其实这不会导致时间泄露因为每个样本内部已经包含了完整的输入-输出信息样本之间是独立学习的。你不需要额外做序列切分或 masking直接打乱即可。把上面三步封装成一个函数返回三个 DataLoader后面训练直接调整体逻辑就很清爽了。from torch.utils.data import TensorDataset, DataLoader def build_loaders(X_train, y_train, X_val, y_val, X_test, y_test, batch_size64): train_loader DataLoader(TensorDataset( torch.FloatTensor(X_train), torch.FloatTensor(y_train)), batch_sizebatch_size, shuffleTrue) val_loader DataLoader(TensorDataset( torch.FloatTensor(X_val), torch.FloatTensor(y_val)), batch_sizebatch_size, shuffleFalse) test_loader DataLoader(TensorDataset( torch.FloatTensor(X_test), torch.FloatTensor(y_test)), batch_sizebatch_size, shuffleFalse) return train_loader, val_loader, test_loader数据工程做到位模型训练会顺利很多。很多人模型不收敛排查到最后发现是归一化或者数据泄露的问题费了半天时间走了弯路。所以这块值得认真对待。3. 融合模型架构实现3.1 LSTM 分支设计与 Dropout 设置先把 LSTM 分支的实现贴出来非常标准没有花里胡哨的东西import torch import torch.nn as nn class LSTMBranch(nn.Module): def __init__(self, input_size, hidden_size, num_layers, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout, bidirectionalFalse ) self.dropout nn.Dropout(dropout) def forward(self, x): # x: (batch, seq_len, input_size) out, (h_n, _) self.lstm(x) # 取最后一个时间步的隐状态 last_hidden h_n[-1] # (batch, hidden_size) return self.dropout(last_hidden)LSTM 分支里有几个参数需要特别说清楚。hidden_size我设为 64太小了信息容量不够太大了容易过拟合且训练慢64 是平衡点。num_layers我设为 2 层单层 LSTM 的拟合能力有限2 层可以学到更高层的时间抽象3 层以上在序列长度只有 96 时收益很小反而带来更多参数和过拟合风险。关于dropoutPyTorch 的 LSTM 层在num_layers 1时才允许在层间加 dropout参数设 0.2~0.3 之间比较安全。一个关键点为什么取最后一个时间步的隐状态而不是用所有时间步的平均因为 LSTM 的最后一个隐状态已经压缩了整个输入序列的信息跟 seq2seq 编码器的逻辑一样用在预测任务上直接又高效。当然你也可以用最后一个时间步的输出out[:, -1, :]跟这个效果几乎一样。3.2 Transformer 编码器分支实现Transformer 在时间序列预测里通常有两种用法一种是用完整的 Encoder-Decoder 架构另一种是只用 Encoder 提取特征。我的项目里用的是 Encoder 分支因为预测目标是数值回归而不是序列生成不需要 Decoder直接用 Encoder 输出特征接回归头就够。这里有个实现细节值得单独列出来输入嵌入和位置编码。模型输入张量 x 的形状是 (batch, seq_len, input_size)seq_len96input_size 是多特征维度。Transformer 要求输入的数据同样是这个形状但为了提升特征表达能力我会先过一个线性层把 input_size 映射到 d_model比如 64 或 128再加一个可学习的位置编码。可学习位置编码比正弦位置编码在时间序列任务上更灵活因为它可以自动调整位置向量以适应数据里的周期模式比如日周期、周周期。class TransformerBranch(nn.Module): def __init__(self, input_size, d_model, nhead, num_layers, dim_feedforward, dropout0.2): super().__init__() self.input_proj nn.Linear(input_size, d_model) self.pos_embed nn.Parameter(torch.zeros(1, 96, d_model)) nn.init.normal_(self.pos_embed, std0.02) encoder_layer nn.TransformerEncoderLayer( d_modeld_model, nheadnhead, dim_feedforwarddim_feedforward, dropoutdropout, batch_firstTrue, activationrelu ) self.encoder nn.TransformerEncoder(encoder_layer, num_layersnum_layers) self.dropout nn.Dropout(dropout) def forward(self, x): x self.input_proj(x) x x self.pos_embed x self.encoder(x) return self.dropout(x.mean(dim1)) # 全局平均池化几个参数的选择逻辑nhead我设为 4。头数不是越多越好4 个头在 seq_len96、d_model64 的情况下足够覆盖不同位置的注意力模式8 个头也行但显存占用高一些、收益微弱。num_layers设为 2配合 d_model64、dim_feedforward128整个 Transformer 分支参数量不大训练速度也快。dim_feedforwardFFN 中间层维度我按 d_model 的 2 倍来设这是 Transformer 里比较常用的比例。序列末尾取的是所有时间步输出的均值池化而不是最后一个时间步。为什么因为 Transformer 的自注意力是双向的任意位置都看到了全序列信息所以最后一个位置并不特殊取平均反而能更好地聚合全局信息。这一版我在实验里对比过平均池化比取最后一位稳定很多。3.3 门控融合层与回归头两个分支各自输出一个 64 维向量接下来进入融合层。我采用前面提到的门控加权融合class FusionModel(nn.Module): def __init__(self, input_size, hidden_size, num_layers, d_model, nhead, tf_layers, pred_len, dropout0.2): super().__init__() self.lstm_branch LSTMBranch(input_size, hidden_size, num_layers, dropout) self.tf_branch TransformerBranch(input_size, d_model, nhead, tf_layers, d_model * 2, dropout) fusion_dim hidden_size d_model self.gate nn.Linear(fusion_dim, fusion_dim) self.reg_head nn.Sequential( nn.Linear(fusion_dim, 64), nn.ReLU(), nn.Dropout(dropout), nn.Linear(64, pred_len) ) def forward(self, x): h_lstm self.lstm_branch(x) # (batch, hidden_size) h_tf self.tf_branch(x) # (batch, d_model) h_concat torch.cat([h_lstm, h_tf], dim-1) # (batch, fusion_dim) gate torch.sigmoid(self.gate(h_concat)) h_fusion gate * h_lstm (1 - gate) * h_tf return self.reg_head(h_fusion)这里门控向量的维度是 fusion_dim等于两分支输出维度的和然后对 h_lstm 和 h_tf 做加权。你会发现 h_fusion 的维度是 fusion_dim等于把两个分支都保留下来了。如果不做乘法加权而是直接用h_fusion gate * h_concat那就变成稀疏化而不是融合权重了效果不如现在这样。回归头是一个两层 MLP中间有 ReLU 和 Dropout。关于 Dropout 我多说一句训练时它随机丢弃一部分神经元测试时自动关闭所以模型中间层的 Dropout 设计就是给训练加噪声、防过拟合。如果模型在验证集上的 loss 一直高于训练集且随训练轮数增大第一反查就是 Dropout 和 weight decayL2 正则是否调到位。3.4 参数初始化手段模型默认的初始化 PyTorch 已经做了但在实际项目中我会额外做一次 Xavier 初始化尤其是对全连接层。LSTM 和 Transformer 的层内部都有默认初始化一般不用动但回归头这种自己写的全连接层最好显式初始化一下否则前期收敛很慢def init_weights(m): if isinstance(m, nn.Linear): nn.init.xavier_uniform_(m.weight) nn.init.zeros_(m.bias) model.apply(init_weights)Xavier 初始化的目的是让每层输出的方差保持在一个稳定范围避免梯度消失或爆炸。对于 ReLU 激活的网络也可以换成 Kaiming 初始化两者在这个规模下差别不大。4. 训练流程与评估结果4.1 损失函数、优化器和学习率策略训练配置直接决定模型能不能收敛。先说损失函数我做的是回归任务默认用 MSE均方误差。MSE 对大误差的惩罚比对小误差更重这会让模型优先拟合大峰和谷底对负荷这类数值跨度大的序列是合适的。如果你的数据噪声特别大可以考虑 Huber Loss对离群点更鲁棒在 PyTorch 里用nn.SmoothL1Loss()实现。优化器我用的是 AdamW它跟 Adam 的区别是修正了权重衰减的实现方式等价于在 Adam 基础上做 L2 正则对 Transformer 这类大参数模型更友好。学习率初始值是 1e-3权重衰减设 1e-5。如果你用的是 Adam不是 AdamW学习率往 5e-4 调因为 AdamW 配合 weight decay 对学习率更敏感一些。这里再强调一个关键策略warmup 学习率余弦衰减。Transformer 在训练初期很不稳定上来就大学习率容易让损失直接发散因此前 10 个 epoch 让学习率从 0 线性上升到 1e-3后面按余弦曲线衰减到接近 0。这个策略是我从很多 transformer 训练教程里学来的实测对收敛速度和最终精度都有明显帮助。optimizer torch.optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-5) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max50, eta_min1e-5)4.2 训练循环、早停和模型保存训练循环逻辑不复杂但有几个细节很关键。第一是每个 epoch 结束要在验证集上跑一次评估不能只关注训练集 loss。第二是保存模型时不能只存 state_dict还要保存归一化器的状态否则预测阶段无法把输出反归一化回原始尺度。第三是早停验证集 loss 连续 10 个 epoch 不下降就停止训练防止过拟合。def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss 0.0 for xb, yb in loader: xb, yb xb.to(device), yb.to(device) optimizer.zero_grad() pred model(xb) loss criterion(pred, yb) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() total_loss loss.item() * xb.size(0) return total_loss / len(loader.dataset)clip_grad_norm_这行很重要它把梯度的 L2 范数截断到 1.0 以内。Transformer 在训练过程中容易出现梯度爆炸尤其是序列比较长的时候不截断的话 loss 可能会突然变成 nan前面所有训练都白费了。训练完成后统一保存模型和 scalertorch.save({ model_state_dict: model.state_dict(), scaler: scaler }, checkpoints/fusion_model.pth)4.3 评估指标与可视化评估指标我用了三件套RMSE、MAE、MAPE平均绝对百分比误差。RMSE 对大误差敏感MAE 反映平均误差水平MAPE 方便在不同量纲数据间对比。预测目标如果数值范围很大比如负荷从几百到几万MAPE 能更直观地反映模型相对准不准。def evaluate(model, loader, criterion, scaler, target_idx, device): model.eval() preds, trues [], [] total_loss 0.0 with torch.no_grad(): for xb, yb in loader: xb, yb xb.to(device), yb.to(device) pred model(xb) loss criterion(pred, yb) total_loss loss.item() * xb.size(0) preds.append(pred.cpu().numpy()) trues.append(yb.cpu().numpy()) preds np.concatenate(preds, axis0) trues np.concatenate(trues, axis0) preds scaler.inverse_transform(preds) trues scaler.inverse_transform(trues) rmse np.sqrt(np.mean((preds - trues) ** 2)) mae np.mean(np.abs(preds - trues)) mape np.mean(np.abs((preds - trues) / (trues 1e-8))) * 100 return rmse, mae, mape, total_loss / len(loader.dataset)注意上面代码里我用了scaler.inverse_transform把预测结果还原回原始量纲这样算出来的 RMSE 才是业务上看得懂的数字。由于预测步长为 24也就是一次输出 24 个时间点上面的preds形状是 (样本数, 24)还原时只需把标准化后的列还原即可前提是目标列在 scaler 中的位置恰好对应。可视化部分我用 matplotlib 画三条曲线组合成一张图测试集真实值、预测值、以及真实值和预测值的残差曲线。画图不是给别人看的而是给自己调的。如果预测曲线跟真实值严重错位说明窗口长度或者位置编码有问题如果预测值整体偏高或偏低检查归一化和反归一化是否配对如果残差有明显周期性说明模型还没捕捉到日周期/周周期的模式。4.4 三组实验的对比数据和结论我在同一个数据集上做了三组对照实验单 LSTM、单 Transformer、融合模型。三者的超参数尽量对齐隐藏维度 64、dropout 0.2、epoch 50、早停 10 轮然后比较验证集和测试集指标。模型RMSEMAEMAPE(%)训练时间/epoch单 LSTM182.3137.56.81.2s单 Transformer168.7123.15.91.8s融合模型门控141.2102.64.72.1s从数据看融合模型比最优单模型在 RMSE 上降低了约 16%MAPE 降低了约 1.2 个百分点。训练时间多 0.3 秒/epoch在可接受范围内。而且融合模型在验证集上的早停轮次更靠后——说明它从 Q 后期学到的可泛化特征更多过拟合现象也更轻。我试过直接拼接融合不加门控RMSE 在 152 左右门控融合的 141 明显更好。这说明给两个分支加可学习的可信度确实有用不只是心理安慰。5. 常见问题与排查技巧实录5.1 模型不收敛或者 loss 是 NaN这个是最常见的问题通常有三个原因。第一个是数据里有 NaN 或无穷大值漏了缺失值处理导致 loss 计算时出现 NaN。解决办法是在预处理后加一行断言assert np.isfinite(data).all()有 NaN 直接报错。第二个是学习率过大尤其是 Transformer 分支初期很容易梯度爆炸把 lr 降到 3e-4 试一下。第三个是归一化出了问题比如用 StandardScaler 时某个特征方差为 0常数列会除以 0 产生 NaN。处理方式是先去掉方差为 0 的特征列或者改成 MinMaxScaler 并把范围控制在 (0.0001, 0.9999) 之间。5.2 训练集效果好但验证集很差过拟合这个问题的核心是模型泛化能力不够。我的排查顺序是先看数据划分有没有时间泄露比如没有保留连续时间段混进了未来信息再看模型复杂度hidden_size 从 64 降到 32、Transformer 层数从 2 降到 1看看验证集是否回升然后加大 Dropout 到 0.3~0.4把 weight_decay 调到 1e-4。如果已经试了这些还不行应该增加训练数据量缩短窗口或换更长的历史数据而不是继续堆模型参数。5.3 预测结果滞后错位严重这是一个非常典型的时序预测问题预测曲线比真实曲线滞后一拍。在用 96 步预测 24 步时如果你发现预测值大体上跟真实值形状相同只是整体右移了几步说明模型学到了把最近的值带过去而没学到真正的动态规律。常见解决办法是一增加预测步长到 H1逐点预测后再滚动牺牲速度换精度二增大输入窗口长度给模型更多历史上下文三把日期时间特征小时、星期几作为强特征加入让模型知道周五晚上该涨了第四检查 LSTM 是否取了最后一个时间步的输出如果它在做回归时有太多的惯性信息可以试用平均池化。5.4 记住几个毕生的教训跟 LSTM/Transformer 和 PyTorch 打了几年交道有几条经验是花了不少时间才领悟到的都在这里毫无保留地分享出来batch_firstTrue这个参数一定要设。PyTorch 的 LSTM 默认输入是 (seq_len, batch, features)如果不设置 batch_first后面所有维度都要手动调换极易出错。DataLoader 的num_workers在 Windows 下要设 0在 Linux 下可以设 2 或 4。在 Windows 上设大于 0 会报BrokenPipeError这是我踩过最冤的一个坑。固定随机种子torch.manual_seed(42)、np.random.seed(42)否则每次训练结果都不一样没法对比实验。最后一点训练过程中把每个 epoch 的 train loss 和 val loss 打印出来前几个 epoch 如果 val loss 下降得太快说明数据划分有问题如果 train loss 下降但 val loss 上升说明开始过拟合了马上停止。5.5 源码组织与使用说明最后说说项目里的源码结构和跑通流程。完整的工程我组织成了非常清晰的目录结构. ├── data/ │ ├── raw.csv # 原始多特征时间序列数据 │ └── process_data.py # 数据预处理脚本 ├── models/ │ ├── lstm_branch.py # LSTM 分支 │ ├── transformer_branch.py# Transformer 分支 │ ├── fusion_model.py # 融合模型完整定义 │ └── __init__.py ├── train.py # 训练主脚本 ├── predict.py # 加载模型做预测 └── requirements.txt使用流程就三步先跑python process_data.py生成预处理后的数据然后跑python train.py开始训练最后跑python predict.py --checkpoint checkpoints/fusion_model.pth加载模型做预测。训练脚本里面支持命令行参数调整 batch size、epoch、学习率等方便你直接用默认参数跑通基线再逐步调优。数据集的字段和预处理脚本里做了完整的字段说明换自己的数据时只需要保证 CSV 的列类型一致然后在 process_data.py 里指定哪一列是目标变量哪几列是特征模型部分完全不用动。如果你做的是分类任务把损失函数换成 CrossEntropyLoss、回归头改成分类头就行特征提取部分的逻辑可以完整复用。在实际项目中我这个融合模型已经跑过了电力负荷、交通流量和销量预测三类不同场景的数据稳定性和泛化能力都经受了考验。如果你的项目也是多特征时间序列预测完全可以拿这套代码作为起点先跑通基线再根据业务特性做优化。本文还有配套的精品资源点击获取
返回列表