TimesNet-Transformer时间序列预测实战:MATLAB实现与调优 📅 发布时间:2026/9/20 22:39:06 👁 浏览次数: 简介面向熟悉MATLAB与深度学习框架的研发人员、数据科学家和工程师这份下载包以TimesNet-Transformer多尺度卷积时序网络为核心给出多变量时间序列预测的完整MATLAB项目实例。文档从项目背景、目标与挑战切入详细拆解了模型架构和核心代码覆盖数据预处理、模型训练、结果评估以及GUI交互界面并结合多尺度卷积与Transformer编码器说明如何提取局部特征、捕捉长距离依赖适用于金融市场预测、能源负荷预测、智能制造设备故障预测等场景。资源包为单个docx文档体积77KB内容包含项目特点、模块化架构设计、可视化分析说明以及未来改进方向如自适应多尺度卷积、图神经网络便于读者按目录快速定位与学习。已有519人学习下载适合希望在工程中快速落地MATLAB深度学习时序建模的中高级开发者。1. 核心架构拆解为什么把 TimesNet 和 Transformer 放在一起先聊个实际感受单用 Transformer 做时间序列预测最大的问题是它把时间步当成了一个单纯的 token 序列注意力机制虽然能捕捉长程依赖但时间序列里那种“按周、按月、按季度重复出现的局部模式”很难被有效编码。你喂给它 96 步的历史窗口它可能学到的是“第 10 步和第 80 步有关联”却很难意识到“每年 7 月用电量都会冲高”这种周期性规律。TimesNet 这个思路恰好补上了这块短板。它的核心洞察是时间序列里的周期性本质上是一种可以“折叠”的结构。比如一个以 24 小时为周期的序列你把 24 个时间步折叠成一个二维张量的一行那 7 天的数据就能折叠成一个 7×24 的矩阵。在这个矩阵里行方向是天的演进列方向是小时的变化周期内的局部特征变成了二维空间里的纹理。再用卷积去扫这个纹理就能同时提取“哪几个小时是高峰”“高峰在相邻几天怎么漂移”这两类信息比直接在原始一维序列上做卷积要高效得多。Transformer 编码器在这里扮演的角色是处理折叠成二维之后仍然存在的长程依赖。卷积擅长提取局部模式但如果你要预测未来 48 小时的气温当前时刻和 36 小时前的一个冷空气过程之间的关联就得靠注意力机制来跨距离捕捉。所以 TimesNet-Transformer 的搭配逻辑很清楚TimesNet 负责把多尺度周期特征“挖”出来Transformer 负责在特征之上建模全局依赖两个分支的特征融合后送到预测头。我在实际项目里最常见的做法是双分支并行结构而不是串联。原因很简单串联会让 Transformer 的输入变成 TimesNet 的中间特征一旦 TimesNet 某个周期提取不准错误会一路传导到输出并行结构让两个分支各自输出特征向量在融合层拼接后再映射到预测结果容错性更好调参时也更容易定位问题出在哪个分支。这篇博文后面的代码实现就是按并行双分支来写的。2. 数据预处理与实验设计2.1 数据集选择与多变量输入构造做多变量时间序列预测数据集的选择直接决定你后面所有工作的价值。我比较推荐从 UCI 机器学习库或者 Kaggle 上找公开数据集比如用电负荷数据包含气温、湿度、历史负荷、节假日标记等多个变量或者空气质量数据包含 PM2.5、CO、NO2、温度、气压等。这类数据的特点是变量之间有真实的耦合关系预测目标变量时其他变量的贡献是实打实的。拿到原始数据后第一步要做的是时间对齐。很多公开数据集的时间戳并不是严格等间隔的可能中间缺了几天或者某几个小时的记录是空值。我踩过的坑是直接用fillmissing函数默认参数填充结果把一段连续的缺失值填成了同一条直线模型在测试集上的误差被严重低估。正确做法是先画出缺失值分布图判断是随机缺失还是连续缺失随机缺失用线性插值基本够用连续缺失超过 3 个时间步时最好是找同期历史数据做均值替代或者直接从训练集中剔除这一段。多变量输入的构造方式决定了模型能看到的“视野”。我的经验是用滑动窗口法设定回看窗口长度为input_len预测窗口长度为output_len以步长 1 滑动切分样本。假设数据集有 5 个变量回看窗口 96 步预测未来 24 步那么每个样本的输入形状是(96, 5)标签形状是(24, 1)如果只预测其中一个变量。这里有个细节如果你的预测目标也包含多个变量比如同时预测温度和湿度那标签形状就是(24, 2)损失函数相应地要改成多输出 MSE。2.2 归一化策略与训练集/测试集切分归一化对这类模型的影响比很多人想象的要大。TimesNet 里的卷积操作对输入尺度比较敏感如果某个变量的数值范围是 0 到 1而另一个变量是 1000 到 5000卷积核计算出的特征图会被大数值变量主导小数值变量的周期模式基本被淹没。我通常用 Z-score 归一化也就是每一列减去均值再除以标准差。注意三个细节一是归一化参数只能用训练集的均值和标准差来计算不能用整个数据集的否则会有信息泄露二是测试集的归一化也要用训练集的参数不能在测试集上重新计算三是预测结果反归一化时要用目标变量那一列的均值和标准差。训练集和测试集的切分我的建议是按时间顺序切分不要随机打乱。时间序列数据有天然的时间依赖性随机打乱会让模型偷看到未来的信息测试集上的指标再好看也没有实际意义。常见比例是前 70% 到 80% 作为训练集剩下的作为测试集。如果你还想做模型选择可以在训练集尾部再切出一段验证集。3. 模型搭建MATLAB 代码逐段解析3.1 TimesNet 多尺度卷积特征提取块的实现TimesNet 的核心是周期发现和二维折叠。在 MATLAB 深度学习工具箱中你没法直接用内置层完成折叠操作需要用dlarray和自定义层来解决。我先给出一个实现二维折叠的示例逻辑% 输入 x: 形状为 (1, input_len, num_vars) 的 dlarray % 按周期 p 将序列重塑为 (p, input_len/p, num_vars) 的二维张量 function x_2d fold_sequence(x, p, input_len) num_vars size(x, 3); num_periods floor(input_len / p); x_2d reshape(x(:, 1:num_periods*p, :), [p, num_periods, num_vars]); x_2d permute(x_2d, [2, 1, 3]); % 调整为 (num_periods, p, num_vars) end实际的 TimesNet 区块包含周期估计、折叠、二维卷积、解折叠四个环节。在 MATLAB 中我习惯将整个 TimesNet 块封装为一个自定义层这样能在dlnetwork中直接串联classdef timesnetBlock nnet.layer.Layer properties NumFilters KernelSize NumPeriods InputLen end properties (Learnable) Conv1 Conv2 end methods function layer timesnetBlock(numFilters, kernelSize, numPeriods, inputLen) layer.NumFilters numFilters; layer.KernelSize kernelSize; layer.NumPeriods numPeriods; layer.InputLen inputLen; layer.Conv1 convolution2dLayer(kernelSize, numFilters, Padding, same); layer.Conv2 convolution2dLayer(kernelSize, numFilters, Padding, same); end function Z predict(layer, X) % X 形状: (1, inputLen, numVars) inputLen layer.InputLen; numVars size(X, 3); p layer.NumPeriods; numPeriods floor(inputLen / p); % 折叠为二维 X_folded reshape(X(:, 1:numPeriods*p, :), [numPeriods, p, numVars]); X_folded permute(X_folded, [2, 1, 3]); % (p, numPeriods, numVars) % 二维卷积提取周期内特征 Z1 layer.Conv1(X_folded); Z1 relu(Z1); Z2 layer.Conv2(Z1); Z2 relu(Z2); % 解折叠回一维 Z2 permute(Z2, [2, 1, 3]); Z reshape(Z2, [1, numPeriods*p, numVars]); end end end注意这里NumPeriods的选择。实际数据往往包含多个周期比如用电数据既有 24 小时周期又有 168 小时7 天周期。我的做法是设置多组周期分别为[24, 168]每组周期生成一个独立的卷积分支最后把多个分支的输出通过全连接层融合。这样模型能自适应地提取不同时间尺度上的模式避免单一周期丢失重要信息。3.2 Transformer 编码器的 MATLAB 实现Transformer 编码器在 MATLAB 中没有直接的内置层但可以从自注意力机制开始一步步搭建。我实现一个简化版的多头自注意力层classdef multiHeadSelfAttention nnet.layer.Layer properties NumHeads HeadDim ModelDim end properties (Learnable) Wq, Wk, Wv, Wo end methods function layer multiHeadSelfAttention(modelDim, numHeads) layer.ModelDim modelDim; layer.NumHeads numHeads; layer.HeadDim modelDim / numHeads; % Xavier 初始化 layer.Wq dlarray(randn(modelDim, modelDim) * sqrt(2/modelDim)); layer.Wk dlarray(randn(modelDim, modelDim) * sqrt(2/modelDim)); layer.Wv dlarray(randn(modelDim, modelDim) * sqrt(2/modelDim)); layer.Wo dlarray(randn(modelDim, modelDim) * sqrt(2/modelDim)); end function Y predict(layer, X) % X 形状: (seqLen, batchSize, modelDim) [seqLen, ~, modelDim] size(X); numHeads layer.NumHeads; headDim layer.HeadDim; Q pagemtimes(X, layer.Wq); % (seqLen, batchSize, modelDim) K pagemtimes(X, layer.Wk); V pagemtimes(X, layer.Wv); % 重塑为多头形式 Q reshape(Q, seqLen, [], numHeads, headDim); K reshape(K, seqLen, [], numHeads, headDim); V reshape(V, seqLen, [], numHeads, headDim); % 缩放点积注意力 scores pagemtimes(permute(Q, [1, 3, 2, 4]), permute(K, [3, 1, 2, 4])); scores scores / sqrt(headDim); weights softmax(scores, DataFormat, SSCB); % 加权求和 context pagemtimes(permute(weights, [1, 3, 2, 4]), V); context reshape(context, seqLen, [], modelDim); % 输出投影 Y pagemtimes(context, layer.Wo); end end endpagemtimes是 MATLAB R2022a 之后引入的函数专门用于批量矩阵乘法比循环拼接高效得多。Transformer 编码器的完整结构还包括位置编码、残差连接和 LayerNorm这些在dlnetwork中可以组合dlnetTransformer dlnetwork(... [sequenceInputLayer(modelDim, Name, trans_input), ... positionEmbeddingLayer(modelDim, inputLen, Name, pos_embed), ... multiHeadSelfAttentionLayer(modelDim, 4, Name, mhsa_1), ... layerNormalizationLayer(Name, ln1), ... fullyConnectedLayer(modelDim * 4, Name, ffn1), ... reluLayer(Name, relu_ffn), ... fullyConnectedLayer(modelDim, Name, ffn2), ... layerNormalizationLayer(Name, ln2)], ... Initialize, true);这里有个容易忽略的点positionEmbeddingLayer在官方工具箱中有实现但版本不同函数名略有差异。如果你用的版本没有这个层可以自定义一个可学习的位置编码层原理是创建一个形状为(inputLen, modelDim)的参数矩阵加到输入序列上。位置编码是 Transformer 能感知顺序的关键不加的话注意力机制会把“今天 8 点”和“昨天 8 点”当成同一个位置预测结果会乱套。3.3 双分支融合与整体网络组装TimesNet 分支和 Transformer 分支的输出形状需要对齐后才能融合。TimesNet 的输出经过多层卷积后是一个特征向量Transformer 的输出是序列特征我的做法是先将 TimesNet 输出通过fullyConnectedLayer映射到与 Transformer 隐藏维度一致的维度然后用concatenationLayer将两个特征向量拼接最后接若干全连接层输出预测结果% 假设 inputLen 96, numVars 5, outputLen 24 % 分支一TimesNet timesnet_out timesnetBlock(32, [3, 3], 24, 96); % 提取周期特征 % 分支二Transformer transformer_out dlnetTransformer; % 融合 fusion concatenationLayer(3, 2, Name, fusion); fc1 fullyConnectedLayer(64, Name, fc_fusion); drop dropoutLayer(0.2, Name, drop_fusion); fc_out fullyConnectedLayer(outputLen, Name, fc_out); % 通过 layerGraph 组装 lgraph layerGraph(); lgraph addLayers(lgraph, timesnet_out); lgraph addLayers(lgraph, transformer_out); lgraph addLayers(lgraph, [fusion, fc1, drop, fc_out]); % 手动连接各分支到融合层 lgraph connectLayers(lgraph, timesnet_output, fusion/in1); lgraph connectLayers(lgraph, transformer_output, fusion/in2); % 转换为 dlnetwork dlnet dlnetwork(lgraph);组装时要特别注意concatenationLayer的维度参数。在 MATLAB 中(3, 2)表示在第 3 维上拼接两个输入。如果两个分支的输出都是形状为(1, batchSize, featureDim)的 dlarray那么第 3 维拼接就是特征维拼接得到(1, batchSize, featureDim*2)后续全连接层才能正确接收。4. 训练细节、超参数调优与评估4.1 训练循环与自定义损失函数用 MATLAB 训练这类网络的常规方式是自定义训练循环。官方trainNetwork函数对自定义层和复杂网络结构支持有限所以我用dlnetworkminibatchqueue的组合% 构建 minibatchqueue mbq minibatchqueue(trainingData, ... MiniBatchSize, 32, ... MiniBatchFormat, {SCB, CB}, ... OutputCast, single); % 自定义训练循环 numEpochs 100; learnRate 0.001; averageGrad []; averageSqGrad []; figure; for epoch 1:numEpochs shuffle(mbq); while hasdata(mbq) [X, Y] next(mbq); [loss, gradients] dlfeval(modelLoss, dlnet, X, Y); [dlnet, averageGrad, averageSqGrad] adamupdate(dlnet, gradients, ... averageGrad, averageSqGrad, epoch, learnRate); end end function [loss, gradients] modelLoss(dlnet, X, Y) Y_pred forward(dlnet, X); loss mse(Y_pred, Y); gradients dlgradient(loss, dlnet.Learnables); end损失函数用 MSE 是最直接的但如果你的预测目标有不同的物理意义比如温度误差 1 度比湿度误差 1% 要严重得多可以考虑加权 MSE。我在一个工业项目里用过loss mean((Y_pred - Y).^2 .* weights)权重向量根据每个变量的量纲差异手动设定效果比标准化后直接 MSE 要好。4.2 关键超参数学习率、批次大小、周期数选择整理一份我实测下来的参数参考表参数推荐范围我的经验值调参心得回看窗口长度48 ~ 16896太短捕捉不到日周期太长增加计算量预测窗口长度12 ~ 4824超过 48 后误差迅速增大模型趋于平庸TimesNet 周期数数据主周期 ± 50%[24, 168]用 FFT 先观察频谱峰值不要盲目设卷积核数量16 ~ 6432超过 64 后收益很小计算量翻倍Transformer 隐藏维度32 ~ 12864和输入变量数、数据量有关注意力头数2 ~ 84必须能整除隐藏维度学习率1e-4 ~ 1e-25e-4用余弦退火调度前 20 轮高后降低Dropout0.1 ~ 0.30.2数据量大时可去掉 dropout 加速收敛周期数的选择有个实操技巧先对每个变量做快速傅里叶变换FFT画出频谱图观察功率最大的几个频率对应的周期值。比如频谱在 0.0417 Hz对应 24 小时和 0.006 Hz对应 168 小时处有明显峰值那周期就设为 [24, 168]。如果数据有年周期而你的数据跨度不够一年那就别硬设 365模型学不到的。4.3 结果评估指标计算与预测效果可视化评估时我习惯同时看三个维度的指标整体误差RMSE、MAE、方向准确率、极端值捕捉能力。MATLAB 里计算这些指标很直接rmse sqrt(mean((Y_true - Y_pred).^2, all)); mae mean(abs(Y_true - Y_pred), all); % 方向准确率预测趋势方向与实际趋势方向一致的比例 dir_true sign(diff(Y_true)); dir_pred sign(diff(Y_pred)); dir_acc mean(dir_true dir_pred, all);可视化预测效果时我会画三种图第一种是预测值和真实值的时间序列对比曲线直观看出整体贴合程度第二种是误差分布直方图观察误差是否呈正态分布如果明显偏态说明模型有系统性偏差第三种是分变量误差热力图定位到具体是哪个变量预测得最差。有一次我发现某个变量的误差远大于其他变量查了半天发现是这个变量在数据预处理时没有做滞后校正时间轴偏了一天。5. 常见问题与排查技巧实录5.1 训练不收敛或 Loss 震荡时间序列预测任务里Loss 震荡最常见的原因是学习率太大。Transformer 编码器对学习率非常敏感我用过 0.01 的学习率Loss 直接发散到 NaN降到 0.0005 后稳定收敛。第二个原因是批次大小太小导致梯度估计噪声太大。第三个原因比较隐蔽归一化参数用错了如果测试集的归一化参数没沿用训练集的验证 Loss 会周期性跳变因为每个 batch 的数值分布都不一样。排查思路是先固定一个很小的学习率1e-4跑 20 轮同时打印训练集和测试集的 Loss。如果训练集 Loss 下降但测试集 Loss 不降那就是过拟合增加 Dropout 或减小模型容量如果两边都不降检查数据预处理是否有 bug尤其是折叠reshape那一步很多人会把维度搞混。5.2 预测结果出现延迟或滞后如果你发现预测曲线比真实曲线整体向右平移了一段也就是模型输出差不多等于“把过去的值往后搬”这是时间序列预测模型非常典型的退化现象。原因通常是模型过度依赖了输入序列最后一两个时间步的信息而没学到真正的动态规律。我在 TimesNet-Transformer 里遇到这种情况时第一个检查项是 TimesNet 的卷积核大小。卷积核越大越倾向于提取平滑特征预测结果越滞后。第二个检查项是损失函数如果目标序列存在明显趋势纯 MSE 会鼓励模型输出均值附近的预测这时候可以考虑在损失中加入一阶差分项。5.3 训练速度慢、显存不足MATLAB 的dlarray在做自定义层时如果算子写得不够向量化循环会非常慢。一个常见性能瓶颈是在多头注意力实现中用for循环遍历每个头导致梯度计算图非常庞大。解决方法是尽量用pagemtimes替代循环。显存不足则可以考虑减小批次大小或者缩短历史窗口长度或者用dlarray的single精度而不是默认的double。6. 扩展思路从预测到决策的实战升级模型跑通之后我建议你做一个更有价值的扩展把预测结果接入到一个简单的决策模块中。比如预测的是设备温度当未来 24 小时某时刻的温度预测值超过阈值时系统自动触发预警。这种从“预测”到“决策”的闭环才是时间序列项目在实际业务中的最终价值。另一个值得尝试的方向是多步滚动预测和直接多步预测的对比。滚动预测每次预测一步把预测值作为下一步的输入缺点是误差会累积直接多步预测一次输出多个时间步训练简单但长程精度往往不如滚动预测。TimesNet-Transformer 在直接多步预测上的表现通常优于滚动预测因为自注意力可以同时建模未来多个时间步之间的关系你可以对比一下两者在同一个数据集上的差异这会让你的实验内容更丰富。在我实际做过的一个设备故障预警项目中TimesNet-Transformer 相比单纯的 LSTM 模型在 48 步预测的 RMSE 上降低了约 18%方向准确率提升了 7 个百分点。但也要客观地说模型复杂度带来的训练时间增加是明显的如果数据量只有几千个时间步LSTM 或者 XGBoost 可能反而是更务实的选择。这也是我想强调的一点模型选型永远服务于数据规模、预测时域和业务成本约束而不是单纯追求架构的先进性。本文还有配套的精品资源点击获取