news 2026/10/13 1:11:57

基于MNIST的神经网络模型对比:从全连接到CNN的实战训练详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于MNIST的神经网络模型对比:从全连接到CNN的实战训练详解

1. 从MNIST数据集到神经网络:我为什么把这篇放在系列第二篇

MNIST这个数据集,做过深度学习入门的人都不陌生,60万张28×28的手写数字灰度图,几乎是所有人第一个跑通的图像分类任务。我上一篇写了数据预处理和基线逻辑回归的做法,这篇接着往下走,核心是神经网络模型的训练,以及不同模型结构在同一任务上的结果对比。

坦白说,MNIST已经被各路教程写烂了,但“烂大街”不代表没有价值。恰恰因为它简单,才适合用来做模型结构的对照实验——同样的数据、同样的训练流程,只改网络结构,看效果差多少。这篇文章我不会只贴准确率,我会把训练的完整过程、每个关键参数的取舍逻辑、以及我踩过的坑都写清楚。适合正在学深度学习、想搞懂模型对比该怎么设计实验的人参考。

先交代一下环境:Python 3.10,PyTorch 2.x,GPU是某款入门级显卡,显存不大,所以所有模型都控制在小规模范围内,保证单卡能跑动。数据集已经通过上一篇的流程做好了归一化和数据划分,训练集55000张、验证集5000张、测试集10000张,直接喂给DataLoader用。

我想先聊一个很多人忽略的点:拿MNIST做模型对比实验,到底在对比什么?如果你只是把不同模型跑一遍然后报个准确率,那这个实验基本没有参考价值。真正有意义的对比,需要控制变量——数据增强开不开、学习率怎么调、训练轮数是否一致、参数初始化方式是否相同。把这些变量钉死,模型结构差异带来的效果差异才看得清楚。这也是我这篇文章贯穿始终的实验设计原则。

2. 三个对照模型的设计思路:层数、卷积核和全连接到底怎么选

选模型不能拍脑袋。我先定了一个基线,再在这个基线上做有针对性的改动,这样每一组对比都有明确指向。

第一组是简单全连接网络,结构为784-128-10。输入是一张28×28的图展平成784维向量,中间一个128节点的隐藏层,ReLU激活,输出层10个节点对应0-9十个数字。这个模型参数量大约10万个,可以说是神经网络里最小的配置之一了。选它的原因很直接:作为“下限基准”,看最朴素的网络能达到什么水平。

第二组是加深版全连接网络,结构为784-256-128-64-10。同样是全连接,但增加了一层,同时各层宽度做了阶梯式递减。参数量大约25万个,主要是想验证一个经典结论:在MNIST这种简单任务上,单纯增加网络深度和宽度,对最终准确率的提升非常有限,反而会带来训练时间的明显增加。

第三组是卷积神经网络,结构为Conv(1→32, 3×3) + Conv(32→64, 3×3) + MaxPooling + Dropout + Flatten + FC(64×7×7→128) + FC(128→10)。参数量大约120万个,远超前两个模型。选它的理由是检验卷积特征提取在图像任务上的压倒性优势到底有多大。

三组模型覆盖了“浅层全连接”“深层全连接”“卷积网络”三个典型结构,每一组之间只有一个维度的本质差异,这样对比结论才能站得住。我见过不少人做实验,模型A用Adam、模型B用SGD,数据增强也不一致,最后跑出来说“卷积比全连接好”,这个结论其实是不可信的,因为变量没有控制住。

2.1 参数初始化:为什么我统一用Kaiming初始化

模型对比实验里最容易被忽略的是参数初始化,但对训练结果影响极大。全连接网络我用的是默认的PyTorch初始化方式,本质上就是均匀分布随机初始化。卷积网络我改用了Kaiming初始化,这是因为ReLU激活函数配合Kaiming初始化,能有效缓解梯度消失和梯度爆炸的问题。

实际操作中,PyTorch的nn.Conv2d和nn.Linear默认初始化已经比较合理,但我还是在卷积层上显式调用了kaiming_normal_,原因是想让每一层的输出方差在初始阶段保持稳定。这个细节在MNIST上可能只影响前期收敛速度,但在更大规模图像任务上,初始化方式直接决定你能不能训起来。

三组模型我都固定使用相同的初始化种子,确保每组实验的起点可控。种子统一设成42,这只是个约定俗成的数字,关键是让实验可复现——你照着我的代码跑,应该得到一模一样的结果,而不是“大概差不多”。

2.2 训练超参数的统一约定

