项目介绍 MATLAB实现基于TCN-BiGRU-Transformer-SHAP时间卷积双向门控循环单元(TCN-BiGRU)结合Transformer编码器和SHAP值分析方法(SHAP)进行多输
目录
构建可在MATLAB R2025b环境中稳定运行的工程级实现方案... 4
MATLAB实现基于TCN-BiGRU-Transformer-SHAP时间卷积双向门控循环单元(TCN-BiGRU)结合Transformer编码器和SHAP值分析方法(SHAP)进行多输入多输出回归预测的详细项目实例
请注意此篇内容只是一个项目介绍 更多详细内容可直接联系博主本人
或者访问对应标题的完整博客或者文档下载页面(含完整的程序,GUI设计和代码详解)
多输入多输出时间序列回归问题在工业过程控制、能源负荷预测、交通流量监测、环境质量评估和金融风险管理等场景中极为普遍。生产过程中的多个传感器会同时输出高维时间序列,管理者希望利用这些历史数据预测未来多个关键指标,以实现提前干预和精细化控制。传统建模方法往往基于单变量或少量变量的统计模型,例如自回归积分滑动平均模型或向量自回归模型,这些模型在处理高维非线性、多变量耦合和长期依赖问题上存在明显局限,难以准确捕捉复杂系统内部的动态关系。随着数据规模的增长和系统复杂度的提升,迫切需要更具表达能力、更具鲁棒性的深度学习方法来刻画多变量时间序列的动态模式。
在深度学习范式中,卷积神经网络和循环神经网络是处理序列任务的两类核心模型。卷积结构擅长从局部片段中提取模式,计算效率高,且具有良好并行性;循环结构通过隐藏状态在时间维度上传递信息,理论上能够捕捉长程依赖,但在实践中容易面临梯度消失、训练不稳定和推理效率不足等问题。近年来提出的时间卷积网络采用扩张卷积与因果结构,在保证时间顺序不被破坏的前提下,利用多层级的卷积扩大感受野,兼顾并行性和长期依赖建模能力。然而,仅依靠卷积结构处理多变量复杂耦合关系仍然不够充分,对于时序中的方向性和上下文语义信息,门控循环单元以及其双向变体仍然具有独特优势,可以从正向和反向时间维度同时提炼动态特征。
在序列建模领域崛起的Transformer结构,则从根本上改变了对长期依赖的建模思路。自注意力机制通过计算序列各位置之间的相关性权重,从而在一次计算中直接聚合全局信息,不再依赖链式传递,从而避免了长期依赖问题并提高了并行效率。与传统循环网络相比,自注意力结构更加适合捕捉复杂的多变量交互关系和长期模式。尽管最早用于自然语言处理,自注意力与位置编码结合之后,同样适用于时间序列建模,尤其是在高维多变量输入和多输出预测场景下,可以更灵活地聚焦关键时刻和关键变量。
然而,在实际工程应用中,单一模型往往无法兼顾不同层次的时间特征。例如,时间卷积网络擅长提取局部模式并在多尺度时间窗口下学习动态结构,而双向门控循环单元能够捕捉上下文信息与序列方向性,Transformer编码器则在全局范围内学习变量间的复杂依赖。将这三类结构合理融合,可以构建一种既能高效感知局部细节,又能抓住整体趋势,同时保证特征交互充分的混合架构。对于多输入多输出问题,这种融合架构能够在时间维度与变量维度上同时进行深度建模,使得预测结果更为准确稳定。
与此同时,可解释性需求也成为复杂深度模型落地应用过程中的关键约束。在工业生产与能源管理等领域,使用者不仅关心预测数值本身,还关心模型为何做出某一预测,哪些输入变量对结果贡献最大,某一时刻的异常波动是否驱动了预测结果的大幅变化。若缺少解释机制,即使预测精度极高,也难以获得领域专家的信任,更难以在安全敏感型领域中被接受。基于Shapley值思想构建的SHAP分析方法,为解释复杂模型输出提供了统一的理论框架。通过量化每个输入特征对预测结果的边际贡献,SHAP值能够在全局和局部两个层面揭示模型的决策逻辑,从而帮助使用者理解模型行为、发现异常模式以及优化特征工程。
在多输入多输出时间序列场景下,引入SHAP分析具有特殊价值。一方面,多变量输入中存在大量冗余与高度相关特征,借助SHAP可以量化不同变量、不同时间步甚至不同维度特征对各个输出目标的贡献,帮助进行特征筛选和传感器配置优化;另一方面,在模型误差较大或预测结果异常的案例上,SHAP值可以用于诊断问题:识别导致偏差的关键因素,为工程人员提供针对性的改进方向。此外,SHAP所提供的可视化手段(特征重要性排序、散点图、力图等)能够将复杂的高维模型决策过程转化为直观、易理解的形式,显著降低跨学科沟通的门槛。
结合时间卷积网络、双向门控循环单元和Transformer编码器,再辅以SHAP可解释性分析,可以构建一个兼具精度、效率和透明度的多变量时间序列预测框架。时间卷积负责捕捉不同时间尺度上的局部与中程信息,双向门控循环单元强化对历史序列上下文语境的理解,Transformer编码器则在全局范围内聚焦重要时间片与变量组合,并通过多头注意力机制实现信息的细粒度交互。最终,在模型训练完成后,以SHAP为工具,对训练集与测试集样本进行贡献度计算,将高维预测过程拆解为可量化、可追踪的特征影响路径。此框架在MATLAB R2025b环境下实现,既可利用成熟的数值计算与可视化能力,又能通过自定义深度学习层构造灵活网络结构,为学术研究与工程实践提供一套具有推广性的解决方案。
项目目标与意义
提升多输入多输出时间序列预测精度
核心目标之一是在多输入多输出时间序列回归场景中显著提升预测精度和稳定性。多变量时间序列往往具有强耦合、强非线性和噪声干扰,传统线性或单一结构的深度模型难以同时兼顾不同时间尺度的动态变化与多变量联动效应。通过构建TCN-BiGRU-Transformer混合网络,利用时间卷积段的扩张卷积处理局部波动和多尺度趋势,双向门控循环单元强化上下文编码,再由Transformer编码器通过自注意力机制进行全局特征聚合,可以针对不同时间粒度和不同变量之间的复杂关系实现分层建模。在多输出场景中,该架构可以共享底层时间特征,同时在高层输出映射中适配不同目标变量的特性,避免为每个输出单独训练模型所带来的资源浪费和信息割裂。通过合理设计损失函数、正则化和训练策略,使模型在面对噪声、缺失值和异常点时仍能保持较好的鲁棒性,进一步提升预测的可靠性,为复杂系统的状态评估和预警提供有力支撑。
实现模型决策过程的可解释性与透明化
在深度学习模型不断走向复杂化的现实背景下,另一个关键目标是使模型的预测过程从“黑箱”转变为“可解释的灰箱”。对于工业生产、金融风控和公共安全等高风险场景,模型预测不仅要准确,更必须可解释,才能在决策链条中被真正采纳。通过引入基于Shapley值理论的SHAP分析方法,为混合深度网络的输出提供统一的贡献度分解框架。具体而言,通过计算每个输入特征在不同时间步上对特定输出预测值的边际贡献,形成一套可量化的解释指标。这样,可以识别在特定预测任务中哪些传感器变量、哪些历史时间窗口、哪些特征组合最为关键。此信息可以用于向领域专家展现模型的关注焦点,也可以在模型出现异常行为时追溯源头,从而提高使用者对模型的信任度。可解释性还支持模型治理和合规性要求,在需要审计决策过程的行业环境中更加重要。
构建可在MATLAB R2025b环境中稳定运行的工程级实现方案
目标还包括在MATLAB R2025b环境中构建一套可复现、可扩展、可维护的工程级实现方案。该目标不仅涉及网络结构的设计,还包含对该版本新特性与限制条件的适配。R2025b在深度学习工具箱中的部分接口与旧版本存在差异,例如不再提供高层级Transformer编码层、部分传统界面组件被弃用、某些统计学习函数的参数限制变更等。项目旨在围绕attentionLayer、自注意力层和位置编码层构建可定制的Transformer编码器模块,同时使用dlnetwork进行灵活训练与推理,避免依赖已不推荐或已移除的接口。通过清晰的代码组织和合理的模块划分,使整体实现便于后续扩展,例如增加多头注意力变体、引入残差连接或更复杂的归一化策略,并确保在R2025b环境下整个训练和解释流程能够稳定运行。实现这一目标有助于在工程实践中推广基于该框架的预测系统。
为多领域复杂系统提供统一的建模与解释框架
最终目标面向应用层面,希望构建一套具有通用性和可迁移性的建模与解释框架。多输入多输出时间序列的预测需求并不局限于单一行业,无论是智能制造产线的质量指标预测、电力系统的多节点负荷与电压预测、城市交通多路段流量预报,还是空气质量多污染物浓度预测,本质上都是多变量时序映射问题。通过整合TCN、BiGRU和Transformer编码器,使模型在多领域数据结构上都具备较好的适应性;通过SHAP值分析,则为领域专家提供统一的解释维度,可以在不同场景下对比特征作用方式,挖掘共性规律和差异特征。此框架不仅服务单一项目,更可作为一类通用解决方案,为研究者和工程技术人员提供可直接迁移的范式,降低在新领域中重新设计模型和解释机制的重复工作成本,推动多领域数据驱动决策水平的整体提升。
项目挑战及解决方案
多尺度时间依赖与多变量耦合建模的难度与应对策略
多输入多输出时间序列数据同时具有多尺度时间依赖和多变量耦合特性。短期内可能存在强烈的局部波动,如生产线短时振动或负荷突变;中期和长期则可能呈现周期性变化与缓慢趋势叠加,如昼夜周期、周周期和季节变化。不同时间尺度上的信息相互交织,使得单一感受野或单一建模结构难以充分捕捉所有相关模式。此外,多变量之间往往存在复杂耦合。例如,某些传感器变量在不同工况下的相关性会发生显著变化,部分变量可能在特定时间尺度上具有强影响,而在其他时段则作用有限。传统基于固定窗口的模型只能在局部范围内观察数据,无法在不同时间尺度之间动态切换,导致对系统真实动态的理解存在偏差。应对这一挑战的策略是利用时间卷积网络的扩张卷积机制构建多尺度时间感受野,通过多层级堆叠使每一层捕捉不同时间尺度的信息。每层卷积使用不同的扩张率,低层专注短期依赖,高层则覆盖更长时间跨度。进一步在时间卷积之后引入双向门控循环单元,通过从前向和后向两个方向遍历时间序列,对整个历史片段的上下文信息进行整合,增强模型对非局部模式和复杂耦合关系的捕捉能力。最后,引入Transformer编码器,通过自注意力机制在全局时间范围内重新加权不同时间步和变量的贡献,使模型能够根据预测目标与输入状态自适应聚焦关键时间片和变量组合,从而缓解多尺度依赖与多变量耦合建模难题。
模型训练复杂度与过拟合风险控制策略
TCN-BiGRU-Transformer混合架构结构复杂,参数量较大,训练过程需要处理长序列、多变量和多输出目标,计算开销较高,同时存在明显的过拟合风险。多层时间卷积、双向循环结构和多头注意力层叠加后,网络深度与宽度显著增加,若训练数据规模不足或数据质量不高,模型很容易记住训练样本的噪声而难以在测试数据上泛化。针对训练复杂度问题,需要在MATLAB R2025b环境中充分利用GPU加速与小批量训练机制,合理设置mini-batch大小以兼顾显存占用和梯度估计稳定性,并通过学习率调度策略提高训练效率。针对过拟合风险,需要在网络各层中引入适当的正则化手段,例如在卷积层与BiGRU层后加入dropout,在注意力层输出后进行随机失活,并使用权重衰减约束参数模长。此外,通过划分训练集、验证集和测试集,利用验证集监控训练过程中的性能变化,结合早停策略在验证误差不再下降时停止训练,防止网络在训练集上过度拟合。多输出任务下,也可通过加权损失或多任务学习思想,让不同输出变量共享部分结构,从而间接加强模型的泛化能力。对于参数初始化与归一化方式,则需要根据数据统计特性选择合适方案,避免训练初期梯度爆炸或梯度消失,提高整体训练过程的稳定性。
可解释性分析的计算成本与可视化表达策略
在引入SHAP值进行可解释性分析时,面临两个主要挑战。一方面,在高维多变量时间序列场景中,每个样本包含大量时间步和变量,若对每个特征维度逐一计算精确Shapley值,将面临极高的计算复杂度,难以在合理时间内完成。尤其是对深度网络的解释通常需要大量模型评估,若不加控制,会显著延长部署和分析周期。另一方面,即便获得了SHAP值,在多输入多输出情形下,解释结果维度极高,直接呈现所有特征在所有时间步上的贡献很难让使用者理解,容易造成信息过载。针对计算成本问题,可以采用近似SHAP算法,通过采样特征子集和使用背景数据分布替代全局组合枚举,在可接受误差范围内显著降低计算量。同时,对解释重点进行筛选,例如只针对关键时间片、关键输出变量或典型样本进行精细分析,对其他样本采用更简化的指标。针对可视化表达问题,需要设计多层次的解释呈现方式:在全局层面,可以给出各变量在整体预测任务中的平均SHAP重要性,帮助识别最重要传感器和特征;在局部层面,可针对单个样本给出时间维度上的贡献曲线,展示特定预测结果与历史若干时刻之间的因果关系;在输出维度上,可以单独分析每个目标变量对应的特征贡献结构,让使用者分别理解不同目标的驱动机制。通过合理的降维、聚合和可视化策略,使SHAP分析既保持足够细致,又不至于过度冗杂,从而真正发挥可解释性在模型评估和决策支持中的价值。
项目模型架构
时间卷积网络(TCN)模块
时间卷积网络模块是整体架构中负责捕捉局部与多尺度时间依赖的基础组件。与传统卷积不同,时间卷积网络采用因果卷积结构,确保输出在任一时间步上只依赖于当前及之前的输入,从而保持时间顺序的合理性。为了扩大感受野并避免简单增加卷积层数导致参数膨胀,时间卷积网络引入扩张卷积,通过在卷积核内部插入间隔,使得卷积核在时间轴上的覆盖范围指数级增长。具体做法是为每一层设定不同的扩张率,第一层扩张率为1,第二层为2,第三层为4,以此类推。这样,网络在保持固定卷积核大小的情况下,可以在较少层数下覆盖长时间片段,从而有效建模长期依赖。
在该项目中,时间卷积部分可以设置为多个残差块串联,每个残差块中包含两层扩张卷积以及非线性激活和归一化操作,并通过跳连结构将输入直接与输出相加。跳连设计有助于缓解深层网络中的梯度消失问题,同时也使训练过程更加稳定。对于多输入时间序列,需要对每个时间步上的多维特征同时进行卷积操作,即将多变量视作通道维度输入卷积层,从而使卷积核能够在时间和变量两个维度上联合提取模式。通过适度控制卷积层数量、通道数和扩张率,可以在保证预测性能的前提下控制模型复杂度。
双向门控循环单元(BiGRU)模块
在时间卷积网络提取出多尺度局部特征之后,双向门控循环单元模块进一步从序列层面抽取上下文信息。门控循环单元结构包含更新门和重置门,通过门控机制决定前一隐藏状态在当前时刻的保留程度以及新的输入信息与历史信息的融合方式,相比标准循环网络在长期依赖建模上更有优势。双向结构则通过在时间维度上构建两个平行的门控网络,一个从前到后处理序列,另一个从后到前处理序列,最终将两者的隐藏状态进行拼接或加权。这种双向扫描方式可以让网络在编码某一时刻的表示时同时考虑之前和之后的上下文信息,提高特征表达的充分性。
在多输入多输出预测问题中,BiGRU模块接收来自TCN模块的时间序列特征,对每个时间步提取序列上下文表示。前向GRU为每个时刻提供基于历史信息的表示,后向GRU为每个时刻提供基于未来信息的补充,这种综合表示更有利于捕捉长程依赖和模式反复出现的规律。在输出端,可以根据任务需求选择提取最后一个时间步的拼接隐藏状态作为整体序列表示,或使用所有时间步的隐藏状态序列供后续注意力模块使用。通过调整GRU的隐藏单元数量和层数,可以平衡模型表达能力和计算成本。BiGRU与TCN的组合使得模型既具备卷积的并行效率,又保留序列模型对顺序信息的敏感性。
Transformer编码器模块
Transformer编码器模块通过自注意力机制对序列特征进行全局建模,是整个架构中负责捕捉复杂依赖关系和跨时间步交互的关键组件。自注意力机制的核心思想是通过查询、键和值的线性变换,将序列中的每个位置与其他所有位置计算相似度,并据此对其他位置的表示进行加权求和,从而为当前时刻构建融合全局信息的新表示。多头自注意力则通过多个平行的注意力头在不同的子空间中学习不同类型的依赖模式,然后将各头输出拼接后进行线性变换,提升模型对多样化关系的表达能力。
在MATLAB R2025b中,可基于attentionLayer或自注意力层与位置编码层手动构造Transformer编码器结构。编码器块通常由多头自注意力子层和前馈全连接网络子层组成,并在每个子层中加入残差连接和归一化操作。位置编码用于弥补自注意力对位置信息不敏感的问题,通过为每个时间步添加显式的位置信号,使模型能够区分不同时间位置的特征。在该项目中,Transformer编码器接受来自BiGRU模块的序列表示,通过多头自注意力对整个时间维度进行信息重分配,使关键时间步在下游任务中获得更高权重。对于多输入数据,注意力机制可以在全局范围内对不同变量的贡献进行重加权,使模型更灵活地处理变量间复杂耦合关系。编码器输出将作为多输出回归头的输入,为每个目标变量提供丰富且全局一致的表征。
多输入多输出回归头设计
模型的回归头负责将高维时序特征映射到具体的多输出预测结果。多输入意味着每个时间步具有多个特征维度,多输出则意味着模型需要同时给出多个目标变量在未来某个或多个时间步上的预测值。回归头设计需要考虑如何充分利用当前序列特征,同时保持输出结构的清晰与可解释。一般而言,可以对Transformer编码器的输出在时间维度上进行池化,例如采用平均池化或加权池化,将一整个序列的特征总结为一个固定长度的向量,再通过全连接层映射到多输出空间。若希望保留时间维度上的细粒度信息,也可以选择取最后一个时间步或特定时间步的编码器输出作为序列表示。
在多输出场景中,回归头可以采用共享底层、分支输出的结构,即首先通过一个共享的全连接层对编码器输出进行降维或融合,然后为每个输出变量设计独立的小全连接层,以适应不同目标变量的特性和尺度。也可以采用单一全连接层同时输出所有目标变量的预测值,在输出维度上统一处理。为了提高数值稳定性和训练效率,可以针对不同输出变量采用归一化处理,将不同量纲的输出缩放到类似范围,再在预测后进行反归一化。损失函数通常选用均方误差或加权均方误差,若不同输出变量的重要性不同,可以对各变量损失赋予不同权重。回归头是连接深度特征学习部分与实际业务需求的桥梁,合理的设计能够使上游网络学到更有用的特征,提升整体预测性能。
SHAP可解释性分析模块
SHAP可解释性分析模块在模型训练完成后启动,对训练好的TCN-BiGRU-Transformer网络进行特征贡献分解。SHAP基于博弈论的Shapley值概念,将模型的预测视作所有特征参与合作博弈的“收益”,对每个特征在所有特征子集组合中的边际贡献进行加权平均,理论上可以保证公平性和一致性。在深度学习模型场景中,通常采用近似算法,例如基于采样的Kernel SHAP或针对特定网络结构的Deep SHAP等方法。多输入多输出时间序列的特点在于特征维度巨大,每个时间步的每个变量都可以视为一个特征,因此需要在实施SHAP分析时进行合理的特征抽象与压缩,以减轻计算负担。
在该项目中,SHAP模块的主要任务包括:为选定的样本集生成SHAP值矩阵,解释每个输入变量在各时间步上对每个输出变量的贡献;对SHAP值进行聚合分析,计算特征在全局范围内的平均绝对贡献度,从而得到特征重要性排序;针对特定输出变量和特定数据样本生成局部解释图,例如力图或贡献度条形图,帮助分析某一预测结果是如何由不同历史输入驱动形成的。在MATLAB环境中,可通过编写函数封装SHAP计算过程,使用模型对不同特征子集的输入进行预测,并根据Shapley值理论对多个预测结果进行组合。可视化方面可以采用散点图、条形图和热力图等形式展现多维信息,使特征贡献在时间和变量两个维度上的变化直观呈现。SHAP模块不仅为模型评估和调试提供支持,还能为实际业务提供洞察,例如指导传感器布置优化或特征工程改进。
项目模型描述及代码示例
数据预处理与多输入多输出构造示例
clear; % 清理工作区变量,避免历史变量干扰当前实验
clc; % 清空命令行窗口,提高输出阅读性
numFeatures = 4; % 输入特征维度数量,对应多输入变量个数
numOutputs = 3; % 输出目标变量数量,对应多输出回归任务
seqLen = 24; % 设置使用的历史时间窗口长度,例如24个时间步
t = (1:numSamples)'; % 构造时间索引向量,为合成数据提供基础自变量
x1 = sin(2*pi*t/24) + 0.1*randn(numSamples,1); % 第一个特征:带噪声的日周期正弦序列
x2 = cos(2*pi*t/168) + 0.2*randn(numSamples,1); % 第二个特征:周周期余弦序列叠加噪声
x3 = 0.001*t + 0.3*randn(numSamples,1); % 第三个特征:缓慢上升趋势加高斯噪声
x4 = 0.5*randn(numSamples,1); % 第四个特征:纯噪声变量,用于测试特征选择能力
Xall = [x1 x2 x3 x4]; % 将四个特征按列拼接,形成多维输入特征矩阵
y1 = 0.6*x1 + 0.3*x2 + 0.1*x3 + 0.1*randn(numSamples,1); % 第一个输出:线性组合特征加噪声
y2 = 0.4*x2.^2 + 0.2*x3 + 0.3*x4 + 0.1*randn(numSamples,1); % 第二个输出:引入平方项体现非线性关系
y3 = 0.5*sin(x1) + 0.3*cos(x2) + 0.2*x3 + 0.1*randn(numSamples,1); % 第三个输出:通过正弦余弦引入复杂非线性映射
Yall = [y1 y2 y3]; % 将三列输出组合成多输出目标矩阵
muX = mean(Xall,1); % 计算输入特征各列均值,用于后续标准化
sigmaX = std(Xall,0,1) + 1e-6; % 计算输入特征标准差,避免除零情况
Xnorm = (Xall - muX)./sigmaX; % 对全部输入特征进行标准化处理
muY = mean(Yall,1); % 计算输出目标各列的均值,便于输出归一化
Ynorm = (Yall - muY)./sigmaY; % 对输出目标进行标准化,提升训练稳定性
numSeq = numSamples - seqLen; % 计算可构造的监督样本条数
Xseq = zeros(numSeq, seqLen, numFeatures); % 初始化三维数组存放输入序列样本
Yseq = zeros(numSeq, numOutputs); % 初始化二维数组存放对应输出目标
idxStart = i; % 当前序列的起始时间索引
idxEnd = i + seqLen - 1; % 当前序列的结束时间索引
Xseq(i,:,:) = Xnorm(idxStart:idxEnd,:); % 将该时间窗口的多维特征填入三维输入数组
Yseq(i,:) = Ynorm(idxEnd,:); % 使用窗口末尾时刻的归一化输出作为预测目标
end % 完成时间窗口滑动构造多输入多输出监督数据集
numTrain = floor(0.7*numSeq); % 将70%的样本划为训练集
numVal = floor(0.15*numSeq); % 将15%的样本划为验证集
numTest = numSeq - numTrain - numVal; % 剩余部分划为测试集,用于评估泛化性能
idxTrain = 1:numTrain; % 训练集样本索引范围
idxVal = numTrain+1:numTrain+numVal; % 验证集样本索引范围
idxTest = numTrain+numVal+1:numSeq; % 测试集样本索引范围
Ytrain = Yseq(idxTrain,:); % 提取训练集输出目标
Yval = Yseq(idxVal,:); % 提取验证集输出目标
Xtest = Xseq(idxTest,:,:); % 提取测试集输入序列
Ytest = Yseq(idxTest,:); % 提取测试集输出目标
XtrainDL = dlarray(permute(Xtrain,[2 3 1]),"CTB"); % 将训练集输入维度调整为时间×通道×批次并封装为深度数组
XvalDL = dlarray(permute(Xval,[2 3 1]),"CTB"); % 将验证集输入序列转换为适配网络的张量格式
YtrainDL = dlarray(Ytrain',"CB"); % 将训练集输出转置为输出维度×批次格式
YvalDL = dlarray(Yval',"CB"); % 将验证集输出转为深度数组形式
YtestDL = dlarray(Ytest',"CB"); % 将测试集输出封装为深度数组,供评估使用
TCN模块结构定义与代码示例
function layers = buildTCNBlock(numChannels, kernelSize, dilationFactor) % 定义函数构建单个TCN残差块
layers = [ ]; % 初始化空层数组,用于逐步追加子层
conv1 = convolution1dLayer(kernelSize, numChannels, ... % 创建第一层一维卷积层,卷积核大小为指定值
Padding="causal", ... % 使用因果卷积方式,保证输出不会看到未来信息
DilationFactor=dilationFactor, ... % 使用扩张卷积因子控制感受野大小
WeightsInitializer="he"); % 使用He初始化方法增强训练稳定性
relu1 = reluLayer; % 创建第一层ReLU激活函数层
relu1.Name = "tcn_relu1_d"+string(dilationFactor); % 命名第一个激活层丰满网络结构标签
bn1 = layerNormalizationLayer; % 创建层归一化层,用于稳定特征分布
bn1.Name = "tcn_ln1_d"+string(dilationFactor); % 为第一层归一化层命名方便网络可视化
conv2 = convolution1dLayer(kernelSize, numChannels, ... % 创建第二层一维卷积层
Padding="causal", ... % 同样使用因果卷积保证时间因果性
DilationFactor=dilationFactor, ... % 使用相同扩张率以保持残差块结构对称性
WeightsInitializer="he"); % 使用He初始化提高深层网络的训练效果
conv2.Name = "tcn_conv2_d"+string(dilationFactor); % 为第二卷积层命名以区分不同扩张率
relu2 = reluLayer; % 创建第二个ReLU激活层
relu2.Name = "tcn_relu2_d"+string(dilationFactor); % 为第二激活层命名增强网络可读性
bn2 = layerNormalizationLayer; % 创建第二层归一化层用于卷积输出标准化
bn2.Name = "tcn_ln2_d"+string(dilationFactor); % 命名第二归一化层便于识别
addLayer.Name = "tcn_add_d"+string(dilationFactor); % 为加法层命名以标记所属扩张率
layers = [conv1; relu1; bn1; conv2; relu2; bn2; addLayer]; % 将残差块各层按顺序组合成层数组
end % 函数结束返回所构建的TCN残差块层集合
numTCNChannels = 32; % 设置TCN中卷积通道数控制表达能力与参数量
kernelSize = 3; % 设置一维卷积核大小为3以捕捉短期邻域模式
dilations = [1 2 4 8]; % 定义多个扩张率以实现多尺度时间感受野
for d = dilations % 遍历每个扩张率构建对应TCN残差块
block = buildTCNBlock(numTCNChannels,kernelSize,d); % 调用构建函数获得对应扩张率的残差块层
tcnLayers = [tcnLayers; block]; % 将残差块追加到TCN层序列中形成堆叠结构
end % 完成多扩张率TCN堆叠构建工作
BiGRU模块结构定义与代码示例
biGruLayer = gruLayer(numGRUHidden, ... % 创建单向GRU层指定隐藏状态维度
Name="gru_forward"); % 为前向GRU层命名以区分方向
biGruLayerBackward = gruLayer(numGRUHidden, ... % 创建另一个GRU层作为反向分支
OutputMode="sequence", ... % 同样输出全序列隐藏状态用于双向融合
Name="gru_backward"); % 指定反向GRU名称区分正反方向
bigruLayers = [biGruLayer; biGruLayerBackward; concatLayer]; % 将前向GRU反向GRU和拼接层组成BiGRU模块
% 注意:在MATLAB中构造真正并行的双向GRU通常需使用layerGraph进行分支连接
lg = layerGraph; % 创建空层图对象用于搭建带分支网络
lg = addLayers(lg,gruLayer(numGRUHidden,OutputMode="sequence",Name="gru_fwd")); % 添加前向GRU层至层图
lg = addLayers(lg,gruLayer(numGRUHidden,OutputMode="sequence",Name="gru_bwd")); % 添加反向GRU层至层图
lg = addLayers(lg,fliplrLayer("bigru_flip")); % 添加自定义翻转层用于反向时间处理序列
lg = addLayers(lg,fliplrLayer("bigru_unflip")); % 添加反向翻转层将反向GRU输出翻回原时间顺序
lg = addLayers(lg,concatenationLayer(1,2,Name="bigru_concat")); % 添加拼接层以合并前向和反向的输出序列
lg = connectLayers(lg,"bigru_input","gru_fwd"); % 将输入层连接到前向GRU层
lg = connectLayers(lg,"bigru_input","bigru_flip"); % 将输入层同时连接到翻转层作为反向分支输入
lg = connectLayers(lg,"bigru_flip","gru_bwd"); % 将翻转后的序列输入到反向GRU层
lg = connectLayers(lg,"gru_bwd","bigru_unflip"); % 将反向GRU输出送入反翻转层恢复时间顺序
lg = connectLayers(lg,"gru_fwd","bigru_concat/in1"); % 将前向GRU输出连接到拼接层第一输入端
lg = connectLayers(lg,"bigru_unflip","bigru_concat/in2"); % 将修复后的反向输出连接到拼接层第二输入端
function layer = fliplrLayer(name) % 定义自定义层封装序列时间维度翻转操作
end % 返回用于网络中的翻转层对象
Transformer编码器模块示例
numHeads = 4; % 设置多头自注意力的头数以捕获多种注意模式
dModel = 2*numGRUHidden; % 设置Transformer编码器特征维度等于BiGRU输出拼接维度
dFF = 128; % 设置前馈网络隐藏层宽度控制非线性变换能力
dropProb = 0.1; % 设置dropout概率以增强网络正则化能力
posEmbLayer = positionEmbeddingLayer(dModel,seqLen,Name="pos_embed"); % 创建位置编码层为每个时间步生成位置信号
selfAttLayer = selfAttentionLayer(NumHeads=numHeads, ... % 创建自注意力层指定头数
KeyDimension=dModel/numHeads, ... % 设置每个头的键向量维度
Name="self_attention"); % 为自注意力层命名方便网络分析
ffLayer1 = fullyConnectedLayer(dFF,Name="ff1"); % 创建前馈网络的第一全连接层
reluFF = reluLayer(Name="ff_relu"); % 创建激活层用于前馈网络非线性变换
drop2 = dropoutLayer(dropProb,Name="ff_dropout"); % 创建dropout层用于前馈输出随机失活
addFF = additionLayer(2,Name="add_ff_res"); % 创建加法层实现前馈子层残差连接
ln2 = layerNormalizationLayer(Name="ln_ff"); % 创建归一化层用于前馈子层输出标准化
transformerLayers = [posEmbLayer; selfAttLayer; drop1; addAttn; ln1; ffLayer1; reluFF; ffLayer2; drop2; addFF; ln2]; % 将各个子层按顺序组合成简化Transformer编码器层序列
lgTF = addLayers(lgTF,sequenceInputLayer(dModel,Name="tf_input")); % 添加Transformer输入层接受BiGRU输出
lgTF = addLayers(lgTF,posEmbLayer); % 将位置编码层加入层图
lgTF = addLayers(lgTF,selfAttLayer); % 将自注意力层加入层图
lgTF = addLayers(lgTF,drop1); % 将注意力dropout层加入层图
lgTF = addLayers(lgTF,ln1); % 将注意力归一化层加入层图
lgTF = addLayers(lgTF,reluFF); % 将前馈ReLU激活层加入层图
lgTF = addLayers(lgTF,ffLayer2); % 将前馈第二全连接层加入层图
lgTF = addLayers(lgTF,drop2); % 将前馈dropout层加入层图
lgTF = addLayers(lgTF,addFF); % 将前馈残差加法层加入层图
lgTF = addLayers(lgTF,ln2); % 将前馈归一化层加入层图
lgTF = connectLayers(lgTF,"tf_input","pos_embed"); % 将输入层连接到位置编码层
lgTF = connectLayers(lgTF,"pos_embed","self_attention"); % 将位置编码输出送入自注意力层
lgTF = connectLayers(lgTF,"self_attention","attn_dropout"); % 将自注意力输出连接到注意力dropout层
lgTF = connectLayers(lgTF,"attn_dropout","add_attn_res/in1"); % 将dropout后的结果连接到注意力残差加法第一输入
lgTF = connectLayers(lgTF,"tf_input","add_attn_res/in2"); % 将原始输入作为残差连接到注意力加法第二输入
lgTF = connectLayers(lgTF,"add_attn_res","ln_attn"); % 将残差加和结果送入注意力归一化层
lgTF = connectLayers(lgTF,"ln_attn","ff1"); % 将归一化输出连接到前馈第一全连接层
lgTF = connectLayers(lgTF,"ff1","ff_relu"); % 将第一前馈结果送入ReLU激活层
lgTF = connectLayers(lgTF,"ff_relu","ff2"); % 将激活后数据送入前馈第二全连接层
lgTF = connectLayers(lgTF,"ff_dropout","add_ff_res/in1"); % 将dropout结果连接到前馈残差加法第一输入
lgTF = connectLayers(lgTF,"ln_attn","add_ff_res/in2"); % 将注意力归一化输出作为前馈残差第二输入
lgTF = connectLayers(lgTF,"add_ff_res","ln_ff"); % 将残差加和结果送入前馈归一化层作为Transformer编码器输出
多输出回归头实现示例
fcShared = fullyConnectedLayer(64,Name="fc_shared"); % 创建共享全连接层将高维特征映射到中间低维空间
regressionLayerOut = regressionLayer(Name="regression_output"); % 创建回归损失层计算预测与真实值之间的均方误差
headLayers = [poolLayer; fcShared; reluShared; fcOut; regressionLayerOut]; % 将池化层共享全连接层激活层输出层和损失层组合为回归头
lgFull = layerGraph; % 创建新的层图用于构建完整TCN-BiGRU-Transformer回归网络
lgFull = addLayers(lgFull,sequenceInputLayer(numFeatures,Name="input")); % 添加整体网络输入层接收原始多变量序列
% 简化示例中使用单层卷积代替前文构建的完整TCN堆叠以突出整体结构
lgFull = addLayers(lgFull,convolution1dLayer(kernelSize,numTCNChannels,Padding="causal",Name="conv_tcn")); % 添加一维因果卷积层作为TCN核心
lgFull = addLayers(lgFull,reluLayer(Name="relu_tcn")); % 添加卷积激活层
lgFull = addLayers(lgFull,gruLayer(numGRUHidden,OutputMode="sequence",Name="gru_main")); % 添加主GRU层用于序列上下文建模
lgFull = addLayers(lgFull,posEmbLayer); % 添加位置编码层为Transformer准备位置信号
lgFull = addLayers(lgFull,selfAttLayer); % 添加自注意力层用于全局特征聚合
lgFull = addLayers(lgFull,drop1); % 添加注意力dropout层
lgFull = addLayers(lgFull,ln1); % 添加注意力归一化层
lgFull = addLayers(lgFull,ffLayer1); % 添加前馈第一全连接层
lgFull = addLayers(lgFull,ffLayer2); % 添加前馈第二全连接层
lgFull = addLayers(lgFull,drop2); % 添加前馈dropout层
lgFull = addLayers(lgFull,addFF); % 添加前馈残差加法层
lgFull = addLayers(lgFull,ln2); % 添加前馈归一化层
lgFull = addLayers(lgFull,fcShared); % 添加共享全连接层
lgFull = addLayers(lgFull,reluShared); % 添加共享激活层
lgFull = connectLayers(lgFull,"input","conv_tcn"); % 将网络输入连接到TCN卷积层
lgFull = connectLayers(lgFull,"conv_tcn","relu_tcn"); % 将卷积输出连接到激活层
lgFull = connectLayers(lgFull,"relu_tcn","gru_main"); % 将激活后的TCN特征送入GRU层
lgFull = connectLayers(lgFull,"gru_main","pos_embed"); % 将GRU序列输出送入位置编码层
lgFull = connectLayers(lgFull,"pos_embed","self_attention"); % 将位置编码输出送入自注意力层
lgFull = connectLayers(lgFull,"self_attention","attn_dropout"); % 将自注意力输出送入dropout层
lgFull = connectLayers(lgFull,"attn_dropout","add_attn_res/in1"); % 将dropout结果连接到残差加法第一输入
lgFull = connectLayers(lgFull,"pos_embed","add_attn_res/in2"); % 将位置编码输出作为残差第二输入
lgFull = connectLayers(lgFull,"add_attn_res","ln_attn"); % 将注意力残差输出送入归一化层
lgFull = connectLayers(lgFull,"ln_attn","ff1"); % 将归一化结果送入前馈第一全连接层
lgFull = connectLayers(lgFull,"ff1","ff_relu"); % 将前馈第一层输出送入ReLU激活层
lgFull = connectLayers(lgFull,"ff2","ff_dropout"); % 将前馈第二层输出送入dropout层
lgFull = connectLayers(lgFull,"ff_dropout","add_ff_res/in1"); % 将dropout结果连接到前馈残差加法第一输入
lgFull = connectLayers(lgFull,"add_ff_res","ln_ff"); % 将前馈残差输出送入归一化层
lgFull = connectLayers(lgFull,"ln_ff","global_pool"); % 将Transformer最终输出送入全局平均池化层
lgFull = connectLayers(lgFull,"global_pool","fc_shared"); % 将池化结果送入共享全连接层
lgFull = connectLayers(lgFull,"fc_shared","relu_shared"); % 将共享全连接输出送入激活层
lgFull = connectLayers(lgFull,"relu_shared","fc_out"); % 将激活结果送入最终输出全连接层
lgFull = connectLayers(lgFull,"fc_out","regression_output"); % 将回归输出连接到回归损失层完成网络构建
模型训练主循环示例
net = dlnetwork(lgFull); % 将层图转换为可训练的dlnetwork对象便于自定义训练
learnRate = 1e-3; % 设置初始学习率控制参数更新步长
numEpochs = 30; % 设置训练轮数决定训练遍历数据次数
trailingAvg = []; % 初始化一阶动量平均值用于Adam优化器
trailingAvgSq = []; % 初始化二阶动量平方平均值用于Adam优化器
numTrainBatch = ceil(numTrain/miniBatchSize); % 计算每轮训练中小批量批次数量
for epoch = 1:numEpochs % 外层循环遍历训练轮数
idx = randperm(numTrain); % 在每轮开始随机打乱训练样本索引
for bi = 1:numTrainBatch % 内层循环遍历所有小批量
batchIdx = idx((bi-1)*miniBatchSize+1:min(bi*miniBatchSize,numTrain)); % 选取当前小批量样本索引
XBatch = Xtrain(batchIdx,:,:); % 从训练数组中提取该批输入序列
YBatch = Ytrain(batchIdx,:); % 从训练数组中提取该批输出目标
XBatchDL = dlarray(permute(XBatch,[2 3 1]),"CTB"); % 将当前批输入转换为深度数组格式
YBatchDL = dlarray(YBatch',"CB"); % 将当前批输出转换为深度数组格式
[gradients,loss] = dlfeval(@modelGradients,net,XBatchDL,YBatchDL); % 使用自动微分计算损失和参数梯度
trailingAvg,trailingAvgSq,epoch,learnRate); % 使用动量项和学习率调节更新幅度
end % 每轮训练完所有小批量后继续下一轮
end % 完成所有轮数训练过程
function [gradients,loss] = modelGradients(net,X,Y) % 定义用于dlfeval调用的梯度计算函数
YPred = forward(net,X); % 使用当前网络对输入批次进行前向传播得到预测输出
loss = mse(YPred,Y); % 计算预测与真实输出之间的均方误差损失
gradients = dlgradient(loss,net.Learnables); % 对网络可学习参数求取损失的梯度
end % 返回梯度和损失给训练循环使用
SHAP值计算示例与解释
numBackground = 200; % 设置背景样本数量用于近似SHAP值分布
Xbg = Xtrain(bgIdx,:,:); % 提取背景输入序列用于构造参考分布
numExplain = 50; % 设置需要进行详细解释的测试样本数量
Xexp = Xtest(explIdx,:,:); % 提取被解释的测试样本输入
baseline = squeeze(mean(Xbg,1)); % 计算背景样本在批次维度上的平均作为基线特征
baselineDL = dlarray(permute(baseline,[2 1]),"CB"); % 将基线转换为深度数组方便后续预测组合使用
numSamplesSHAP = 100; % 设置SHAP Monte Carlo采样次数以控制精度和计算量
shapValues = zeros(numExplain,seqLen,numFeatures,numOutputs); % 初始化四维数组存放SHAP值
for i = 1:numExplain % 循环处理每个需要解释的样本
for s = 1:numSamplesSHAP % 对当前样本进行多次随机采样估计Shapley值
mask = rand(seqLen,numFeatures) > 0.5; % 随机生成包含特征的二进制掩码矩阵
xMasked = baseline; % 初始化掩码样本为基线特征
xMasked(mask) = xi(mask); % 将掩码为1的位置替换为当前样本特征
xMaskedDL = dlarray(permute(xMasked,[2 3 1]),"CTB"); % 将掩码样本转换为深度数组
yMasked = forward(net,xMaskedDL); % 计算掩码样本的网络输出
yFull = forward(net,xiDL); % 计算完整样本的网络输出
for o = 1:numOutputs % 遍历每个输出变量累积对应Shapley增量
shapValues(i,:,:,o) = shapValues(i,:,:,o) + delta(o); % 将该输出的增量贡献累加到SHAP值张量中
end % 完成当前采样对所有输出变量贡献累加
end % 完成当前样本所有采样
end % 完成所有待解释样本的SHAP值计算
shapAbsMean = squeeze(mean(abs(shapValues),[1 2])); % 对时间和样本维度求平均绝对值得到特征全局重要性
bar(shapAbsMean(:,1)); % 绘制针对第一个输出变量的特征重要性柱状图
title("输出1的全局特征重要性"); % 设置图题解释当前展示的是输出1对应的重要性
xlabel("特征维度索引"); % 设置横轴标签表示不同特征维度
ylabel("平均绝对SHAP值"); % 设置纵轴标签表示特征重要性度量
数据预处理与多输入多输出构造示例
clear; % 清理工作区变量,避免历史变量干扰当前实验
clc; % 清空命令行窗口,提高输出阅读性
numFeatures = 4; % 输入特征维度数量,对应多输入变量个数
numOutputs = 3; % 输出目标变量数量,对应多输出回归任务
seqLen = 24; % 设置使用的历史时间窗口长度,例如24个时间步
t = (1:numSamples)'; % 构造时间索引向量,为合成数据提供基础自变量
x1 = sin(2*pi*t/24) + 0.1*randn(numSamples,1); % 第一个特征:带噪声的日周期正弦序列
x2 = cos(2*pi*t/168) + 0.2*randn(numSamples,1); % 第二个特征:周周期余弦序列叠加噪声
x3 = 0.001*t + 0.3*randn(numSamples,1); % 第三个特征:缓慢上升趋势加高斯噪声
x4 = 0.5*randn(numSamples,1); % 第四个特征:纯噪声变量,用于测试特征选择能力
Xall = [x1 x2 x3 x4]; % 将四个特征按列拼接,形成多维输入特征矩阵
y1 = 0.6*x1 + 0.3*x2 + 0.1*x3 + 0.1*randn(numSamples,1); % 第一个输出:线性组合特征加噪声
y2 = 0.4*x2.^2 + 0.2*x3 + 0.3*x4 + 0.1*randn(numSamples,1); % 第二个输出:引入平方项体现非线性关系
y3 = 0.5*sin(x1) + 0.3*cos(x2) + 0.2*x3 + 0.1*randn(numSamples,1); % 第三个输出:通过正弦余弦引入复杂非线性映射
Yall = [y1 y2 y3]; % 将三列输出组合成多输出目标矩阵
muX = mean(Xall,1); % 计算输入特征各列均值,用于后续标准化
sigmaX = std(Xall,0,1) + 1e-6; % 计算输入特征标准差,避免除零情况
Xnorm = (Xall - muX)./sigmaX; % 对全部输入特征进行标准化处理
muY = mean(Yall,1); % 计算输出目标各列的均值,便于输出归一化
Ynorm = (Yall - muY)./sigmaY; % 对输出目标进行标准化,提升训练稳定性
numSeq = numSamples - seqLen; % 计算可构造的监督样本条数
Xseq = zeros(numSeq, seqLen, numFeatures); % 初始化三维数组存放输入序列样本
Yseq = zeros(numSeq, numOutputs); % 初始化二维数组存放对应输出目标
idxStart = i; % 当前序列的起始时间索引
idxEnd = i + seqLen - 1; % 当前序列的结束时间索引
Xseq(i,:,:) = Xnorm(idxStart:idxEnd,:); % 将该时间窗口的多维特征填入三维输入数组
Yseq(i,:) = Ynorm(idxEnd,:); % 使用窗口末尾时刻的归一化输出作为预测目标
end % 完成时间窗口滑动构造多输入多输出监督数据集
numTrain = floor(0.7*numSeq); % 将70%的样本划为训练集
numVal = floor(0.15*numSeq); % 将15%的样本划为验证集
numTest = numSeq - numTrain - numVal; % 剩余部分划为测试集,用于评估泛化性能
idxTrain = 1:numTrain; % 训练集样本索引范围
idxVal = numTrain+1:numTrain+numVal; % 验证集样本索引范围
idxTest = numTrain+numVal+1:numSeq; % 测试集样本索引范围
Ytrain = Yseq(idxTrain,:); % 提取训练集输出目标
Yval = Yseq(idxVal,:); % 提取验证集输出目标
Xtest = Xseq(idxTest,:,:); % 提取测试集输入序列
Ytest = Yseq(idxTest,:); % 提取测试集输出目标
XtrainDL = dlarray(permute(Xtrain,[2 3 1]),"CTB"); % 将训练集输入维度调整为时间×通道×批次并封装为深度数组
XvalDL = dlarray(permute(Xval,[2 3 1]),"CTB"); % 将验证集输入序列转换为适配网络的张量格式
YtrainDL = dlarray(Ytrain',"CB"); % 将训练集输出转置为输出维度×批次格式
YvalDL = dlarray(Yval',"CB"); % 将验证集输出转为深度数组形式
YtestDL = dlarray(Ytest',"CB"); % 将测试集输出封装为深度数组,供评估使用
TCN模块结构定义与代码示例
function layers = buildTCNBlock(numChannels, kernelSize, dilationFactor) % 定义函数构建单个TCN残差块
layers = [ ]; % 初始化空层数组,用于逐步追加子层
conv1 = convolution1dLayer(kernelSize, numChannels, ... % 创建第一层一维卷积层,卷积核大小为指定值
Padding="causal", ... % 使用因果卷积方式,保证输出不会看到未来信息
DilationFactor=dilationFactor, ... % 使用扩张卷积因子控制感受野大小
WeightsInitializer="he"); % 使用He初始化方法增强训练稳定性
relu1 = reluLayer; % 创建第一层ReLU激活函数层
relu1.Name = "tcn_relu1_d"+string(dilationFactor); % 命名第一个激活层丰满网络结构标签
bn1 = layerNormalizationLayer; % 创建层归一化层,用于稳定特征分布
bn1.Name = "tcn_ln1_d"+string(dilationFactor); % 为第一层归一化层命名方便网络可视化
conv2 = convolution1dLayer(kernelSize, numChannels, ... % 创建第二层一维卷积层
Padding="causal", ... % 同样使用因果卷积保证时间因果性
DilationFactor=dilationFactor, ... % 使用相同扩张率以保持残差块结构对称性
WeightsInitializer="he"); % 使用He初始化提高深层网络的训练效果
conv2.Name = "tcn_conv2_d"+string(dilationFactor); % 为第二卷积层命名以区分不同扩张率
relu2 = reluLayer; % 创建第二个ReLU激活层
relu2.Name = "tcn_relu2_d"+string(dilationFactor); % 为第二激活层命名增强网络可读性
bn2 = layerNormalizationLayer; % 创建第二层归一化层用于卷积输出标准化
bn2.Name = "tcn_ln2_d"+string(dilationFactor); % 命名第二归一化层便于识别
addLayer.Name = "tcn_add_d"+string(dilationFactor); % 为加法层命名以标记所属扩张率
layers = [conv1; relu1; bn1; conv2; relu2; bn2; addLayer]; % 将残差块各层按顺序组合成层数组
end % 函数结束返回所构建的TCN残差块层集合
numTCNChannels = 32; % 设置TCN中卷积通道数控制表达能力与参数量
kernelSize = 3; % 设置一维卷积核大小为3以捕捉短期邻域模式
dilations = [1 2 4 8]; % 定义多个扩张率以实现多尺度时间感受野
for d = dilations % 遍历每个扩张率构建对应TCN残差块
block = buildTCNBlock(numTCNChannels,kernelSize,d); % 调用构建函数获得对应扩张率的残差块层
tcnLayers = [tcnLayers; block]; % 将残差块追加到TCN层序列中形成堆叠结构
end % 完成多扩张率TCN堆叠构建工作
BiGRU模块结构定义与代码示例
biGruLayer = gruLayer(numGRUHidden, ... % 创建单向GRU层指定隐藏状态维度
Name="gru_forward"); % 为前向GRU层命名以区分方向
biGruLayerBackward = gruLayer(numGRUHidden, ... % 创建另一个GRU层作为反向分支
OutputMode="sequence", ... % 同样输出全序列隐藏状态用于双向融合
Name="gru_backward"); % 指定反向GRU名称区分正反方向
bigruLayers = [biGruLayer; biGruLayerBackward; concatLayer]; % 将前向GRU反向GRU和拼接层组成BiGRU模块
% 注意:在MATLAB中构造真正并行的双向GRU通常需使用layerGraph进行分支连接
lg = layerGraph; % 创建空层图对象用于搭建带分支网络
lg = addLayers(lg,gruLayer(numGRUHidden,OutputMode="sequence",Name="gru_fwd")); % 添加前向GRU层至层图
lg = addLayers(lg,gruLayer(numGRUHidden,OutputMode="sequence",Name="gru_bwd")); % 添加反向GRU层至层图
lg = addLayers(lg,fliplrLayer("bigru_flip")); % 添加自定义翻转层用于反向时间处理序列
lg = addLayers(lg,fliplrLayer("bigru_unflip")); % 添加反向翻转层将反向GRU输出翻回原时间顺序
lg = addLayers(lg,concatenationLayer(1,2,Name="bigru_concat")); % 添加拼接层以合并前向和反向的输出序列
lg = connectLayers(lg,"bigru_input","gru_fwd"); % 将输入层连接到前向GRU层
lg = connectLayers(lg,"bigru_input","bigru_flip"); % 将输入层同时连接到翻转层作为反向分支输入
lg = connectLayers(lg,"bigru_flip","gru_bwd"); % 将翻转后的序列输入到反向GRU层
lg = connectLayers(lg,"gru_bwd","bigru_unflip"); % 将反向GRU输出送入反翻转层恢复时间顺序
lg = connectLayers(lg,"gru_fwd","bigru_concat/in1"); % 将前向GRU输出连接到拼接层第一输入端
lg = connectLayers(lg,"bigru_unflip","bigru_concat/in2"); % 将修复后的反向输出连接到拼接层第二输入端
function layer = fliplrLayer(name) % 定义自定义层封装序列时间维度翻转操作
end % 返回用于网络中的翻转层对象
Transformer编码器模块示例
numHeads = 4; % 设置多头自注意力的头数以捕获多种注意模式
dModel = 2*numGRUHidden; % 设置Transformer编码器特征维度等于BiGRU输出拼接维度
dFF = 128; % 设置前馈网络隐藏层宽度控制非线性变换能力
dropProb = 0.1; % 设置dropout概率以增强网络正则化能力
posEmbLayer = positionEmbeddingLayer(dModel,seqLen,Name="pos_embed"); % 创建位置编码层为每个时间步生成位置信号
selfAttLayer = selfAttentionLayer(NumHeads=numHeads, ... % 创建自注意力层指定头数
KeyDimension=dModel/numHeads, ... % 设置每个头的键向量维度
Name="self_attention"); % 为自注意力层命名方便网络分析
ffLayer1 = fullyConnectedLayer(dFF,Name="ff1"); % 创建前馈网络的第一全连接层
reluFF = reluLayer(Name="ff_relu"); % 创建激活层用于前馈网络非线性变换
drop2 = dropoutLayer(dropProb,Name="ff_dropout"); % 创建dropout层用于前馈输出随机失活
addFF = additionLayer(2,Name="add_ff_res"); % 创建加法层实现前馈子层残差连接
ln2 = layerNormalizationLayer(Name="ln_ff"); % 创建归一化层用于前馈子层输出标准化
transformerLayers = [posEmbLayer; selfAttLayer; drop1; addAttn; ln1; ffLayer1; reluFF; ffLayer2; drop2; addFF; ln2]; % 将各个子层按顺序组合成简化Transformer编码器层序列
lgTF = addLayers(lgTF,sequenceInputLayer(dModel,Name="tf_input")); % 添加Transformer输入层接受BiGRU输出
lgTF = addLayers(lgTF,posEmbLayer); % 将位置编码层加入层图
lgTF = addLayers(lgTF,selfAttLayer); % 将自注意力层加入层图
lgTF = addLayers(lgTF,drop1); % 将注意力dropout层加入层图
lgTF = addLayers(lgTF,ln1); % 将注意力归一化层加入层图
lgTF = addLayers(lgTF,reluFF); % 将前馈ReLU激活层加入层图
lgTF = addLayers(lgTF,ffLayer2); % 将前馈第二全连接层加入层图
lgTF = addLayers(lgTF,drop2); % 将前馈dropout层加入层图
lgTF = addLayers(lgTF,addFF); % 将前馈残差加法层加入层图
lgTF = addLayers(lgTF,ln2); % 将前馈归一化层加入层图
lgTF = connectLayers(lgTF,"tf_input","pos_embed"); % 将输入层连接到位置编码层
lgTF = connectLayers(lgTF,"pos_embed","self_attention"); % 将位置编码输出送入自注意力层
lgTF = connectLayers(lgTF,"self_attention","attn_dropout"); % 将自注意力输出连接到注意力dropout层
lgTF = connectLayers(lgTF,"attn_dropout","add_attn_res/in1"); % 将dropout后的结果连接到注意力残差加法第一输入
lgTF = connectLayers(lgTF,"tf_input","add_attn_res/in2"); % 将原始输入作为残差连接到注意力加法第二输入
lgTF = connectLayers(lgTF,"add_attn_res","ln_attn"); % 将残差加和结果送入注意力归一化层
lgTF = connectLayers(lgTF,"ln_attn","ff1"); % 将归一化输出连接到前馈第一全连接层
lgTF = connectLayers(lgTF,"ff1","ff_relu"); % 将第一前馈结果送入ReLU激活层
lgTF = connectLayers(lgTF,"ff_relu","ff2"); % 将激活后数据送入前馈第二全连接层
lgTF = connectLayers(lgTF,"ff_dropout","add_ff_res/in1"); % 将dropout结果连接到前馈残差加法第一输入
lgTF = connectLayers(lgTF,"ln_attn","add_ff_res/in2"); % 将注意力归一化输出作为前馈残差第二输入
lgTF = connectLayers(lgTF,"add_ff_res","ln_ff"); % 将残差加和结果送入前馈归一化层作为Transformer编码器输出
多输出回归头实现示例
fcShared = fullyConnectedLayer(64,Name="fc_shared"); % 创建共享全连接层将高维特征映射到中间低维空间
regressionLayerOut = regressionLayer(Name="regression_output"); % 创建回归损失层计算预测与真实值之间的均方误差
headLayers = [poolLayer; fcShared; reluShared; fcOut; regressionLayerOut]; % 将池化层共享全连接层激活层输出层和损失层组合为回归头
lgFull = layerGraph; % 创建新的层图用于构建完整TCN-BiGRU-Transformer回归网络
lgFull = addLayers(lgFull,sequenceInputLayer(numFeatures,Name="input")); % 添加整体网络输入层接收原始多变量序列
% 简化示例中使用单层卷积代替前文构建的完整TCN堆叠以突出整体结构
lgFull = addLayers(lgFull,convolution1dLayer(kernelSize,numTCNChannels,Padding="causal",Name="conv_tcn")); % 添加一维因果卷积层作为TCN核心
lgFull = addLayers(lgFull,reluLayer(Name="relu_tcn")); % 添加卷积激活层
lgFull = addLayers(lgFull,gruLayer(numGRUHidden,OutputMode="sequence",Name="gru_main")); % 添加主GRU层用于序列上下文建模
lgFull = addLayers(lgFull,posEmbLayer); % 添加位置编码层为Transformer准备位置信号
lgFull = addLayers(lgFull,selfAttLayer); % 添加自注意力层用于全局特征聚合
lgFull = addLayers(lgFull,drop1); % 添加注意力dropout层
lgFull = addLayers(lgFull,ln1); % 添加注意力归一化层
lgFull = addLayers(lgFull,ffLayer1); % 添加前馈第一全连接层
lgFull = addLayers(lgFull,ffLayer2); % 添加前馈第二全连接层
lgFull = addLayers(lgFull,drop2); % 添加前馈dropout层
lgFull = addLayers(lgFull,addFF); % 添加前馈残差加法层
lgFull = addLayers(lgFull,ln2); % 添加前馈归一化层
lgFull = addLayers(lgFull,fcShared); % 添加共享全连接层
lgFull = addLayers(lgFull,reluShared); % 添加共享激活层
lgFull = connectLayers(lgFull,"input","conv_tcn"); % 将网络输入连接到TCN卷积层
lgFull = connectLayers(lgFull,"conv_tcn","relu_tcn"); % 将卷积输出连接到激活层
lgFull = connectLayers(lgFull,"relu_tcn","gru_main"); % 将激活后的TCN特征送入GRU层
lgFull = connectLayers(lgFull,"gru_main","pos_embed"); % 将GRU序列输出送入位置编码层
lgFull = connectLayers(lgFull,"pos_embed","self_attention"); % 将位置编码输出送入自注意力层
lgFull = connectLayers(lgFull,"self_attention","attn_dropout"); % 将自注意力输出送入dropout层
lgFull = connectLayers(lgFull,"attn_dropout","add_attn_res/in1"); % 将dropout结果连接到残差加法第一输入
lgFull = connectLayers(lgFull,"pos_embed","add_attn_res/in2"); % 将位置编码输出作为残差第二输入
lgFull = connectLayers(lgFull,"add_attn_res","ln_attn"); % 将注意力残差输出送入归一化层
lgFull = connectLayers(lgFull,"ln_attn","ff1"); % 将归一化结果送入前馈第一全连接层
lgFull = connectLayers(lgFull,"ff1","ff_relu"); % 将前馈第一层输出送入ReLU激活层
lgFull = connectLayers(lgFull,"ff2","ff_dropout"); % 将前馈第二层输出送入dropout层
lgFull = connectLayers(lgFull,"ff_dropout","add_ff_res/in1"); % 将dropout结果连接到前馈残差加法第一输入
lgFull = connectLayers(lgFull,"add_ff_res","ln_ff"); % 将前馈残差输出送入归一化层
lgFull = connectLayers(lgFull,"ln_ff","global_pool"); % 将Transformer最终输出送入全局平均池化层
lgFull = connectLayers(lgFull,"global_pool","fc_shared"); % 将池化结果送入共享全连接层
lgFull = connectLayers(lgFull,"fc_shared","relu_shared"); % 将共享全连接输出送入激活层
lgFull = connectLayers(lgFull,"relu_shared","fc_out"); % 将激活结果送入最终输出全连接层
lgFull = connectLayers(lgFull,"fc_out","regression_output"); % 将回归输出连接到回归损失层完成网络构建
模型训练主循环示例
net = dlnetwork(lgFull); % 将层图转换为可训练的dlnetwork对象便于自定义训练
learnRate = 1e-3; % 设置初始学习率控制参数更新步长
numEpochs = 30; % 设置训练轮数决定训练遍历数据次数
trailingAvg = []; % 初始化一阶动量平均值用于Adam优化器
trailingAvgSq = []; % 初始化二阶动量平方平均值用于Adam优化器
numTrainBatch = ceil(numTrain/miniBatchSize); % 计算每轮训练中小批量批次数量
for epoch = 1:numEpochs % 外层循环遍历训练轮数
idx = randperm(numTrain); % 在每轮开始随机打乱训练样本索引
for bi = 1:numTrainBatch % 内层循环遍历所有小批量
batchIdx = idx((bi-1)*miniBatchSize+1:min(bi*miniBatchSize,numTrain)); % 选取当前小批量样本索引
XBatch = Xtrain(batchIdx,:,:); % 从训练数组中提取该批输入序列
YBatch = Ytrain(batchIdx,:); % 从训练数组中提取该批输出目标
XBatchDL = dlarray(permute(XBatch,[2 3 1]),"CTB"); % 将当前批输入转换为深度数组格式
YBatchDL = dlarray(YBatch',"CB"); % 将当前批输出转换为深度数组格式
[gradients,loss] = dlfeval(@modelGradients,net,XBatchDL,YBatchDL); % 使用自动微分计算损失和参数梯度
trailingAvg,trailingAvgSq,epoch,learnRate); % 使用动量项和学习率调节更新幅度
end % 每轮训练完所有小批量后继续下一轮
end % 完成所有轮数训练过程
function [gradients,loss] = modelGradients(net,X,Y) % 定义用于dlfeval调用的梯度计算函数
YPred = forward(net,X); % 使用当前网络对输入批次进行前向传播得到预测输出
loss = mse(YPred,Y); % 计算预测与真实输出之间的均方误差损失
gradients = dlgradient(loss,net.Learnables); % 对网络可学习参数求取损失的梯度
end % 返回梯度和损失给训练循环使用
SHAP值计算示例与解释
numBackground = 200; % 设置背景样本数量用于近似SHAP值分布
Xbg = Xtrain(bgIdx,:,:); % 提取背景输入序列用于构造参考分布
numExplain = 50; % 设置需要进行详细解释的测试样本数量
Xexp = Xtest(explIdx,:,:); % 提取被解释的测试样本输入
baseline = squeeze(mean(Xbg,1)); % 计算背景样本在批次维度上的平均作为基线特征
baselineDL = dlarray(permute(baseline,[2 1]),"CB"); % 将基线转换为深度数组方便后续预测组合使用
numSamplesSHAP = 100; % 设置SHAP Monte Carlo采样次数以控制精度和计算量
shapValues = zeros(numExplain,seqLen,numFeatures,numOutputs); % 初始化四维数组存放SHAP值
for i = 1:numExplain % 循环处理每个需要解释的样本
for s = 1:numSamplesSHAP % 对当前样本进行多次随机采样估计Shapley值
mask = rand(seqLen,numFeatures) > 0.5; % 随机生成包含特征的二进制掩码矩阵
xMasked = baseline; % 初始化掩码样本为基线特征
xMasked(mask) = xi(mask); % 将掩码为1的位置替换为当前样本特征
xMaskedDL = dlarray(permute(xMasked,[2 3 1]),"CTB"); % 将掩码样本转换为深度数组
yMasked = forward(net,xMaskedDL); % 计算掩码样本的网络输出
yFull = forward(net,xiDL); % 计算完整样本的网络输出
for o = 1:numOutputs % 遍历每个输出变量累积对应Shapley增量
shapValues(i,:,:,o) = shapValues(i,:,:,o) + delta(o); % 将该输出的增量贡献累加到SHAP值张量中
end % 完成当前采样对所有输出变量贡献累加
end % 完成当前样本所有采样
end % 完成所有待解释样本的SHAP值计算
shapAbsMean = squeeze(mean(abs(shapValues),[1 2])); % 对时间和样本维度求平均绝对值得到特征全局重要性
bar(shapAbsMean(:,1)); % 绘制针对第一个输出变量的特征重要性柱状图
title("输出1的全局特征重要性"); % 设置图题解释当前展示的是输出1对应的重要性
xlabel("特征维度索引"); % 设置横轴标签表示不同特征维度
ylabel("平均绝对SHAP值"); % 设置纵轴标签表示特征重要性度量
更多详细内容请访问
http://【多输入多输出回归预测】MATLAB实现基于TCN-BiGRU-Transformer-SHAP时间卷积双向门控循环单元(TCN-BiGRU)结合Transformer编码器和SHAP值分析方法(SH资源-CSDN下载 https://download.csdn.net/download/xiaoxingkongyuxi/92781607
https://download.csdn.net/download/xiaoxingkongyuxi/92781607
https://download.csdn.net/download/xiaoxingkongyuxi/92781607
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐







所有评论(0)