MATLAB实现TCN-Transformer-LSTM组合模型与SHAP多输出时序预测
简介本资源是一套面向时间序列多输出回归任务的MATLAB深度学习解决方案适用于能源负荷预测、工业过程建模、金融时序分析等场景适合具备基础深度学习与MATLAB编程能力的研究生、工程师及科研人员。资源融合TCN强时序感知、Transformer长程依赖建模与LSTM动态演化能力集成SHAP可解释性分析模块支持新样本批量预测与多目标同步输出显著提升模型实用性与决策可信度。压缩包共7个文件56KB含3个Excel数据文件原始数据、新输入、预测结果、3个核心MATLAB脚本主训练main.m、SHAP计算shapley_function.m、新数据预测newpre.m及1份运行说明txt结构精炼、即装即用。目前已有60人学习下载提供从数据读取、归一化、混合模型构建、训练评估到可视化分析与特征贡献解读的完整闭环流程附带网络结构图、预测对比曲线、误差分布直方图及SHAP摘要图等全套结果生成逻辑。 这段时间接了不少时序回归的需求发现一个挺明显的变化越来越多人不再满足于单一模型出结果而是把TCN、Transformer、LSTM这类结构揉在一起用还要加可解释性分析和多输出预测。之所以想写这篇是因为我手里正好有一份用MATLAB实现的TCN-Transformer-LSTM组合模型完整工程做了回归预测、SHAP特征归因、新数据预测和多输出支持踩了不少坑也沉淀了不少可复用的经验。无论你是做负荷预测、水文预报、交通流预测还是设备寿命回归这套思路都能直接借鉴。这篇文章就从模型为什么这么组开始一路讲到MATLAB代码怎么落地、SHAP怎么做、新数据预测怎么接。1. 为什么非要把TCN、Transformer、LSTM栓在一起1.1 三种结构各自根本解决什么问题先说TCN。时间卷积网络本质上是一堆因果膨胀卷积的堆叠。因果卷积保证了你预测t时刻只用t时刻之前的信息不会看到未来数据这是时序预测的基本纪律。膨胀卷积则让你在层数不深的情况下就能把感受野拉得很大比如卷积核大小是3膨胀率按1、2、4、8往上翻四层之后感受野就是124815个时间步。它的优势是训练快、对局部模式敏感尤其适合提取波形里的短时形态特征像负荷曲线里的早晚高峰形态、振动信号里的冲击特征TCN抓这类局部模式非常稳。Transformer的核心是自注意力机制。它跟TCN和LSTM最大的不同在于任意两个时间步之间的距离都是1信息传递不受步长限制。这就解决了长序列里“很久以前的一个小事件对现在产生决定性影响”的问题。比如交通预测里早高峰的某个突发拥堵可能影响两小时后的路况LSTM在这种长距离依赖上容易衰减Transformer则能直接建立远距离关联。但Transformer的问题也很明显它没有内置的时间顺序概念必须加位置编码而且对局部模式不那么敏感纯Transformer在细粒度波形拟合上往往不如TCN。LSTM则是对时间递进关系建模的经典选择。它的门控机制——遗忘门、输入门、输出门——让信息可以按需写入和丢弃非常适合学习序列的动态演化规律。跟TCN和Transformer相比LSTM是逐时间步递归的结构更贴近“过程性”的序列逻辑但正因为递归训练速度慢且长序列下有梯度衰减的风险虽然比原始RNN强不少但也不是无限长。1.2 组合排布方式与我的选型逻辑组合模型不是简单把三层堆起来就行排布顺序直接决定效果。常见的有三种思路。第一种是并行结构TCN、Transformer、LSTM三个分支各提取特征最后拼接。好处是特征来源多样坏处是计算量成倍增加而且三个分支可能学到大量重叠信息实际提升有限。第二种是串行结构先TCN做局部特征提取再把特征序列送入Transformer捕捉全局依赖最后进LSTM做时序递进建模。这种结构是目前组合模型里效果比较稳的我在项目里采用的就是这条路线。原因很直接原始输入先经过TCN的因果膨胀卷积把局部模式提炼成更高层的特征序列同时因为TCN输出的是整个时间步的特征序列Transformer可以继续做全局注意力最后LSTM在已经富含上下文信息的特征上建模递归压力小很多收敛也更快。第三种是残差融合即在主路径外把原始输入或中间特征短接到最后。这个在MATLAB里要多写几行connectLayers但往往能减少深层结构带来的信息损耗。我在代码里保留了第一个TCN残差块的输出作为最终融合特征的一部分实测对提升小样本场景的精度有帮助后面训练配置部分会细说。提示如果你的数据量很小少于几千条不建议TCN、Transformer、LSTM三层全部上很容易过拟合。两层组合比如TCNLSTM往往更稳妥三层组合更适合数据充足、特征复杂的场景。1.3 这个模型适合什么问题不适合什么问题适合的场景有几类多变量、强耦合、带周期性又有长程依赖的时间序列比如区域负荷预测、水文径流预报、多传感器状态监测、股票或商品价格的辅助分析等。这类数据往往是“局部形态 全局周期性 递进演化”三种特性并存单模型很难同时吃透组合结构刚好对症。不太适合的场景我也提醒一句如果数据量只有几百条、特征维度很低、序列长度很短就别凑这个热闹了。组合模型的好处是在特征复杂时体现的简单问题用线性回归或者单一LSTM可能又快又准。另外如果你需要的是严格的可解释模型比如银行风控要被监管审深度学习加SHAP只是“事后解释”替代不了白盒模型。2. 数据预处理不做好模型结构再花哨也白搭2.1 多序列输入怎么组织才符合MATLAB习惯MATLAB的Deep Learning Toolbox对序列输入有两种主流格式一种是cell数组每个元素是一个numFeatures乘numTimeSteps的矩阵适合trainNetwork另一种是dlarray维度标签可以写成“CTB”channel、time、batch适合用dlnetwork做自定义训练。我的项目是回归任务多输入多输出推荐直接用cell数组组织训练数据。假设你有8个输入特征历史窗口长度是30样本总数是5000那么XTrain就是一个1乘5000的cell数组每个cell内部是8乘30的矩阵。对应YTrain同样是一个1乘5000的cell数组如果输出维度是3那每个cell就是3乘1的矩阵代表你要预测的未来三个量。这里有个细节如果输出是未来多个时间步而不是只有一步比如预测未来24小时的负荷曲线那YTrain每个cell应该是输出维度乘预测步数的矩阵相当于把多步预测也当作多输出回归来处理。我的工程里两者都支持代码里通过一个输出维度参数来切换。2.2 归一化参数必须“冻结”这一点最容易翻车做时序预测归一化几乎是必须的。我习惯用z-score归一化原因是对异常值不像min-max那么敏感而且TCN和Transformer里的偏置项对均一尺度更友好。归一化的坑不在计算而在落地训练时算出的均值和标准差必须保存下来用到验证集、测试集和未来新数据上绝对不能重新计算。否则你的预测结果在反归一化时会出现系统性偏移但训练误差看着却很好特别容易误判模型质量。在MATLAB里我是这么做的% 训练集统计量 mu_X mean(X_train_raw, 2); % 按特征维度求均值 std_X std(X_train_raw, 0, 2); mu_Y mean(Y_train_raw, 2); std_Y std(Y_train_raw, 0, 2); % 归一化 X_train (X_train_raw - mu_X) ./ std_X; Y_train (Y_train_raw - mu_Y) ./ std_Y; % 保存到mat文件后续预测时直接load save(normalize_params.mat, mu_X, std_X, mu_Y, std_Y);注意这里都是按特征维度求统计量不是把整个矩阵摊平。原因很简单每个物理量都有自己的量纲和波动范围混在一起归一化等于把特征间的相对关系打乱了。2.3 窗口大小怎么选以及拼接样本时的注意事项窗口大小本质上是“用多长的历史来预测未来”。这个参数对模型效果影响非常大。我的经验判断方法先做自相关分析看目标序列的自相关系数在多少个滞后阶之后掉到不显著这个滞后阶就是窗口的下限。更实操的办法是取目标序列的一个典型波动周期。比如日负荷数据有明显的24小时周期窗口至少应该覆盖一个完整周期取48或者72更好让模型能看到“昨天这个时候”和“前天这个时候”的状况。样本构造还有个容易忽略的点相邻样本高度重叠会造成训练集和验证集信息泄漏。如果按步长为1滑动窗口训练集最后一个样本的窗口里包含了验证集第一个样本的部分时间点。你需要把样本按时间先后切分切分前先洗乱会导致泄漏切分后再对训练集内部洗乱才对。更严格的做法是按时间顺序切出训练集、验证集、测试集且验证集和测试集应该在时间上完全晚于训练集这更符合真实预测场景。3. 在MATLAB里搭TCN、Transformer、LSTM三层模型3.1 TCN层没有内置我的实现思路MATLAB深 learning 工具箱没有现成的TCN层需要自己写。两种方案一是写自定义层classdef继承nnet.layer.Layer二是用convolution2dLayer的因果填充配合膨胀率硬凑。我最终选择了自定义层因为封装性更好方便复用和调参。自定义TCN层的核心是两个属性膨胀率和卷积核大小。关键是因果卷积的填充方式。如果卷积核大小是k膨胀率是d那么为了保证输出长度不变需要在序列左侧填充(k-1)*d个零。注意只填左侧不填右侧这就是因果的体现。MATLAB里convolution1dLayer其实不支持直接设置左侧填充但convolution2dLayer可以通过调整填充大小来模仿。更省事的做法是自己在自定义层的前向传播里用dlconv配合padding参数实现。如果不想写复杂自定义层我提供一个折中方案用普通卷积层做特征提取然后手动裁剪未来端。具体是先把输入序列用零填充卷积后取前numTimeSteps个时间步忽略最后几个因“看到未来”而受污染的输出。虽然不如严格因果卷积干净但工程上也能跑适合快速验证。3.2 Transformer编码器两条可行的落地路径Transformer在MATLAB里的实现路径我试过两条。第一条是用2023a之后新增的transformerLayer它内部包含多头注意力直接用就行但问题是这个层更偏BERT那种编码器风格灵活性有限而且无法方便地拿到注意力权重等中间结果对SHAP分析帮助不大。第二条是自写一个多头注意力自定义层这也是我最终采用的方案。核心是三个步骤输入特征分别通过三个全连接层得到Q、K、V。计算注意力分数除以sqrt(d_k)做缩放然后softmax。注意力权重加权V再过一个输出全连接层。如果你的特征是多个时间步的序列Q、K、V都是[numFeatures, numTimeSteps, batchSize]这种维度MATLAB的dlarray和dlconv操作起来比较顺手。但要注意MATLAB里做批量矩阵乘法时容易把维度搞混建议每步都检查一下size。位置编码我用的是正弦位置编码而不是可学习位置编码。原因有两个一是正弦位置编码可以外推到训练时没见过的序列长度二是省去训练额外参数对防止过拟合有帮助。代码上就是给输入特征加上一个与序列位置相关的矩阵数值量级要控制好在0.1左右太小没效果太大会破坏原始特征分布。3.3 LSTM与输出头的衔接自注意力提取完特征后特征序列进入LSTM层做最后的时序递进建模。这里有个关键选择LSTM的OutputMode要选last还是sequence。我的项目是多输出回归最终要得到的是未来一个或多个目标的预测值所以LSTM层之后不需要输出整个序列选last就可以只取最后一个时间步的隐藏状态。LSTM隐藏单元数我一般设置在32到128之间。这个参数过大容易过拟合过小则丢失时序模式。一个可行的做法是从64起步观察验证集误差如果欠拟合就往128调整如果过拟合就往32调。输出头的设计上如果用trainNetwork直接在LSTM后面接一个fullyConnectedLayer输出节点数等于你的输出维度。如果做多步预测比如预测未来24个值就设24个输出节点。这里不建议在最后加激活函数回归任务用线性输出就好让模型自己学输出范围。组合模型的整体结构在MATLAB里一般用layerGraph来搭lgraph layerGraph(); lgraph addLayers(lgraph, sequenceInputLayer(numFeatures, Name, input)); lgraph addLayers(lgraph, tcnBlock1); % 自定义TCN残差块 lgraph addLayers(lgraph, attentionLayer); % 自定义多头注意力层 lgraph addLayers(lgraph, lstmLayer(64, OutputMode, last, Name, lstm)); lgraph addLayers(lgraph, fullyConnectedLayer(numOutputs, Name, fc)); lgraph addLayers(lgraph, regressionLayer(Name, output)); lgraph connectLayers(lgraph, input, tcn_in); lgraph connectLayers(lgraph, tcn_out, att_in); lgraph connectLayers(lgraph, att_out, lstm); lgraph connectLayers(lgraph, lstm, fc); lgraph connectLayers(lgraph, fc, output);这只是最简结构。实际工程里我在TCN层之间加了层归一化和dropoutTransformer和LSTM之间也加了dropout这些对稳定训练帮助很大。3.4 训练配置与防过拟合训练配置上我推荐adam优化器初始学习率设在1e-3左右配合余弦退火或者分段下降。mini-batch大小取决于序列长度序列越长每个样本占的内存越大64是个比较稳的起点。早停是必须的。MATLAB的trainingOptions里没有像Keras那样的EarlyStopping回调但可以设置ValidationPatience比如连续20轮验证误差不下降就停止。我还会把OutputNetwork设为best-validation这样训练结束后自动恢复到验证集表现最好的那一轮权重而不是最后一轮。dropout的设置不能一视同仁。TCN部分dropout设0.1到0.2Transformer的注意力层后设0.2左右LSTM输入到输出的过渡层设0.2。如果你数据量不大dropout太高会欠拟合这个要观察训练曲线动态调节。4. 多输出回归的损失计算与评价指标4.1 多输出的两种实现模式代码上是如何切换的多输出在代码层面其实很简单就是输出层的节点数大于1。但有一个隐藏问题如果你直接让模型输出多个值而它们之间的尺度差异很大那回归损失会被量级大的那个输出主导。比如同时预测温度和负荷温度数值在20到30负荷在几千模型会优先拟合负荷而忽略温度。解决方案有两个。一个是在预处理阶段就把每个输出特征分别归一化到均值为0方差为1这样每个输出的损失贡献是等权的。另一个是自定义加权损失函数给不同输出分配不同的权重。我的项目里是前者因为代码更简洁效果也足够好。用trainNetwork时回归层默认用均方误差损失也就是MSE。如果你需要自定义损失那就得改用dlnetwork加自定义训练循环。这条路更灵活但代码量明显增加适合对损失函数有特殊要求的读者。4.2 评价指标不要只看MSE多输出要单看再加总看多输出回归的评价我建议先算每个输出各自的MAE、RMSE和R2再算一个综合指标。单看综合指标会掩盖某个输出表现差的问题只看单个输出又看不清整体。表格对比是最直观的输出变量MAERMSER2输出10.821.150.94输出23.455.020.87输出31.261.820.91R2的计算在MATLAB里没有内置函数自己写也就几行function r2 computeR2(yTrue, yPred) ss_res sum((yTrue - yPred).^2, all); ss_tot sum((yTrue - mean(yTrue, all)).^2, all); r2 1 - ss_res / ss_tot; end另一个实战经验是画“预测值-真实值”散点图理想情况下点应该贴在对角线yx附近。如果散点显示预测值普遍偏低或偏高那多半是反归一化或者训练集与测试集分布不一致的问题这个需要回到数据预处理去排查而不是调模型。5. SHAP可解释性分析在MATLAB里的落地5.1 MATLAB里做SHAP的三条路径这是标题里最吸引人的部分也是很多人问得最多的。先说结论MATLAB没有完整的DeepSHAP官方实现但有三条可行路径按推荐程度排序。第一条是Statistics and Machine Learning Toolbox的shapley函数。这个函数不是专门为深度学习设计的但你可以先把预测结果用可解释的简单模型近似再对近似模型算SHAP值。这种做法在工程上叫“代理模型”虽然不够精确但胜在一键可跑适合快速出结果。第二条是用自定义蒙特卡洛采样估计Shapley值。这个思路是对于每个待解释样本随机挑一批背景样本然后对特征子集做组合采样计算加入某个特征前后的预测差异多次平均就是该特征的近似Shapley值。这个方案在MATLAB里实现不难但计算量巨大特征多、背景样本多时非常慢。第三条是使用积分梯度Integrated GradientsMATLAB从R2022a起在Deep Learning Toolbox里提供了integratedGradients函数。积分梯度虽然和SHAP的数学原理不完全相同但它同样能给每个输入特征分配一个归因分数反映该特征对预测结果的贡献方向与大小。我在工程里最终用的就是这条路因为它在保证解释性的前提下计算效率比蒙特卡洛SHAP高了一个数量级对深度网络的适应性更好。5.2 特征归因结果的解读示例假设我的输入是8个特征包括历史负荷、温度、湿度、风速、节假日标志等模型要预测未来一小时负荷。跑完积分梯度后得到一个1乘8的归因向量正值代表该特征推动预测值升高负值代表推动预测值降低。举个实际结果温度特征的归因值是0.75说明当前时刻温度对负荷预测有显著正向影响符合常识——气温升高会推高制冷负荷。节假日标志的归因值是-0.92说明非工作日对负荷是明显的负向作用也符合规律。风速的归因值接近0提示这个特征在预测任务里贡献有限可以考虑在未来模型简化时去掉。这里有个重要提醒SHAP值反映的是“模型从数据里学到的关联”不是因果关系。如果数据本身有偏SHAP值就会放大这个偏置。比如训练数据里高温时段恰好都是工作日模型可能把温度和工作日混在一起SHAP值会同时给两者分配高贡献。5.3 从SHAP结果反推模型和特征工程SHAP分析不该只是画一张图交差更该用来指导模型迭代。我在工程里总结了一套流程先统计所有测试样本的SHAP均值绝对值按贡献大小排序。贡献最大的前三个特征通常就是核心输入贡献接近零的特征可以考虑剔除输入维度降下来训练速度和稳定性都会提升。然后是检查方向是否合理。如果某个特征的SHAP方向跟领域常识相反说明数据里可能存在异常或者特征之间存在高耦合这时候要回到数据仔细排查。还可以做样本级别的对比分析。选取两个相邻时间点的预测样本对比它们的SHAP值分布能直观看到哪些特征的变化驱动了预测结果的跳变。这在负荷预测里特别实用比如凌晨两点负荷突然上升SHAP分析可能会显示温度特征贡献突变再结合气象数据就能解释是不是冷空气过境导致的。注意如果最终要出报告一定要写清楚SHAP值是在哪个测试集、哪个时间范围内统计的。SHAP值对输入分布敏感换一批样本归因结果可能就有变化不标注上下文的数据很容易被误读。6. 新数据预测与模型保存加载的完整链路6.1 保存模型和归一化参数一个都不能少训练结束后要部署到新数据上预测至少保存三样东西模型本身、归一化参数、特征顺序。模型用save函数存成mat文件归一化参数就是我第二节提到的mu_X、std_X那组变量。特征顺序很多人忽略但实测非常关键。如果训练时特征是[温度, 湿度, 负荷]预测时新数据给的是[负荷, 湿度, 温度]模型输出就完全错了而且这种错很难察觉因为代码不报错、数值也有输出只是结果没意义。我习惯把特征列名写成一个字符串数组和归一化参数一起存featureNames {temp, humidity, wind, load}; save(model_artifacts.mat, net, mu_X, std_X, mu_Y, std_Y, featureNames);6.2 新数据预测的处理步骤新数据预测的完整流程是读取新数据、按特征顺序对齐、用保存的归一化参数做变换、按相同窗口大小构造样本、送入模型预测、反归一化、输出结果。关键点是窗口构造方式必须跟训练时完全一致。如果训练时窗口是30步、步长是1预测时也要用过去30步作为输入预测目标落在第31步。如果你想预测未来24小时而模型是单步输出那就需要滚动预测用预测出的第1步结果拼到输入末尾去掉最老的一步再预测第2步以此类推。滚动预测容易累积误差我的经验是预测步数越远最好把不确定性也附带输出比如用多次dropout得到预测区间这个MATLAB里可以通过开启预测时的dropout来实现。反归一化是最容易出问题的地方。预测输出是归一化尺度要得到实际值必须乘以std_Y再加上mu_Y。如果你的输出是多个变量这里一定要逐维度对齐别把输出1的std用到了输出2上。6.3 新数据分布漂移最隐蔽的坑新数据和训练数据分布不一致很多模型上线后效果变差都是这个原因。一个简单有效的检测方法是预测前先算新数据内部特征的均值和方差跟训练时的mu_X、std_X对比。如果偏差超过一定阈值比如均值偏差超过1.5倍标准差那就要警惕了可能需要重新训练或者做迁移学习。这个检查在MATLAB里几行就能实现new_mean mean(new_X, 2); new_std std(new_X, 0, 2); deviation abs(new_mean - mu_X) ./ std_X; if any(deviation 1.5) warning(新数据存在明显分布漂移请检查输入特征范围。); end7. 实测中踩过的坑与提速经验7.1 训练慢到怀疑人生先排查这三个问题使用MATLAB训练这种三层组合模型速度慢几乎都会遇到。我踩过最典型的三个坑是第一个是CPU训练没开GPU。设置trainingOptions时要显式指定ExecutionEnvironment, gpu前提是你装了Parallel Computing Toolbox并且有NVIDIA显卡。没有GPU的话这个模型训练起来会非常痛苦尤其是数据量大、序列长的时候。第二个是自定义的Transformer注意力层可能没有开启编译优化。MATLAB默认的自动优化有时候不会覆盖自定义层导致前向传播非常慢。可以尝试用dlarray fullyconnect softmax的组合这些内置操作更容易被优化。第三个是mini-batch大小设得过大导致内存溢出。Transformer的注意力矩阵是[batchSize, numTimeSteps, numTimeSteps]序列长度100、batchSize128时注意力矩阵就是128乘100乘100再乘多个头内存涨得飞快。把batchSize降到32或者16往往速度和稳定性都会提升。7.2 验证集误差震荡怎么判断该不该停组合模型的验证曲线通常不如单模型平滑尤其是Transformer部分的注意力权重还在动态调整时验证误差可能出现锯齿状波动。这时候不要一看到验证误差上升就马上停止也不要全等patience参数自动停。我自己的判断标准是观察训练误差是否还在下降如果训练误差下降而验证误差震荡可能是学习率偏大先降一半试试。如果训练误差和验证误差同时停滞那可能是模型容量不够考虑加宽LSTM或增加注意力头数。如果训练误差持续下降、验证误差持续升高那就是过拟合此时早停才是正确的选择。7.3 没有对比实验的模型工程是不完整的最后我强烈建议在提交结果或写报告时把单模型和组合模型的指标放在一起对比。没有对比组合模型到底提升了多少心里是没底的。我在工程里通常跑四组单独TCN、单独LSTM、TCNLSTM、TCNTransformerLSTM。表格一列出来提升非常直观。模型测试MAE测试RMSE测试R2单独LSTM2.313.420.82单独TCN2.083.110.85TCNLSTM1.762.640.89TCNTransformerLSTM1.532.280.92最后再分享一个小技巧如果发现组合模型相对TCNLSTM提升很小比如不到百分之三那就要反思是不是数据本身根本不存在长程依赖Transformer这一层是多余的。这种情况下砍掉Transformer反而能让模型更轻量、训练更快。模型不是越复杂越好组合的每一层都应该有它存在的理由而SHAP和对比实验恰恰能帮你验证这个理由是否成立。本文还有配套的精品资源点击获取