尧图精选

基于Matlab的CNN-LSTM时序回归预测模型搭建与实战解析

🕒 发布时间:2026/9/11 18:32:52 📁 来源:尧图网络
简介面向MATLAB回归预测建模需求CNN-LSTM卷积神经网络-长短期记忆组合模型资源包提供了从数据准备、网络搭建到结果评估的完整代码。适用于需要快速上手组合神经网络的科研人员、学生或工程师在时序回归、多变量预测等场景中可直接替换Excel数据集运行。压缩包共9个文件以两个m源码为核心配合一个xlsx示例数据和txt说明文档另有五张jpg图表展示训练集、测试集的预测结果对比与误差曲线整体仅854KB轻量易用。目前已有238人学习下载代码中文注释清晰按示例修改数据格式后运行主程序即可完成训练与预测自动输出评价指标和图像适合用于毕业设计、论文复现或项目快速验证。1. 为什么回归预测要选 CNN-LSTM 组合模型做时序回归预测时单独用卷积神经网络CNN或长短期记忆神经网络LSTM都会遇到各自的天花板。CNN 擅长提取局部特征但记忆能力弱拿它处理长时间依赖关系会丢失上下文LSTM 对时间步的记忆能力强但在面对多变量、高噪声输入时原始特征直接进 LSTM 容易被无关波动干扰训练也不容易收敛。CNN-LSTM 组合模型的思路不是叠层数而是让 CNN 先做一次特征抽象与降噪再把压缩后的特征序列交给 LSTM 捕捉长期依赖最后经全连接层输出回归值。这个组合尤其适合风速、电价、负荷、交通流这类既有局部突变又有长期趋势的数据。Matlab 里落地这个模型有一条清晰路径准备测试数据集、归一化、构造序列输入、定义网络层、设置训练选项、预测、反归一化、绘图、计算评价指标。网上不少项目把流程简化成跑通就完事但在实际实验里数据划分、图层参数、序列长度、评价指标口径这些细节才是决定模型能不能用的关键。本文会直接把每一步可抄的代码和参数讲透包括那些容易让新手卡住报错的坑。适合读这篇的人正在用 Matlab 做回归预测、需要输出对比曲线和量化指标的研究生以及想把 CNN-LSTM 从概念落到可复现实验的工程师。下面的内容按数据准备、模型搭建、训练预测、指标验证四个环节展开每个环节都给完整代码和参数说明。2. 训练前的数据准备时序数据归一化与测试数据集划分2.1 输入格式CNN 与 LSTM 要求的特征排列Matlab 中 trainNetwork 处理序列数据时输入层根据网络第一层类型不同接受的格式不同。CNN-LSTM 网络的输入层要使用 sequenceInputLayer它接收的数据格式是 numFeatures × numTimeSteps 的矩阵如果是多个独立样本则需要 numFeatures × numTimeSteps × numObservations 的三维数组或者使用 cell 数组每个 cell 存放一个样本的矩阵。% 假设原始数据是单变量序列长度为 2000 data sin(0.01 * (1:2000)) 0.1 * randn(2000, 1); % 用过去 20 个时间步预测未来 1 个时间步 numTimeSteps 20; numFeatures 1; % 构造输入和输出 X []; Y []; for i 1:(length(data) - numTimeSteps) X cat(3, X, data(i : i numTimeSteps - 1, :)); % 每个样本是 1 x numTimeSteps Y(end1, 1) data(i numTimeSteps, 1); end % X 转为 1 x 20 x numObservationsY 为 numObservations x 1 X reshape(X, [numFeatures, numTimeSteps, size(X, 3)]);代码说明循环里用 cat(3,...) 按第三维拼接每个观测是一个 1×numTimeSteps 的行向量转置后正好符合 sequenceInputLayer 对单样本的格式要求。最后 reshape 不改变数据顺序只是让维度显式化。这里构造的是单变量序列多变量时把 numFeatures 改成变量个数data 前两维对应特征和时间步。参数说明numTimeSteps 是滑动窗口长度也叫滞后阶数。窗口太小模型看不到足够的上下文窗口太大样本量减少且训练变慢。常见做法是先通过自相关图autocorr看序列衰减到显著区间外的滞后期或者直接尝试 10、20、30 三档对比验证损失。numFeatures 要与 data 的列数一致单变量为 1多变量输入则为实际特征数量。2.2 归一化mapminmax 与反归一化的配对使用CNN 内部使用的激活函数对输入尺度敏感LSTM 的 sigmoid 和 tanh 同样在输入过大或过小时梯度饱和。所以训练前必须做归一化。Matlab 里 mapminmax 是最常见的选择它可以把每行特征缩放到 [-1, 1] 区间。% 归一化对全部原始数据统一做归一化注意这里为了演示先统一处理 dataNorm mapminmax(data, -1, 1); % 这里 data 是 1 x Nmapminmax 按行处理转置后还原为 N x 1 % 重新构造 X 和 Y处理归一化后的数据 X zeros(numFeatures, numTimeSteps, numObservations); for i 1:numObservations X(:, :, i) dataNorm(i : i numTimeSteps - 1, :); Y(i, 1) dataNorm(i numTimeSteps, 1); end % 按时间顺序切分前 80% 训练后 20% 测试 numTrain floor(0.8 * numObservations); XTrain X(:, :, 1:numTrain); YTrain Y(1:numTrain, :); XTest X(:, :, numTrain1:end); YTest Y(numTrain1:end, :);代码说明mapminmax 的调用形式为 mapminmax(data, ymin, ymax)默认按行处理。data 转置成行向量之后做归一化再转置回来能避免列方向处理造成的错位。后续构造 X 和 Y 全部基于归一化后的序列保证同一个尺度。数据划分的说明这里按时间顺序前 80% 训练、后 20% 测试不做随机打乱。时序预测中随机打乱会破坏时间依赖导致测试集出现训练集之后的数据属于数据泄露。划分后需要记住训练集的归一化参数测试集的归一化和反归一化都要使用训练集算出的 min 和 max。% 保存训练集的归一化参数测试集后续要用同一套参数 [XNorm, ps] mapminmax(XTrain); % 但注意这里是按三维数组处理mapminmax 对三维数组支持不直观。 % 更稳妥的做法先把 XTrain 展开成二维矩阵统一归一化后再 reshape 回三维。注意这里有一个 Matlab 新手的常见误区mapminmax 直接对三维数组处理时行为难以预测。更稳妥的做法是把三维数组按时间步方向展平归一化再还原结构。上面的代码只是为了演示 mapminmax 的用法实际工程实现时推荐下面这样XFlat reshape(XTrain, numFeatures, []); [XFlatNorm, ps] mapminmax(XFlat, -1, 1); XTrainNorm reshape(XFlatNorm, size(XTrain)); % 测试集用训练集的 ps 归一化 XTestFlat reshape(XTest, numFeatures, []); XTestNorm reshape(mapminmax(apply, XTestFlat, ps), size(XTest)); % 标签也做同样的处理 YTrainNorm mapminmax(YTrain, -1, 1); YTestNorm mapminmax(apply, YTest, psY); % 标签单独保存一组参数 psY参数说明mapminmax(apply, data, ps) 是应用已保存的参数到新数据的标准写法。ps 是在训练集上由 mapminmax 自动生成的包含 xmin、xmax、ymin、ymax 的结构体。反归一化时使用 mapminmax(reverse, normalizedData, ps)。注意 X 和 Y 要分别保存归一化参数因为它们的数值范围完全不同不要共用一组参数。2.3 测试数据集的组织方式与评估口径训练集、验证集、测试集三者的角色要分清。训练集用于更新网络权重验证集用于监控过拟合并决定是否早停测试集只用于最终评估。项目中如果数据量不大可以从训练集末段划出 10% 做验证集测试集保持独立的最后 20%。数据集样本范围用途是否需要归一化参数训练集前 70%权重更新、梯度计算独立计算 ps验证集训练集末尾 10%监控验证损失、早停使用训练集 ps测试集最后 20%最终回归指标评估使用训练集 ps表格说明验证集不能使用自己的归一化参数否则模型看到的数据分布会偏移。测试集同理所有归一化参数只能来自训练集。如果使用 validation 选项Matlab 会在每个 epoch 结束时计算验证损失但需要注意验证集是从训练集内部划分不是独立数据。测试集的预测结果在反归一化后用于计算评价指标输出的是和原始数据同一量纲的值。如果直接拿归一化后的预测值算指标RMSE 等数值会偏小到失去物理意义这一点在最终报告里很容易被忽略。3. 在 Matlab 中搭建 CNN-LSTM 组合模型的图层结构与参数3.1 图层如何衔接卷积核、池化与 LSTM 输入维度的匹配CNN-LSTM 的网络结构不是简单把两类层堆起来。卷积层输出的特征图要能被 LSTM 当作时间序列接收关键在第 3 维——时间步数量的变化。以一维卷积为例输入格式为 numFeatures × numTimeSteps卷积层沿时间步方向滑动输出仍是二维矩阵特征维度变为滤波器数量时间步长度根据 padding 和 stride 变化。所以 LSTM 层的输入维度是 numFilters × 新的时间步数numHiddenUnits 控制记忆单元的容量。常见的 Matla lab 网络结构代码layers [ sequenceInputLayer(numFeatures, Name, input) convolution1dLayer(3, 32, Padding, same, Name, conv1) batchNormalizationLayer(Name, bn1) reluLayer(Name, relu1) maxPooling1dLayer(2, Stride, 2, Name, pool1) convolution1dLayer(3, 64, Padding, same, Name, conv2) batchNormalizationLayer(Name, bn2) reluLayer(Name, relu2) lstmLayer(64, Name, lstm1) dropoutLayer(0.2, Name, dropout) fullyConnectedLayer(1, Name, fc) regressionLayer(Name, output) ]; lgraph layerGraph(layers); analyzeNetwork(lgraph);代码说明convolution1dLayer(filterSize, numFilters, Padding, same)filterSize 是卷积核长度numFilters 是输出通道数。这里第一层 32 个滤波器提取局部特征池化层将时间步压缩一半第二层用 64 个滤波器进一步抽象。batchNormalizationLayer 放在卷积层后 relu 前有助于加速收敛并减少对初始学习率的敏感度。关键细节lstmLayer 的输入维度会自动匹配前一层的输出特征数不用手动指定 inputSize。但要注意 maxPooling1dLayer 会改变时间步长度如果 stride 不为 1时间步数会减少LSTM 层看到的是一个缩短后的序列这相当于在做时间维度的降采样有助于降低计算量但过度池化会丢失短周期模式一般只做一到两次池化。3.2 超参数怎么定卷积核、隐含单元数与学习率的取舍超参数选择没有唯一标准但有可用的参考区间。filterSize 对应局部窗口大小对周期型数据取 3 到 9 之间对应时间步的数量级别numHiddenUnits 控制在 32 到 128数据量大、序列长时取更大值InitialLearnRate 设为 0.001 到 0.01使用 Adam 优化器时不需要手动调整学习率衰减。超参数推荐区间调整依据filterSize3 ~ 9数据局部周期长度、采样频率numFilters第一层16 ~ 64特征复杂度通道数翻倍规则poolSize2 ~ 3时间步压缩幅度过大丢失短周期numHiddenUnits32 ~ 128序列长度、数据量MiniBatchSize16 ~ 128内存占用与梯度稳定性InitialLearnRate0.001 ~ 0.01损失震荡就调低收敛慢就调高MaxEpochs100 ~ 300配合验证损失做早停参数说明numFilters 通常按 16→32→64 递增每层翻倍是卷积网络的常见配置避免单层通道数过高导致参数量爆炸。numHiddenUnits 大小直接决定 LSTM 的参数量设置过大在小数据集上会过拟合。训练选项中的 InitialLearnRate 是最敏感的参数如果训练损失曲线出现剧烈震荡优先降低学习率而不是调网络结构。3.3 训练选项配置与验证集监控trainingOptions 是 Matlab 训练流程的配置入口。除了常规的求解器、学习率、轮数ValidationData 和 OutputFcn 是影响训练体验的两个关键项。ValidationData 用于每轮结束后计算验证损失配合 ValidationFrequency 控制检查频率。options trainingOptions(adam, ... InitialLearnRate, 0.005, ... MaxEpochs, 200, ... MiniBatchSize, 64, ... GradientThreshold, 1, ... Shuffle, every-epoch, ... Verbose, true, ... VerboseFrequency, 50, ... ValidationData, {XValNorm, YValNorm}, ... ValidationFrequency, 20, ... Plots, training-progress, ... OutputFcn, (info)stopIfImprovement(info, 5));代码说明GradientThreshold 设为 1 来裁剪梯度LSTM 对梯度爆炸敏感梯度裁剪是稳定性保障。Shuffle 设为 every-epoch 可以让每个 epoch 的 mini-batch 顺序重新洗牌帮助训练收敛。ValidationFrequency 为 20 表示每 20 个迭代计算一次验证损失。Plots 的 training-progress 选项可以在训练窗口实时显示损失曲线训练结束后仍可查看。对于早停函数可以写一个简单的子函数当一个固定轮次窗口内验证损失没有下降时停止训练。OutputFcn 接收 info 结构体info.State 为 iteration 时检查 info.ValidationLoss 的变化。这里不提供完整代码因为 Matlab 自带的 OutputFcn 机制配合深度学习工具箱文档中的示例可以快速实现。验证集的作用体现在两点一是判断是否早停二是监控是否过拟合。训练损失持续下降但验证损失开始上升说明模型开始记忆训练集此时应停止训练或增加 dropout 比例。4. 训练过程、预测图像绘制与反归一化还原4.1 模型训练与训练过程曲线解读trainNetwork 接收上面定义的 layers、训练数据和 options训练完成后返回一个训练好的网络对象。训练时需要注意的是如果数据量较大Matlab 会默认使用 GPU 加速如果没有 GPU会自动回退到 CPU但速度会慢很多可以手动指定 ExecutionEnvironment 参数。net trainNetwork(XTrainNorm, YTrainNorm, lgraph, options);训练完成后训练窗口的损失曲线可以看到训练损失和验证损失的变化。一个健康的训练过程表现为训练损失和验证损失同步下降最终验证损失趋于平稳。如果训练损失一直接近 0 而验证损失偏高说明过拟合如果两者都偏高不下降可能是学习率太低或网络结构不够深。关于训练中断的恢复问题Matlab 的 trainNetwork 本身不支持直接断点续训但可以在训练前保存初始随机种子或者在 OutputFcn 中定期保存 net 对象。使用 restore 命令恢复工作区中的变量并不能恢复随机种子带来的确切初始化状态更好的做法是每次从头训练并记录配置参数。4.2 测试集预测、反归一化与预测图像绘制训练完成后用测试集做前向预测。关键步骤是先对输入做同样的归一化预测然后反归一化回原始量纲。% 测试集预测 YPredNorm predict(net, XTestNorm, MiniBatchSize, 64); % 输出是 numObservations x 1 % 反归一化 YPred mapminmax(reverse, YPredNorm, psY); YTestOrig mapminmax(reverse, YTestNorm, psY); % 注意这里 YTestNorm 已经是归一化的psY 是在训练集标签上计算得到的参数 % 绘制测试集真实值与预测值对比图 figure; plot(YTestOrig, b-, LineWidth, 1.5); hold on; plot(YPred, r--, LineWidth, 1.5); legend(真实值, 预测值); xlabel(测试集样本点); ylabel(目标变量); title(CNN-LSTM 测试集预测对比); grid on;代码说明predict 函数返回归一化标签空间的预测值必须先反归一化再进行后续绘图和指标计算。反归一化时 psY 是在训练集标签上调用 mapminmax 后生成的参数测试集标签和预测结果共用同一组参数。如果不做这步预测曲线会被压缩在 [-1, 1] 区间真实值和预测值不在同一量纲上图看起来偏差巨大实际上只是归一化未还原。绘图时真实值和预测值应该用同一种线型区分常见做法是实线加虚线的组合。图例中注明是测试集这样看图的人能明确知道评估是在未见过的数据上完成的。除了真实值与预测值的对比误差分布图也是预测图像的一部分。常见的做法是绘制残差直方图和误差随样本变化的曲线。% 计算残差 residual YTestOrig - YPred; % 绘制误差时序图 figure; plot(residual); xlabel(测试集样本点); ylabel(预测误差); title(CNN-LSTM 预测残差序列); grid on; % 绘制误差直方图 figure; histogram(residual, 30); xlabel(预测误差); ylabel(频数); title(预测误差分布);误差直方图可以快速看出偏差是否集中在 0 附近如果直方图明显偏斜或出现双峰说明模型存在系统性误差可能需要检查数据划分、归一化方式或考虑对目标变量做对数变换。4.3 多维输出的处理方式如果预测目标是多步未来的值比如同时预测未来 1 到 5 个时间点做法有两种。一种是改变网络输出层fullyConnectedLayer 的输出维度改为实际需要预测的步数回归层保持不变另一种是保持一步预测用递归的方式逐点预测即把上一步的预测值作为下一步的输入。递归预测的代码逻辑% 递归多步预测假设需要预测 10 步 numPredSteps 10; inputWindow XTestNorm(:, :, 1); % 取第一个测试样本 predictions zeros(1, numPredSteps); for step 1:numPredSteps predStep predict(net, inputWindow); predictions(step) predStep; % 把新预测值拼接到输入窗口末尾丢弃最前方的数据 inputWindow [inputWindow(:, 2:end), predStep]; end这里要注意递归预测会累积误差因为每一步步的预测误差会传入后续步骤。如果递归步数过长预测曲线会趋向序列的均值或漂移。工程上的折衷方案是每次预测多个步长用模型直接输出一个向量虽然训练难度略增但误差不会滚雪球。5. 回归评价指标计算与结果验证技巧5.1 RMSE、MAE、MAPE 与 R² 的 Matlab 实现评价指标是回归预测的最终量化结果。RMSE均方根误差 对较大误差更敏感MAE平均绝对误差反映平均偏差MAPE平均绝对百分比误差适合目标变量没有接近 0 的值的情况R²决定系数表示模型解释目标变量方差的比例。% 计算回归评价指标 residual YTestOrig - YPred; RMSE sqrt(mean(residual.^2)); MAE mean(abs(residual)); MAPE mean(abs(residual ./ YTestOrig)) * 100; SS_res sum(residual.^2); SS_tot sum((YTestOrig - mean(YTestOrig)).^2); R2 1 - SS_res / SS_tot; fprintf(RMSE: %.4f\n, RMSE); fprintf(MAE: %.4f\n, MAE); fprintf(MAPE: %.2f%%\n, MAPE); fprintf(R²: %.4f\n, R2);代码说明SS_res 是残差平方和SS_tot 是真实值与均值之差的平方和R² 的取值越接近 1 越好。MAPE 计算中如果 YTestOrig 含有接近 0 的值指标会爆炸此时应改用 SMAPE 或直接不报告 MAPE。注意所有指标都基于反归一化后的原始量纲数据不是归一化空间的数值。同一测试集上R² 在 0.9 以上通常表示模型拟合度较好但对时序预测来说R² 高不一定说明模型学到了动态特征——目标变量的强自相关性会让朴素预测比如直接沿用上一时刻值也获得很高的 R²。所以建议同时报告 RMSE 与一个基准模型的对比比如持续预测的 RMSE才能证明 CNN-LSTM 确实学到了超越历史值的模式。5.2 指标对比的常见陷阱很多实验报告的指标只在测试集上计算一次没有说明模型训练过程中是否反复看了测试集。如果你在调参过程中频繁用测试集结果做决策测试集就变成了验证集最终报告的指标会偏乐观。正确的流程是训练集训练验证集早停测试集只做最终评估。样本量较小时可以使用滚动验证的方式多次划分训练集和测试集报告指标均值和标准差。5.3 一个隐藏技巧用特征重要性和误差分析缩小模型与业务的差距模型训练完指标也算了接下来一个值得做的验证是误差所在的时间段。把残差按时间顺序画出来后找到误差最大的几个区段看这些区段是否对应数据的突变或季节性波动。如果发现模型在数据突变处误差显著增大说明窗口长度 numTimeSteps 不足模型看不到突变前的足够上下文此时应增大卷积核长度或扩大滑动窗口。这比盲目堆层数有效得多。如果测试集的预测曲线整体滞后于真实值说明模型对时间依赖的建模偏向于复制上一刻而不是预测变化。缓解手段包括去掉一层 LSTM 的序列拼接减少池化层对高频信息的丢弃或在输入中加入差分后的增量特征让模型既能学绝对水平也能学变化趋势。差分序列可以让卷积核更好地捕捉局部变化这是 CNN-LSTM 在处理非平稳序列时一个实用的小技巧% 构造差分特征 dataDiff diff(data); % 将差分序列和原序列拼接形成双通道输入 dataMulti [data(2:end), dataDiff]; % 后续构造 X 时 numFeatures 改为 2加入差分特征后卷积层能同时看到绝对值和变化率两个维度的信息实际预测中通常可以获得更好的峰值拟合效果。若配合现有模型做误差对比可以直观检查新特征是否真正带来 RMSE 的下降。还有一点值得提一下网上不少 Matlab 项目只给结构和代码不讲训练中报错的代码位置比如在 sequenceInputLayer 之后接 convolution1dLayer 时提示特征数不匹配大多数情况是数据维度顺序写反了。建议在每一步后用 size 打印变量维度确认是 numFeatures × numTimeSteps × numObservations 的顺序再进入下一层。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →