资讯详情

资讯详情

Matlab实现CNN-LSTM组合模型用于时间序列回归预测的完整流程

简介这份Matlab资源面向需要做回归预测的科研与工程人员提供CNN-LSTM卷积神经网络与长短期记忆网络组合模型的完整实现覆盖数据读取、训练、预测与评价。压缩包共9个文件以两个.m程序文件为核心辅以数据集xlsx、readme说明以及多张预测结果和误差曲线jpg整体仅854KB轻量易用。目前已有238人学习下载。文件内中文注释清晰只需按示例修改Excel数据格式并替换数据集即可运行所有预测图像与评价指标均自动生成方便直接对比模型效果。资源还配套误差曲线、训练进度等可视化输出适合希望快速上手组合神经网络回归建模的初学者也可作为基准实验参考。1. 拿到这个 Matlab CNN-LSTM 组合包先别急着跑数据很多做回归预测的人拿到时间序列第一反应就是把数据直接塞进 LSTM。但单靠 LSTM 处理多特征、局部突变明显的序列训练时间长还容易对最近一段数据过度敏感。这个 Matlab 包把一维卷积神经网络和长短期记忆网络串成一条流水线卷积层先在时间窗口上提取局部模式LSTM 再对卷积输出做时序建模最后接回归层输出连续值。包里除了主程序 CNN_LSTM.m还有计算指标的 calulateE.m、示例数据集 .xlsx以及预测结果对比图、样本误差曲线和训练进度图是一条完整可复现的回归预测流程。适合想在 Matlab 里快速验证 CNN-LSTM 思路的工程师也适合做负荷、价格、流量这类连续值预测的基线。替换 Excel 数据前先打开 readme.txt 确认列顺序。2. CNN-LSTM 结构拆解一维卷积在哪一步介入时序回归2.1 为什么回归预测不能只靠 LSTM 或 CNN 单打对于连续值预测最常见的错误是用单层 LSTM 直接吃原始序列。LSTM 对时间依赖有建模能力但输入维度过高、特征之间存在局部交叉时它会把大量参数花在记忆原始值上造成收敛慢。CNN 虽然善于抓局部模式但它本身没有状态窗口滑动之后仍然不知道这些模式按什么顺序出现。CNN-LSTM 组合模型把两者的长处拼在一起先用一维卷积把局部时间窗口内的多特征交叉提取出来再让 LSTM 在卷积结果上继续做时序建模。这个结构不是多此一举而是把「特征工程」和「时间依赖」分成两步处理卷积输出的特征图比原始数据更干净LSTM 的负担也小很多。2.2 一维卷积不是图像专利它在时间维上滑动Matlab 里用convolution1dLayer处理时间序列卷积核不再扫图像的宽和高而是扫时间维。比如输入每个时刻有 5 个特征定义一个filterSize3的卷积层它每次覆盖连续 3 个时间步把 5 个特征投影成 16 个新通道。这个过程相当于在序列局部做了一次加权组合突出突变、拐点这类短时模式。要注意Padding, same参数它让卷积输出长度和输入一致否则每过一层序列就会变短后面 LSTM 看到的时间步也会减少。下面的代码就是这个包对应的卷积层定义numFeatures 5; % 每个时刻的特征数 filterSize 3; % 卷积核覆盖 3 个时间步 numFilters 16; % 输出通道数即 16 个局部模式 convLayer convolution1dLayer(filterSize, numFilters, Padding, same);这段代码定义了随后接入 LSTM 的卷积层。numFeatures必须和数据集的特征列数一致filterSize越大感受野越大但太大会把不同阶段的模式混在一起numFilters决定模型复杂度16 起步训练集很大时可以加到 32 或 64。2.3 LSTM 吃的是特征序列不是原始数据卷积层输出的结果仍然是按时间顺序排列的只是每个时刻的向量变成了 16 维特征。这时接入lstmLayer(32, OutputMode, last)LSTM 会按时间步逐个读入这 16 维特征并在内部维护一个 32 维的隐藏状态。last表示只输出最后一个时间步的隐藏状态适合「给定一段历史窗口预测下一个值」的回归任务。如果做多步预测可以改成sequence拿全部时间步的输出。下面用一个表把每层职责和关键参数列清楚层Matlab 函数在组合模型中的任务关键参数输入层sequenceInputLayer(numFeatures)声明每个时间步的特征维数numFeatures一维卷积convolution1dLayer(k, f, Padding,same)提取局部时间窗内的特征交叉核长 k、滤波器数 f归一化batchNormalizationLayer稳定卷积输出分布加速收敛无激活reluLayer引入非线性抑制负值无池化maxPooling1dLayer(p, Stride, s)时间步下采样减少 LSTM 压力池化窗 p、步长 sLSTMlstmLayer(u, OutputMode,last)在高层特征上建模时间依赖隐藏单元数 u输出fullyConnectedLayer(1) regressionLayer映射到连续值并计算损失输出维 1这里的maxPooling1dLayer不是必选项。时间窗口只有几十步时池化会明显压缩信息建议去掉窗口在几百步以上时再考虑池化。LSTM 隐藏单元数 u 过小会欠拟合过大容易过拟合一般取 32 到 128 之间做对比实验。2.4 与 PyTorch 实现相比Matlab 这套组合包的取舍深度学习里用 PyTorch 实现 BP 神经网络回归预测或者 CNN-LSTM自由度更高配合 SHAP 分析还能看特征贡献。但代价是数据加载、padding、学习率调度都要自己写。Matlab 的 Deep Learning Toolbox 把trainNetwork到训练进度图打包好调整结构基本只改 layers 数组适合先验证模型方向。缺点是一旦训练报错堆栈信息不如 Python 直观数据维度问题也常藏在 cell 数组里。所以这套包适合「快速跑通、后续再迁移到 Python 精细打磨」的场景。3. 主程序 CNN_LSTM.m 的运行链路从 xlsx 到预测图3.1 先想清楚样本组织方式宽表直读还是滑窗生成主程序第一步不是建网络而是把数据集.xlsx 读进来并整理成模型要的格式。项目里的数据集一般是数值矩阵前面若干列是特征最后一列是目标值。现在的推荐函数是readmatrix旧脚本里的xlsread也能用但返回格式更啰嗦。直接读的写法是data readmatrix(数据集.xlsx); Xraw data(:, 1:end-1); % 特征列 Yraw data(:, end); % 目标列如果你的数据是每个时刻一行而且不打算做滑窗到这一步就可以按后面 3.2 继续。但很多时间序列原始数据只有一个长列这时需要把长序列切成固定长度的样本每个样本是一段特征窗口加一个目标值。常见做法是用前 T 个时间步预测第 T1 个点T 24; % 窗口长度用前 24 步预测下一步 numSamples size(data, 1) - T; X cell(numSamples, 1); Y zeros(numSamples, 1); for i 1:numSamples X{i} data(i:iT-1, 1:end-1); % 特征数 × T 的矩阵 Y(i) data(iT, end); % 目标值 end这里把每个样本存成 cell 数组的一个元素元素内部是「特征数 × 时间步」的矩阵行数等于特征列数列数等于窗口长度 T。之所以要转置是因为sequenceInputLayer要求时间维在第二维不转置的话Matlab 会认为每个时间步有 T 个特征语义完全反了。如果项目 readme 已经给出宽表格式跳过滑窗即可。提示如果 Excel 第一行是文字表头readmatrix会把这些文本变成 NaN训练时直接报错。可以先用readmatrix(数据集.xlsx, NumHeaderLines, 1)跳过表头。3.2 归一化用 mapminmax 时最容易写反方向训练前必须归一化否则卷积层输出的梯度会被量纲差异带偏。这里不能用每个样本各自的 min/max而应该在整个训练集上统计测试集沿用同一组参数。常见错误是用mapminmax直接作用在每个 cell 上导致每个窗口被单独压缩测试时无法还原。我一般先把所有窗口拼起来算特征维的 min 和 maxallX cell2mat(X); fmin min(allX, [], 2); fmax max(allX, [], 2); for i 1:numel(X) X{i} (X{i} - fmin) ./ (fmax - fmin eps); end yMin min(Y); yMax max(Y); Y (Y - yMin) / (yMax - yMin eps);参数说明min(allX, [], 2)沿着特征维求最小值结果是一个列向量eps防止特征恒定不变时出现除零。目标值单独存下yMin和yMax因为在预测完成后要反归一化才能和原始数据比较。接下来按时间顺序切分训练集和测试集不要用randperm打乱时间序列一旦随机打乱未来信息会混进训练集评价指标会虚高trainRatio 0.8; idx round(numSamples * trainRatio); XTrain X(1:idx); YTrain Y(1:idx); XTest X(idx1:end); YTest Y(idx1:end);这里idx是训练样本数量后面样本全部用作测试。如果数据量少可以用trainRatio0.75但至少留 20 个样本做测试否则预测对比图没有参考意义。3.3 搭 CNN-LSTM 层sequenceInputLayer 到 regressionLayer搭网络前先确认特征数numFeatures size(XTrain{1}, 1);。下面这个 layers 数组基本就是 CNN_LSTM.m 里主结构的骨架numFeatures size(XTrain{1}, 1); layers [ sequenceInputLayer(numFeatures) convolution1dLayer(3, 16, Padding, same) batchNormalizationLayer reluLayer maxPooling1dLayer(2, Stride, 2) lstmLayer(32, OutputMode, last) fullyConnectedLayer(1) regressionLayer ];sequenceInputLayer(numFeatures)声明输入是numFeatures × 时间步的序列。convolution1dLayer(3, 16)里 3 是卷积核长度16 是滤波器数量Padding,same保持时间步不变。batchNormalizationLayer对每个时间步的特征做归一化稳定训练。maxPooling1dLayer(2,Stride,2)把时间步压缩一半这里会丢失一部分位置信息如果训练窗口只有 20 步建议把这层直接删掉。lstmLayer(32)的 32 是隐藏单元数last只保留最后时刻的隐状态。最后接 1 个神经元和regressionLayer就是标准的单输出回归。3.4 trainNetwork 训练选项真正影响收敛的参数有了 layers剩下就是trainingOptions和trainNetwork。包里的训练进度图就是这里Plots,training-progress生成的options trainingOptions(adam, ... MaxEpochs, 60, ... MiniBatchSize, 32, ... InitialLearnRate, 0.005, ... LearnRateSchedule, piecewise, ... LearnRateDropPeriod, 20, ... LearnRateDropFactor, 0.2, ... Plots, training-progress, ... Verbose, true); net trainNetwork(XTrain, YTrain, layers, options);几个参数要单独说。MaxEpochs60在窗口样本不多时够用如果训练进度图上训练 loss 还在明显下降可以加到 100。MiniBatchSize32是内存和稳定性的折中样本少时报错就改成 16。InitialLearnRate0.005是经验值如果第一条 loss 曲线直接飞出很大范围把学习率降到 0.001。LearnRateSchedule用piecewise配合DropPeriod让后期 step 小一点避免在最优值附近震荡。trainNetwork输入直接用 3.1 生成的 cell 数组不需要再套一层 cell。预测和画图是主程序的收尾包里的测试集预测结果对比.jpg 就是这段逻辑YPred predict(net, XTest); YPred YPred(:); % 统一成列向量 YPredRaw YPred * (yMax - yMin) yMin; YTestRaw YTest * (yMax - yMin) yMin; figure; plot(YTestRaw, LineWidth, 1.2); hold on; plot(YPredRaw, LineWidth, 1.2); legend(真实值, 预测值); saveas(gcf, 测试集预测结果对比.jpg);predict返回的YPred是行向量还是列向量取决于观测数如果绘图时报维度错误先对YPredRaw做一次(:)处理。反归一化必须用训练集保存的yMin/yMax不能重新拿测试集算否则评价指标会失真。4. calulateE.m 与三张评价图回归指标怎么对齐模型行为4.1 calulateE.m 里的四个指标包里的calulateE.m负责把预测值和真实值换算成具体指标。拆开看核心就是 R2、RMSE、MAE、MAPE 这四个function [R2, RMSE, MAE, MAPE] calulateE(YTest, YPred) YTest YTest(:); YPred YPred(:); R2 1 - sum((YTest - YPred).^2) / sum((YTest - mean(YTest)).^2); RMSE sqrt(mean((YTest - YPred).^2)); MAE mean(abs(YTest - YPred)); MAPE mean(abs((YTest - YPred) ./ YTest)) * 100; endR2实际上是比较「预测残差」和「用均值当预测值」的差距越接近 1 表示模型比直接猜均值好得多如果 R2 为负说明模型还不如直接用均值预测。RMSE对较大误差更敏感因为误差先平方再开方。MAE是平均绝对偏差更贴近人对误差的感受。MAPE用真实值做分母真实值接近 0 时数值会爆炸所以看 MAPE 时要先确认测试集没有接近 0 的样本。下面的表可以贴在测试报告里指标表达式说明常见误区R2见代码越接近 1 越好样本均值漂移时 R2 会很低不代表模型不可用RMSEsqrt(mean(e^2))对大误差敏感异常值会把 RMSE 拉高MAEmean(abs(e))平均绝对偏离看不出误差波动MAPEmean(abs(e/y))*100%无量纲y 接近 0 时失去意义可用 SMAPE 替代调用calulateE时输入必须是反归一化后的原始量纲数据。包的 readme 里一般会说明这一点但很多人直接对归一化结果算 MAPE算出来的百分比会小到好看却不能说明真实误差。4.2 训练集预测结果对比和测试集预测结果对比的分工项目里同时有训练集预测结果对比.jpg 和测试集预测结果对比.jpg这两张图要分开解读。训练集对比图衡量的是「模型把训练数据学到什么程度」曲线如果几乎重合说明拟合能力足够测试集对比图才是真正的效果。如果训练集贴合、测试集明显偏离优先怀疑过拟合对策是增大数据量、减小 LSTM 隐藏单元数或者在trainingOptions里加L2Regularization, 0.01。如果测试集的预测曲线比真实曲线整体晚一个时间步出现那不是随机误差而是滑窗时把下一时刻的信息提前放进了特征常见于把目标列一并当作特征列。4.3 样本误差曲线看分布比看均值有用训练集样本误差曲线.jpg 和测试集样本误差曲线.jpg 提供的是逐样本误差。曲线围绕 0 上下波动且振幅稳定说明误差近似白噪声如果误差整段偏正或偏负说明预测存在系统性偏差常见于没有反归一化、输出层缺少偏置或者训练前数据没做零均值化。如果个别样本的误差突刺特别大把对应行的原始数据找出来看看多数情况是脏数据空值、重复值、单位不一致。只看 RMSE 会掩盖这些局部问题所以包里有这两张曲线图是必要的。4.4 把指标计算接到主程序末尾实际跑完CNN_LSTM.m后最省事的做法是把评价函数直接接到预测后面[R2, RMSE, MAE, MAPE] calulateE(YTestRaw, YPredRaw); fprintf(R2%.4f, RMSE%.4f, MAE%.4f, MAPE%.2f%%\n, ... R2, RMSE, MAE, MAPE);这里YPredRaw和YTestRaw都必须先按 3.4 的方式反归一化并且用(:)统一成列向量。四个指标一起看才能判断模型是整体偏移还是存在大误差点。单独看 R2 很容易被极端值骗到因为一次离谱的预测会把残差平方和拉大R2 立刻掉下去。5. 预测结果不稳定先查随机种子和训练顺序5.1 用多个随机种子验证稳定性CNN-LSTM 的权重是随机初始化的同一个数据集连续跑两次RMSE 可能差出 10%。不要先着急调结构先固定随机种子跑多次确认结果波动范围。Matlab 里在trainNetwork前调用rng(seed)即可CPU 训练基本能复现GPU 上受底层算法影响可能仍有细微差异。验证脚本可以这样写for seed 1:5 rng(seed * 100); net trainNetwork(XTrain, YTrain, layers, options); YPred predict(net, XTest); YPred YPred(:); YPredRaw YPred * (yMax - yMin) yMin; [R2(seed), RMSE(seed)] calulateE(YTestRaw, YPredRaw); end fprintf(RMSE mean%.4f, std%.4f\n, mean(RMSE), std(RMSE));这个脚本把 5 次独立训练的结果留下来看std而不是单次 RMSE。如果标准差过大说明模型对初始化很敏感优先减小InitialLearnRate或增加训练样本而不是继续增加网络层数。调参时每次只改一个变量并固定同一个 seed否则你看到的差异可能是随机波动不是参数带来的。5.2 预测结果回写到 Excel和源头数据对齐模型评估完之后把预测结果导出来和原始数据对齐比只看三张图更直接。常见做法是输出一个 csvExcel 直接能打开Tout table(YTestRaw, YPredRaw, YTestRaw - YPredRaw, ... VariableNames, {真实值, 预测值, 误差}); writetable(Tout, 预测结果对比.csv);第 4 列还可以加上样本编号方便按误差曲线上的突刺定位到具体时间段。导出的 csv 和包里的.xlsx保持同一行顺序这样排查问题时能迅速找到是哪个窗口、哪个样本导致的偏差。预测结果回写这一步看着简单却能让模型迭代效率高很多。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →