简介本资源是一套基于MATLAB实现的卷积神经网络CNN手写数字识别完整项目面向高校本科生课程设计、毕业设计及深度学习入门实践者解决MNIST手写体图像分类这一经典机器学习任务。压缩包共9个文件包含5个核心MATLAB函数如Conv.m、ReLU.m、Softmax.m等构成CNN前向/反向传播模块、1个预加载MNIST数据集的.mat文件、1份说明文档README.md、1个运行日志log及1个空占位txt文件总大小29.03MB结构清晰、模块解耦便于理解CNN各层原理与MATLAB工程实现细节。已有78人下载学习所有源码均经严格测试无需额外配置即可一键运行配套注释详尽覆盖数据加载、网络搭建、训练迭代、准确率评估全流程特别适合缺乏深度学习实战经验但具备基础MATLAB编程能力的学习者快速上手并拓展改进。 拿到基于Matlab的卷积神经网络手写体识别.zip这份项目其实是很多人的第一份深度学习落地体验。国内高校的课程作业、毕业设计里Matlab 的出场率一直不低尤其是图形图像处理方向。这个压缩包里的内容很直白用 Matlab 的深度学习工具箱搭建一个卷积神经网络CNN对 28×28 灰度手写数字图片做分类本质上就是 MNIST 任务的 Matlab 版实现。它能帮你解决几个具体问题一是把 CNN 从理论公式变成能跑的代码二是搞清楚训练一个图像分类模型需要哪些数据、哪些步骤三是给你一个可以直接写进实验报告里的准确率。适合 Matlab 有基础、但还没碰过深度学习工具箱的同学也适合马上要交图像处理大作业、需要一个能复现的完整案例的人。这篇文章我不打算讲晦涩的数学推导就按我跑通这类项目的真实路径从工程结构、网络设计、训练调参到踩坑实录给你完整拆一遍。1. 项目整体设计与思路拆解1.1 压缩包里通常有什么拿到手先别急着双击打开train.m就跑先看清楚包的目录结构。我见过不同的人打包这个项目目录会有差异但核心文件基本逃不出下面这几类训练脚本常见命名是train.m、trainCNN.m、main.m测试脚本常见命名是test.m、evaluate.m单张图片识别脚本常见命名是classifyImage.m、predictOne.m数据集文件夹可能是digitDataset这种自带的小数据集也可能是外置的 MNIST 文件夹训练好的模型文件比如trainedNet.mat项目说明文档README.txt或者实验报告 PDF这里有个很容易被忽略的点如果压缩包里已经有trainedNet.mat理论上你可以跳过训练直接跑测试脚本看效果。但既然是学习项目我建议还是完整走一遍训练流程因为很多细节——数据怎么读、网络怎么建、训练选项怎么配——都在训练脚本里写着只看测试脚本你学不到关键部分。另外提醒一下有些压缩包里的数据文件夹是用相对路径引用的如果你把解压出来的文件挪了位置脚本里写的digitDataset路径就会失效。我习惯先把整个文件夹解压到不含中文和空格的路径下比如D:\CNN_Handwrite然后再打开 Matlab 运行省得后面报一堆找不到路径的错。1.2 为什么选 Matlab 而不是 Python说实话在深度学习领域 Python 才是主流TensorFlow、PyTorch 生态更丰富。但 Matlab 做手写体识别这个任务有一个天然优势它把整个流程的“管道”都封装好了。你不需要自己写反向传播不需要手动管理张量维度也不需要搞环境配置——装好 Matlab、确认深度学习工具箱存在trainNetwork一行命令就把训练过程拉起来了。这种“开箱即用”的体验对课程作业来说非常友好。相比之下配置 Python 深度学习环境那一套装 CUDA、装 cuDNN、配虚拟环境就能劝退不少人。还有一个实际原因很多学校机房的电脑只装了 Matlab甚至老师课件里用的就是 Matlab 示例代码。在这种环境下你就算 Python 写得再熟也没法让机房电脑跑 PyTorch。所以在这个场景下Matlab 不是“更好的选择”而是“最稳妥的选择”。从计算资源来看手写体识别是个非常轻量的任务网络规模很小CPU 完全能跑。我用一台老笔记本无独立 GPU训练 MNIST 子集几分钟就能完成一个 epoch。这意味着你不需要纠结 GPU 配置这也是它适合做入门项目的重要原因。1.3 项目的定位与可复用边界这个项目本质上是 LeNet-5 结构的一个简化变体。它要解决的问题非常聚焦输入是 28×28 的灰度图输出是 0~9 十个数字的类别标签。理解了这个定位你就能判断它在什么范围内可复用。如果想把模型改成识别字母、识别简单物体只需要改数据集、改网络最后一层的输出维度比如 26 类字母并保持输入图片尺寸一致即可。但如果想识别高清大图或者复杂场景这个网络结构和 28×28 的输入限制就不够用了需要换成 ResNet、MobileNet 这类更深的结构或者用迁移学习。这就引出一个建议跑通这个项目后不要急着删。它完全可以当作一个“模板工程”来做扩展实验。我自己后来在这个基础上改过三次一次把数字改成手写字母一次在前面加了一层用于 64×64 输入尺寸还有一次把分类头换成二分类判断“某个数字是否出现”。这些扩展都是在原项目结构上做增量修改成本很低。2. 环境准备与数据集处理2.1 版本与工具箱要求动手之前先确认你的 Matlab 版本和工具箱这一步能省掉后面很多莫名其妙的报错。手写体识别主要依赖深度学习工具箱Deep Learning Toolbox这是核心没有它trainNetwork、convolution2dLayer这些函数都不存在。从 R2018b 开始深度学习工具箱逐步完善了图像分类的 API我建议用 R2020a 以上版本因为更早期的版本里imageDataAugmenter、trainingOptions的参数名称和默认行为都有差异照着新版本代码写可能在老版本上报错。另外如果你要做数据增强还需要图像处理工具箱Image Processing Toolbox比如imresize、imrotate这些函数。绝大多数 Matlab 完整安装都会带这个工具箱一般不用担心。但如果你用的是精简安装版可以在命令窗口运行ver查看已安装工具箱列表确认一下。关于 GPU这个任务 CPU 就能跑不一定需要 GPU。如果你有支持 CUDA 的独显并且安装了 Parallel Computing Toolbox训练速度会快很多。但要注意Matlab 的 GPU 加速对驱动和 CUDA 版本比较敏感后面我会专门讲这个坑。提示在命令窗口输入matlabroot可以查看 Matlab 安装目录输入ver能列出所有已安装工具箱。训练前花一分钟检查这两项比报错后再排查高效得多。2.2 数据集选择自带的还是完整版 MNIST这个项目里数据集的选择直接决定了最终效果这里我对比一下两种常见做法。第一种是用 Matlab 内置的digitDataset。这个数据集在文档示例里经常出现结构是 10 个文件夹分别命名为 0 到 9每个文件夹存了几百张手写数字图。优点是不需要联网下载、数据量小、加载快非常适合跑通流程。缺点也很明显总共大约一万张图都不到容易过拟合训练出的模型泛化能力一般。第二种是用完整版 MNIST。它包含 60000 张训练图和 10000 张测试图是公认的基准数据集。用完整数据集训练的效果明显更好准确率能到 99% 以上。但需要联网下载并且数据格式是 IDX 二进制格式不能直接用imageDatastore读文件夹要先写脚本转化成图片文件或读取成数组。我个人的建议是先用自己的digitDataset把流程跑通然后再换成完整 MNIST 训练一遍看效果提升。这样你既能快速验证代码正确性又能体会到数据量对模型性能的影响。下表是我整理的两者对比对比项digitDataset完整 MNIST获取方式Matlab 内置需要下载并解析 IDX 文件图片数量约 1000 张/类60000 训练 10000 测试加载方式imageDatastore直接读先解析成图片或用自定义读取函数训练时间CPU几分钟内完成十几到几十分钟泛化能力一般容易过拟合强接近实际手写场景适用场景快速验证流程追求高准确率、写实验报告如果你选择完整 MNIST网上有好多现成脚本可以从 Yann LeCun 官网下载 IDX 文件并转成图片。也可以用 Matlab 的helperDownloadMNIST辅助函数深度学习工具箱文档里有示例。我更推荐转成图片文件夹的方式因为后面可以直接用imageDatastore非常顺手。2.3 数据预处理到底做了什么预处理是新手最容易忽略、也是效果差异最大的环节。这个项目里输入图片是 28×28 灰度图但你自己准备的数据未必满足这个条件所以代码里通常会有这几步第一是统一尺寸。用imresize把所有图片缩放到 28×28。这一步对某些数据集是必须的因为不同来源的图片分辨率可能不一样。第二是转灰度图。如果你用的是彩色图要先用rgb2gray转成单通道。第三是归一化。把像素值从 0~255 的 uint8 范围变成 0~1 的 double 范围用im2double就能实现。为什么归一化很重要因为神经网络的梯度计算对输入数值范围敏感。如果输入像素值都在 0~255 这个较大的范围里初始权重下的梯度可能过大导致训练不稳定。归一化之后数值范围变小梯度更平滑训练收敛更快。下面是一个典型的数据读取与增强代码片段实际项目中基本都是这个套路% 读取 digitDataset 文件夹 imds imageDatastore(digitDataset, ... IncludeSubfolders, true, ... LabelSource, foldernames); % 划分训练集和验证集 [imdsTrain, imdsVal] splitEachLabel(imds, 0.8, randomized); % 定义数据增强 augmenter imageDataAugmenter(... RandRotation, [-10 10], ... RandXTranslation, [-2 2], ... RandYTranslation, [-2 2]); % 创建增强数据存储 augImdsTrain augmentedImageDatastore([28 28], imdsTrain, ... DataAugmentation, augmenter);imageDataAugmenter是 Matlab 里做数据增强的标准方式。随机旋转、平移都是经典操作相当于在原始数据上做微小的形变扩充训练样本的多样性减少过拟合。要注意augmentedImageDatastore不会改变磁盘上的图片文件它是在训练过程中实时对图片做变换所以不会额外占用磁盘空间。这里有个细节值得记一下augmentedImageDatastore第一个参数[28 28]指定了输出图片的尺寸如果你的原始图片不是 28×28它会自动 resize。这其实帮你省了一件麻烦事不需要自己先预处理所有图片。3. 网络结构设计与参数选择3.1 完整网络结构一个精简 LeNet-5这个项目的网络结构通常是一个精简的 LeNet-5包含了卷积层、池化层、全连接层结构清晰且计算量小。下面是一份可以直接跑通的网络定义代码layers [ imageInputLayer([28 28 1], Name, input) convolution2dLayer(3, 8, 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, 32, Padding, same, Name, conv3) batchNormalizationLayer(Name, bn3) reluLayer(Name, relu3) fullyConnectedLayer(10, Name, fc) softmaxLayer(Name, softmax) classificationLayer(Name, output) ];网络结构就是三组“卷积 批归一化 ReLU 最大池化”最后接一个全连接层和分类层。输入是 28×28×1 的单通道图像输出是 10 个类别的概率分布。这里我加了两点不少教程里没有的东西一是batchNormalizationLayer二是给每层都起了名字。批归一化能显著加速收敛对训练稳定性帮助很大起名字则是为了后面调试和可视化更方便强烈建议保持这个习惯。3.2 每层为什么这么设计很多资料只会告诉你“CNN 包含卷积层、池化层、全连接层”但不会告诉你每层的作用和为什么这样设置参数。我按照实际训练感受解释一下卷积层负责提取局部特征。7×7 的卷积核能捕捉笔画局部模式3×3 也能但小卷积核计算量更小、组合性更好所以现在主流都用小卷积核连续堆叠。我把第一个卷积层的卷积核数量设为 8后面逐步增加到 16、32这是因为浅层只需要少量特征深层需要更多抽象特征。Padding设为same是为了让卷积后空间尺寸不变方便池化层按固定尺寸降采样。池化层的作用是下采样和增强平移不变性。maxPooling2dLayer(2, Stride, 2)会把 28×28 变成 14×14再到 7×7。这种逐层降维让网络逐步看到更大的感受野同时减少后续全连接层的参数量。全连接层将前面卷积池化得到的特征图展开成向量然后映射到类别分数。fullyConnectedLayer(10)的 10 就是 0~9 的十类数字和数据集类别数一致。最后softmaxLayer把分数变成概率分布classificationLayer在训练时计算交叉熵损失在测试时输出预测标签。用生活化类比来说卷积层像是一堆不同角度的放大镜专门找图像里的笔画特征池化层像是把一张大图片缩小成地图丢掉不重要的细节但保留关键结构全连接层相当于把提取到的特征汇总成“这像是哪个数字”的投票结果。3.3 网络深度和参数数量怎么权衡评估网络除了看准确率还要关注参数量和计算开销。上面这个网络参数量很少大概几十万个参数CPU 训练完全无压力。你可以试着把卷积核数量从 8/16/32 翻倍成 16/32/64参数量会翻四倍左右训练时间也会明显增加但准确率不一定有同等比例提升。这个权衡在做课程项目时尤为重要。我有一次实验把网络加深到五层卷积准确率只提升了 0.2 个百分点训练时间却多了三倍。在数据集有限的情况下网络加深带来的收益非常有限反而容易过拟合。所以对于手写体识别这个任务三组卷积已经够用。如果数据量大、追求极致准确率可以考虑用预训练的 ResNet 做迁移学习。但这样做有两个代价一是预训练模型输入尺寸通常是 224×224和手写数字的 28×28 不匹配需要调整二是预训练特征主要是针对自然图像的对笔迹特征未必友好。我自己的经验是专心把 CNN 结构调好比直接套迁移学习在这个任务上更有效。4. 训练过程与优化策略4.1 训练选项逐项解析网络定义好了下一步就是训练。训练选项集中在trainingOptions这个函数里新手最容易在这里犯迷糊因为参数太多了。我列一个我用过的、稳定有效的配置并逐项解释options trainingOptions(adam, ... InitialLearnRate, 0.001, ... MaxEpochs, 15, ... MiniBatchSize, 128, ... ValidationData, augImdsVal, ... ValidationFrequency, 30, ... Shuffle, every-epoch, ... Plots, training-progress, ... Verbose, true);adam优化器在大多数情况下表现稳定对学习率要求不高。初始学习率 0.001 是 Adam 的“黄金默认值”不大不小收敛稳定。如果你换用sgdm学习率可以调到 0.01 左右但收敛速度和稳定性通常不如 Adam。MaxEpochs设为 15 表示把整个训练集过 15 遍。对 digitDataset 这种小数据量10~20 个 epoch 足够收敛再多了就开始过拟合。MiniBatchSize128 表示每次用 128 张图计算梯度。批量太大梯度方向稳定但内存占用高太小则训练不稳定。128 是我反复试下来在 CPU 内存和收敛效果之间比较平衡的取值。ValidationData用来在每个验证周期结束后评估模型在验证集上的表现。ValidationFrequency30 表示每 30 次迭代验证一次这样能实时观察模型有没有过拟合。Shuffle设为 every-epoch在每个 epoch 开始前打乱数据顺序避免模型学到样本顺序带来的假规律。注意ValidationData如果用了augmentedImageDatastore验证时数据增强最好关掉或保持随机种子固定否则每次验证结果波动会很大影响判断。4.2 训练过程怎么读监控图训练开始后Matlab 会弹出一个进度图上面有两条关键曲线训练准确率和损失值。如何读懂这两条曲线其实直接决定了你会不会调参训练损失loss持续下降训练准确率逐步上升说明网络在学习方向正确。如果损失下降一段后停滞而验证准确率开始下滑这就是过拟合信号说明网络开始“背答案”而不是“学规律”。更常见的现象是损失在某个点附近大幅震荡这通常意味着学习率过大需要调小一个数量级。训练曲线如果一开始损失就是 NaN那基本可以断定学习率太大梯度爆炸导致权重变成非数值。解决方法先把学习率从 0.001 降到 0.0001或者加梯度裁剪。不过深度学习工具箱默认不加裁剪如果你用sgdm更要注意学习率初始值。4.3 提升精度的实操经验这部分是我最想写的因为很多时候同样一个网络、同一份数据不同预处理和训练策略最终准确率能差好几个百分点。我的经验排序如下第一优先数据增强。这是最有效且成本最低的改进手段。旋转 ±10 度、平移 2 个像素能让 digitDataset 的验证准确率明显更稳。我试过不增强时最终验证准确率在 97% 附近波动加了增强后稳定在 98.5% 以上。第二优先学习率调度。训练到中后期可以把学习率降到原来的十分之一。用trainingOptions里LearnRateSchedule, piecewise和LearnRateDropPeriod可以自动降学习率。我习惯在第 10 个 epoch 后把学习率降一半效果比固定学习率好。第三优先正则化。trainingOptions里的L2Regularization默认是 0.0001一般不用改。但如果发现验证集准确率和训练集差距过大可以试着增大到 0.001。注意正则化太大会让模型偏向欠拟合所以这个参数要小步调地试。第四优先调网络结构。这一步只建议在前三步做完了还没达到期望时再考虑。增加卷积核数量、加一层卷积、或改成更深的网络结构作用通常不如数据增强明显。5. 模型测试与单张手写数字识别5.1 在验证集上评估最终准确率训练完成后trainedNet变量就是训练好的网络。接下来要做的第一件事是在没参与训练的数据上评估效果而不是急着去识别单张图。% 使用验证集评估 predLabels classify(trainedNet, augImdsVal); valLabels imdsVal.Labels; % 计算准确率 accuracy sum(predLabels valLabels) / numel(valLabels); fprintf(验证集准确率: %.2f%%\n, accuracy * 100); % 绘制混淆矩阵 figure; confusionchart(valLabels, predLabels);classify函数一次处理一个数据存储里的所有图片返回预测标签。混淆矩阵能直观看到哪些类别容易被搞混。我的手写数字实验里最容易混淆的是 4 和 9、7 和 1、3 和 8原因很直观——这些数字的局部特征相似尤其是书写潦草时几乎难以区分。有一种现象值得注意如果验证集准确率很高但识别自己手写的图片却经常出错那问题大概率出在“数据来源分布不一致”上。训练数据和你写字的风格差异太大这个模型自然就“偏科”了。这也是为什么后面单张图片测试不能省。5.2 单张图片识别的完整流程单张图片识别脚本是这个项目最容易被“贴到报告里”的部分。核心逻辑很简单读图、缩放、转灰度、归一化、调用classify。function [label, score] classifyImage(net, imgPath) % 读取图片 img imread(imgPath); % 如果是彩色图转灰度 if size(img, 3) 3 img rgb2gray(img); end % 缩放到 28x28归一化到 [0,1] img imresize(img, [28 28]); img im2double(img); % 网络输入要求是 28x28x1xN因此 reshape imgBatch reshape(img, [28 28 1 1]); % 预测 [label, score] classify(net, imgBatch); end这段代码里有个容易踩的坑imread可能返回 uint8 类型像素范围是 0~255。直接喂给网络可能因为数值范围不对导致预测结果很怪。所以先im2double把范围变到 0~1保证和训练时的输入一致。另外classify返回的score是每个类别的概率值默认输出形式是 categorical 类型可以用double(label)或string(label)转成字符串打印。用 Windows 自带的画图写字时要特别注意保存为 PNG 格式比 JPEG 好得多JPEG 压缩会在笔画边缘产生噪点影响识别。我踩过一次坑用手机拍了一个手写数字有反光和阴影结果识别出来完全不对后来发现是背景灰度不均导致。5.3 测试集外图片的注意事项把模型部署到“真实场景”前有几个坑必须提前明白图片背景要干净。如果图片里除了数字还有杂物、线条CNN 容易把背景噪声当成特征。训练数据本身就是白底黑字测试时如果背景发黄、发灰或者有网格效果会急剧下降。建议测试时用白色背景、黑色笔迹或者先对图片做二值化预处理。笔画粗细会影响结果。网络在训练时看到的笔画粗细有一定的范围如果你用超粗的马克笔写字可能与训练分布偏差较大识别率明显下降。我在测试时就发现同样一个“5”用细铅笔写能识别对用粗记号笔写就变成了“6”。数字位置和大小也很关键。训练数据里数字基本居中如果你的测试图片数字在角落或者特别小效果就不好。可以先把图片裁剪成紧凑包围盒再缩放到 28×28能明显改善这个问题。我一直觉得这个“测试集外也能用”的边界自己心里有数比追求一个虚高的指标重要得多。6. 常见问题与排查技巧实录6.1 典型报错速查表跑这类项目时报错信息五花八门。我整理了一个速查表基本覆盖了最常见的几种情况报错信息可能原因解决办法Undefined function or variable trainNetwork没装 Deep Learning Toolbox检查工具箱安装后重启 MatlabTraining data must have valid labels标签不是 categorical 类型检查Labels属性用categorical()转换Input size mismatch网络输入层尺寸和图片尺寸不一致确认imageInputLayer和augmentedImageDatastore的尺寸Out of memory数据批量过大或图片过大调小 MiniBatchSize或改用 CPU 训练Unable to read file ...路径错误或文件名含中文把工程目录放到英文路径下检查imageDatastore路径Invalid validation dataValidationData 格式不对确保验证集也是augmentedImageDatastore或imageDatastoreCUDNN_STATUS_NOT_INITIALIZEDGPU 环境和 toolbox 不匹配更新显卡驱动或直接在trainingOptions里设ExecutionEnvironment,cpu这里重点说一下最后一行GPU 相关的坑我遇到过不止一次。Matlab 的深度学习 GPU 加速要求 CUDA 和 cuDNN 版本与 Matlab 严格匹配版本稍微不对就会出现各种 CUDNN 报错。如果你只是跑手写体识别这种小网络CPU 训练其实完全够用与其花时间查驱动版本不如直接把执行环境设为cpu。6.2 训练不收敛时的排查路线模型训练不收敛准确率一直在 10% 附近徘徊这种问题最让人崩溃。我给出我的排查顺序先看数据是否打乱。如果Shuffle设置为never网络可能会先看到大量同一类别的样本梯度方向偏斜训练非常慢。把Shuffle设为every-epoch是最基本的保险。再看学习率是否合适。用训练曲线上看如果损失值剧烈震荡大概率学习率过大改成0.0001试试。如果损失几乎不变可能学习率过小或者网络结构有问题。我经验里 Adam 0.001 很少出问题。然后看数据归一化。如果输入图片像素范围是 0~255 而不是 0~1网络更容易出现梯度爆炸。检查代码里有没有im2double或rescaling。另外标签也得确认是 categorical 类型不是 double 类型——深度学习工具箱要求标签是分类变量。最后才考虑网络结构。如果以上都正常但仍不收敛可能是网络太深太宽、参数量远大于数据量。把卷积核数量减半看看训练曲线是否恢复。我的心得是不收敛时优先怀疑数据问题其次怀疑训练超参最后才怀疑结构这个顺序能帮你少走很多弯路。6.3 我跑这个项目踩过的几个坑这里分享几个我印象深刻的坑不是网上能查到的通用报错而是真会浪费时间的细节第一个坑是“训练结果没法复现”。trainingOptions默认不固定随机种子每跑一次训练结果都有细微不同。这其实正常但如果你写报告、对比实验结果建议在脚本开头固定随机种子比如rng(42)这样每次训练结果一致对比才有意义。第二个坑是“ar 层名称不能重复”。给网络层起名时名字必须全局唯一不能出现两个conv。我当时复制粘贴网络层改参数时忘了改Name结果trainNetwork报错Layer names must be unique排查了半天。如果你用layerGraph做可视化重名更是直接崩。第三个坑是“验证集里也能混入增强”。一开始我图省事用同一个augmentedImageDatastore同时当训练集和验证集结果训练曲线和验证曲线几乎完全重合准确率虚高得离谱。后来才意识到验证集不该加随机增强应该用原始图片。这一点在概念上不难理解但实际操作时很容易顺手把训练数据增强对象复用到了验证阶段。第四个坑是“模型保存和加载的类型问题”。用save(trainedNet.mat, net)保存后如果直接net load(trainedNet.mat)得到的其实是结构体而不是网络对象。必须用net load(trainedNet.mat).net或者netStruct load(...)再取字段。我在测试脚本里忽略这一步调用classify(net, img)时报了Invalid network当时差点以为是模型坏了。这几个坑单独看都不大但任何一个卡住你一小时都很正常。把它们写出来就是希望大家能直接绕过去。根据我实际做这个项目的体会最想强调的还是那句别把验证集准确率当唯一目标。要把数据预处理、训练集划分、测试集外验证这几件事真正做到位模型才算真正能用。如果你时间有限优先把train.m到classifyImage.m这条链路跑通就已经能覆盖大多数课程展示了。后续扩展方向也很多把输出类别从 10 改成 26 就变成字母识别把输入尺寸放大就适用于分辨率更高的图片甚至可以在这套结构上换成预训练网络做迁移学习。这个 zip 本质上是个脚手架关键看你愿意往上面加多少自己的东西。本文还有配套的精品资源点击获取