ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Matlab CNN实战:15类场景分类从数据到部署

Matlab CNN实战:15类场景分类从数据到部署 简介这份资源面向高校机器学习课程学习者与需要完成图像场景分类作业的学生提供基于卷积神经网络的Matlab完整实现方案帮助解决从数据读取、网络搭建到训练评估的全流程问题。压缩包共4512个文件约93.95MB其中4432个jpg构成15种场景分类图像数据集38个m脚本与16个mat数据文件承载CNN训练与模型参数另含少量c、cpp、py及mexw64等文件用于SVM、GentleBoost等传统分类器的底层支持与混合调用。已有285人学习下载说明该方案在课程作业场景中具备一定参考价值。读者可获得可直接运行的Matlab源码、按类别整理的场景数据集、网络训练与测试脚本以及传统分类器与CNN对比实验的完整代码便于快速复现结果、理解卷积特征提取流程并在此基础上调整网络结构或替换数据集完成二次开发。1. 从一份 Matlab 作业说起CNN 做 15 类场景分类到底难在哪很多人第一次拿到「基于 CNN 实现图像场景分类」这类作业第一反应是打开 Matlab 的 Deep Learning Toolbox找几个现成层一拼trainNetwork一跑等着 accuracy 曲线往上爬。真跑起来才发现15 类场景数据集的类间差异极小厨房和餐厅、街道和高速公路类内差异又极大同一类卧室的拍摄角度、光照完全不同训练集稍微不平衡模型就整体倒向样本多的那几类。更现实的问题是Matlab 的深度学习接口和 Python 生态差异不小imageDatastore的标签读取、augmentedImageDatastore的预处理、trainingOptions里那一堆参数任何一个设错都会让训练结果变成玄学。这份作业真正要解决的不是「CNN 能不能分类」而是「在 Matlab 这套工具链里怎么把 15 类场景数据喂进去、把网络调到一个能交差的精度、并且整个过程可复现」。它适合正在做课程设计的学生、需要快速验证场景分类 baseline 的工程师以及想从 Python 转到 Matlab 深度学习流程的人。下面按数据准备、网络搭建、训练调参、避坑、进阶验证的顺序把这条链路走一遍。2. 15 类场景数据集怎么进 Matlab从目录结构到 imageDatastore2.1 先搞清楚数据集的目录约定拿到「15 种场景分类数据集」压缩包解压后大概率是两种结构之一一种是每个类别一个文件夹文件夹名就是标签另一种是train/和test/分开各自下面再按类别分文件夹。Matlab 的imageDatastore对第一种结构支持最好因为它默认用文件夹名作为Labels。如果你的数据是第二种直接对train和test分别建 datastore 即可不要手动去改目录。先做一件事确认每个类别的图片数量和格式。这一步不做后面训练时出现「某一类只有 30 张、另一类有 300 张」的惨状你连问题出在哪都不知道。% 假设数据集根目录为 datasetRoot下面有 15 个类别文件夹 datasetRoot D:\scenes15; imds imageDatastore(datasetRoot, ... IncludeSubfolders, true, ... LabelSource, foldernames); % 查看每类样本数 countEachLabel(imds)IncludeSubfolders设为true是为了让 datastore 递归扫描子文件夹LabelSource设为foldernames表示用文件夹名当标签。countEachLabel返回一个表列出每个类别和对应图片数。如果发现某类样本数不到其他类的三分之一后面要么做重采样要么在损失函数里加权否则模型基本会忽略这个类。2.2 划分训练集和验证集别用测试集调参很多人图省事把全部数据拿去训练再从训练集里随便抽几张当验证。正确做法是先按比例划分验证集只用于看训练过程中的泛化表现测试集留到最后一次都不碰。[imdsTrain, imdsVal] splitEachLabel(imds, 0.8, randomized); % 确认划分后每类数量 countEachLabel(imdsTrain) countEachLabel(imdsVal)splitEachLabel的第二个参数 0.8 表示每类取 80% 给训练剩下 20% 给验证。randomized保证不是按文件名顺序切避免数据本身有序导致分布偏移。划分完一定要再countEachLabel看一眼确认每类在训练集和验证集里都有样本。如果某一类在验证集里是 0说明这类样本太少需要调整比例或做数据增强。2.3 输入尺寸统一别让 CNN 在第一层就翻车CNN 的输入层要求固定尺寸而场景数据集的图片尺寸往往五花八门。Matlab 里用augmentedImageDatastore做统一缩放同时可以挂上数据增强。inputSize [224 224 3]; augTrain augmentedImageDatastore(inputSize, imdsTrain, ... ColorPreprocessing, gray2rgb); augVal augmentedImageDatastore(inputSize, imdsVal, ... ColorPreprocessing, gray2rgb);inputSize选 224×224×3 是因为大多数预训练网络如 ResNet、GoogLeNet都吃这个尺寸。ColorPreprocessing设为gray2rgb是为了防止数据集中混入灰度图导致通道数不匹配。如果你打算从零训练而不是用预训练网络输入尺寸可以小一点比如 128×128能显著降低显存和训练时间但精度会掉一些。注意augmentedImageDatastore默认只做缩放不做翻转、旋转等增强。要加增强需要在trainingOptions之外单独配置或者用imageDataAugmenter配合。很多人以为 datastore 自动增强了结果训练精度上不去其实是根本没增强。3. 在 Matlab 里搭一个能打的 CNN从零训练还是迁移学习3.1 从零搭 CNN 的层结构怎么定如果作业要求必须自己搭网络那就按「卷积-池化-卷积-池化-全连接-输出」的经典结构来。15 类场景分类不算特别复杂但也不能太浅否则欠拟合。layers [ imageInputLayer([224 224 3], Name, input) convolution2dLayer(3, 32, Padding, same, Name, conv1) batchNormalizationLayer(Name, bn1) reluLayer(Name, relu1) maxPooling2dLayer(2, Stride, 2, Name, pool1) convolution2dLayer(3, 64, Padding, same, Name, conv2) batchNormalizationLayer(Name, bn2) reluLayer(Name, relu2) maxPooling2dLayer(2, Stride, 2, Name, pool2) convolution2dLayer(3, 128, Padding, same, Name, conv3) batchNormalizationLayer(Name, bn3) reluLayer(Name, relu3) maxPooling2dLayer(2, Stride, 2, Name, pool3) fullyConnectedLayer(256, Name, fc1) reluLayer(Name, relu4) dropoutLayer(0.5, Name, drop1) fullyConnectedLayer(15, Name, fc2) softmaxLayer(Name, softmax) classificationLayer(Name, output) ];这个结构里convolution2dLayer(3, 32)表示 3×3 卷积核、32 个输出通道Padding, same保证输出尺寸和输入一致避免每层都在缩。batchNormalizationLayer放在卷积和 ReLU 之间能明显加快收敛。dropoutLayer(0.5)放在全连接层后面防止过拟合。最后一层fullyConnectedLayer(15)的输出数必须等于类别数classificationLayer负责算交叉熵损失。3.2 迁移学习更稳的选择如果作业没强制要求从零搭迁移学习是更靠谱的路子。Matlab 自带resnet18、googlenet等预训练网络直接改最后几层就行。net resnet18; lgraph layerGraph(net); % 替换最后三层 newLayers [ fullyConnectedLayer(15, Name, new_fc, ... WeightLearnRateFactor, 10, BiasLearnRateFactor, 10) softmaxLayer(Name, new_softmax) classificationLayer(Name, new_output) ]; lgraph replaceLayer(lgraph, fc1000, newLayers(1)); lgraph replaceLayer(lgraph, prob, newLayers(2)); lgraph replaceLayer(lgraph, ClassificationLayer_predictions, newLayers(3));WeightLearnRateFactor和BiasLearnRateFactor设为 10意思是新加的全连接层学习率是其他层的 10 倍。因为预训练网络的卷积层已经学到了通用特征不需要大改而新的分类层是随机初始化的需要快速学习。replaceLayer的层名必须和原网络里的层名完全一致resnet18里最后三层分别叫fc1000、prob、ClassificationLayer_predictions写错一个字母就会报错。3.3 trainingOptions 里那几个必须调的参数options trainingOptions(sgdm, ... InitialLearnRate, 1e-3, ... MaxEpochs, 30, ... MiniBatchSize, 32, ... ValidationData, augVal, ... ValidationFrequency, 30, ... Shuffle, every-epoch, ... Verbose, true, ... Plots, training-progress);sgdm是带动量的随机梯度下降比纯sgdm稳。InitialLearnRate设 1e-3 是常见起点如果 loss 震荡厉害就降到 1e-4。MiniBatchSize设 32显存不够就降到 16 或 8。ValidationFrequency设 30 表示每 30 个 iteration 验证一次太小会拖慢训练太大看不到过拟合趋势。Shuffle, every-epoch保证每个 epoch 打乱数据顺序这个不设的话模型很容易记住顺序。提示如果训练时 accuracy 一直卡在 1/15 附近也就是随机猜先检查标签是不是没读对再看学习率是不是太大导致 loss 爆炸。这两个是新手最常见的翻车点。4. 训练过程中的避坑与排查那些让精度上不去的细节4.1 现象训练 accuracy 很高验证 accuracy 很低原因典型过拟合。15 类场景数据集如果每类只有几百张模型很容易记住训练样本。解决加数据增强随机翻转、随机裁剪、颜色抖动增大 dropout 比例或者直接用迁移学习。Matlab 里可以用imageDataAugmenter配合augmentedImageDatastoreaugmenter imageDataAugmenter( ... RandXReflection, true, ... RandXTranslation, [-20 20], ... RandYTranslation, [-20 20], ... RandScale, [0.8 1.2]); augTrain augmentedImageDatastore(inputSize, imdsTrain, ... DataAugmentation, augmenter);RandXReflection做水平翻转RandXTranslation和RandYTranslation做平移RandScale做缩放。注意验证集不要加增强否则验证结果没有参考意义。4.2 现象训练 loss 不下降accuracy 不动原因学习率太大导致梯度爆炸或者数据标签全错或者输入没归一化。解决先把学习率降到 1e-4 试一轮用countEachLabel确认标签分布检查augmentedImageDatastore输出的图片像素范围是不是 0-255如果是考虑在输入层后加一个归一化层或者用ColorPreprocessing做归一化。Matlab 的imageInputLayer默认不做归一化这一点和 Python 里transforms.ToTensor()自动除以 255 不一样很多人在这里踩坑。4.3 现象训练到一半报错「Out of memory」原因MiniBatchSize太大或者输入尺寸太大或者同时开了多个 datastore 占内存。解决把MiniBatchSize降到 16 或 8把inputSize从 224 降到 128训练前clear掉不用的变量。如果用的是 GPU用gpuDevice查看显存占用必要时重启 Matlab 释放显存。4.4 现象验证集 accuracy 波动很大一会儿高一会儿低原因验证集太小或者ValidationFrequency太低导致采样点太少。解决增大验证集比例比如从 0.2 提到 0.3或者提高ValidationFrequency比如从 30 降到 10。另外Shuffle, every-epoch只打乱训练集验证集顺序是固定的如果验证集本身按类别排序也会导致波动。4.5 现象Matlab 2023 打开中文路径或中文注释乱码原因Matlab 2023 在某些系统 locale 下对 UTF-8 支持有问题尤其是 Windows 中文环境。解决把数据集路径改成全英文脚本文件保存为 UTF-8 无 BOM 格式注释尽量用英文。如果必须用中文注释在脚本开头加feature(DefaultCharacterSet, UTF-8)但不保证所有版本都生效。最稳的办法是路径和变量名全英文注释用拼音或英文。5. 训练完之后怎么验证混淆矩阵、错例分析和一次完整的复现流程5.1 用混淆矩阵看模型到底错在哪训练完拿到trainedNet别只看一个 accuracy 数字。用验证集跑一遍预测画混淆矩阵。% 对验证集做预测 [predLabels, scores] classify(trainedNet, augVal); trueLabels imdsVal.Labels; % 画混淆矩阵 figure; confusionchart(trueLabels, predLabels); title(Validation Confusion Matrix);classify返回预测标签和每个类的分数。confusionchart会画出矩阵对角线是正确分类非对角线是错分。重点看哪两类之间错得最多比如「街道」和「高速公路」经常混说明这两个类的特征太接近需要考虑加更多区分性强的样本或者换更深的网络。5.2 错例分析把分错的图挑出来看% 找出分错的样本索引 idxWrong find(predLabels ~ trueLabels); % 随机看几张 numShow min(9, numel(idxWrong)); figure; for i 1:numShow subplot(3,3,i); img readimage(imdsVal, idxWrong(i)); imshow(img); title([T: char(trueLabels(idxWrong(i))) ... P: char(predLabels(idxWrong(i)))]); endreadimage按索引读原图title里显示真实标签和预测标签。看完错例你可能会发现很多错分是因为图片本身模糊、光照极端、或者标注有问题。这些发现比单纯调参更有价值。5.3 一次完整的复现流程把上面的步骤串起来一个可复现的脚本大概长这样%% 1. 加载数据 datasetRoot D:\scenes15; imds imageDatastore(datasetRoot, ... IncludeSubfolders, true, LabelSource, foldernames); %% 2. 划分 [imdsTrain, imdsVal] splitEachLabel(imds, 0.8, randomized); %% 3. 预处理 inputSize [224 224 3]; augmenter imageDataAugmenter(RandXReflection, true); augTrain augmentedImageDatastore(inputSize, imdsTrain, ... DataAugmentation, augmenter); augVal augmentedImageDatastore(inputSize, imdsVal); %% 4. 迁移学习 net resnet18; lgraph layerGraph(net); newLayers [ fullyConnectedLayer(15, Name, new_fc, ... WeightLearnRateFactor, 10, BiasLearnRateFactor, 10) softmaxLayer(Name, new_softmax) classificationLayer(Name, new_output) ]; lgraph replaceLayer(lgraph, fc1000, newLayers(1)); lgraph replaceLayer(lgraph, prob, newLayers(2)); lgraph replaceLayer(lgraph, ClassificationLayer_predictions, newLayers(3)); %% 5. 训练 options trainingOptions(sgdm, ... InitialLearnRate, 1e-3, ... MaxEpochs, 30, ... MiniBatchSize, 32, ... ValidationData, augVal, ... ValidationFrequency, 30, ... Shuffle, every-epoch, ... Plots, training-progress); trainedNet trainNetwork(augTrain, lgraph, options); %% 6. 验证 [predLabels, ~] classify(trainedNet, augVal); accuracy mean(predLabels imdsVal.Labels); fprintf(Validation Accuracy: %.2f%%\n, accuracy * 100); confusionchart(imdsVal.Labels, predLabels);这个脚本里每一步都有明确的输入输出换一个数据集只需要改datasetRoot和类别数。MaxEpochs设 30 是经验值如果验证 accuracy 在 20 个 epoch 后不再涨可以提前停。MiniBatchSize和InitialLearnRate是最需要根据机器和数据集调的参数其他基本可以不动。5.4 一个容易被忽略的技巧冻结前几层如果数据集和预训练数据ImageNet差异不大可以冻结前面的卷积层只训练最后几层。这样训练更快过拟合风险也更低。% 冻结前 10 层 layersToFreeze 1:10; lgraph freezeWeights(lgraph, layersToFreeze);freezeWeights不是 Matlab 自带函数需要自己写一个简单循环把对应层的WeightLearnRateFactor和BiasLearnRateFactor设为 0。冻结的层数越多训练越快但精度可能下降。一般冻结前 1/3 到 1/2 的层比较稳妥。我自己做这类作业时最大的教训是不要一上来就调网络结构先把数据看一遍。很多精度上不去的问题根源在数据本身——标签错、样本不平衡、图片损坏。把数据清理干净用迁移学习跑一个 baseline再在这个基础上调比盲目改层结构有效得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表