尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Transformer时间序列预测实战:从自注意力到Pytorch多步预测

Transformer时间序列预测实战:从自注意力到Pytorch多步预测 简介时间序列预测是数据科学中的核心任务广泛应用于电力负荷估计、交通流量预测与工业监控等场景。传统循环神经网络如LSTM在处理长序列时存在信息衰减与串行计算瓶颈难以充分捕捉远距离依赖关系。Transformer凭借自注意力机制能够在一次前向传播中直接建立序列任意位置间的关联有效缓解了长程依赖问题成为时间序列建模的新利器。其原理包括多头注意力、位置编码等关键设计通过全局信息感知与并行计算优势显著提升了预测精度与训练效率。在工程实践中基于Pytorch框架可实现单步预测与多步预测涵盖数据归一化、滑动窗口构造、模型训练与调参等完整流程。面对不同预测步长迭代、直接映射与Seq2Seq等策略各有取舍合理选择与配置超参数是落地应用的关键。本文围绕Transformer时间序列预测的实战方案详细展示Pytorch完整源码与数据实验帮助开发者快速搭建高性能预测模型。1. Transformer凭什么做时间序列预测从注意力机制说起1.1 为什么是Transformer而不是LSTM先说我为什么会写这个项目。我大概在两年前开始用LSTM做时间序列预测最初觉得效果还行但随着预测序列长度增加、数据本身带有较强周期性依赖时LSTM的预测质量明显下滑。究其原因LSTM本质上是个循环结构当前时刻的隐状态依赖于上一时刻的输出这样的串行处理方式使得它在捕获长距离依赖时存在信息衰减。你训练一个LSTM预测未来十步前几步还能看后面几步基本就是一条平线这种情况在很多做电力负荷预测、交通流量预测的朋友那里应该非常常见。后来我转向Transformer最直接的感受是自注意力机制能在一次前向计算中让序列中任意两个位置直接建立关联不存在LSTM那种逐步传递导致的信息衰减。这意味着模型能直接看到几天前、几周前的相似模式然后利用这些模式决定预测值。这个项目标题写的是Transformer时间序列预测单步、多步实验Pytorch完整源码和数据说白了就是把我踩坑后的最终方案整理成一套可复跑、可扩展的完整代码库覆盖两种预测任务单步预测用过去N个时间点预测下一个时间点和多步预测用过去N个时间点预测未来M个时间点。数据我准备了合成数据和一个真实场景数据集方便你直接看效果。1.2 自注意力机制为什么它适合序列建模我尽量用通俗的方式解释。自注意力做的事情可以类比成你手里有一堆历史观察点现在要对未来做一个判断你会根据当前聚焦的问题在自己记忆里找出最相关的几个历史片段参考它们做决定。具体点说对于一个长度为L的输入序列自注意力机制会给每个位置生成三个向量Query查询、Key键、Value值。打分的过程就是拿当前时刻的Query去和所有历史时刻的Key算相似度一般用点积得到一组权重然后用这组权重去加权求和所有时刻的Value。谁的Key和当前Query的相似度高谁的Value就被更多地搬到输出里来。放到时间序列里举个例子你今天要预测明天下午两点的用电量模型会把今天下午两点的Query和前几天下午两点的Key做相似度计算如果前天这个时间点的模式很像今天那前天的Value也就是数量值对输出的贡献就大自然预测结果就更偏向这类历史模式。这就是Transformer在时间序列任务上能打的核心原因。它不靠循环逐步传递而是让每个位置直接访问全局信息。1.3 位置编码让顺序进入模型视野自注意力有一个先天问题它本身是不感知顺序的。你把一段序列倒过来输入注意力计算出来的权重恰好是对称的因为点积运算不区分谁前谁后。但时间序列最重要的信息往往是顺序——昨天、今天、明天的先后关系直接决定了趋势。所以需要在输入里注入位置信息最常见的方式是位置编码。我用的公式非常经典来自Attention Is All You Need那篇论文$$PE_{(pos, 2i)} \sin(pos / 10000^{2i / d_{model}})$$ $$PE_{(pos, 2i1)} \cos(pos / 10000^{2i / d_{model}})$$这样每个位置都有一个固定的、由正弦余弦函数生成的编码向量加到原始的序列特征上模型就能区分第3个时间点和第30个时间点。在Pytorch里实现位置编码其实十行代码就够import torch import math def positional_encoding(seq_len, d_model): pe torch.zeros(seq_len, d_model) position torch.arange(0, seq_len, dtypetorch.float).unsqueeze(1) div_term torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) pe[:, 0::2] torch.sin(position * div_term) pe[:, 1::2] torch.cos(position * div_term) return pe.unsqueeze(0) # shape: [1, seq_len, d_model]这里有一点可能有人会问既然时间序列本身每个点对应一个具体时刻是不是可以不使用位置编码我实测过对于短序列比如窗口长度16、32去掉位置编码的影响不大但序列超过64之后位置编码带来的效果提升就很明显了。建议始终保留成本很低收益却很确定。2. 跑起来之前环境配置与数据准备2.1 Pytorch环境搭建CPU与GPU怎么选先说我用的环境方便你对照参考项目我的配置Python3.8Pytorch2.0.1GPU版CUDA11.8其他依赖numpy、pandas、matplotlibPytorch安装这个环节卡住过很多人。我的建议是如果只用CPU或数据量不大直接安装CPU版本就够了避免CUDA版本不匹配的问题。用pip安装pip install torch --index-url https://download.pytorch.org/whl/cpu如果有NVIDIA显卡、想用GPU加速需要先确认显卡对应的CUDA版本用nvidia-smi查看驱动支持的CUDA最高版本然后安装匹配的Pytorch版本pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118有些朋友安装GPU版之后发现torch.cuda.is_available()返回False十有八九是安装的Pytorch版本和本机CUDA驱动版本不匹配或者装成了CPU版。这里有个小技巧装好之后立刻跑一下这行代码确认import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.device_name(0) if torch.cuda.is_available() else Using CPU)如果显示Using CPU就是CUDA没接上回归到检查驱动、重装对应的Pytorch版本这一步。2.2 数据集选择与归一化处理我在项目里提供的数据分两部分一部分是带有趋势、周期和噪声的合成数据方便你快速验证代码逻辑是否正确另一部分是一个真实场景数据适合检验模型的实际表现。训练时间序列模型前归一化这一步是绝对不能省略的。Transformer对输入尺度非常敏感如果原始数据波动范围是0到1000模型可能很难收敛。最常用的方式是MinMax归一化from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler() data_scaled scaler.fit_transform(data.reshape(-1, 1))预测的时候要记得把结果反向变换回原始尺度否则你看到的预测值和真实值对不上。在项目代码里我最常用的是pred_original_scale scaler.inverse_transform(pred_scaled)有个容易犯的错是用全部数据去fit归一化器导致测试集信息泄漏。我习惯的顺序是先按时间顺序切分出训练集和测试集再用训练集去fit归一化器最后用同一个scaler去transform测试集。2.3 滑动窗口把预测问题变成监督学习Transformer不是天生会预测未来它本质是一个映射函数输入一段序列输出一个结果。所以要把时间序列构造成监督学习的格式用过去W个时间点的值预测未来一个或多个时间点的值。这个W就是我们常说的滑动窗口大小也叫seq_len。我一般这样构造数据def create_sequences(data, seq_len, pred_len1): xs, ys [], [] for i in range(len(data) - seq_len - pred_len 1): x data[i:i seq_len] y data[i seq_len:i seq_len pred_len] xs.append(x) ys.append(y) return np.array(xs), np.array(ys)窗口大小的选择直接影响预测效果。窗口太小模型看不到足够的历史模式窗口太大引入的无关噪声又会干扰学习同时训练成本上升。我用下来比较顺手的经验是如果数据存在明显周期性比如24小时周期窗口至少该包含1到2个完整周期。比如小时级电力数据有24小时周期窗口取48或96比较合适。3. 单步预测完整实现从模型定义到训练闭环3.1 模型结构把Transformer变成一个预测器最常见的Transformer时间序列单步预测做法是使用Transformer编码器提取序列特征然后接一个全连接输出层把编码器输出的最后一个位置或所有位置池化后映射成一个预测值。这里有一个我踩过的坑值得说一下最初我照搬NLP的分类做法在输出层后面加了GELU激活函数结果回归任务效果很差。回归任务输出层直接是线性层不加任何激活函数。深层特征提取部分可以用GELU或ReLU但最后的输出必须是线性的。我给出的核心模型定义如下import torch import torch.nn as nn import torch.nn.functional as F class TimeSeriesTransformer(nn.Module): def __init__(self, d_model64, nhead4, num_layers2, seq_len48, pred_len1, dropout0.1): super().__init__() self.d_model d_model self.seq_len seq_len self.pred_len pred_len self.input_proj nn.Linear(1, d_model) self.pos_encoder nn.Parameter(positional_encoding(seq_len, d_model), requires_gradFalse) encoder_layer nn.TransformerEncoderLayer( d_modeld_model, nheadnhead, dim_feedforwardd_model * 4, dropoutdropout, batch_firstTrue ) self.encoder nn.TransformerEncoder(encoder_layer, num_layersnum_layers) self.output_layer nn.Linear(d_model, pred_len) def forward(self, x): # x shape: [batch, seq_len, features] x self.input_proj(x) * math.sqrt(self.d_model) x x self.pos_encoder x self.encoder(x) # 取序列最后一个位置作为特征 out self.output_layer(x[:, -1, :]) return out有几个细节我需要解释一下input_proj把单维特征映射到d_model维因为Transformer内部需要统一的特征维度。如果你的数据是多维特征比如同时有温度、湿度、负荷把nn.Linear(1, d_model)改成nn.Linear(num_features, d_model)即可。编码器输出的x[:, -1, :]取的是序列最后一个位置的隐状态。在NLP任务里取[CLS]令牌对应的输出在时间序列任务里取最后一个时间点也可以因为它被自注意力机制融入了全局历史信息。另一种做法是沿序列维度做平均池化我测试下来两种方式区别不大取最后一个位置计算量更小。positional_encoding函数我设置为requires_gradFalse因为位置编码是固定的先验信息不需要训练。3.2 训练循环损失函数、优化器与epoch设计单步预测的损失函数我用的是MSE均方误差偶尔对比实验时会关注MAE。MSE对大误差更敏感会让模型倾向于避免极端偏差。如果你希望预测结果更平滑、对离群值不那么敏感改用MAE或HuberLoss会更好。训练循环比较常规但有几个细节很容易被忽视def train_one_epoch(model, train_loader, optimizer, criterion, device): model.train() total_loss 0.0 for x_batch, y_batch in train_loader: x_batch x_batch.to(device) y_batch y_batch.to(device) optimizer.zero_grad() output model(x_batch) # shape: [batch, pred_len] loss criterion(output, y_batch) loss.backward() optimizer.step() total_loss loss.item() * x_batch.size(0) return total_loss / len(train_loader.dataset)学习率我初始设为1e-3使用Adam优化器。如果训练过程出现loss震荡不下降我会降低学习率到5e-4或1e-4。还可以配合ReduceLROnPlateau做动态调整scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, factor0.5, patience5, verboseTrue )每训练完一个epoch在验证集上计算loss如果连续5个epoch没有下降就把学习率乘以0.5。这招在Transformer上效果非常实用尤其是数据不够大的情况下。关于epoch数量我的经验是不要死板地设100或200。更稳妥的方式是配合早停patience10验证集loss连续10个epoch没有下降就停止训练然后恢复最优模型参数。3.3 在测试集上完成一次完整的预测训练完成后预测流程如下model.eval() predictions [] with torch.no_grad(): for x_batch in test_loader: x_batch x_batch.to(device) output model(x_batch) predictions.append(output.cpu().numpy()) predictions np.concatenate(predictions, axis0) predictions_original scaler.inverse_transform(predictions)这里有一个需要弄明白的细节测试集加载的x_batch是归一化后的输入scaler是之前用训练集拟合的归一化器。预测完成后需要把结果逆归一化后再和真实值对比否则算出来的误差没有任何意义。画图观察也很关键。我一般会画三张图第一张是全数据集的预测曲线和真实曲线叠放对比第二张是测试集后半段的放大图看看细节第三张是误差分布直方图。实验里单步预测通常表现非常好如果数据本身有强周期性预测曲线几乎能和真实曲线重合MSE能压到很低。这也是很多人做完单步预测后觉得Transformer太强了的原因但别高兴太早多步预测才是真正的考验。4. 从单步到多步三种预测策略的代码实现与取舍4.1 迭代多步预测最简单但误差会累积在单步模型已经训好的前提下最快实现多步预测的办法就是迭代式预测把上一步的预测值当作下一步的输入一次预测一个点重复M次。def iterative_multi_step_predict(model, initial_window, steps, scaler, device): model.eval() current_window initial_window.clone() # shape: [seq_len, features] predictions [] with torch.no_grad(): for _ in range(steps): input_seq current_window.unsqueeze(0).to(device) # [1, seq_len, features] pred model(input_seq) # [1, pred_len] pred_value pred[0, 0].item() predictions.append(pred_value) # 滚动更新窗口丢弃最早一个点拼接新预测值 current_window torch.cat([ current_window[1:], torch.tensor([[pred_value]], dtypecurrent_window.dtype, devicecurrent_window.device) ], dim0) return np.array(predictions)这个方法的优点是用已有的单步模型就能做不需要额外训练实现成本极低。缺点是误差会随着步数累积。因为每一步的输入都包含上一步的预测误差到后期误差可能被放大。实验里如果单步预测MSE是0.01迭代到第20步时MSE可能涨到0.1甚至更多。在周期性强、数据本身较平稳的场景下这种累积可以接受但在数据波动大、趋势变化剧烈的场景下后期效果会比较差。4.2 直接多步预测一次性输出所有目标时间步直接多步预测的做法是把输出层的维度从pred_len1改成pred_lenM。还是用前面那个TimeSeriesTransformer模型只需要把pred_len参数设置成你想要的预测步数。训练数据和单步有一个关键区别create_sequences函数里的pred_len要设置为M确保标签y是未来M个连续时间点的值。model TimeSeriesTransformer( d_model64, nhead4, num_layers2, seq_len48, pred_len10 # 一次预测未来10步 )这种方式的代码改动最小但它有个内在缺陷预测未来10个点时每个点的预测难度其实不一样第一个时间点离现在最近、不确定性最低第10个时间点最远、不确定性最高。但直接多步预测让它们共用同一个输出层等于强制模型用同样的参数拟合不同难度的目标这对于长步数预测效果通常不如迭代法稳定。我实测下来步数不超过5时直接多步预测的效果不错步数到10以上模型往往会开始摆烂——预测结果趋近于平均值曲线变得非常平滑失去了细节。直观原因就是远端的不确定性太高模型找不到足够的信息来预测精确值干脆往均值方向收敛来缓解损失。4.3 基于Decorder的生成式多步预测更严谨但训练成本更高如果追求更好的多步预测效果推荐用Transformer的Decoder部分实现序列到序列Seq2Seq的结构。Encoder读入历史输入Decoder以自回归的方式逐步生成未来值。训练时使用Teacher Forcing——先把真实目标序列输给Decoder让模型学会基于历史信息逐步解码预测时不使用Teacher Forcing把上一步预测出来的值作为下一步输入。这里给出一个简化的Decoder多步预测模型示意class TimeSeriesTransformerSeq2Seq(nn.Module): def __init__(self, d_model64, nhead4, num_encoder_layers2, num_decoder_layers2): super().__init__() self.d_model d_model self.input_proj nn.Linear(1, d_model) self.pos_encoder nn.Parameter(positional_encoding(64, d_model), requires_gradFalse) self.transformer nn.Transformer( d_modeld_model, nheadnhead, num_encoder_layersnum_encoder_layers, num_decoder_layersnum_decoder_layers, dim_feedforwardd_model*4, dropout0.1, batch_firstTrue ) self.output_proj nn.Linear(d_model, 1) def forward(self, src, tgt): src self.input_proj(src) * math.sqrt(self.d_model) src src self.pos_encoder[:, :src.size(1), :] tgt self.input_proj(tgt) * math.sqrt(self.d_model) tgt tgt self.pos_encoder[:, :tgt.size(1), :] output self.transformer(src, tgt) return self.output_proj(output)训练时输入给Decoder的tgt是未来序列的整体并在每个位置前偏移一位Shifted Right。预测时需要初始化一个起始符通常取历史序列最后一个值然后循环地每次预测一个值并更新tgt序列。Seq2Seq方式在多步预测上效果最好因为它允许模型在每个解码步骤中重新访问编码器输出的全部历史信息并逐步调整。但它的训练复杂度也更高收敛速度比Encoder-only慢。如果你的场景明确需要预测10步以上我建议直接上Seq2Seq方案如果只是5步以内直接多步预测的性价比其实最高。实验小节我放在后面统一说这里先把一个重要的策略对比列成表格供你参考策略实现难度训练成本短步长效果长步长效果适用场景迭代多步低低复用单步模型良好误差累积明显快速验证、弱依赖场景直接多步低低优秀容易趋于均值步长≤5Seq2Seq中高高优秀优秀长序列预测核心方案5. 归一化、损失函数与训练技巧实测中的关键坑5.1 数据泄露最容易毁掉模型的隐形杀手在这个项目里我最想强调的一课是数据泄露。很多人写时间序列预测代码时顺手就把整段数据做了一个全局归一化然后再切训练集和测试集。这会带来一种假象测试集的信息在训练时已经被模型偷看到了因为归一化器的最大值、最小值是在全数据集上拟合的。等到真正部署模型输入新的真实数据时它的尺度可能超过了训练时的范围归一化逻辑直接失效模型表现断崖式下跌。正确的做法我之前提过先按时间顺序切分再在训练集上fit归一化器最后用同一个归一化器transform测试集。这一步代码顺序非常关键。类似的泄露还出现在数据构造阶段。create_sequences函数如果写得不小心比如在构造样本时错误地让x包含了未来时间点的信息模型会在训练时通过作弊的方式获得极低的loss但部署后预测完全失败。这一点在构造数据时尤其要检查的仔细x data[i:iseq_len]y data[iseq_len:iseq_lenpred_len]两者之间绝不能有重叠。5.2 学习率、Batch Size与序列长度的影响我整理一下自己在单步实验中跑出来的一组对比数据用同一个合成周期数据集只改变超参数参数实验A实验B实验C学习率1e-35e-41e-4Batch Size323232序列长度484848模型层数224测试集MSE0.0180.0070.006从这个结果可以看出学习率从1e-3降到5e-4后测试集MSE大幅下降。再把层数从2增加到4虽然训练时间增加一倍但误差继续下降了一点。这说明在数据量允许的情况下模型容量和训练充分度同样重要。Batch Size的影响同样存在。我试过把Batch Size从32调到128发现训练速度更快但收敛后的精度有所下降因为大Batch Size在梯度的方差方面更保守容易收敛到局部平坦区域。在时间序列任务里32或64是比较稳妥的选择。关于序列长度有经验说越长越好这个说法有误导性。序列太长会引入大量无关噪声而且Transformer自注意力的计算复杂度是 $O(L^2)$。序列长度从48翻到96训练时间接近翻倍预测精度不一定提升。我建议通过实验来定分别尝试序列长度32、48、64、96在验证集上对比选择误差最低且训练时间可接受的那个。5.3 过拟合判断与可视化时间序列模型同样会过拟合而且因为数据存在时序相关性过拟合表现更隐蔽。判断方法训练loss持续下降验证loss先降后升典型的过拟合信号。预测测试集曲线如果预测曲线在真实值附近抖动剧烈说明模型对训练集噪声过度拟合如果预测曲线过分平滑、几乎是一条直线说明模型容量不足或训练不充分。观察误差分布误差直方图如果集中在0附近但有很长的尾巴说明存在少数极端预测错误这可能是模型对突变点的响应不足。处理过拟合的常用手段增大dropout从0.1提到0.2或0.3能有效降低验证集与训练集的差距。减小模型层数和d_model降低模型容量。增加数据量或对训练数据做滑动窗口增强——让窗口以1为步长滑动生成更多样本。注意相邻样本高度相关所以这种增强对时间序列不能完全等价于随机扩充但仍有帮助。应用早停节省时间的同时减少过拟合。6. 实验对比单步预测和多步预测的差距到底在哪6.1 合成数据上的效果表现我用合成长度为1000的周期数据做了一组实验数据包含正弦趋势加噪声公式大致是$$y(t) \sin(2\pi t / 50) 0.2 \cdot \sin(2\pi t / 13) \epsilon$$其中$\epsilon$是均值为0、标准差为0.05的高斯噪声。我取前700个点训练中间100个点做验证最后200个点做测试。单步预测的结果测试集MSE约0.006RMSE约0.077。从画图上看预测曲线和真实曲线几乎完全重合连噪声部分也贴合得不错。多步预测我使用直接多步策略设置pred_len10测试集MSE约0.035虽然比单步差了不少但整体曲线形态还是正确的周期和趋势都能保持。当我把pred_len调到30时MSE上升到了0.12并且预测曲线的峰值明显被拉低了这就是模型趋向均值预测的典型表现。之后换成Seq2Seq模型做30步预测MSE回到了0.045。差距非常直观在长步数预测场景中生成式方法远优于直接多步法。6.2 真实数据集上的问题突变与趋势项我在项目里还放了一个真实场景数据来自工业环境中的传感器读数。这个数据集不像合成数据那么干净存在几点挑战有突变的离群点传感器偶发故障会产生异常值。带有缓慢漂移的趋势项简单归一化后趋势仍然存在。周期性不是完全固定有漂移。真实数据上单步预测的MSE比合成数据高了一个量级达到0.03左右但曲线形态仍然良好。多步预测的问题就很明显了当真实数据出现突变时模型通常会在1到2个时间步之后才反应过来导致预测曲线在突变点附近明显滞后。这说明Transformer虽然强但并不意味着它是时间序列预测的银弹。它对异常值敏感对趋势外推能力有限对分布突变比如传感器故障导致数据尺度突变的响应速度不够快。实际部署中数据清洗、异常检测和模型本身同等重要。我在项目代码里加了几种简单的异常值处理方法比如中值滤波、3\sigma原则剔除以及可选的差分操作来消除趋势项。6.3 误差累积的数学直觉迭代法后期为什么崩迭代多步预测的误差累积可以从公式层面理解。假设单步预测的误差均值为0方差为$\sigma^2$下一步的输入包含这个误差模型的不确定性与输入噪声叠加后步数增加时误差方差会近似线性甚至超线性增长。在实际中模型通常不是完美的无偏估计器还会存在偏差项因此误差增长的速率比理论计算更快。这也是为什么在步数较长时我更推荐直接多步或Seq2Seq方案。直接多步预测在训练时让模型见过完整的目标序列它有机会学习到步数之间的联合分布信息而不是像迭代法那样一步步盲人摸象。7. 调参实录一套可以照抄的经验项目的核心代码跑通之后你可能会发现效果还是很一般这大概率不是代码的问题而是超参数的问题。我把自己在这套代码上调参的真实经历整理成几条可以直接套用的经验希望能帮你省一点时间。第一先跑通再调优。不要一上来就追求最好的效果先用小d_model32、小num_layers1、大学习率跑完一个训练周期确认流程没有bugloss在下降预测曲线有基本形状然后再逐步加大模型规模。第二学习率是最值得先调的超参数。我习惯的做法是先用1e-3跑10个epoch观察loss曲线。如果loss震荡严重降到1e-4如果loss下降缓慢提到3e-3。Transformer对学习率比LSTM敏感得多一个不合适的学习率可以让模型完全训不出来。第三不要忽略dropout。在时间序列这类样本量不大的任务里dropout的作用比分类任务中更重要。我测过dropout0和dropout0.1的对比测试集MSE分别约为0.024和0.009差距非常显著。建议至少设置为0.1数据量小的话可以考虑0.2。第四用验证集做所有决策测试集只做最终评估。切忌用测试集反复调参否则即使测试集没有参与训练你也在通过人工方式把测试集信息注入到模型选择过程中相当于另一种形式的数据泄露。这是我在项目中一直坚持的流程。最后也是最实用的一条把实验参数、随机种子、最佳结果记录好。我在项目代码里加入了torch.manual_seed(42)这类固定随机种子的设置让每次训练可复现。时间序列模型有个特点不同随机种子下训练出来的模型效果差异可能很大如果你不固定随机种子不同次实验之间的结果波动会让你很难判断是代码问题还是模型本身的随机性问题。我个人在实际操作中发现把这几个参数固定下来之后剩下的更多是力气活多跑几组不同的窗口长度和预测步数用验证集挑出最优组合再在测试集上做最终评估。这套项目源码把环境配置、数据处理、模型构建、三种多步预测策略和评估可视化都组装在了一起。你可以直接替换成自己的数据改改seq_len和pred_len就能在几十行配置内完成一次完整的时间序列预测实验。本文还有配套的精品资源点击获取
返回列表