既然要做对比,训练策略必须完全一致。优化器我全部选了Adam,学习率1e-3,批大小128,训练轮数10轮。损失函数都是交叉熵,这个在分类任务上没有悬念。

为什么不选SGD?原因很简单,MNIST数据量小、任务简单,Adam的收敛速度和稳定性都更好,能在更少的轮数内达到接近上限的准确率。做对比实验时,选一个收敛快的优化器,能把训练时间成本压下来,同时不影响“相对表现”的判断。Adam唯一的缺点是最终精度可能会比调好的SGD略低一点点,但对MNIST这种任务,这个差距在0.1%以内,不影响结论。

学习率1e-3我没有做额外调整。有人可能会说应该用学习率衰减,但我故意不加,因为加了衰减就多了一个变量,三组模型的衰减策略如果设置不当,反而会干扰对比。10轮训练,MNIST足够收敛,不需要额外调度。

数据增强我也统一没开。平移、旋转、缩放这些增强手段对MNIST有效,但对模型对比实验来说是个干扰项。所有模型看的是同一批原始数据,公平。

3. 训练过程的实测记录:损失曲线、训练时间和GPU占用

这部分是我觉得最有料的地方,因为训练过程的动态变化能告诉你很多最终准确率看不出的信息。

第一个跑的是784-128-10的浅层全连接网络。第一轮结束后,训练集准确率大约96.2%,验证集94.1%,损失从初始的2.3降到0.17左右。这个速度是很快的,毕竟模型小、参数少。但注意,验证集和训练集的差距在1到2个百分点之间,说明这个模型已经开始有轻微过拟合倾向了。到第10轮结束时,训练准确率98.1%,验证准确率97.3%,测试集准确率97.2%。

训练时间方面,单轮只花了大约6秒,10轮总共一分钟出头。GPU占用率很低,因为参数量太小,大部分时间都花在数据搬运而不是计算上。

第二个跑的是784-256-128-64-10的加深全连接网络。第一轮结束,训练准确率96.8%,验证集94.8%,略好于浅层模型。但往后几轮,提升幅度明显放缓。到第10轮,训练准确率98.4%,验证集97.6%,测试集97.5%。

这里有个很有意思的现象:模型参数从10万增加到25万,最终准确率只提升了0.3个百分点。而且训练时间从单轮6秒涨到了15秒,翻了2.5倍。这说明在MNIST这种线性可分性已经很强的任务上,盲目加深全连接网络是典型的“事倍功半”。

第三个跑的是CNN。第一轮结束时,训练准确率98.9%,验证集98.5%,直接超过了两个全连接网络训练10轮的效果。到第10轮,训练准确率99.5%,验证集99.2%,测试集99.1%。

训练时间上,CNN单轮大约40秒,是浅层全连接的6倍多,深层全连接的2.6倍。但换来的是接近2个百分点的准确率提升,这笔账非常划算。

我把三组模型的关键数据整理成了一张表,方便对照:

模型参数量单轮训练时间第1轮验证集准确率第10轮验证集准确率测试集准确率
全连接784-128-10约10万约6秒94.1%97.3%97.2%
全连接784-256-128-64-10约25万约15秒94.8%97.6%97.5%
CNN(2层卷积+2层全连接)约120万约40秒98.5%99.2%99.1%

3.1 损失曲线的细节:为什么CNN的起点就这么高

观察损失曲线,最让我意外的是CNN第一轮的损失直接从初始的2.3降到了0.05以下,而两个全连接网络第一轮结束时损失还在0.17左右。也就是说,CNN用一轮训练就把全连接网络十轮的效果甩开了。

原因在于卷积操作天然利用了图像的局部空间结构。数字的笔画特征——横线、竖线、弧线、交叉点——都是局部的,卷积核在3×3的小窗口内就能捕获这些模式,而且参数共享让特征提取更高效。全连接网络把每个像素当成独立维度,像素之间的空间关系被抹掉了,学起来自然费力。

如果画训练曲线图,你会看到浅层全连接网络的损失下降是一条平滑的指数衰减线,深层全连接网络开头更快但后期趋缓,CNN则是“断崖式下跌”然后进入平台期。曲线的形态差异,本质上是模型容量和数据特征复杂度之间的匹配程度差异。

3.2 GPU占用和显存观察:小模型也有小模型的烦恼

很多人觉得小模型跑得快就不需要在意资源,实际上在工程实践中小模型最大的问题是“喂不饱GPU”。我观察了训练时的资源占用情况,浅层全连接网络在GPU上的利用率波动很大,因为计算量小,数据加载和预处理反而成了瓶颈。

