最近在Matlab 2022环境下实现了一个基于CNN-LSTM的猫狗图像分类器,虽然最终准确率不算太高(约60%),但完整走通了从数据准备到模型评估的全流程。这个项目特别适合想了解如何在Matlab中结合CNN和LSTM进行图像分类的开发者参考。下面我将详细拆解每个环节的技术细节和踩坑经验。
传统CNN在图像分类任务中表现出色,但CNN-LSTM的组合可以捕捉图像中的时序特征。对于视频分类或需要考虑图像局部区域时序关系的场景特别有用。虽然我们的猫狗分类是静态图像,但这个架构演示了如何处理更复杂的视觉序列问题。
注意:Matlab 2022对深度学习工具箱进行了重要更新,特别是sequenceInputLayer和LSTM层的兼容性改进。2020及以下版本无法直接运行本文代码。
使用1000张猫狗图像(各500张),按照以下目录结构存放:
code复制pet_images/
├── cat/
│ ├── cat001.jpg
│ └── ...
└── dog/
├── dog001.jpg
└── ...
这种按类别分文件夹的存放方式是Matlab ImageDatastore的标准输入格式,能自动继承文件夹名作为标签。
使用splitEachLabel进行训练测试集分割(4:1比例):
matlab复制imds = imageDatastore('pet_images','IncludeSubfolders',true,'LabelSource','foldernames');
[imdsTrain,imdsTest] = splitEachLabel(imds,0.8,'randomized');
常见陷阱及解决方案:
matlab复制trainCount = countEachLabel(imdsTrain)
testCount = countEachLabel(imdsTest)
matlab复制rng(42); % 固定随机种子
Matlab默认读取的图像可能尺寸不一,必须统一到相同尺寸(如227x227):
matlab复制augimdsTrain = augmentedImageDatastore([227 227],imdsTrain);
augimdsTest = augmentedImageDatastore([227 227],imdsTest);
重要提示:训练和测试集必须使用完全相同的预处理参数,否则会导致性能下降。
完整网络架构代码如下:
matlab复制layers = [
sequenceInputLayer([227 227 3],'Name','input') % 关键:序列输入
convolution2dLayer(3,8,'Padding','same','Name','conv1')
batchNormalizationLayer('Name','bn1')
reluLayer('Name','relu1')
maxPooling2dLayer(2,'Stride',2,'Name','pool1')
sequenceFoldingLayer('Name','fold') % CNN转LSTM的关键
lstmLayer(32,'OutputMode','last','Name','lstm')
fullyConnectedLayer(2,'Name','fc')
softmaxLayer('Name','softmax')
classificationLayer('Name','classOutput')];
各层作用解析:
CNN-LSTM最难的部分就是维度匹配。必须理解:
sequenceFoldingLayer正是完成这个转换的关键:
这些保守参数确保了模型能在普通电脑上运行,但牺牲了准确率。
推荐使用以下训练选项:
matlab复制options = trainingOptions('adam',...
'ExecutionEnvironment','auto',...
'MiniBatchSize',16,...
'MaxEpochs',10,...
'InitialLearnRate',1e-4,...
'Shuffle','every-epoch',...
'Plots','training-progress');
关键参数说明:
启动训练:
matlab复制net = trainNetwork(augimdsTrain,layers,options);
常见问题处理:
虽然示例中没有显式使用验证集,但推荐添加:
matlab复制options = trainingOptions(...,...
'ValidationData',augimdsVal,...
'ValidationFrequency',30);
matlab复制predLabels = classify(net,augimdsTest);
accuracy = sum(predLabels == imdsTest.Labels)/numel(imdsTest.Labels)
matlab复制confMat = confusionmat(imdsTest.Labels,predLabels);
confusionchart(confMat,{'cat','dog'})
增加模型容量:
数据增强:
matlab复制imageAugmenter = imageDataAugmenter(...
'RandRotation',[-20 20],...
'RandXReflection',true);
迁移学习:
错误信息:"Error using trainNetwork. Layer 'lstm': Expected input to have size 512 but got 21632"
解决方案:
错误信息:"Could not read JPEG file header"
解决方案:
matlab复制% 检查图像完整性
for i = 1:numel(imds.Files)
try
imread(imds.Files{i});
catch
fprintf('损坏文件: %s\n',imds.Files{i});
end
end
可能原因及对策:
matlab复制bilstmLayer = bilstmLayer(64,'OutputMode','last','Name','bilstm');
matlab复制attentionLayer = attentionLayer('Name','attention');
matlab复制multiScaleLayers = [
convolution2dLayer(3,16,'Padding','same','Name','conv1')
maxPooling2dLayer(2,'Stride',2,'Name','pool1')
convolution2dLayer(3,32,'Padding','same','Name','conv2')
maxPooling2dLayer(2,'Stride',2,'Name','pool2')
depthConcatenationLayer(2,'Name','depthcat')
];
matlab复制exportONNXNetwork(net,'cnn_lstm_model.onnx');
matlab复制cfg = coder.config('lib');
codegen -config cfg classify -args {ones(227,227,3,'single')} -report
经过完整实现后,虽然基础版本的准确率只有60%左右,但通过上述改进方法可以逐步提升到80%以上。这个项目最大的价值在于展示了如何在Matlab环境中实现CNN与LSTM的有机结合,为更复杂的时序图像处理任务奠定了基础。