多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

MATLAB实现TCN多输入回归预测:从膨胀卷积到空间Dropout

MATLAB实现TCN多输入回归预测:从膨胀卷积到空间Dropout 简介一套基于MATLAB的TCN时间卷积网络多输入回归预测完整方案围绕7个输入特征预测1个变量的典型回归任务面向需要深度学习建模的科研、竞赛及工程应用场景。包内文件总计10个包含2个MATLAB源码、6张训练及预测效果图、1个Excel多输入数据集和1份说明文档其中源码部分提供主函数与自定义spatialDropoutLayer整体压缩包仅540KB便于下载与快速起步。该资源已有1033人学习/下载在同类型任务中具备较高的参考价值。代码遵循TCN的核心设计思路支持多特征输入与单变量输出通过运行MainTCNNN.m即可完成模型训练、验证与预测并配合data.xlsx复现完整流程可视化图像直观展示损失曲线和拟合效果说明文档则针对MATLAB版本不同可能出现的乱码问题给出了具体处理办法降低了上手门槛。1. TCN 的价值不在网络深而在能并行的时序建模做多输入回归预测时很多人第一反应是 LSTM 或 GRU。但如果你的数据是 7 个特征、1 个输出变量的中等长度序列LSTM 的串行递归会明显拖慢训练而且梯度在长序列上容易衰减调参成本很高。时间卷积网络 TCN 用因果卷积加膨胀卷积实现时序建模卷积核在时间维度上滑动序列内部的计算可以完全并行训练速度通常比同规模 LSTM 快几倍同时感受野可以通过堆叠膨胀层线性扩展。本篇基于 MATLAB 2021b 实现的一套完整 TCN 多输入回归预测源码包含自定义 spatialDropoutLayer、训练主脚本和 7 输入 1 输出的示例数据适合那些已经跑通 BP 或 LSTM 回归、想在 MATLAB 里快速对比 TCN 效果的工程师。下面直接从网络结构拆起逐步落到能直接改参数的代码。2. TCN 核心结构解析与 MATLAB 自定义层实现2.1 因果卷积与膨胀卷积先理解“时间不能越界”TCN 的因果卷积要求输出 t 时刻的值只能依赖 t 及 t 之前的输入不能用未来数据。MATLAB 的convolution1dLayer并不直接提供“causal”开关常见做法是在序列输入前手动补零或者用sequenceFoldingLayer配合自定义前向函数。对于回归任务我一般用更简单的方案对输入序列做左侧补零使得卷积窗口在每步滑动时始终处于当前时间点左侧这样普通卷积就变成了因果卷积。膨胀卷积dilated convolution的作用是在不增加参数量的情况下扩大感受野。MATLAB 中通过convolution1dLayer(filterSize, numFilters, DilationFactor, d)实现d 通常取 1、2、4、8 这样的倍增序列。假设滤波器长度为 k膨胀因子为 d则单层有效感受野为(k-1)*d1。堆叠 L 层时总感受野按层累加。配合残差连接后网络可以做到上百步的感受野而每层的参数量只和 k 和输出通道数相关。2.1.1 自定义因果卷积层的思路如果你想封装一个可复用的因果卷积层可以在一个自定义层的前向函数里先对输入X在时间维左侧补(filterSize-1)*dilationFactor个零再调用内置卷积。MATLAB 的nnet.layer.Layer接口允许这样操作。源码包里没有暴露这个封装层但主程序里直接用了带Padding的默认卷积实际上是把补零交给convolution1dLayer的Padding参数完成。2.2 残差连接和空间 Dropout 的 MATLAB 实现深层 TCN 必须带残差连接否则梯度很难传到浅层。标准残差块包含两个卷积层、两个激活层和两个空间 Dropout。空间 Dropout 和普通 Dropout 的区别在于普通 Dropout 随机丢弃每个时间步上的单个特征值空间 Dropout 会整列丢弃某个特征维度上的所有时间步。对时序数据来说空间 Dropout 更能保留时间关联性。源码中的spatialDropoutLayer.m就是一个自定义层。它继承nnet.layer.Layer核心代码是classdef spatialDropoutLayer nnet.layer.Layer properties Probability end methods function layer spatialDropoutLayer(p) layer.Name spatialDropout; layer.Description Spatial dropout for sequence data; layer.Probability p; end function X predict(layer, X) % 训练时在 train 函数中执行随机丢弃 if layer.Probability 0 % 输入 X 维度: features x timeSteps mask rand(size(X,1), 1) layer.Probability; X X .* mask; end end function X forward(layer, X) % 推理时直接缩放保证期望一致 X X .* (1 - layer.Probability); end end end这个实现里predict和forward在 MATLAB 的dlnetwork中分别对应推理和训练路径。注意forward中我直接乘以(1 - Probability)这是 inverted dropout 的简化版本训练时丢弃比例为 p推理时整体缩放这样不需要固定随机种子也能保持统计一致。实际使用中建议在训练阶段也用forward否则某些深层的批归一化统计量会因 dropout 位置的不同而抖动。2.3 感受野与网络层数的定量关系以源码里的典型配置为例卷积核k3膨胀因子按[1,2,4,8]递增每个膨胀因子对应一个残差块共 4 层。这时最后一层输出对应的原始序列感受野为1 sum((k-1)*d_i)即1 2*1 2*2 2*4 2*8 31个时间步。如果你的输入序列长度只有 10堆 4 层就会让部分卷积核落到全部补零区等于浪费了层数。反过来如果序列长度是 2004 层感受野 31 步可能不够捕捉长期依赖此时需要把膨胀因子继续翻倍到[1,2,4,8,16,32]。判断层数是否合适最直接的办法是打印每一层的输出size或者观察损失曲线如果训练末期验证损失还在下降但速度变缓通常可以试着加一层膨胀因子更大的残差块如果验证损失已经持续震荡则说明网络容量过大或感受野远超序列长度。源码里的TCNNN*.png就是不同训练阶段的预测对比图可以看到前 20 步拟合明显好于后 30 步这正是感受野不足的典型信号。3. 数据预处理与主程序 MainTCNN.m 逐段拆解3.1 读取 data.xlsx 并构造训练集标签data.xlsx是 7 个输入特征和 1 个输出变量组成的表格行是样本列是特征。读入后要做两件事归一化和切分。源码里认为数据本身就是对齐的多输入单输出回归样本不需要按时间窗重组但如果原始数据是连续时间序列最好把每个样本改成[前T步的7个特征, 当前步的输出]的结构。这里我按源码的设定处理直接按行切分。data readmatrix(data.xlsx); % 8列前7列为输入第8列为输出 X data(:, 1:7); % 转换维度: 特征数 x 样本数 Y data(:, 8); % 归一化到 [0,1] X_mean mean(X, 2); X_std std(X, 0, 2); Y_mean mean(Y); Y_std std(Y); X_norm (X - X_mean) ./ (X_std eps); Y_norm (Y - Y_mean) ./ (Y_std eps); % 按 8:2 切分训练/验证 numSamples size(X, 2); numTrain floor(0.8 * numSamples); XTrain X_norm(:, 1:numTrain); YTrain Y_norm(:, 1:numTrain); XVal X_norm(:, numTrain1:end); YVal Y_norm(:, numTrain1:end);注意这里mean和std只用了训练集的统计量来计算验证集不能把验证集混进归一化参数否则验证阶段信息泄漏。代码里加了eps是为了防止某个特征标准差为 0 时除零。3.2 构建 TCN 残差块网络主程序MainTCNN.m用了循环来构建多个残差块每个残差块内部包含两次卷积、两次批归一化、两次 ReLU 和一次空间 Dropout。循环结束后接一个全连接层和回归输出层。下面是剔除了可视化部分的精简版numFeatures 7; numFilters 32; filterSize 3; numBlocks 4; dilations 2.^(0:numBlocks-1); % [1 2 4 8] layers sequenceInputLayer(numFeatures, Normalization, none); for b 1:numBlocks d dilations(b); % 残差块内第一组卷积 layers [layers; convolution1dLayer(filterSize, numFilters, DilationFactor, d, Padding, causal, PaddingValue, 0); batchNormalizationLayer; reluLayer; spatialDropoutLayer(0.1); % 自定义层来自 spatialDropoutLayer.m convolution1dLayer(filterSize, numFilters, DilationFactor, d, Padding, causal, PaddingValue, 0); batchNormalizationLayer; reluLayer; spatialDropoutLayer(0.1)]; end layers [layers; fullyConnectedLayer(1); regressionLayer];这里把Padding设为causal是 MATLAB 2021b 之后支持的选项它等价于左侧补零。如果你的版本没有这个选项就手动设置Padding为[filterSize-1 0]的对称形式但那样会改变输出长度。PaddingValue必须为 0否则因果性会被破坏。每个残差块内没有显式写残差连接是因为 MATLAB 的addLayers和connectLayers需要在dlnetwork对象里手工连接。完整源码中是用analyzeNetwork检查后补上的additionLayer。3.3 训练选项设置与验证集使用options trainingOptions(adam, ... MaxEpochs, 200, ... MiniBatchSize, 32, ... InitialLearnRate, 0.001, ... LearnRateSchedule, piecewise, ... LearnRateDropPeriod, 50, ... LearnRateDropFactor, 0.5, ... ValidationData, {XVal, YVal}, ... ValidationFrequency, 20, ... Shuffle, every-epoch, ... Plots, training-progress, ... Verbose, 0); net trainNetwork(XTrain, YTrain, layers, options);MiniBatchSize对于序列数据是样本个数不是时间步数。因为每条样本本身就是独立序列如果样本长度不一需要先用padsequences对齐。源码里的data.xlsx每条样本等长所以可以直接训练。ValidationFrequency20 表示每 20 个迭代验证一次这个值太小会拖慢训练太大则验证损失曲线不够平滑。LearnRateDropPeriod50 的含义是在第 50、100、150 轮时学习率减半对回归任务来说初始学习率 0.001 相对保守如果看到损失前 10 轮几乎不动可以提高到 0.01。4. 训练结果可视化、参数调优与报错排查4.1 从 TCNNN1-6.png 读懂训练状态源码中的TCNNN1.png到TCNNN6.png是不同时期保存的训练过程截图。看这类图时我一般先看验证损失和训练损失的差距如果两条线贴得很近且都在下降说明模型没有过拟合如果训练损失已经降到 0.01 以下而验证损失还在 0.1 以上说明空间 Dropout 概率设得太低或者网络容量过大。再看第二个子图的实测值与预测值散点如果散点沿 45 度线分布说明预测误差主要来自随机噪声如果散点有明显弧度说明输出层之前的激活函数约束不够应该去掉reluLayer或改成线性激活。4.2 关键超参数对 TCN 回归的影响下面这张表是我在类似项目里反复调参后总结的规律直接套用可以减少很多试错时间。就以源码的数据规模为参考约 1000 条样本7 输入 1 输出。参数源码默认值影响调整方向卷积核大小 filterSize3决定单层感受野宽度序列噪声大用 5噪声小用 3膨胀因子 dilations1,2,4,8控制总感受野序列长度超过 100 时加 16,32滤波器数量 numFilters32特征提取容量训练损失高但验证不高时增加到 64空间 Dropout 概率0.1正则强度过拟合明显时调到 0.2~0.3批大小32训练稳定性和速度样本少于 500 时用 16初始学习率0.001收敛速度损失震荡时降到 0.0005一个容易踩的坑是增大filterSize之后Padding的causal选项依旧有效但补零长度会自动变成filterSize-1乘以膨胀因子这会导致前后几个时间步的输出被大量零填充覆盖。如果你在预测时发现序列首尾的预测值都被拉向均值多半就是这个原因解决办法是在训练时手动裁剪前几个时间步的预测损失。MATLAB 的regressionLayer无法只对部分时间步计算损失常见做法是改用dlnetwork自定义训练循环但源码里没有这一步所以看验证集首尾偏差时不要方差太大就继续调学习率。4.3 编码乱码、版本兼容与运行环境检查摘要里特别提到“程序乱码是由于版本不一致导致”这其实是 MATLAB 脚本文件编码问题。中文版 MATLAB 默认使用 GBK英文版用 UTF-8用记事本打开源码再另存为 UTF-8 或 GBK 就能修复。注意不要用 MATLAB 的编辑器直接改因为编辑器会用当前系统编码重新解释可能导致中文变问号。正确做法是用记事本打开MainTCNN.m看到中文正常后选择“另存为”编码选 UTF-8然后重新打开。如果是 2021b 以下版本运行报错causal选项无法识别就把convolution1dLayer的Padding改成[filterSize-1 0]并手动裁剪输出。% 版本兼容写法手动补零实现因果卷积 l convolution1dLayer(3, 32, DilationFactor, 2, ... Padding, [2 0]); % filterSize-12 乘以膨胀因子后的左侧补零上面写法的问题在于Padding使用了单一值对膨胀卷积来说实际补零量是(filterSize-1)*dilationFactor。所以如果膨胀因子是 2左侧要补(3-1)*24但Padding参数只接受普通窗口不能自动乘膨胀因子。正确做法是给每个卷积层手动计算Padding为[(k-1)*d 0]并且验证输出长度等于输入长度。这就是源码里为什么要在spatialDropoutLayer之外还要写很多判断条件的原因也是你在自己项目里最容易卡住的地方。5. 用训练好的 TCN 模型对新样本批量预测并导出 Excel5.1 保存网络与加载网络训练完成后直接保存整个net下次加载不需要重建网络结构。save(tcn_net.mat, net, X_mean, X_std, Y_mean, Y_std);X_mean、X_std这些归一化参数必须一起存否则加载模型后无法对新数据做同样的变换。注意trainNetwork返回的net是SeriesNetwork或DAGNetwork可以直接传给predict。5.2 批量预测与反归一化load(tcn_net.mat); X_new readmatrix(new_data.xlsx); % 新样本7列 X_new X_new; % 特征 x 样本 X_new_norm (X_new - X_mean) ./ (X_std eps); Y_pred_norm predict(net, X_new_norm); Y_pred Y_pred_norm * Y_std Y_mean; % 反归一化 csvwrite(predicted_result.csv, Y_pred);这里predict对多输入回归返回一个numClasses x numSamples的矩阵因为输出层只有一个神经元所以Y_pred_norm是 1 行 N 列。反归一化时不要忘记Y_mean是训练集的均值Y_std是标准差多次预测时应该固定这两组值不能每次重新计算。5.3 把预测结果和真实值一起写入表格实际业务中通常需要对比真实值和预测值用表格输出最直观Y_val_norm (YVal - Y_mean) ./ (Y_std eps); Y_val_pred_norm predict(net, XVal); Y_val_pred Y_val_pred_norm * Y_std Y_mean; T table((1:length(YVal)), YVal, Y_val_pred, ... VariableNames, {SampleID, TrueValue, PredValue}); writetable(T, prediction_validation.xlsx);然后可以用scatter(T.TrueValue, T.PredValue)检查散点是否贴合对角线。最后一个小技巧是如果新样本长度和训练样本不一致不要直接predict先把每条序列独立切成长度一致的子窗口分别预测再对重叠区域取平均。这样可以减少边缘预测偏差尤其在感受野只有 31 步时这个做法比强行补零效果更好。本文还有配套的精品资源点击获取
返回列表