我用的是Worker数为4的DataLoader,批大小128,单张图片28×28,一组数据只有几十KB,加载几乎不耗时。但即便如此,浅层模型的GPU占用峰值也只有20%左右,大部分时间在等待下一批数据。

相比之下,CNN虽然参数多,但卷积层的大量矩阵运算能很好地把GPU利用起来,实测占用率稳定在50%到60%。这让一个反直觉的事实浮出水面:在给定硬件上,CNN的单位训练时间性价比远高于全连接网络,因为它的计算密度更高、利用率更好。

另外说一句,MNIST这种小图完全不需要分布式训练,单张GPU绰绰有余。如果你看到有人拿MNIST做多卡并行,那基本是在练技术,不是在解决实际问题。

4. 测试集上的详细结果分析:错误样本都在哪些数字上

只看总体准确率还是会漏掉很多信息。我把测试集10000张图按数字类别拆开,统计了每个类别的错误数量,发现了一些值得琢磨的模式。

CNN在测试集上总共错了89张,其中最容易混淆的是4和9,占了23个错误;其次是7和2,占了15个;3和8之间的混淆也有11个。全连接网络的错误分布更分散,4和9同样是最多的,但7和1、7和9之间的混淆也频繁出现。

这个结果从人类视角来看并不奇怪——4和9确实手写时容易写得很像,7和2也是经典混淆对。但要注意的是,CNN已经比全连接网络对这些难分样本的识别能力强得多:全连接网络在4/9这个类别上错了58张,CNN只错23张。

为什么卷积网络在难分样本上更强?我的理解是,卷积层学到的是边缘和纹理的组合特征,对笔画的方向和连接关系更敏感,而全连接网络学的更多的是全局像素的线性组合,对局部结构的微小差异不够敏感。手写4和9的区别往往只在一个小弯钩的朝向,这个信息量在像素级别很微弱,需要专门的局部特征提取能力。

4.1 错误样本的图像特征:手写体的“质量”问题

我把错分样本挑出来看了一遍,发现大部分错误样本都集中在书写潦草、连笔严重、或超出正常书写区域的情况。比如占比最高的4/9错误,基本都是手写4的时候横折和竖弯的衔接方式被模型识别成了9的上半部分。

还有一个值得注意的现象:所有模型在数字1上的错误率都极低,CNN甚至一个都没错。原因是数字1的结构简单、区分度高,与其他数字几乎没有混淆空间。这说明MNIST的错误模式遵循一个朴素的规律——同类样本内部差异越小、类间差异越大,越容易学。

另一个有意思的发现是,全连接网络在数字0上的错误数比CNN多出两倍多。0本身形状简单,不该有那么多错误,我翻看了错误样本才发现,我测试集里有一部分0是带有明显斜角的,书写者把0写得像左倾斜的椭圆,全连接网络会把它判成6。CNN对这类斜角0的容忍度更高,因为它捕捉的是圆环的闭合性,而不是某个固定角度的像素排列。

4.2 用混淆矩阵看差异:准确率相同但错误模式可能完全不同

如果只看最终准确率,深层全连接和浅层全连接只有0.2%的差距,很容易让人觉得“加深没用”。但如果画出混淆矩阵,你会发现深层网络的4/9错误数从浅层的61个降到了45个,代价是7/9错误数从12个升到了17个。

这说明加深全连接网络并没有从本质上改变模型的判别逻辑,只是把部分错误从一类转移到了另一类。准确率数字的微小提升,掩盖了错误分布的零散变化。所以我一直觉得,做模型对比实验,光看最终指标是不完整的,至少要配合混淆矩阵或者错分样本可视化,才能判断模型的行为差异在哪里。

5. 训练流程中的几个关键细节:学习率、Dropout和过拟合的关系

很多初学者训练MNIST最常犯的错误是不加正则化。MNIST任务简单,模型容易过拟合,尤其是CNN这种参数量达到120万的模型,如果没有Dropout,训练集准确率会轻松超过99.8%,但验证集会停滞在98.5%上下,测试集甚至会掉到98%以下。

我在这组对比里,CNN的Dropout层放在最后的全连接层之前,概率设为0.5。训练时随机丢掉一半的神经元连接,测试时全部保留,这是一种隐式的模型集成策略。全连接网络我没有加Dropout,因为参数少、本身过拟合不严重,加了反而会让原本就不高的表达能力再打折扣。

