资讯动态

Matlab实现CNN、LSTM与CNN-LSTM多变量时间序列分类对比指南

发布时间:2026/10/21 16:06:07 来源:尧图企业网站定制
做多变量序列分类特别是想对比CNN、LSTM和CNN-LSTM谁更合适的时候很多人第一反应是打开Python框架。但如果你手头的数据是表格化、硬件采集或者已有大量业务代码在Matlab里重开一套技术栈反而麻烦。我最近正好用Matlab把三套模型在同一份多变量数据集上完整跑了一遍从数据整理、模型搭建到结果对比把过程中遇到的坑和关键选择都整理出来了。这篇文章适合正在做设备状态识别、信号分类、时间序列多指标分类预测的同学尤其是不想完全脱离Matlab工作流、又想快速出一个多模型对比结论的从业者。先说结论Matlab的深度学习工具箱Deep Learning Toolbox配合自定义网络层搭建CNN、LSTM和CNN-LSTM的复杂度远低于预期核心难点其实大部分在数据组织和维度对齐上而不是模型本身。下面直接进入正题。1. 项目整体设计与三模型对比思路1.1 这个任务到底在解决什么问题多变量分类预测本质上就是把一条多维时间序列映射到一个离散类别标签上。比如你采集了设备的振动、电流、温度三个通道的信号每段信号切一条样本经过模型后输出正常/故障A/故障B这样的结果。和回归预测最大的区别是输出是类别而不是数值所以网络最后通常接softmax层用分类交叉熵作为损失函数。这类任务在工业场景里非常常见关键难点有三层第一是多个变量之间不是独立存在的通道之间存在耦合关系第二是故障特征经常藏在局部窗口里比如某个瞬间的冲击信号第三是类别之间可能存在混淆仅仅看单一时间步的数值很难区分。三种模型解决的侧重点不同所以放在同一份数据上对比才有意义。1.2 三个模型的选型逻辑为什么偏偏是这三个模型而不是上Transformer或者GRU这里有一个很实际的项目选型逻辑。CNN擅长提取局部特征通过卷积核在时间轴上滑动能抓住短窗口内的模式。它的优势是训练快、参数少、不容易过拟合缺点是对时间顺序的长程依赖建模能力弱。LSTM通过门控机制记住长距离信息适合处理前面某段时间的状态决定了后面的类别这类问题。缺点是训练较慢对小样本不友好且单独使用时局部特征捕捉能力不足。CNN-LSTM先让卷积层把原始多变量信号压缩成更高层、更紧凑的特征序列再把这些特征送入LSTM学习时间依赖。这样既减轻了LSTM的输入压力又保留了时序建模能力在多通道、低信噪比的数据上通常效果最好。实际动手之前最好先用小样本把你的数据和任务梳理清楚否则后面调参很容易抓瞎。1.3 为什么用Matlab做这个实验Matlab在工程诊断、信号处理领域的使用率一直很高硬件在环、代码生成、Simulink等生态是Python短期取代不了的。而且Deep Learning Toolbox现在已经支持sequenceInputLayer、convolution1dLayer、lstmLayer等原生的序列建模层配置训练选项只需要一行函数式调用比手写训练循环省事很多。另外Matlab在数据可视化上也有天然优势训练过程曲线可以直接交互查看混淆矩阵有专门的chart函数这对快速判断模型是否收敛非常实用。如果你只是想做算法对比验证、不想维护一套Python推理环境Matlab几乎是效率最高的一条路。2. 数据准备的完整流程与预处理细节2.1 多变量序列的分类数据应该以什么格式组织在Matlab的trainNetwork中输入多变量序列有固定的组织要求每一个观测observation是一个二维矩阵尺寸是特征数×时间步数。所有观测放在一个列cell数组里。对应的标签是categorical类型向量与观测一一对应。这个格式特别容易搞混的是特征数和时间步数谁在前。先说结论必须以特征数为行、时间步数为列。比如四个特征、长度200的序列每个观测就是4×200的矩阵。如果你不小心用200×4trainNetwork一般不会明确告诉你你转置错了而是会在某个卷积层或LSTM层报维度错误排查起来非常难受。2.2 用合成数据验证整个流程为了在没有现成工业数据的情况下先验证流程我做了一份合成数据用来模拟三个类别的多变量时间序列。核心思路是每个类别使用不同频率的基础信号加上噪声和幅值偏移通过频率特征来区分类别。代码如下% 生成三类多变量时间序列每类120条样本每条样本200个时间步4个特征 rng(11); numFeatures 4; timeSteps 200; numObsPerClass 120; numClasses 3; XTrain {}; YTrain []; for c 1:numClasses for i 1:numObsPerClass t (0:timeSteps-1) / timeSteps; % 每个类别基础频率不同加入随机扰动增加难度 freq c * 3 randn * 0.3; signal zeros(numFeatures, timeSteps); for f 1:numFeatures signal(f, :) sin(2*pi*freq*t f) c * 0.2 0.3 * randn(1, timeSteps); end XTrain{end1, 1} signal; YTrain(end1, 1) categorical(c); end end生成之后先把数据随机打乱再按比例划分训练集和验证集idx randperm(numel(YTrain)); XTrain XTrain(idx); YTrain YTrain(idx); numTrain floor(0.7 * numel(YTrain)); XValidation XTrain(numTrain1:end); YValidation YTrain(numTrain1:end); XTrain XTrain(1:numTrain); YTrain YTrain(1:numTrain);如果换成真实数据只要保证每个观测都是特征数×时间步数的矩阵标签是分类变量或者数字逻辑完全一致。2.3 归一化与数据集划分的坑多变量序列数据的归一化建议按特征来做。什么意思就是四个通道各自的均值和方差分别处理而不是把整个矩阵拉成一个向量去做min-max。这里有一个很容易踩的坑测试集归一化必须复用训练集的统计量。很多人在把训练集、验证集放一起做归一化后再切分这在工程上是数据泄漏会让验证集准确率虚高。正确做法是先切分数据集然后在训练集上算出每个特征的均值、标准差再把这个统计量同步应用到验证集或测试集。另一个关键点是真实场景里类别可能不平衡划分时最好用stratify方式按类别比例抽样保证训练集和验证集的类别分布接近。我在数据处理阶段踩过的正是这些看似基础的坑后来都变成了排查模型问题的前置检查项。3. 三个分类模型的Matlab搭建与训练全流程3.1 训练选项与通用配置三个模型用的输入数据格式是一样的所以训练选项也可以共用。我习惯统一设置成Adam优化器初始学习率0.001最大轮数60MiniBatchSize固定32并加上验证集监控options trainingOptions(adam, ... MaxEpochs, 60, ... MiniBatchSize, 32, ... InitialLearnRate, 0.001, ... LearnRateSchedule, piecewise, ... LearnRateDropFactor, 0.1, ... LearnRateDropPeriod, 25, ... ValidationData, {XValidation, YValidation}, ... ValidationFrequency, 20, ... Plots, training-progress, ... Verbose, true);经验之谈对于分类任务学习率不要一开始就上0.01非常容易产生震荡特别是CNN-LSTM这种层数较深的模型。用0.001起步等训练曲线稳定后如果收敛太慢再往上调。Matlab的training-progress图可以直接看到训练集和验证集的损失曲线一旦验证曲线开始往上走而训练曲线还在下降基本就是过拟合信号。3.2 CNN模型搭建步骤针对固定长度的多变量序列CNN使用一维卷积层convolution1dLayer卷积核沿着时间轴滑动。第一层用sequenceInputLayer指定输入特征数这个数字必须和数据样本的行数一致。layersCNN [ sequenceInputLayer(numFeatures) convolution1dLayer(3, 32, Padding, same) batchNormalizationLayer reluLayer maxPooling1dLayer(2, Stride, 2) convolution1dLayer(3, 64, Padding, same) batchNormalizationLayer reluLayer globalAveragePooling1dLayer fullyConnectedLayer(numClasses) softmaxLayer classificationLayer ];这里的结构我解释一下第一层卷积用32个卷积核每个卷积核宽度为3padding设置为same这样卷积后时间步数不变池化层把时间步压缩一半降低后续计算量第二个卷积块用64个卷积核提取更深层特征。最后用globalAveragePooling1dLayer把特征图平均成一个向量再连接全连接层。使用这个小技巧可以减少需要逐个调整的全连接输入尺寸问题。如果你在低版本Matlab里没有这个层就把每个池化后的时间步数与通道数手动算清楚用flattenLayer来连接也能达到目的。CNN的优势在这个流程中体现得很明显从头到尾只需要关注卷积核数量和层数不需要考虑太多时间步长对齐的问题模型结构非常直观。3.3 LSTM模型搭建步骤LSTM部分比CNN还要简单但设置上有几个细节需要注意。第一个是lstmLayer的OutputMode参数。如果后面直接接全连接分类层必须设置OutputMode为last表示只把最后一个时间步的输出拿出来做分类如果设置成sequence输出维度变成了序列长度×隐含单元数再接全连接层大概率报维度错误。第二个是dropout我加了0.2的丢包率对小样本来说这是性价比很高的正则化手段。代码layersLSTM [ sequenceInputLayer(numFeatures) lstmLayer(128, OutputMode, last) dropoutLayer(0.2) fullyConnectedLayer(numClasses) softmaxLayer classificationLayer ];单个LSTM层在这个任务上通常能跑出不错的基线但有个现象它对数据归一化特别敏感。如果输入特征之间的量纲差距很大比如电压是220、温度是80、振动是0.1不归一化会让LSTM的收敛速度变得极慢。所以LSTM工作前我基本会先单独检查一下训练集各通道的分布再决定是否需要标准化。3.4 CNN-LSTM混合模型搭建步骤混合模型是我整个项目里最花时间调试的部分。结构上不能直接把CNN的序列输出丢给LSTM因为经过卷积和池化之后序列的时间长度已经变了。好在卷积层不要求输入的具体时间步数只要池化后输出是一个特征序列LSTM就能接受。layersCNNLSTM [ sequenceInputLayer(numFeatures) convolution1dLayer(3, 32, Padding, same) batchNormalizationLayer reluLayer maxPooling1dLayer(2, Stride, 2) convolution1dLayer(3, 64, Padding, same) batchNormalizationLayer reluLayer lstmLayer(64, OutputMode, last) dropoutLayer(0.2) fullyConnectedLayer(numClasses) softmaxLayer classificationLayer ];这里我直接把第二个卷积块后的特征序列送到LSTM没有加globalAveragePooling因为平均池化会把时间维压掉LSTM就没有序列可学了。混合模型的核心思路是CNN部分负责降维和提取局部特征LSTM部分负责在压缩后的时间维度上学习长期关系。实际运行中这个模型训练时间大约是纯CNN的3-4倍但在我测试的数据上准确率明显更高。有一点需要注意CNN部分的池化步长不能过大否则时间步数太少LSTM能学到的时序信息有限。3.5 多模型循环训练与结果保存三个模型可以分别调用trainNetwork训练也可以用循环统一跑便于后续对比。我的做法是把网络层放在struct里写成cell数组自动训练allLayers {layersCNN, layersLSTM, layersCNNLSTM}; modelNames {CNN, LSTM, CNN-LSTM}; trainedNets cell(1, 3); for i 1:3 fprintf(正在训练模型: %s\n, modelNames{i}); trainedNets{i} trainNetwork(XTrain, YTrain, allLayers{i}, options); end训练结束后用save把网络和验证信息都保存下来方便后面统一评估。如果不想每个模型都重新训练一遍也可以逐个运行反正options可以复用。我在这个环节养成了一个习惯每个模型训练完立刻记录当时的训练时间用tic/toc包起来后面做选型对比时能一并分析。4. 训练实测中的常见报错与排查记录4.1 输入维度错误全家桶这个环节必须单独拿出来说因为在我反复调试中最常遇到的就是维度问题。第一个报错是Input data has incorrect number of dimensions。出现这个错误第一反应就是序列Data的格式不对。trainNetwork对sequence输入要求cell数组每个cell是特征数×时间步数的矩阵。如果你传成普通三维数组哪怕数值完全一样也会触发这个错误。第二个是卷积层报Expected input to have 4 channels之类的问题。正常情况下sequenceInputLayer第一个参数是特征数如果你的数据是4个特征这里必须写4。很多人误把时间步数当成了输入特征数结果卷积层直接拒绝输入。要记住sequenceInputLayer只关心每个观测矩阵的第一维也就是特征数时间步数是可以动态变化的。第三个是LSTM层和全连接层之间的维度问题。如果lstmLayer设置了OutputMode, sequence输出形状是隐含单元数×时间步数你要接全连接层就必须把序列浓缩成一条向量否则连接处报维度不匹配。最简单的做法是设置OutputMode, last。4.2 不收敛、过拟合与类别失衡验证集准确率始终保持在某一个常数比如一直在66.7%三类分布均匀时的随机水平这种情况多半不是模型结构的问题而是学习率设置不合理或者数据归一化没做对。优先检查三点学习率、MiniBatchSize、是否有类别严重失衡。类别不平衡是我真实踩过的坑。某类样本数量是另一类的三倍模型很快就学会把所有样本都预测为多数类整体准确率看起来还行但少数类一个都识别不出来。这种情况下可以给分类层加ClassWeights参数classWeights [1.2, 0.8, 1.0]; layersCNNLSTM [ % 前面结构保持不变 fullyConnectedLayer(numClasses) softmaxLayer classificationLayer(ClassWeights, classWeights) ];ClassWeights会放大少数类的损失贡献让模型更重视少数类。这是我做故障数据时学会的一个重要调整比单纯加数据有效得多。过拟合则表现为验证损失在下降后又反弹。解决办法按优先级排序增大dropout比率、减少LSTM隐含单元数、降低训练轮数、加L2正则化。Matlab训练选项里有个L2Regularization参数默认是1e-4数据量少时我会调到1e-3。4.3 训练效率问题CNN-LSTM训练慢是一个普遍现象。除了减少LSTM隐含单元数外可以先把MiniBatchSize加大前提是你的GPU显存受得了。如果显存不足报OOM不要直接换更大的机器先检查是不是把验证集也一次性放进GPU了可以把ValidationFrequency调大减少验证频次或者把输入数据改成single类型能省不少显存。数据读取方面如果每次都在循环里切片cell数组训练效率会很低。最好一次性把训练集放进变量让trainNetwork在内部做batch划分。另外如果样本是等长的可以先把所有样本堆叠成特征数×时间步数×样本数的三维数组再用mat2cell转换处理速度明显快过逐条赋值cell。5. 三模型结果对比怎么看5.1 从混淆矩阵和数据指标看差异训练结束后我三步走进行评估整体准确率、混淆矩阵、逐类精确率和召回率。整体准确率容易算YPredCNN classify(trainedNets{1}, XValidation); accCNN mean(YPredCNN YValidation);混淆矩阵用confusionchart展示最直观figure; confusionchart(YValidation, YPredCNN); title(CNN验证集混淆矩阵);从我这组合成数据的结果来看趋势很有代表性纯CNN的准确率在80%上下浮动的可能性较高因为信号类别主要由频率区分局部感受野已经够用但遇到噪声较大、时域特征重叠的样本时会错分LSTM大约能到85%以上对时间模式更敏锐CNN-LSTM在同样条件下往往能到90%以上尤其对容易混淆的边界样本改善明显。实话说这只是合成数据下的趋势换成真实数据差别会很大但这个对比思路是通用的。训练时间上LSTM是CNN的2倍左右CNN-LSTM是CNN的3到4倍。做报告或者选型时把训练时间、验证准确率、分类边界情况放在一张表里比只贴一个准确率数字有说服力得多。5.2 不同数据规模下的选型建议如果你有几十到几百条样本量级不大我建议先跑CNN原因非常简单它最不容易过拟合而且训练快能快速判断你的特征工程和标签是否合理。如果CNN都跑不出一个可用的基线说明数据本身问题大了码LSTM和混合模型就是在错误的地基上盖楼。如果数据量中等且确认类别与时间序列的长程依赖强相关LSTM开始显现优势。可以尝试把滑动窗口的长度调大让LSTM有更多上下文可用。如果数据量充足而且你有GPU资源CNN-LSTM是效果上限最高的选择。但从工程角度看它并不是万能药当序列很长时LSTM部分的计算量会明显上升需要平衡CNN的池化步长和信息保留程度。我个人的选择策略是先用CNN建立baseline再用CNN-LSTM做最终模型LSTM则作为中间验证看时序建模对任务到底贡献了多大的收益。5.3 从结果反推数据质量做模型对比的一个副产品是能反推你的数据质量。如果三个模型在验证集上的准确率都很低但训练集准确率都接近100%那基本是数据划分和归一化泄漏的问题或者数据本身类间重叠太严重。如果CNN和CNN-LSTM准确率接近但LSTM明显更差这说明可能数据的时间依赖并不强加LSTM是在无用功。如果CNN-LSTM显著优于CNN说明数据里确实存在需要跨时间窗口才能捕捉的特征LSTM部分是值得保留的。抱着这个思路去分析结果就不会陷入一味的精度攀比而是能从实验数据中得到对业务问题更有价值的判断。做完全部三组模型之后我个人最大的体会是Matlab做这类多变量分类预测的难点从来不在调库而在于对sequence数据格式的理解和维度流的把控。用CNN-LSTM也好、纯LSTM也好底层逻辑都是把你手里的表格或波形整理成模型能消化的矩阵序列再通过合适的网络结构把特征提炼出来。最后分享一个小技巧在搭建网络之前先单独构造一个随机的小数据集跑一遍完整的trainNetwork流程只用一个batch确认前向传播没有维度问题再上真实数据。这个习惯帮我省下了大量调试报错的时间。三模型的对比实验做完你得到的其实远不止一个准确率而是一套对数据、对问题、对网络结构的判断依据。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