本篇博文介绍了利用
numpy
库,搭建一个简单的神经网络实现手写数字识别问题使用的数据库是大名鼎鼎的
MINST
,这个库的图片以256级灰度图存储,分辨率为28*28
,如下图训练集由$60000$张图片组成,测试集由$10000$张图片组成
一组数据由一张图片、一个标签组成,标签的值为0,1,,,9
1. 神经网络结构介绍
- 借鉴$3blue1brown$的结构,输入层为$28\times28=784$个神经元,中间层为两层$16$个神经元的隐藏层,输出层为$10$个神经元。这一结构并不是必要的,比如将$16$改成$20$,或者改成一层$20$一层$16$都是可以的。只要保证参数足够多、深度足够深就能够实现预测。
- 期望值:如果数字为$5$,则期望输出层输出${0,0,0,0,0,1,0,0,0,0,}$
- 预测值:计算时,将所有的像素值按顺序输入到输入层,并将输出层中值最大的神经元的序号作为预测输出
2. 激活函数选择:
- 最后一层选择$Sigmoid$函数作为激活函数,前两层选择$Relu$函数作为激活函数。
- 其实可以都用$Sigmoid$或者都用$Relu$,但是训练速度和准确性会稍微差一些。因为$Sigmoid$是非线性函数,能够为神经网络带来更多非线性特征,但是容易出现梯度消失的问题,前向反向传播计算速度也慢,训练较慢;$Relu$接近于线性函数,虽然容易降低神经网络的准确度,却不容易出现梯度消失问题,训练速度较快。
3.参数初始化
各个全连接层的$w$和$b$不能简单采取标准正态分布进行初始化,这会导致梯度消失问题。需要再根据该层的输出维度数$n^{(l-1)}$进行调整。
具体的,根据该层的激活函数不同:
- $Relu$:将标准正态分布得到的随机值再乘以$\frac{2}{n^{(l-1)}}$
$Sigmoid$:将标准正态分布得到的随机值再乘以$\frac{1}{n^{(l-1)}}$
$Tanh$:将标准正态分布得到的随机值再乘以$\frac{1}{n^{(l-1)}}$
此部分理论见吴恩达深度学习https://www.bilibili.com/video/BV1FT4y1E74V?p=57
4. 损失函数选择:
- 这一模型较为简单,其实选啥都行。
- 我这里选的是$均方损失函数$也就是$MSE$,公式为$(\hat{y}-y)^2$
5. 梯度下降优化算法
- 实际不需要优化也可以有不错的效果,使用最简单的随机梯度下降即可
6. batch_size选择
- 最开始将
batch_size
设置为$100$,但是这样训练速度较慢,通过不断实验,发现设置为$20$的效果最好 - 另外,在训练每一轮时,可以将所有样本重新随机排列,保证训练的随机性,这一行为也叫做洗牌shuffle。但经过实验,是否洗牌的影响不是很大
7. 训练效果
- 前150轮的测试集Loss值和正确率图👇
这一曲线和同样的结构使用
pyTorch
的训练速度、准确率大致相同,都能在30轮时达到$93\%$的准确率,最终达到$95\%$左右准确率(我自己编写的代码甚至比pyTorch
还要高上0.5%嘿嘿嘿 \( ̄▽ ̄)/)
8. 其他
具体的代码实现还有更多想说的,放在下面这篇文章里
像如何隐藏层数、隐藏层维度数、激活函数选择、参数初始化方法、损失函数选择、batch_size选择问题都属于是超参数(Hyperparameter),如何选择种种超参数也是机器学习中的一大难点。这里我并没有使用科学的办法选择这些超参数,而是简单选择了一下。想要更好的训练效果,需要单独编写程序进行比较、选择。