ARTICLE DETAIL

资讯详情

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

Matlab实现MNIST手写数字识别:从IDX解析到CNN实战

Matlab实现MNIST手写数字识别:从IDX解析到CNN实战 简介一套基于Matlab神经网络实现Minst手写数字识别的课程设计资源包面向机器学习初学者及需要完成图像识别相关课题的学生解决从手写数字数据集加载、神经网络搭建到训练评估的完整流程问题。资源共9个文件压缩包约9.41MB以Matlab脚本.m为主涵盖主程序、输出、激活函数及数据读取等模块另有标签图像数据文件.idx、README说明文档及许可证便于直接运行与二次修改。目前已有473人学习适合作为课程设计参考。借助这套资料读者可系统掌握神经网络基础理论、MNIST数据预处理、模型训练与准确率评估方法包括前馈网络结构、反向传播、Sigmoid/ReLU激活函数、交叉熵损失与梯度下降优化等关键知识点实测识别率可达94%95%同时通过清晰的代码结构与说明文档可快速定位关键实现为课程设计报告和后续深度学习项目打下基础。1. 用Matlab做Minst手写数字识别为什么我劝你别直接用Python的现成库MNIST手写数字识别是深度学习入门绕不开的一道坎标题里的“Minst”是MNIST的常见笔误不影响你理解任务内容6万张28×28灰度手写数字图片要训练一个神经网络把它们正确分成0到9十类。为什么非要用Matlab做而不是在Python里调两行Keras接口搞定因为在Matlab里你得从解析IDX二进制数据开始亲手搭建网络结构观察反向传播中梯度消失的现象再一步步把训练参数调稳。这个过程对做课程设计、毕业设计或者想真正理解神经网络运行机理的人来说价值远大于跑出一个准确率数字。下面按我自己的实践顺序展开读数据、搭网络、调参数、排坑、验证每一步都给出能直接复现的代码和踩坑记录。2. 认识MNIST数据集与Matlab的读入姿势从IDX二进制到可训练的矩阵2.1 MNIST的数据格式不是图片文件是IDX二进制MNIST官方提供的四个文件都不是常见图片格式。训练集图像文件train-images-idx3-ubyte、训练集标签文件train-labels-idx1-ubyte以及对应的t10k开头两个测试集文件全部采用IDX格式存储。这套格式本质上是连续字节流靠文件头里的元数据说明后续数据的长宽和数量没有压缩、没有编码比PNG或JPEG简单得多。以训练图像文件为例文件头固定占32个字节分成4个int32字段魔数、图像数量、行数、列数。魔数0x00000803用来校验文件类型当你用fread读出魔数不是2051时说明文件路径错了或字节序没配对。像素数据从第32个字节开始每28×28784个字节组成一张图按行优先排列。也就是说第0到783字节是第一张图第784到1567字节是第二张图以此类推。标签文件更简单头8个字节是魔数和标签数量之后一个字节对应一个0到9的标签。为什么要抠这个二进制细节因为Matlab的imread只认识PNG、JPEG这些带封装头部的图像格式对IDX完全无感。我第一次用imageDatastore加readimage去读MNIST时Matlab直接报错说文件格式无法识别。这里要有一个明确的认知MNIST数据是纯数值流把它映射成矩阵才是正路不需要经过图像解码。Matlab的强项恰恰是矩阵操作所以这个数据格式对Matlab反而友好——只要你会解析字节流。2.2 用Matlab读取MNIST最小可运行的解析函数下面给出一个我常驻工具箱里的读取函数它能把图像文件和标签文件一次性读进来。注意使用了fread的机器格式参数ieee-be这是IDX格式能否正确解析的关键。function [images, labels] loadMNIST(imageFile, labelFile) % 读取MNIST的IDX格式文件 % 输入: imageFile - 图像文件路径, labelFile - 标签文件路径 % 输出: images - 28*28*N的uint8数组, labels - N*1的uint8向量 % ---- 读取标签文件 ---- fid fopen(labelFile, rb); magic fread(fid, 1, int32, 0, ieee-be); % 魔数, 应为2049 numLabels fread(fid, 1, int32, 0, ieee-be); % 标签数量 labels fread(fid, inf, uint8); % 剩余字节全是标签 fclose(fid); % ---- 读取图像文件 ---- fid fopen(imageFile, rb); magic fread(fid, 1, int32, 0, ieee-be); % 魔数, 应为2051 numImages fread(fid, 1, int32, 0, ieee-be); % 图像数量 rows fread(fid, 1, int32, 0, ieee-be); % 行数28 cols fread(fid, 1, int32, 0, ieee-be); % 列数28 images fread(fid, inf, uint8); % 所有像素字节流 fclose(fid); % 字节流重排成图像数组 % 注意: MNIST按行优先存储, 而Matlab的reshape按列优先 % 这里先按[cols, rows]填充再转置前两维, 才能得到方向正确的图像 images reshape(images, [cols, rows, numImages]); images permute(images, [2, 1, 3]); end这个函数的逻辑分三块。第一块用fread读标签文件头部在第三个参数位置指定ieee-be大端字节序因为IDX格式按大端存储而x86机器默认小端。第二块读图像文件的四个头字段魔数、数量、行数、列数各4字节。第三块把读到的像素字节流先按[cols, rows, numImages]重塑再用permute把前两维转置。为什么非要转置因为MNIST的字节顺序是行优先也就是第一行的28个像素在前而Matlab的reshape按列优先填充直接重塑会让图像变成转置后的样子。用过一次permute之后你就会发现如果不做这一步imshow显示出来的数字是横躺的。调用方式很简单四个文件分别传入就行[trainImages, trainLabels] loadMNIST(... data/train-images-idx3-ubyte, data/train-labels-idx1-ubyte); [testImages, testLabels] loadMNIST(... data/t10k-images-idx3-ubyte, data/t10k-labels-idx1-ubyte);如果文件是直接从官网下载解压的路径和文件名都对得上这段基本不会报错。魔数校验可以加一个assert在函数开头例如assert(magic 2051, 这不是MNIST图像文件)能帮你提早发现下载文件损坏或路径串位的问题。2.3 数据预处理归一化、标签转one-hot、训练集划分原始像素值范围是0到255直接喂给神经网络会引发两个问题。第一数值太大容易让激活函数输入落在饱和区梯度接近于零权重更新缓慢。第二不同样本的对比度差异被放大网络会过多关注亮度而不是笔画形状。常见的做法是除以255把像素范围压到0到1之间。对MNIST这种灰度图来说这是最简单也最稳妥的归一化。标签需要从标量转成one-hot向量。比如数字3要转成[0 0 0 1 0 0 0 0 0 0]这样输出层的10个节点各自对应一个类别的概率多分类交叉熵损失才能正确计算。Matlab里的ind2vec函数就是干这个的它要求标签从1开始而MNIST的标签是0到9所以转换前必须加1。% 归一化: uint8转double并缩放到[0,1] trainImages double(trainImages) / 255; testImages double(testImages) / 255; % 标签转one-hot: 注意ind2vec要求标签从1开始 trainLabelsVec double(trainLabels); trainLabels full(ind2vec(trainLabelsVec 1, 10)); testLabelsVec double(testLabels); testLabels full(ind2vec(testLabelsVec 1, 10)); % 可视化检查: 随机显示前100张图 figure; montage(trainImages(:, :, 1:100), Size, [10 10]); title(MNIST训练集前100张样本);用montage把前100张图拼成一张大图能快速确认读入的数据方向和亮度是否正确。如果显示出来的数字是横躺的说明reshape那里转置写反了如果背景是黑的而数字是白的恭喜你方向对了。这种可视化检查花不了十秒却能避免带着错误数据训练一整天。训练集和验证集的划分用randperm生成随机索引保证打乱顺序的同时让图像和标签使用同一组下标。我习惯从6万张训练图里留出5000张作为验证集每个epoch结束后观察验证损失用来判断是否过拟合。rng(42); % 固定随机种子, 保证结果可复现 idx randperm(size(trainImages, 3)); trainIdx idx(1:55000); valIdx idx(55001:end); XTrain trainImages(:, :, trainIdx); YTrain trainLabels(trainIdx, :); XVal trainImages(:, :, valIdx); YVal trainLabels(valIdx, :);固定随机种子这个习惯很重要。如果不设置rng每次运行得到的数据划分都不同调参时很难判断准确率的变化是来自参数调整还是数据划分的随机波动。另外要注意trainLabels必须也用trainIdx索引一遍否则图像和标签错位训练损失会一直居高不下。这个问题我踩过后面避坑章节会专门展开。3. 搭建神经网络从BP到CNN的选型理由与Matlab实现3.1 为什么BP神经网络能跑通MNIST前馈结构与反向传播MNIST入门最常见的网络是前馈神经网络也就是大家常说的BP神经网络或多层感知机。它的结构是一个输入层、一个或多个隐藏层、一个输出层。输入层接收784个像素值也就是把28×28的图片按行展开成一维向量。输出层有10个节点对应0到9十个数字。隐藏层的节点数量和层数决定了网络能逼近多复杂的函数。反向传播是这个网络能训练起来的核心机制Matlab的Deep Learning Toolbox把这套流程封装好了你不需要手动实现梯度公式。但理解原理对调参特别有用。前向传播把输入逐层加权求和、经过激活函数得到输出把预测值和真实标签摆在一起计算损失反向传播从输出层的误差出发用链式法则把误差分摊到每一层的权重上再根据梯度下降更新权重。这个过程迭代多次损失就会逐步下降。那为什么不直接用Keras因为封装程度越高你对网络内部发生什么就越没有感知。在Matlab里搭一个feedforwardnet训练日志会直接显示每次迭代的梯度大小、验证集表现、mu值这些信息。当你看到梯度某一步突然变成NaN或者验证集准确率在某个epoch后开始回落你能结合反向传播的知识判断是学习率问题还是网络容量过大。这些经验在换到任何深度学习框架时都是通用的。激活函数的选择也是一个绕不开的点。经典BP网络常用tansig也就是双曲正切输出范围是-1到1。CNN里更常用relu计算简单且能缓解梯度消失。如果你用sigmoid做隐藏层激活MNIST这种十分类任务很容易出现梯度消失网络训练十几轮后损失还是纹丝不动。换成relu之后收敛速度立刻不同。3.2 用Matlab脚本方式搭建BP网络并在MNIST上训练在Matlab里搭BP网络有两条路。一条是打开nntool图形界面点几个按钮完成建网和训练优点是直观缺点是操作过程难以复现课程设计报告里也没法贴代码。另一条是用脚本写我强烈推荐脚本方式换参数只需改一行结果可复现还能把训练过程自动记录下来。% 数据展平: 把 28x28xN 转成 784xN 矩阵, 每列一个样本 XTrainFlat reshape(XTrain, 784, []); XValFlat reshape(XVal, 784, []); % 搭建前馈网络: 一个隐藏层, 128个节点 hiddenSize 128; net feedforwardnet(hiddenSize); % 关闭train函数默认的数据自动划分 % 否则它会自动取15%做验证、15%做测试, 污染我们自己的划分 net.divideFcn divideind; net.divideParam.trainInd 1:size(XTrainFlat, 2); net.divideParam.valInd []; net.divideParam.testInd []; % 配置训练算法和终止条件 net.trainFcn trainscg; % 缩放共轭梯度法, 内存占用小 net.trainParam.epochs 50; % 最大迭代轮数 net.trainParam.goal 0; % 不设误差目标 net.trainParam.min_grad 1e-10; % 梯度下限 net.trainParam.showWindow false; % 关闭训练窗口, 后台跑 % 训练: 注意train要求样本在行方向, 所以输入要转置 net train(net, XTrainFlat, YTrain); % 预测验证集 YValPred net(XValFlat); [~, valPredIdx] max(YValPred, [], 2); valPredIdx valPredIdx - 1; % one-hot索引从1开始, 标签从0开始 valTrueIdx vec2ind(YVal) - 1; accuracy sum(valPredIdx valTrueIdx) / length(valTrueIdx); fprintf(验证集准确率: %.4f\n, accuracy);这里有一个非常容易翻车的点feedforwardnet的输入矩阵要求每一行是一个样本每一列是一个特征维度。Python里习惯是样本在行、特征在列而这个要求和Scikit-learn一致但和许多深度学习框架相反。上面代码里XTrainFlat转置之后就是55000×784每行一张展平后的图片。如果你把维度传反train通常不报错但训练出的模型准确率会在10%附近打转和随机猜测差不多。trainFcn参数的选择也要说一句。trainscg是缩放共轭梯度法适合中等规模数据集内存占用小。trainlm是Levenberg-Marquardt收敛快但需要存储雅可比矩阵6万样本下内存压力很大8GB内存的机器容易直接卡死。如果只是想快速验证网络结构能不能跑通我一般用trainscg。epochs设50是因为MNIST上BP网络通常在30轮左右就收敛设太大会浪费时间设太小可能欠拟合。3.3 进阶用Deep Learning Toolbox搭建CNN识别MNISTBP网络在MNIST测试集上准确率能到97%到98%再往上顶就比较吃力了。因为展平成向量丢掉了图像的空间结构笔画的位置关系、局部边缘特征全部混在一起。卷积神经网络CNN用卷积核在图像上滑动提取局部特征参数量还更小。Matlab从R2018b之后的Deep Learning Toolbox支持用layerGraph方式搭建CNN我常用的结构是两层卷积加两层池化再加两个全连接层。% 构建CNN网络结构 layers [ imageInputLayer([28 28 1], Normalization, none) % 输入层: 28x28灰度图, 单通道 convolution2dLayer(3, 32, Padding, same) % 3x3卷积核, 32个滤波器 reluLayer % ReLU激活 maxPooling2dLayer(2, Stride, 2) % 2x2最大池化, 步长2 convolution2dLayer(3, 64, Padding, same) % 第二层卷积, 64个滤波器 reluLayer maxPooling2dLayer(2, Stride, 2) fullyConnectedLayer(128) % 展平后接全连接层 reluLayer fullyConnectedLayer(10) % 输出层: 10个类别 softmaxLayer % 归一化成概率 classificationLayer % 计算交叉熵损失 ]; % 调整输入维度到 HxWxCxN: 灰度图通道数为1 XTrainCNN reshape(XTrain, 28, 28, 1, []); XValCNN reshape(XVal, 28, 28, 1, []); % 标签必须是categorical类型 YValCategorical categorical(vec2ind(YVal) - 1); % 指定训练参数 options trainingOptions(adam, ... InitialLearnRate, 0.001, ... MaxEpochs, 10, ... MiniBatchSize, 128, ... ValidationData, {XValCNN, YValCategorical}, ... ValidationFrequency, 50, ... Plots, training-progress, ... Verbose, true); % 训练 netCNN trainNetwork(XTrainCNN, categorical(vec2ind(YTrain) - 1), layers, options);CNN的输入是四维数组高、宽、通道数、样本数。灰度图只有一个通道所以第三个维度固定是1。很多初学Matlab深度学习的人直接拿28×28×N的三维数组喂给trainNetwork报错信息说的是“输入数据维度不正确”这就是把通道维度漏掉了。这个结构里第一个卷积层有32个3×3卷积核输出32张特征图池化后尺寸从28×28降到14×14。第二个卷积层加倍到64个卷积核池化后降到7×7。全连接层128个节点把特征整合成类别得分。整体参数规模不大CPU上训练MNIST大概几分钟就能跑完一轮。CNN在MNIST上的准确率超过99%是很轻松的。相比BP网络CNN不需要手动做特征工程卷积核自动学习边缘、纹理等特征。代价是训练时间长一些但对MNIST这种小数据集完全不是问题。如果你的电脑有NVIDIA GPU并且安装了Parallel Computing Toolbox训练速度还能再快一个量级。4. 训练与调参MNIST收敛的5个关键参数和验证方法4.1 输入层、隐藏层、输出层的尺寸怎么定才算合理网络结构的尺寸由数据和任务决定不是随意拍的。输入层一定是784这是MNIST每张图片展开后的像素数没有讨论空间。输出层一定是10因为分类任务就是10个数字。需要设计的是隐藏层的层数和每层节点数。对于BP网络一个隐藏层就能以任意精度逼近连续函数这是万能逼近定理的结论。但实际中一个隐藏层往往需要很多节点才能达到理想效果两个隐藏层反而可以用更少的参数做到同样的表达力。MNIST这种任务我从128这个值起步然后对比[256]、[128 64]、[256 128]这几组结构。隐藏层节点数太少会欠拟合训练准确率和验证准确率都偏低节点数太多会过拟合训练准确率接近100%但验证准确率明显回落。一个直观判断方法如果训练准确率远高于验证准确率过拟合无疑。这时应该减少节点数或者在网络里加正则化项。CNN的滤波器数量选择有个经验法则从32开始每经过一次池化就翻倍最后全连接层节点数在64到256之间。这个设计源自图像分辨率逐层减半的同时用更多滤波器保留信息。MNIST只有28×28网络不需要太深两三个卷积层就够。堆到五层以上反而会因为感受野覆盖全图而失去局部特征语义训练速度也明显变慢。4.2 学习率、批次、迭代次数的经验区间训练参数里最敏感的是学习率。学习率太大损失函数会在极小值附近来回震荡训练曲线像锯齿学习率太小收敛速度慢得让人失去耐心。Matlab的trainingOptions里默认InitialLearnRate是0.001这个值在MNIST的CNN上通常能直接收敛。BP网络用trainscg时学习率由算法内部自适应一般不需要手动调。批次大小MiniBatchSize影响梯度估计的稳定性和训练速度。批次太小梯度噪声大损失曲线抖动明显批次太大每轮迭代次数减少但内存占用上升。MNIST上64到256都是合理区间我习惯用128。如果你的显卡显存比较小64更稳妥如果想加快训练且内存充足256也行。早停机制用ValidationPatience参数控制比如设为5表示验证损失连续5次不下降就停止训练这样可以避免浪费时间。学习率衰减也值得配一下。trainingOptions里设置LearnRateSchedule, piecewise再配LearnRateDropFactor为0.1、LearnRateDropPeriod为5意思是每5个epoch学习率乘以0.1。前期用大学习率快速下降后期用小学习率精细收敛。MNIST任务规模小不衰减也能收敛但养成这个习惯对以后做大项目有帮助。下表是我在MNIST上常用的一组参考值不同机器和网络结构下可以按这个区间微调。参数BP网络常用值CNN常用值调试方向学习率trainscg自动0.001模型不收敛时减小到0.0001批次大小不适用128损失震荡时增大到256最大迭代5010到15验证损失还在降就加大隐藏层节点128全连接128过拟合就减小L2正则化默认关闭0.0001过拟合时开启4.3 用混淆矩阵和误分类样本验证训练效果准确率是一个笼统的指标它掩盖了哪些类别之间容易混淆。MNIST里经典的混淆对是4和9、3和8、7和1因为它们在书写形态上确实相近。用confusionchart可以直观看到混淆矩阵对角线越亮说明分类越准非对角线上的亮点就是模型容易搞混的类别。% 在测试集上做预测 YPred classify(netCNN, reshape(testImages, 28, 28, 1, [])); YTestActual categorical(testLabelsVec); % 绘制混淆矩阵 figure; confusionchart(YTestActual, YPred); title(MNIST测试集混淆矩阵);如果看到某个特定类别准确率特别低把误分类样本挑出来可视化看看问题出在数据还是模型。比如把4和9反复搞混有可能是训练数据里这两类样本本身笔画特征接近也可能是预处理阶段图像方向不对。% 找出所有预测错误的样本 errIdx find(YPred ~ YTestActual); % 显示前20个错误样本 figure; montage(testImages(:, :, errIdx(1:20)), Size, [4 5]); title(预测错误的前20个测试样本);把错误样本打出来之后经常能看到两类问题。一类是手写笔迹本身潦草人类也难分辨这类错误交给任何模型都是难题。另一类是数据预处理有bug比如图像被翻转或缩放异常。如果是后者说明预处理代码需要回头检查而不是盲目调网络参数。这个排查顺序能节省大量时间。5. 避坑Matlab做MNIST识别常见的5个翻车现场与排查方法5.1 现象fread读出的图像全是乱的显示出来像噪声我第一次解析MNIST时遇到的就是这个问题图像文件读进来之后用imshow显示完全看不出数字形状像一堆黑白噪点。原因很明确IDX格式采用大端字节序而Matlab的fread在x86机器上默认按小端解析int32字段的字节顺序全反了。文件头魔数和尺寸全部错乱像素数据自然对不上。解决方法是给fread指定机器格式参数ieee-be这个参数放在fread的第五个参数位置。如果你是自己写的解析函数而不是用我前面给的loadMNIST这四个头字段的读取都必须带ieee-be。标签文件也一样。顺手把文件头字段打印出来检查魔数是不是2051和2049能更快定位问题。5.2 现象训练时损失一直不降准确率在10%附近徘徊10%是什么概念正好是随机猜一个类别的准确率。遇到这种情况第一个怀疑对象就是标签和图像没对齐。常见起因是打乱索引时只打乱了图像数组没同步打乱标签数组或者用了两个不同的rng种子分别生成图像和标签的随机序列导致两者顺序完全不匹配。解决方法是坚持用同一个索引向量同时索引图像和标签。先rng固定种子再randperm生成索引然后像第2章那样一次完成XTrain和YTrain的划分。另外检查一下one-hot转换时的标签方向ind2vec输入要求是行向量如果你传入的是列向量转出来的one-hot矩阵维度会是反的train函数不报错但训练效果全乱。5.3 现象CNN训练报错说输入维度不对用trainNetwork训练CNN时最常见的报错是“输入数据维度不正确”或“图层输入与数据不匹配”。原因很直接深度学习层的图像输入要求是H×W×C×N四维数组而你的XTrain是28×28×N三维数组。灰度图没有颜色通道的概念但网络层需要一个显式的单通道位置。解决方法是reshape(XTrain, 28, 28, 1, [])补上通道维度这行代码我在第3章已经写过。还要注意图像数据需要是double类型有些层对uint8输入会报错或表现异常归一化后的double类型最稳妥。这个坑在从BP网络转向CNN时几乎人人都会踩一次。5.4 现象BP网络预测所有样本都为同一个类别这是让新手最崩溃的翻车现场训练过程看起来正常损失在下降但所有测试样本都被预测成同一个数字。原因一般出在输出层的权重初始化或学习率过大导致梯度下降陷入局部极小值网络实际上退化成了常数输出。如果隐藏层没有激活函数多层网络等价于单层线性分类器表达能力不足也会出现类似现象。解决方法是先把学习率调低一个数量级然后检查隐藏层是否使用了tansig或relu激活函数。feedforwardnet默认在隐藏层使用tansig如果你用自定义层构建网络需要手动添加。还可以尝试改变随机种子重新初始化权重有时只是初始点在损失曲面上位置不好。5.5 现象验证集准确率很高但自己画的数字图片预测错误模型在测试集上99%准确率自己用画图工具写一个数字导入进去预测结果完全不对。这个坑几乎每个人都会踩原因有三个。第一画布尺寸不是28×28imresize缩放后笔画宽度变形。第二颜色反了MNIST是黑底白字你画的是白底黑字网络把背景当成了前景。第三数字没有居中MNIST训练样本的笔画大致在图片中心你的数字偏到左上角特征分布完全不同。解决方法是预处理时依次做灰度化、缩放、颜色反转、归一化四步。缩放用imresize的bicubic插值比默认的最近邻插值更平滑能保留笔画细节。颜色反转用imcomplement函数。居中问题可以通过计算像素质心做平移修正这一步比较麻烦但对单个数字的验证场景来说手动把数字写在画布中心就够了。6. 把模型用起来手写数字识别的完整验证流程与改进方向6.1 用测试集做最终评估准确率不是唯一指标训练过程中用验证集调参但最终对外公布的应该是测试集上的表现。MNIST测试集有10000张图模型从未在训练中见过这些样本。测试时要保证预处理流程和训练时完全一致不要额外做数据增强或清洗。准确率之外还要记录推理耗时和模型文件大小课程设计答辩时这些数字经常被问到。% 统计CNN在测试集上的推理耗时 tic; YPredTest classify(netCNN, reshape(testImages, 28, 28, 1, [])); elapsedTime toc; fprintf(测试集推理耗时: %.4f秒\n, elapsedTime); testAcc sum(YPredTest categorical(testLabelsVec)) / length(testLabelsVec); fprintf(测试集准确率: %.4f\n, testAcc);6.2 画一张自己的手写数字来验证模型的真实泛化能力这步最朴素也最有效。在Windows画图工具里用黑色画笔写一个数字保存成PNG然后按下面的脚本读入并预测。整个过程模拟了真实使用场景模型要处理它没见过的书写风格、笔画粗细和缩放比例。% 读取自己的手写数字 myImg imread(my_digit.png); if size(myImg, 3) 3 myImg rgb2gray(myImg); % 转灰度 end myImg imresize(myImg, [28 28], bicubic); myImg imcomplement(myImg); % MNIST是黑底白字 myImg double(myImg) / 255; % 送入CNN预测 myImgCNN reshape(myImg, 28, 28, 1, 1); YPredMy classify(netCNN, myImgCNN); fprintf(预测结果: %s\n, char(YPredMy));这里有个实操细节imresize默认的插值方式会改变笔画宽度。数字太细缩放后可能断成两截数字太粗笔画糊成一团。我习惯对比bicubic和nearest两种插值的结果取稳定输出的那个。如果连续预测同一个数字三次结果一致基本可以放心。6.3 值得继续投入的方向从MNIST到真实场景MNIST跑通只是起点。如果要做课设升级或进一步深入学习我建议按这个顺序扩展。第一步把BP网络换成CNN体验准确率从97%到99%以上的提升过程中你会理解卷积核为什么能提取局部特征。第二步在CNN里加数据增强包括随机平移、旋转和弹性畸变这是对抗过拟合最有效的手段Matlab里用imageDataAugmenter就能实现。第三步把任务换成Fashion-MNIST或CIFAR-10你会被迫处理三通道彩色图像和更复杂的类别语义这时才会遇到真正意义上的图像识别难题。一个我反复使用的习惯是每次实验后把网络结构、训练参数、最终准确率记在一个Excel表里。调参时同时改多个参数会让人搞不清哪个改动起了作用单变量实验虽然慢但每一条记录都能成为后续判断的基准。MNIST这个任务本身不难难的是养成可复现的实验习惯。这套从数据读取到调参验证的流程换到任何数据集都通用。希望帮到你。本文还有配套的精品资源点击获取
返回列表