关于学习率,我额外做了一次小范围探测,发现1e-3在Adam下是最稳的,2e-3会导致损失曲线在前两轮剧烈震荡,5e-4则会拖慢收敛速度。这个规律在三种模型上表现一致。学习率的选择和模型大小没有强相关,更多取决于优化器的默认行为和数据集的规模。

还有批大小的影响。我试过把批大小从128调到64,CNN的收敛稍微慢一点但最终准确率持平;调到256,训练速度提升但验证集准确率会掉0.1到0.2个百分点。批大小在这里是个经典的偏差-方差权衡,对MNIST来说128是个性价比很高的折中。

5.1 验证集的监控策略:早停法真的必要吗

很多深度学习项目都强调用早停法防止过拟合,但MNIST这个任务里,我认为必要性不大。原因很简单:10轮训练后模型准确率已经进入平台期,过拟合的增幅远小于随机波动。

我在训练时会在每轮结束后计算验证集准确率并保存最佳模型权重,但我没有设置早停条件。如果你想在更大规模的数据集上做实验,早停法值得认真配置——监控验证损失,连续3个epoch不下降就停止训练并回滚到最佳权重。但在MNIST上,这套操作带来的收益几乎为零。

不过有一点需要注意,我保存最佳模型时用的指标是验证集准确率而不是验证集损失。准确率在接近上限时会出现多个epoch相同的并列情况,而损失还能继续缓慢下降。如果你用损失做判断标准,最终得到的模型通常泛化性更好。

5.2 模型保存与推理流程:别在权重上犯低级错误

训练完的模型保存为state_dict格式,也就是纯权重文件。推理时先加载模型结构,再加载权重,切换到评估模式(model.eval()),这一步很重要,因为Dropout和BatchNorm在训练和测试时的行为不同。

我在第一版推理代码里犯过一个低级错误——忘了调用model.eval(),结果CNN的推理准确率比训练时的验证集准确率低了将近2个百分点。原因是Dropout层在训练模式下随机丢神经元,测试时会引入大量噪声。排查了一下午才发现问题如此简单。

这个错误太经典了,新手一定要记住:训练完做推理,两步缺一不可——model.eval()和torch.no_grad()。前者关掉训练模式的随机行为,后者关掉梯度计算,两个都不调用的话,推理速度会慢好几倍,结果还可能是错的。

6. 全连接到CNN的跨度:从分类边界到特征提取底层逻辑

这一节我想稍微往深聊一点,把这些模型背后的原理讲透,因为只报数字不解释原因,干货就少了一半。

全连接网络做的事情,本质上是把784维的像素向量映射到一个10维的分类得分向量。它找的是一条分类边界,这条边界在784维空间里是一个超平面(或超平面的组合)。MNIST的数字图像在高维空间中有一定的线性可分的底子,所以全连接网络能做到97%左右的准确率,这一点也可以通过t-SNE可视化观察到:不同数字的像素向量在降维后会形成大致可分的聚类,但有重叠区域。

CNN则是先做特征提取再做分类。卷积层输出的特征图,每一层都在提炼更抽象的模式。第一层卷积提取到的基本上是笔画边缘,第二层能组合出弧线、折角这类局部结构,全连接部分则基于这些结构特征做最终判断。这个分层次的特征提取逻辑,让它能找到更精细的分类边界,所以准确率能上到99.1%。

你把这个理解迁移到其他图像任务上,就能明白为什么CNN在图像领域统治了这么多年——它把人对视觉的理解编码到了网络结构里:局部连接、权重共享、空间下采样。全连接不是不行,而是它在图像上的效率太低了。

6.1 为什么简单的任务反而更能看清模型的本质差异

MNIST的难度到现在已经被严重低估了,很多人跑出99%就觉得“没意思了”。但恰恰是这个看似简单的任务,能把不同模型结构的行为模式暴露得最清晰——因为数据干净、任务单纯,干扰因素少,模型结构差异带来的影响不会被其他噪声淹没。

如果你想验证一个设计想法(比如新的激活函数、新的正则化策略),用MNIST做快速验证是最省时间的。在MNIST上跑不动的设计,在更大数据集上大概率也不行;在MNIST上有效果的设计,再迁移到CIFAR或ImageNet上验证,成功率会高很多。

所以我认为MNIST真正的作用不是刷准确率,而是一个“模型诊断工具”。它能帮你快速回答一个问题:这个结构改动到底有没有用?

