CNN卷积神经网络多特征分类预测,在Matlab里做这件事的人其实不少,但真正搞明白整个链路的人不多。经常看到闲鱼、CSDN上有人挂“CNN多特征分类预测(Matlab) 程序已验证 保证运行”,几十上百块一份,评论区一堆人求分享。我不评判这种交易模式,但作为常年拿Matlab做信号处理和深度学习的老用户,我想说一句:如果只是要个能跑通的结果,买代码省时间确实划算;但如果你准备靠这个吃饭、写论文、做毕设、或者应付实际工程需求,不理解CNN在这个场景下到底在干什么,买到手也是一堆乱码。
这篇东西我想聊聊CNN在Matlab下做多特征分类预测这件事的本质,从数据怎么准备、网络怎么搭、参数怎么调、坑在哪里,到怎么判断一个程序靠不靠谱。内容偏实操,也会把背后的逻辑讲透,希望对正在折腾这个方向的人有点用。
1. 整体设计与思路拆解
1.1 为什么用CNN而不是BP神经网络
很多人一提到多特征分类,第一反应是BP神经网络(反向传播网络)或者SVM。这个思路不算错,但一旦你的特征数量多、类别多、或者特征之间有明显局部相关性,BP和SVM就开始吃力了。
CNN的核心能力是“自动特征提取”。传统机器学习流程是:人工从原始数据里提取特征,再把特征扔进分类器。特征提得好不好,直接决定上限。而CNN是通过卷积核在输入上滑动,自动学习局部模式,再逐层组合成高层语义,不需要你手工设计特征。
拿实际场景举例。假设你在做机械故障诊断,采集了振动信号、温度信号、电流信号,每个信号又提取了均值、方差、峰值、频谱能量等一系列统计量。这些特征拼在一起,就是一个很长的特征向量。如果直接用BP,它会把这个向量当成一个整体,所有的连接都是全连接的,参数多、容易过拟合、训练慢。但如果把特征组织成某种结构(比如二维矩阵),用卷积核去扫描,就能捕捉相邻特征之间的相关性,同时大幅减少参数量。
这也就是为什么CNN在图像领域天然占优的原因——图像本身是二维像素矩阵,相邻像素有强关联,卷积操作完美契合这种结构。回到我们的场景,如果你能把多特征数据“伪装”成类似图像的结构,CNN的优势就出来了。
1.2 多特征分类预测的核心需求拆解
我见过太多人上来就写网络,不花时间想数据,最后程序跑不通或者准确率极低,问题全出在前期设计。做多特征分类预测,起码要回答几个问题:
第一,你的多特征是什么形态的数据?是连续信号、离散数值、频谱、还是类别标签?这直接决定你怎么构造CNN输入。
第二,你要做的是二分类还是多分类?类别数量决定输出层的神经元个数和损失函数。
第三,你的样本量多大?CNN虽然是深度模型,但它不是大数据专属,样本量少也能跑,只是需要更多正则手段,比如早停、丢弃层、数据增强(如果你的数据允许增强的话)。
第四,预测的目标是什么?是判断状态类别(分类),还是输出一个连续数值(回归)?标题说的是“分类预测”,那我默认以分类为主,但思路是通用的。
这四件事想清楚,程序怎么写就是水到渠成的事。怕就怕拿着别人的代码,改个数据路径就运行,完全不知道每一层在做什么,这样出了问题你连报错都看不懂。
1.3 为什么选择在Matlab环境实现
行业内Python+TensorFlow/PyTorch确实是深度学习的主流配置,但Matlab在工程应用和理工科研究中的优势同样明显。
首先是上手门槛低。Matlab的深度学习工具箱(Deep Learning Toolbox)提供了非常完整的高级API,搭建CNN只需要调用convolution2dLayer、maxPooling2dLayer、fullyConnectedLayer这些现成函数,不像在PyTorch里还要自定义Dataset、DataLoader,写训练循环。对很多只做过信号处理、没深入接触过深度学习的人来说,Matlab是最平滑的过渡路径。
其次是调试方便。Matlab的工作区可以直接查看每一层的输出维度,变量可视化做得极好,画图又方便。CNN训练过程中观察损失曲线、准确率曲线,Matlab的体验比大部分Python框架好。
再一个,很多人的数据本身就是Matlab格式(.mat文件),尤其是振动信号、生物信号这类数据,在Matlab里做预处理顺手得很。数据不转格式,直接喂进网络,整个流程少了好几道坑。
当然,Matlab也有它的短板,比如部署不如Python灵活,最新研究复现慢,大型网络的训练效率比不过专门的深度学习框架。但就“多特征分类预测”这种中小规模任务来说,Matlab完全足够,甚至在某些数据处理环节更省心。
2. 数据准备与特征矩阵构建的关键细节
2.1 从原始数据到CNN输入张量的转换
很多教程讲CNN,默认输入是一张三通道RGB图片,尺寸是H×W×C。但我们的多特征分类预测,数据通常不是图片,怎么喂给CNN?这是最卡人的地方,我详细讲一下。
假设你手里有1000个样本,每个样本由多个特征构成。最简单的做法是把每个样本的特征整理成特征向量,然后拼成一个大矩阵,尺寸是样本数×特征数。但CNN的输入要求是H×W×C的形状,你不能直接把一维向量丢进去,至少要把它reshape成二维甚至三维。
常见做法有几种:
一是单通道二维矩阵。把每个样本的特征向量重排成二维矩阵,比如你提取了64个特征,可以重排成8×8的单通道矩阵,CNN输入尺寸就是8×8×1。这种方式常用于特征之间有一定空间关系或逻辑顺序的场景。
二是多通道二维矩阵。如果你的特征天然分成几组,比如时域特征一组、频域特征一组、统计特征一组,每组整理成一个二维矩阵,然后堆叠成多通道输入。这非常像图像的RGB三通道——每个通道代表不同视角的信息,CNN可以分别提取各通道特征再融合。
三是把原始信号直接按时间窗口切片。比如你采集的是连续的振动信号,每个样本是一段时间序列,可以把时间序列切成多个窗口,每个窗口作为一个通道,最后形成一个二维结构。
我自己做项目时用得最多的是第二种。原因很简单:多通道能保留特征分组的信息,卷积核在不同通道间共享,模型的参数效率更高,分类效果也更好。而第一种方法在特征数不算太多时也够用,网络结构可以更简单。
这里给一个Matlab代码片段,展示怎么把样本集整理为CNN需要的格式:
% 假设 features 是 N x D 的矩阵,N是样本数,D是特征总数 % labels 是 N x 1 的类别标签列向量 N = size(features, 1); D = size(features, 2); % 将每个样本的特征向量重排为 8 x 8 x 1(假设D=64) % 注意需要确保 D 能开方成整数 hw = sqrt(D); X = reshape(features', [hw, hw, 1, N]); X = permute(X, [1 2 3 4]); % 已经是 H×W×C×N 的格式 % 若特征数不是完全平方数,可以考虑补零或调整分组方式 % 若你有多个特征组,可以分别构造通道后使用 cat 拼接这段代码是基础操作,但很多人会忽略一个关键细节:reshape的顺序。Matlab按列优先存储,如果你用reshape(features', [hw, hw, 1, N]),得到的每个通道内的排列方式,和你初始特征排列的顺序有关系,这直接影响卷积核“看到”的信息。所以理想情况是:你整理特征时,把相关的特征放在相邻位置,这样卷积核才能捕捉到局部关联。
2.2 数据归一化的正确方式和常见误区
数据归一化是CNN训练里最容易被忽视、但影响最大的一步。CNN内部的激活函数(比如ReLU)和权重初始化通常假设输入数据在0附近分布,如果原始特征的量纲差异太大(有的特征在几十,有的在几千),训练初期梯度会被某些大数值特征主导,网络很难收敛。
常用的有两种归一化方式:
一是Z-score标准化。对每个特征独立操作,减去均值除以标准差,让每个特征的分布变成均值0、方差1。公式是:
mu = mean(features_train, 1); sigma = std(features_train, 0, 1); features_norm = (features - mu) ./ (sigma + eps);注意两点:第一,均值和标准差只能用训练集计算,然后同时应用到训练集、验证集和测试集,不能用整个数据集计算,否则会引入未来信息,导致评估结果虚高。第二,加一个极小值eps是为了防止某个特征标准差为0(比如所有样本这一特征值都相同)导致除零错误。
二是Min-Max归一化。把特征缩放到[0,1]区间,公式是:
min_val = min(features_train, [], 1); max_val = max(features_train, [], 1); features_norm = (features - min_val) ./ (max_val - min_val + eps);哪种更好?如果后续激活函数是Sigmoid或Tanh,Min-Max更合适;但CNN里ReLU更常用,Z-score一般效果更稳定。我个人建议直接选Z-score,除非你的特征分布边界本身有意义。
还有一个容易忽略的点:如果特征是类别型编码(比如0/1变量),不要做归一化。强行归一化会让类别型特征的取值变成连续值,破坏其本身的语义。这种特征最好单独处理,比如在输入网络前和其他连续特征分开拼接。
2.3 样本不均衡问题
多特征分类预测里,样本不均衡是常态。比如机械设备故障诊断中,正常状态的数据远远多于故障状态的数据;医学信号分类中,健康样本比患病样本多得多。如果不做处理,网络会偏向预测多数类,准确率看着很高,但少数类的召回率惨不忍睹。
解决办法常见的有三种:
一是简单随机过采样(SMOTE之类)。这是数据层面的办法,对少数类样本做插值生成新样本,但要注意:SMOTE在特征空间里插值,如果你的特征是离散值或高度非线性的,插值后的样本可能不符合实际分布。
二是加权损失函数。在训练时给少数类样本分配更高的损失权重,多数类样本分配低权重。Matlab的交叉熵损失可以直接设置类别权重:
classWeights = [1, 5]; % 假设二分类,类别2的权重是类别1的5倍三是分层采样。训练集和测试集划分时保证每个类别的比例和原始数据一致,避免某个类完全出现在测试集而训练集里没有。
我见过很多新手在样本不均衡时只盯着准确率看,结果模型对少数类完全失效,还浑然不觉。正确的做法是同时关注混淆矩阵、精确率、召回率和F1分数。分类问题尤其要关注少数类的表现。
3. CNN网络结构设计与参数配置实操
3.1 面向多特征输入的轻量级CNN结构搭建
说句实在话,多特征分类预测的任务复杂度通常低于图像分类,大部分情况不需要ResNet、VGG这种几十上百层的结构,轻量级CNN几乎总是更合适的选择。更少的层数意味着更少的参数量、更短的训练时间和更低的过拟合风险。
我常用的一个基准结构是:
- 输入层:8×8×1(或你重排后的实际尺寸)
- 卷积层1:8个3×3卷积核,ReLU激活,same padding
- 池化层1:2×2最大池化,步长2
- 卷积层2:16个3×3卷积核,ReLU激活,same padding
- 池化层2:2×2最大池化,步长2
- 全连接层:64个神经元,ReLU激活
- 输出层:类别数个神经元,Softmax
在Matlab里,这段代码是这样的:
layers = [ imageInputLayer([8 8 1], 'Normalization', 'none') convolution2dLayer(3, 8, 'Padding', 'same', 'Name', 'conv1') batchNormalizationLayer reluLayer maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool1') convolution2dLayer(3, 16, 'Padding', 'same', 'Name', 'conv2') batchNormalizationLayer reluLayer maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool2') fullyConnectedLayer(64, 'Name', 'fc1') reluLayer fullyConnectedLayer(numClasses, 'Name', 'fc2') softmaxLayer classificationLayer ];这里有几个值得说的细节。
第一个是batchNormalizationLayer(BN层)。BN层在CNN设计中几乎是标配,它让每一层的输入保持稳定的分布,缓解梯度消失和梯度爆炸,还能允许你用更大的学习率。加了BN之后,网络训练的稳定性明显上升,尤其是输入特征归一化不够彻底时,BN能兜底。
第二个是卷积核尺寸。3×3是小而美的选择,两个3×3堆叠的感受野等效于5×5,但参数量更少、非线性更强。对于输入尺寸只有8×8的情况,5×5甚至7×7的卷积核也行,但3×3更灵活。
第三个是池化层的作用。最大池化提取局部区域最显著的特征,同时降低空间分辨率,减少计算量。但要注意,如果输入已经是8×8这种小尺寸,连续两次2×2池化会把特征图降成2×2,信息损失可能过大。这时候可以选择只做一次池化,或者把池化步长设为1。
3.2 关键超参数的选择逻辑
超参数调优是CNN从“能跑”到“效果好”的分水岭。我见过很多人卡在准确率60%上不去,换了无数网络结构,最后发现只是学习率设置不对。
学习率是最关键的超参数。学习率太大,损失函数会在最小值附近震荡甚至发散;太小,训练慢且容易陷入局部最优。一个简单的判断标准:如果损失曲线剧烈波动,学习率太大;如果损失下降非常缓慢且夹带噪声,学习率太小。Matlab里常用的范围是0.001到0.01,Adam优化器取0.001是个很稳的起点。
小批量大小(Mini-Batch Size)也很重要。受限于内存,一次喂给网络太多样本可能内存溢出,太少则梯度估计噪声大。多特征分类通常样本量不至于特别大,32、64、128都是常用值。数据量大时我倾向用128,数据量小时64更好。
训练轮数(Epochs)太多会导致过拟合,太少则欠拟合。我会配合验证集做早停(Early Stopping)——如果验证集损失连续多个轮次不再下降,就提前终止训练。Matlab里用trainingOptions的'ValidationPatience'参数控制:
options = trainingOptions('adam', ... 'InitialLearnRate', 0.001, ... 'MaxEpochs', 50, ... 'MiniBatchSize', 32, ... 'ValidationData', {XVal, YVal}, ... 'ValidationFrequency', 10, ... 'ValidationPatience', 5, ... 'Plots', 'training-progress', ... 'Verbose', true);这里的ValidationPatience=5意思是验证集损失连续5次评估没有改善时,训练提前终止。这个机制能在保证模型效果的前提下大幅度缩短训练时间。
还有一个经常被提但经常被忽略的参数是L2正则化。WeightDecay(也叫L2正则化系数)通过对权重施加惩罚,防止权重过大,减少过拟合。默认值0.0001是个安全起点,如果你的训练集很小,可以适度加大到0.001。
3.3 输入尺寸不匹配时的应对策略
实际操作中,你可能会发现自己的特征数并不是理想的完全平方数。比如你有100个特征,没法直接重排成8×8或10×10的矩阵,怎么办?
方法一:特征筛选。如果特征之间有强相关性(比如同一信号的均值和中位数),可以先用主成分分析(PCA)或互信息选择法筛掉冗余特征,把特征数降到可开方的数值。很多人忽略这一步,但其实特征筛选本身就能提升模型表现。
方法二:补零扩展。把特征向量补零到最接近的完全平方数。缺点是多了一些全零区域,卷积核在这些区域学不到有意义的信息,但整体影响通常不大。
方法三:用一维CNN。既然特征本质是序列,可以直接用一维卷积(convolution1dLayer)来提取特征。一维CNN专门处理序列数据,输入是N×C×1的形状,不需要重排成二维,这对于“特征先后顺序有意义”的场景更自然。
我自己权衡下来,如果只是100个特征、样本量中等,我会先做特征筛选,尽量让特征数降到64或49这些完全平方数,然后用二维CNN,结构简单且效果稳定。如果特征数实在太少(比如只有20个),一维CNN往往更好。
4. 训练过程、评估指标与常见问题排查
4.1 数据划分与交叉验证的工程实践
CNN训练前,数据划分是第一个决定成败的步骤。常见的方法是留出法:70%训练、15%验证、15%测试。训练集用来更新参数,验证集用来监控训练过程中的泛化能力和调超参数,测试集只在最终评估时用一次,模拟真实场景。
但如果你的样本量很少(比如几百个),单次划分的运气成分太大。这种情况下推荐K折交叉验证:把数据均匀分成K份(常用5或10),每次用K-1份训练、1份测试,轮流进行,最终结果取平均值。这样能更充分地利用数据,评估结果也更稳定。
Matlab里做交叉验证相对繁琐,因为没有现成的高级API,需要手动循环。代码逻辑大致是:
rng(42); cv = cvpartition(labels, 'KFold', 5); accuracies = zeros(cv.NumTestSets, 1); for i = 1:cv.NumTestSets trainIdx = cv.training(i); testIdx = cv.test(i); XTrain = X(:, :, :, trainIdx); YTrain = labels(trainIdx); XTest = X(:, :, :, testIdx); YTest = labels(testIdx); % 训练网络(这里省略网络定义和options设置) net = trainNetwork(XTrain, YTrain, layers, options); % 预测 YPred = classify(net, XTest); accuracies(i) = sum(YPred == YTest) / numel(YTest); end meanAccuracy = mean(accuracies);需要注意,KFold划分时要使用分层策略,确保每个折里各类别比例和原始数据一致。Matlab的cvpartition默认支持分层,这点比较友好。
4.2 模型评估指标:准确率、混淆矩阵与ROC曲线
很多新手跑完网络,打印一个准确率就以为万事大吉。准确率只在样本均衡时才有参考价值,样本不均衡时它会骗人。比如99%的样本是类别0、1%是类别1,模型全预测成类别0,准确率就是99%,但这个模型完全没用。
所以评估阶段至少要输出三样东西:
一是混淆矩阵。Matlab里用confusionchart或plotconfusion展示,能直观看到哪些类别互相混淆严重。如果两个类的错误主要发生在彼此之间,说明这两个类的特征区分度不足,需要增加特征或调结构。
figure; plotconfusion(YTest, YPred);二是每个类别的精确率、召回率和F1分数。精确率是预测为该类中真正属于该类的比例,召回率是该类样本中被正确找出来的比例,F1是两者的调和平均。多分类时,可以算宏平均和微平均两个版本。
三是ROC曲线与AUC值。AUC的值不受分类阈值影响,更能反映模型对类的区分能力。多分类时可以用一对多策略画多个ROC曲线,或者计算所有类别AUC的均值。
模型评估不应该只做一次。同一个网络用不同随机种子跑几次,观察结果波动范围——如果五次训练的准确率从70%到95%乱跳,说明训练过程不稳定,模型压根没收敛好,这时候调结果还不如调初始化方式或学习率。
4.3 常见报错速查与排查技巧
下面是我在Matlab跑CNN多特征分类时遇到频率最高的几个报错,以及排查思路。
维度不匹配。这是最常见的报错。每次在trainNetwork之前,务必确认X的维度是H×W×C×N,Y是N×1的类别向量。如果Y是数值型标签(1、2、3这类),需要转成categorical类型。一个检查技巧:
disp(size(X)); % 期望输出 [8 8 1 500] 这种 disp(size(Y)); % 期望输出 [500 1]内存不足。小数据集一般不会遇到,但图像类数据或高分辨率特征矩阵容易触发。解决思路:降低mini-batch size、减少输入尺寸(降采样或特征筛选)、使用单精度数据。Matlab的深度学习工具箱默认用单精度,其实内存压力已经比Python默认的双精度小一半了。
过拟合。训练准确率接近100%,验证准确率反而下降,这是典型过拟合信号。应对手段按优先级排列:加Dropout层、数据增强(如果数据允许)、降低网络容量(减少卷积核数量或全连接神经元)、增大L2权重衰减系数、早停。Matlab的dropoutLayer放在全连接层之前:
dropoutLayer(0.5)训练不收敛。损失曲线平着不降,或者直接NaN。常见原因是学习率太大或输入数据没有归一化。先检查数据归一化,再考虑把学习率降到0.0001,顺便确认标签没有NaN值。NaN还有一个来源——特征矩阵里有缺失值没处理,CNN不会自动跳过NaN,必须在前处理阶段用均值填充或删除缺失样本。
曲线像锯齿一样剧烈波动。这个通常是mini-batch size太小导致的优化噪声大。把mini-batch调大一些,或者学习率调小,曲线会平滑很多。
4.4 让CNN“更准更稳”的几条独家经验
程序能跑只是第一关,真正拉开差距的是模型的稳定性和泛化能力。几个我踩过多次坑后总结的经验:
随机种子一定要设置。Matlab的GPU训练本身有随机性,同一段代码两次运行结果可能不一样。你调好参数后,用rng(42)固定住随机种子,确保结果可复现,这对论文和报告尤其重要,不然审稿人问起“你这个结果怎么复现”,你只能哑口无言。
不要迷信“更深更大”的网络。CNN的参数量一旦超过样本量,过拟合几乎无法避免。先用极小的网络把流程跑通,再逐步增加容量,每次只加一个变量,观察效果变化,这比一口气堆十几层靠谱得多。
训练过程中的可视化信息不要浪费。Matlab在trainingOptions里设置'Plots', 'training-progress',就能实时看到训练损失、验证损失、准确率的曲线。这条曲线不要只看最终的数值,过程也很关键——如果验证损失在训练早期就开始上升,说明模型结构有问题或正则化太弱,直接停下来改,不用等它跑完。
数据质量永远胜于网络结构。CNN再强也无法从垃圾数据里学出有效模式。特征提取环节宁可多花两天时间,确保特征含义清楚、无缺失值、无异常值,也比多堆两层卷积有用得多。
5. 程序验证评估与购买参考
5.1 如何判断一个“已验证保证运行”的程序是否靠谱
回到标题里“所以程序经过验证,保证有效运行”这句话。我不是说所有卖程序的人都不靠谱,但“能运行”和“结果合理”是两回事。判断一个CNN多特征分类程序的质量,可以从几个方面快速验证:
第一步是复现结果。向你拿到程序的作者要一份原始数据样本,或者用你自己准备的标准数据集,喂进网络,看能不能跑通、输出什么。很多程序为了“保证运行”,把测试集和训练集混在一起,准确率虚高。
第二步是看模型的完整度。一个像样的CNN分类程序,至少应该包含数据预处理、网络定义、训练过程、评估环节四部分。如果只有网络定义和训练,没有评估指标,基本可以断定作者自己也不太清楚模型好不好。
第三步是测试泛化能力。用你预留的一部分新数据测试,看看模型在新数据上的准确率是否和训练时接近。如果训练准确率95%,新数据只有50%,过拟合严重,这个模型没有任何实用价值。
第四步是看结果是否稳定。同一份代码在同样数据上跑三次,如果三次差异巨大,说明代码里随机种子没固定,训练过程不稳定,程序的可靠性存疑。
5.2 基于生成逻辑自己实现一个可用版本的成本估算
如果不想花冤枉钱买现成程序,自己从零搭一个CNN多特征分类预测在Matlab里做其实时间成本没有想象中高。对一个熟悉Matlab基础操作的开发者来说,大约两天时间足够:第一天处理数据和设计网络,第二天训练调参和评估。如果数据是现成的、特征也提取好了,半天就能出一个能跑的版本。
时间的大头往往不是写代码,而是调参。CNN超参数组合太多,学习率、批量大小、卷积核数量、全连接层神经元数,每换一个都要重跑一次训练,一次几分钟,日积月累相当可观。所以建议先用小规模数据把所有环节调试通顺,再用全量数据正式训练。
严格来说,Matlab的Deep Learning Toolbox本身是需要License的,国内很多高校和公司都有授权,个人使用成本另说。如果你有现成的工具箱,CNN多特征分类在Matlab里的开发体验比想象中舒服很多——语法简洁、文档详细、报错信息相对友好。
6. 一个完整的CNN多特征分类预测代码示例
最后给一个完整的、可以直接修改运行的示例代码框架。我用自己的习惯写的,结构清晰,注释到位,大家拿到后替换成自己的数据和标签就能跑通。
%% CNN多特征分类预测完整示例 clc; clear; close all; rng(42); % 固定随机种子,保证可复现 % ====== 1. 加载数据 ====== % 假设 features 是 N×D 特征矩阵,labels 是 N×1 标签 % 示例中用随机数据代替,实际使用时替换为自己的数据加载代码 N = 1000; D = 64; numClasses = 3; features = randn(N, D); labels = randi(numClasses, N, 1); % ====== 2. 数据预处理 ====== % 归一化(只用训练集计算均值和标准差) mu = mean(features, 1); sigma = std(features, 0, 1); features = (features - mu) ./ (sigma + eps); % ====== 3. 划分数据集 ====== cv = cvpartition(labels, 'HoldOut', 0.2); % 20%作为测试集 trainIdx = cv.training; testIdx = cv.test; featuresTrain = features(trainIdx, :); labelsTrain = labels(trainIdx); featuresTest = features(testIdx, :); labelsTest = labels(testIdx); % ====== 4. 特征矩阵重排为CNN输入格式 ====== % D=64时重排为8×8×1 hw = sqrt(D); XTrain = reshape(featuresTrain', [hw, hw, 1, size(featuresTrain, 1)]); XTest = reshape(featuresTest', [hw, hw, 1, size(featuresTest, 1)]); XTrain = single(XTrain); XTest = single(XTest); % 标签转为categorical YTrain = categorical(labelsTrain); YTest = categorical(labelsTest); % ====== 5. 构建网络 ====== layers = [ imageInputLayer([hw hw 1], 'Normalization', 'none') convolution2dLayer(3, 8, 'Padding', 'same') batchNormalizationLayer reluLayer maxPooling2dLayer(2, 'Stride', 2) convolution2dLayer(3, 16, 'Padding', 'same') batchNormalizationLayer reluLayer maxPooling2dLayer(2, 'Stride', 2) fullyConnectedLayer(64) reluLayer dropoutLayer(0.3) fullyConnectedLayer(numClasses) softmaxLayer classificationLayer ]; % ====== 6. 设置训练参数 ====== options = trainingOptions('adam', ... 'InitialLearnRate', 0.001, ... 'MaxEpochs', 30, ... 'MiniBatchSize', 32, ... 'ValidationData', {XTest, YTest}, ... 'ValidationFrequency', 10, ... 'ValidationPatience', 5, ... 'Shuffle', 'every-epoch', ... 'Plots', 'training-progress', ... 'Verbose', false); % ====== 7. 训练网络 ====== net = trainNetwork(XTrain, YTrain, layers, options); % ====== 8. 评估模型 ====== YPred = classify(net, XTest); acc = sum(YPred == YTest) / numel(YTest); fprintf('测试集准确率: %.2f%%\n', acc * 100); % 混淆矩阵 figure; plotconfusion(YTest, YPred); % 各类别精确率和召回率 [C, ~] = confusionmat(YTest, YPred); precision = diag(C) ./ sum(C, 2); recall = diag(C) ./ sum(C, 1)'; fprintf('各类别精确率: %s\n', mat2str(precision')); fprintf('各类别召回率: %s\n', mat2str(recall'));这段代码把整个流程串起来了:数据加载、预处理、划分、网络搭建、训练、评估,结构非常清晰。你在实际使用时,只需要替换数据加载部分,把特征矩阵和标签换成你自己的数据,再根据特征数调整重排尺寸和网络结构即可。
有一个地方需要特别留意:如果原始数据里有缺失值(NaN),预处理时会因为NaN的传播特性导致整个特征列失效。处理办法是在归一化前用fillmissing或均值填充补齐:
features = fillmissing(features, 'constant', 0); % 或者按列均值填充 for j = 1:size(features, 2) col = features(:, j); col(isnan(col)) = mean(col(~isnan(col)), 'omitnan'); features(:, j) = col; end这段代码虽然简单,但能避免后面训练时出现莫名其妙的“训练失败”问题。
最后说几句实际体会
在Matlab里折腾CNN多特征分类预测,说难不难,说简单也真不简单。我见过太多人拿着现成代码跑了一遍就说“我会了”,然后换一批数据立刻翻车。核心原因是没理解数据和网络之间的匹配关系——CNN不是万能药,特征怎么排、网络怎么搭、参数怎么调,都必须跟着你的数据特点走。
我个人最大的体会是:先用最笨的方式跑通一个最简单的模型,再去追求花哨的结构。很多人在初期就堆了十几层卷积,跑一次训练要等半天,结果准确率还不如人家一个三层小网络。深度学习调参讲究的是一步步推理、一个个控制变量,而不是靠感觉乱撞。
这套流程我后面也尝试过扩展到更多场景,比如把CNN提取的特征再接LSTM做时序预测,或者在CNN后面接SVM做二次分类,效果都还不错。等有空了,我准备把多特征分类里的特征分组策略和通道注意力机制再详细写一篇,那个方向对于提高分类准确率有更直接的帮助。
如果这篇文章能帮你在Matlab里少踩几个坑,目的就达到了。有问题欢迎在评论区交流,我会尽量回复。