手写数字识别这个看似简单的任务,实际上已经困扰计算机视觉领域数十年。早期我们依赖特征工程+传统机器学习(比如SVM+HOG),直到2012年AlexNet横空出世,卷积神经网络(CNN)才真正改变了游戏规则。今天要分享的,就是如何用Matlab这个工程计算神器,从零搭建一个能识别手写数字的CNN模型。
你可能好奇为什么选择Matlab而不是Python的TensorFlow/PyTorch?答案很简单:Matlab的深度学习工具箱提供了极其友好的可视化界面和调试工具,特别适合算法快速原型验证。我在工业级项目中使用过各种框架,但做教学演示和算法验证时,Matlab的交互式体验确实无可替代。
推荐使用Matlab R2021a及以上版本,关键工具箱包括:
安装后建议运行gpuDevice命令检查CUDA支持状态。以我的ThinkPad P15v为例,RTX 3000显卡配合CUDA 11.3可实现比CPU快8倍的训练速度。如果没有GPU也不用担心,小规模数据集完全可以在CPU上运行。
MNIST虽然是经典选择,但我更推荐使用Matlab自带的digitDataset。原因有三:
加载数据只需三行代码:
matlab复制digitDatasetPath = fullfile(matlabroot,'toolbox','nnet','nndemos',...
'nndatasets','DigitDataset');
imds = imageDatastore(digitDatasetPath,...
'IncludeSubfolders',true,'LabelSource','foldernames');
重要提示:务必执行
countEachLabel(imds)检查样本均衡性。我曾遇到因某个数字样本过少导致准确率骤降20%的情况。
直接套用原始LeNet-5会遇到输入尺寸不匹配问题(MNIST是28x28,原网络设计为32x32)。我的改进方案:
matlab复制layers = [
imageInputLayer([28 28 1], 'Name', 'input')
convolution2dLayer(3, 6, 'Padding', 'same', 'Name', 'conv1')
batchNormalizationLayer('Name', 'bn1')
reluLayer('Name', 'relu1')
maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool1')
convolution2dLayer(3, 16, 'Padding', 'same', 'Name', 'conv2')
batchNormalizationLayer('Name', 'bn2')
reluLayer('Name', 'relu2')
maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool2')
convolution2dLayer(3, 120, 'Padding', 'same', 'Name', 'conv3')
batchNormalizationLayer('Name', 'bn3')
reluLayer('Name', 'relu3')
fullyConnectedLayer(84, 'Name', 'fc1')
reluLayer('Name', 'relu4')
fullyConnectedLayer(10, 'Name', 'fc2')
softmaxLayer('Name', 'softmax')
classificationLayer('Name', 'output')
];
关键改进点:
使用trainingOptions配置时,这几个参数最影响效果:
matlab复制options = trainingOptions('sgdm', ...
'InitialLearnRate', 0.01, ...
'MaxEpochs', 15, ...
'Shuffle', 'every-epoch', ...
'ValidationData', imdsValidation, ...
'ValidationFrequency', 30, ...
'Verbose', false, ...
'Plots', 'training-progress');
血泪教训:
启动训练只需:
matlab复制net = trainNetwork(imdsTrain, layers, options);
但高手都会实时监控这些指标:
推荐使用deepNetworkDesigner可视化工具,可以实时调整层参数。有次我发现第一个卷积层权重分布异常,及时调整初始化方式后准确率提升了3%。
在imageDataAugmenter中配置这些变换效果显著:
matlab复制augmenter = imageDataAugmenter(...
'RandRotation', [-10 10], ...
'RandXTranslation', [-3 3], ...
'RandYTranslation', [-3 3], ...
'RandXScale', [0.9 1.1]);
注意:切勿同时应用旋转和平移!这会导致数字语义变化(如6变成9)。我的经验是优先使用平移和小角度旋转(±5度内)。
使用classify函数预测结果后,务必生成混淆矩阵:
matlab复制YPred = classify(net, imdsTest);
confusionchart(YTest, YPred)
常见问题诊断:
部署到嵌入式设备时需要压缩模型:
deepNetworkQuantizer进行8位量化实测可使模型体积缩小4倍,推理速度提升2倍,准确率仅下降0.5%。
梯度消失:当发现准确率长期不上升,尝试:
过拟合:验证集准确率突然下降时:
硬件问题:遇到CUDA out of memory错误:
matlab复制gpuDevice(1); % 清理显存
options.BatchSize = options.BatchSize/2;
最后分享一个私藏技巧:用activations函数可视化卷积层输出,能直观看到网络学到了什么特征。比如第一层通常检测边缘,第二层开始组合成数字局部结构。这个技巧帮我快速定位过多个网络设计缺陷。