7. 常见错误与排查指南:训练不收敛、准确率异常低、复现不一致

训练神经网络最怕遇到“看起来没毛病但结果不对”的情况。以下是几个我在这个项目里遇到或排查过的常见问题,以及对应的解决思路。

第一个问题是损失不下降或者先降后涨。这个现象通常指向学习率过大或者数据没有归一化。MNIST像素值如果直接输入网络而不除以255,模型的输入尺度会很大,梯度更新也会剧烈波动。解决办法是统一做归一化到[0,1]区间,这是我这套实验的标准前置步骤。

第二个问题是验证集准确率远低于训练集。如果差距超过2到3个百分点,基本可以断定过拟合。对策按需选择:加Dropout、增加数据增强、减少模型容量、加入权重衰减。我用CNN时如果去掉Dropout,验证集和训练集准确率差距会从0.3个百分点拉开到1.2个百分点,非常明显。

第三个问题是训练结果和别人的报告对不上。最常见的原因是初始化种子不同、数据划分不同、或PyTorch版本的默认行为有差异。我做对比实验时统一了随机种子,并且固定了一组测试数据,这样就能最大限度保证复现性。如果你用了早停、学习率调度或者随机数据增强,还会引入更多不确定性,这在对比实验里要格外小心。

第四个问题是显存溢出。虽然MNIST数据量小,但如果把批大小设得过大(比如超过4096),或者模型定义时维度写错导致中间激活层意外膨胀,也会爆显存。排查方法很简单——打印每一层的输出张量形状,逐层核对。

7.1 一个容易被忽略的坑:验证集划分不当导致的乐观偏差

数据划分方式对结果的影响比很多人想象的大。如果验证集是从原始训练集中随机抽的5%,而测试集又来自同一个分布源,那模型的验证集准确率会和测试集准确率非常接近。但如果你的验证集划分带有偏差(比如只取了某几个batch的数据),验证集表现会误导你的调参方向。

我在这套流程中的划分方式是纯随机抽样,用了固定的随机种子,确保每次实验的划分一致。这个细节看起来不起眼,但在模型对比中它是保证结论可信的基础。如果你在不同的实验之间让数据划分发生了变化,那模型的准确率差异就无法完全归因于模型结构。

顺便说一个反直觉的判断:在MNIST上训练集和测试集来自同一个分布(都是手写数字扫描),所以过拟合带来的泛化差距不会特别大。但在真实项目里,训练数据和线上数据往往分布不一致,这时验证集的作用就更重要了——它是对真实泛化能力的最接近估计。

8. 实测下来我对这三类模型的最终结论

训练全部结束后,我对这三个模型在MNIST上的表现有了比较明确的判断。

如果你只是想快速验证深度学习流程是否跑通,浅层全连接网络是最合适的——代码量少、训练快、不容易出错,准确率97.2%对这个目的来说绰绰有余。如果你的任务是做一个手写数字识别的演示项目,深层全连接网络没有必要,直接上CNN,99.1%的准确率配合不算太长的训练时间,体验更好。

CNN在MNIST上的优势不是秘密,但我还是想强调一个度的问题:即使是用最简化的CNN,也只有120万参数,这个体量在现代硬件上负担极低。相比之下,如果你用Transformer类的模型来处理MNIST,效果未必比CNN好,训练成本却高出一个数量级——在小数据集上,结构先验的价值远大于模型容量。

我做这个系列实验的初衷,是想用MNIST这个最简单的平台,把不同模型的“性格”看得更清楚。全连接网络像一个老实人,按部就班地学,该错的学不会;CNN像一个有经验的匠人,知道从哪里找关键特征,一上手就抓住了主要矛盾。

最后补充一点实操上的体会:做这类对比实验,不要只记录最终准确率,训练过程中的损失值、验证集准确率、单轮训练时间、错误样本分布,每一样都值得记录下来。你积累的这些对照数据,在之后做更难任务的时候,会成为你判断模型行为和定位问题的宝贵参考。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 1:11:29

三分技术七分人品:计算机行业协作效率的底层逻辑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 1:10:37

杰理AC692X蓝牙音频SoC基本配置实战指南:从管脚分配到音频通路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 1:10:16

ASP.NET请求响应与状态管理实战:从Session丢失到缓存选型

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 1:10:16

JSON文件读写全流程解析:编码、序列化与大文件处理实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 1:10:07

PN结原理与工程实践:从载流子到反向恢复的深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 1:10:05

KEA128串口不定长数据接收:接收中断+定时器超时方案详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华