基于bp神经网络的手写数字识别实验报告

作者:娱乐 来源:焦点 浏览: 【大 中 小】 发布时间:2026-10-17 06:20:24 评论数:

MNIST数据集里的基于那堆28×28灰度图,我盯着看了半小时。神经784个像素点铺开,网络有些数字写得连人眼都犹豫——比如那个歪脖子7,写数横杠拖得老长,字识差点认成2。别实但BP神经网络不靠直觉,验报它靠权重矩阵一层层压下去,基于最后在10个输出节点里挑一个最大的神经。

这次实验我用了三层的网络结构:输入层784个节点,隐层设了128个,写数输出层10个。字识激活函数选Sigmoid,别实损失函数用交叉熵。验报为什么不用ReLU?基于试过,隐层神经元死了一片,准确率卡在89%上不去。换回Sigmoid反而稳,虽然训练慢点,但收敛曲线干净。学习率0.1,批量大小64,迭代20轮。这些数字不是拍脑袋定的,是跑了几组对比后挑的——学习率0.5时损失直接炸成NaN,0.01又慢得像蜗牛爬坡。

前向传播的代码写起来不复杂。输入一个batch的数据,矩阵乘权重加偏置,过激活函数,再乘下一层权重。但反向传播那部分我调了一下午。梯度消失的问题在深层网络里很要命,这里只有一层隐层,情况还好。真正坑的是权重初始化——全零初始化让所有神经元学一样的东西,准确率永远卡在10%左右,跟随机猜没区别。改成高斯分布初始化,标准差设0.01,才看到损失开始往下掉。

训练到第5轮,验证集准确率爬到92%。第10轮,95.3%。第15轮之后基本不动了,在96%附近晃。测试集最终跑了96.8%。这个数字在MNIST上不算惊艳,但考虑到没用卷积、没做数据增强、没加Dropout,纯靠全连接层硬怼,我觉得可以接受。错误案例我翻了几张:有个9被认成4,因为上面那个圈写得太小,几乎缩成一条线;还有个3被认成5,中间连接处断开了。这些图人眼也得愣一下。

混淆矩阵显示,数字1的召回率最高,99.2%。这合理——1的像素分布太有辨识度了,竖着一条,周围全是黑的。最容易混的是4和9,互相错认的比例加起来占了总错误的四成。还有个有意思的现象:数字8几乎不会认错,但偶尔会被认成3,都是因为左边那个圈没封口。

训练时间方面,用CPU跑一轮大概12秒,20轮下来四分钟出头。GPU没试,感觉没必要——这个规模的全连接网络,GPU的加速比不划算,数据传输开销可能比计算本身还大。内存占用峰值不到200MB,普通笔记本完全扛得住。

超参数调优那块我走了点弯路。一开始隐层设了256个节点,想着容量大点总没坏处。结果训练集准确率冲到99.5%,验证集只有95.1%,过拟合了。减到128,验证集反而涨到96.3%。再减到64,掉到95.8%。所以128这个数不是随便写的,是试出来的。另外批量大小也有讲究:64比32稳,比128收敛快。128的批量在早期几轮损失下降有点抖,像心电图。

最后我把训练好的权重导出来,写了个简单的GUI,用鼠标在画板上写数字,程序实时识别。大部分时候准,但写得太快或者笔画太细就会翻车。有次我写了个连笔的5,它认成3。这暴露了全连接网络的一个硬伤:它不理解空间结构,像素位置稍微平移一下,权重就对不上了。卷积神经网络能解决这个问题,但那是下一个实验的事了。

整个实验做下来,最大的感受是:BP神经网络像一把钝刀,能切东西,但不够锋利。96.8%的准确率背后是反复调参、看损失曲线、分析错误样本。没有魔法,就是矩阵运算加梯度下降。那些说“深度学习就是调包”的人,大概没自己手写过反向传播——链式法则推一遍,梯度检查跑一遍,才知道每个数字背后都是实打实的计算。