简介:该资源面向需要掌握时序数据分类的MATLAB用户与机器学习初学者,提供基于长短期记忆神经网络的多输入单输出分类预测方案,可解决多特征输入下的二分类及多分类建模问题,适用于信号识别、故障诊断、行为判别等场景。压缩包共9个文件,约1.88MB,包含3个m脚本文件分别承担主程序、初始化与评估指标计算,1个xlsx数据集、1个说明文档及4张png结果图,结构紧凑、开箱即用。程序内注释详细,替换数据即可运行,并可直接输出分类效果图、迭代优化图与混淆矩阵图,便于观察训练收敛过程与各类别识别精度。目前已有207人学习下载,适合希望快速搭建LSTM分类基线、理解时序建模流程并对照图表调参的读者参考使用。
1. 从一份 MATLAB 源码包说起:LSTM 多输入单输出分类到底怎么落地
你手头如果有一批带标签的多维数据,想按类别分开,又不想自己从零搭网络,那这份 MATLAB 的 LSTM 分类源码包值得看一眼。它解决的是典型的「多特征输入、单标签输出」问题——输入可以是温度、压力、振动、电流等任意多个特征列,输出是二分类或多分类的类别标签。包里带了main.m、initialization.m、fical.m三个脚本,一份数据集.xlsx,还有几张效果图,结构很直白:初始化参数、加载数据、训练 LSTM、画混淆矩阵和迭代曲线。适合做设备故障分类、工况识别、信号分选这类任务的从业者,也适合刚接触 LSTM 分类、想先跑通再改数据的新手。下面按「这是什么 → 怎么跑 → 坑在哪 → 怎么调」的顺序拆一遍。
2. 拆开源码包:LSTM 分类的四个核心文件与数据流
2.1 文件分工与调用关系
拿到包先别急着点运行,把文件角色理清楚能省很多事。main.m是主入口,负责串起整个流程;initialization.m管参数初始化,网络层数、学习率、迭代次数、批大小这些都在这里;fical.m是辅助函数,通常用来做数据归一化、标签编码或者结果可视化;数据集.xlsx是原始数据,一般最后一列是类别标签,前面若干列是特征。调用顺序是main.m先调initialization.m拿参数,再读 Excel,然后调fical.m做预处理,最后建网络、训练、出图。
常见做法是:main.m里用readtable或xlsread读数据,把特征矩阵和标签向量分开,标签做 categorical 转换,然后按比例划分训练集和测试集。这一步看着简单,但划分方式直接决定后面混淆矩阵好不好看,后面避坑章节会细说。
2.2 多输入单输出的数据组织方式
LSTM 在 MATLAB 里做分类,输入格式和普通神经网络不一样。它要求输入是序列形式,哪怕你每个样本只有一个时间步,也要组织成numFeatures × 1 × numSamples的格式,标签是numSamples × 1的 categorical。很多人第一次跑报错「维度不匹配」,就是卡在这里。
% 读取数据,假设最后一列是标签 data = readtable('数据集.xlsx'); X = table2array(data(:, 1:end-1)); % 特征矩阵,numSamples × numFeatures Y = data{:, end}; % 标签列 % 标签转 categorical Y = categorical(Y); % 转成 LSTM 需要的序列格式:numFeatures × 1 × numSamples numFeatures = size(X, 2); numSamples = size(X, 1); XSeq = cell(numSamples, 1); for i = 1:numSamples XSeq{i} = X(i, :)'; % 每个样本转成 numFeatures × 1 end这段代码的逻辑是:先把 Excel 拆成特征和标签两部分,再把每个样本单独装进一个 cell,每个 cell 里是列向量。参数上,numFeatures决定输入层大小,numSamples决定样本总数。如果你的数据本身是时间序列(比如每个样本有多个时间步),那 cell 里应该是numFeatures × timeSteps的矩阵,而不是单列。这一点在改数据时最容易翻车,因为 Excel 里看不出时间步维度,得自己心里有数。
2.3 网络结构与训练参数
initialization.m里一般会定义这几样:输入维度、隐藏单元数、输出类别数、最大迭代轮数、初始学习率、批大小。隐藏单元数常见取值 64 到 256,类别少可以小一点,特征复杂就往上加。学习率初始 0.01 或 0.001,配合分段下降。批大小 16 到 64 都行,样本少就取小。
% initialization.m 典型参数 inputSize = numFeatures; numHidden = 128; numClasses = numel(categories(Y)); maxEpochs = 60; miniBatch = 32; learnRate = 0.01;这些参数不是拍脑袋定的。numHidden太小,模型欠拟合,训练集准确率都上不去;太大,样本少的时候直接过拟合,测试集一塌糊涂。maxEpochs配合验证集早停用,别硬跑满。miniBatch影响梯度稳定性,样本量小于 500 时建议 16 或 32。
2.4 训练与出图
主流程最后是建sequenceInputLayer、lstmLayer、fullyConnectedLayer、softmaxLayer、classificationLayer,然后用trainNetwork训练,再用classify预测,最后confusionchart画混淆矩阵,plot画迭代曲线。
layers = [ ... sequenceInputLayer(inputSize) lstmLayer(numHidden, 'OutputMode', 'last') fullyConnectedLayer(numClasses) softmaxLayer classificationLayer]; options = trainingOptions('adam', ... 'MaxEpochs', maxEpochs, ... 'MiniBatchSize', miniBatch, ... 'InitialLearnRate', learnRate, ... 'ValidationData', {XVal, YVal}, ... 'Plots', 'training-progress', ... 'Verbose', false); net = trainNetwork(XTrain, YTrain, layers, options); YPred = classify(net, XTest); confusionchart(YTest, YPred);OutputMode设成last表示只取最后一个时间步的输出做分类,这是单输出分类的标准做法。如果设成sequence,输出就是每个时间步一个结果,适合序列标注,不适合这里。ValidationData一定要给,否则迭代曲线好看但测试集崩了都不知道。
3. 替换自己的数据:从 Excel 到混淆矩阵的完整操作
3.1 数据格式对齐的三个检查点
换数据之前,先确认三件事。第一,特征列是否全为数值型,有没有混进文本列或者空值;第二,标签列是否在最后一列,类别名是否统一(比如「正常」和「正常 」带空格会被当成两类);第三,样本量是否够,每类至少几十条,否则混淆矩阵全是 0 和 1,没有统计意义。
常见做法是先用summary(data)看一眼每列类型和缺失情况,再用ismissing定位空值。有缺失就补均值或中位数,别直接删行,样本本来就少。
summary(data); idxNaN = find(any(ismissing(data), 2)); if ~isempty(idxNaN) data = fillmissing(data, 'constant', 0); % 或 'previous'、'mean' endfillmissing的第二个参数决定填充策略,'constant'补 0,'previous'用前值,'mean'用均值。分类任务里补 0 要小心,如果 0 本身有物理意义,会引入偏差,建议用均值或中位数。
3.2 划分训练集与测试集的比例选择
样本量小于 500 时,常见做法是 70% 训练、15% 验证、15% 测试;样本量上千,可以 80/10/10。划分要用随机打乱,但别用randperm之后忘了固定随机种子,否则每次跑结果都不一样,调参时根本分不清是参数起作用还是划分变了。
rng(42); % 固定随机种子,保证可复现 n = size(XSeq, 1); idx = randperm(n); nTrain = round(0.7 * n); nVal = round(0.15 * n); XTrain = XSeq(idx(1:nTrain)); YTrain = Y(idx(1:nTrain)); XVal = XSeq(idx(nTrain+1:nTrain+nVal)); YVal = Y(idx(nTrain+1:nTrain+nVal)); XTest = XSeq(idx(nTrain+nVal+1:end)); YTest = Y(idx(nTrain+nVal+1:end));rng(42)是固定种子,数字随便取,但一旦定了就别改,这样每次跑的数据划分一致,调参才有对比意义。randperm打乱索引,再按比例切分,比cvpartition更直观,也方便你手动检查每类样本是否均衡。
3.3 训练参数调整与迭代曲线判读
迭代曲线看两个东西:训练损失和验证损失。两条都下降且贴近,说明正常;训练损失降、验证损失先降后升,说明过拟合,要减隐藏单元、加 dropout 或早停;两条都平着不降,说明学习率太小或模型容量不够。
options = trainingOptions('adam', ... 'MaxEpochs', 80, ... 'MiniBatchSize', 32, ... 'InitialLearnRate', 0.005, ... 'LearnRateSchedule', 'piecewise', ... 'LearnRateDropPeriod', 20, ... 'LearnRateDropFactor', 0.5, ... 'ValidationData', {XVal, YVal}, ... 'ValidationFrequency', 10, ... 'ValidationPatience', 8, ... 'Plots', 'training-progress');LearnRateSchedule设成分段下降,每 20 轮乘 0.5,能让后期收敛更稳。ValidationPatience设 8,意思是验证损失连续 8 次不降就停,省时间也防过拟合。ValidationFrequency设 10,每 10 轮验证一次,太频繁浪费时间,太稀疏看不出趋势。
3.4 混淆矩阵与分类效果图解读
混淆矩阵对角线是正确分类,非对角线是错分。看的时候别只看总体准确率,要看每一类的召回率和精确率。如果某一类召回率特别低,说明这类样本被大量错分到其他类,可能是特征区分度不够,也可能是这类样本太少。
figure; confusionchart(YTest, YPred, ... 'Title', 'LSTM分类混淆矩阵', ... 'RowSummary', 'row-normalized', ... 'ColumnSummary', 'column-normalized');RowSummary和ColumnSummary分别显示行归一化和列归一化,行归一化看召回率,列归一化看精确率。两个都开,一眼就能定位问题类别。如果某类召回率低于 0.6,先别急着调网络,回去看这类样本的特征分布是不是和其他类重叠严重。
4. 避坑与排查:LSTM 分类里最容易翻车的五个地方
4.1 报错「Invalid training data. The output size of the last layer must match the number of classes」
现象:运行trainNetwork直接报维度不匹配。原因:标签 categorical 的类别数和fullyConnectedLayer的输出数不一致,常见于标签列里混入了空值或多余类别。解决:先categories(Y)看实际类别数,再把这个数传给fullyConnectedLayer,别手写死数字。
4.2 训练准确率很高但测试集一塌糊涂
现象:迭代曲线训练损失降到 0.01,验证损失还在 0.5 以上。原因:过拟合,样本少、隐藏单元多、没加正则。解决:减numHidden到 64 或 32,加dropoutLayer(0.3)在 lstmLayer 后面,或者把L2Regularization设成 0.001。
4.3 混淆矩阵某一类全是 0
现象:测试集里某类样本一个都没分对。原因:这类样本在训练集里太少,或者划分时全被分到测试集。解决:检查每类样本数,用分层划分cvpartition(Y, 'HoldOut', 0.3, 'Stratify', true)保证训练测试里每类都有。
4.4 每次运行结果都不一样
现象:同样的代码,跑三次三个准确率。原因:没固定随机种子,数据划分和权重初始化都随机。解决:在main.m开头加rng(42),并且确保trainNetwork之前没有其他随机操作。
4.5 Excel 读取后特征列顺序错乱
现象:明明改的是同一份数据,结果和上次差很多。原因:readtable默认把第一行当变量名,如果 Excel 里有合并单元格或空行,列顺序会错。解决:用readmatrix代替readtable,或者先手动清理 Excel,确保第一行是纯变量名、没有空行。
5. 进阶技巧:把 LSTM 分类从「能跑」推到「能用」
5.1 特征归一化与标准化
LSTM 对输入尺度敏感,特征量纲差太多时,梯度会被大量级特征主导。常见做法是训练集算均值和标准差,然后应用到验证集和测试集,别对整个数据集算,否则测试集信息泄漏。
mu = mean(XTrainRaw, 1); sigma = std(XTrainRaw, 0, 1); sigma(sigma == 0) = 1; % 防止除零 XTrainNorm = (XTrainRaw - mu) ./ sigma; XValNorm = (XValRaw - mu) ./ sigma; XTestNorm = (XTestRaw - mu) ./ sigma;mu和sigma只从训练集算,这是铁律。sigma为 0 的列说明该特征在所有样本里取值相同,对分类没贡献,置 1 避免除零,也可以直接删掉这列。
5.2 双向 LSTM 与多层堆叠
单向 LSTM 只看历史信息,双向 LSTM 同时看前后文,对某些信号分类任务效果更好。MATLAB 里用bilstmLayer替代lstmLayer即可。多层堆叠时,第一层OutputMode设sequence,最后一层设last。
layers = [ ... sequenceInputLayer(inputSize) bilstmLayer(128, 'OutputMode', 'sequence') dropoutLayer(0.3) bilstmLayer(64, 'OutputMode', 'last') fullyConnectedLayer(numClasses) softmaxLayer classificationLayer];双向 LSTM 参数量翻倍,样本少时容易过拟合,建议配合 dropout 和早停。两层堆叠时,第一层输出序列给第二层,第二层只取最后一步,这样既有层次特征又不会维度爆炸。
5.3 用交叉验证替代单次划分
样本量小于 300 时,单次划分的测试结果波动很大,今天 85% 明天 78%。用 5 折交叉验证能给出更稳的估计。
cv = cvpartition(Y, 'KFold', 5, 'Stratify', true); acc = zeros(5, 1); for k = 1:5 idxTrain = training(cv, k); idxTest = test(cv, k); net = trainNetwork(XSeq(idxTrain), Y(idxTrain), layers, options); YPred = classify(net, XSeq(idxTest)); acc(k) = mean(YPred == Y(idxTest)); end fprintf('5折平均准确率:%.2f%%\n', mean(acc) * 100);Stratify设 true 保证每折里各类样本比例一致。5 折跑完取平均,比单次划分靠谱得多。代价是训练时间乘 5,样本少的时候值得。
5.4 一个我踩过的坑
有次换了一份新数据,特征列从 6 列变成 12 列,我改了inputSize但忘了改initialization.m里的numFeatures,结果main.m里读的是新数据,网络建的还是旧维度,报错信息又指向trainNetwork,查了半天才发现是初始化脚本没同步。从那以后我每次换数据都强制走一遍「列数 → inputSize → numFeatures → 网络输入层」这条链,确认三处一致再跑。希望帮到你。
本文还有配套的精品资源,点击获取