神经网络和深度学习

Michael Nielsen的入门教程,从零开始构建一个识别mnist手写数字的神经网络。

反向传播

反向传播就是链式求导过程,训练过程拆细了说有四个方程。这里引入记号\(\delta^{(l)}=\frac{\partial C}{\partial z^{(l)}}\),即“误差”\(\delta^{(l)}\)是关于未激活加权值\(z^{(l)}\)的偏导数。又\(a^{(l)}=g(z^{(l)})\)\(z^{(l)}=w^{(l)}a^{(l-1)}+b^{(l)}\),权重\(w^{(l)}\)的维数是{第l层neuron数} x {第l-1层neuron数}

  • 输出层误差:\(\delta^{(L)}=\frac{\partial{C}}{\partial{a^{(L)}}} \frac{\partial{a^{(L)}}}{\partial{z^{(L)}}}=\frac{\partial{C}}{\partial{a^{(L)}}} g\prime(z^{(L)})\)
  • 其他层误差\(\delta^{(l)}\)用下一层误差\(\delta^{(l+1)}\)表示:\(\delta^{(l)}=\frac{\partial{C}}{\partial{z^{(l+1)}}} \frac{\partial{z^{(l+1)}}}{\partial{a^{(l)}}} \frac{\partial{a^{(l)}}}{\partial{z^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l+1)}}} \frac{\partial{(w^{(l+1)}a^{(l)}+b^{(l+1)})}}{\partial{a^{(l)}}} \frac{\partial{a^{(l)}}}{\partial{z^{(l)}}}=(w^{(l+1)})^T \delta^{(l+1)} g\prime(z^{(l)})\)
  • 层权重的偏导数:\(\frac{\partial{C}}{\partial{w^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l)}}} \frac{\partial{z^{(l)}}}{\partial{w^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l)}}} \frac{\partial{(w^{(l)}a^{(l-1)}+b^{(l)})}}{\partial{w^{(l)}}}=\delta^{(l)}(a^{(l-1)})^T\)
  • 层偏置的偏导数:\(\frac{\partial{C}}{\partial{b^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l)}}} \frac{\partial{z^{(l)}}}{\partial{b^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l)}}} \frac{\partial{(w^{(l)}a^{(l-1)}+b^{(l)})}}{\partial{b^{(l)}}}=\delta^{(l)}\)

由这四个方程就能写出backprop()代码,见network.py

训练过程的改进

用交叉熵代价函数

若反向传播时对权重w和偏置b的梯度太小就学习缓慢,看上面方程知这两梯度跟\(\delta^{(l)}\)有关。考查输出神经元,\(\delta^{(l)}\)就是\(\delta^{(L)}=\frac{\partial{C}}{\partial{a^{(L)}}} g\prime(z^{(L)})\)

使用L2代价函数 \(C=\frac{1}{2}\sum_j(y_j-a_j)^2\)、sigmoid激活,\(\delta^{(L)}=(a^{(L)}-y) g\prime(z^{(L)})\),跟sigmoid斜率有关,sigmoid饱和时学习缓慢。

可用交叉熵 \(C=-\frac{1}{n}\sum\limits_x{[y\ln a+(1-y)\ln(1-a)]}\) 作代价函数,因为满足非负、且网络表现好(预测值a接近实际值y、y为0或1)时代价接近0。使用交叉熵、sigmoid激活,\(\delta^{(L)}=a^{(L)}-y\),无学习缓慢问题。

多分类时用softmax作输出层的激活函数,输出可理解为概率分布。可用对数似然 \(C=-\ln a_y^{(L)}\) 作代价函数,因为满足非负、且网络表现好(\(a_y^{(L)}接近1\))时代价接近0。使用对数似然、softmax激活,\(\delta^{(L)}=a_j^{(L)}-y_j\),无学习缓慢问题。

规范化防止过拟合

神经网络有大量参数,很容易就过拟合。训练集和测试集上的分类准确率相差越大,过拟合越严重。减轻过拟合的方法,除了增加训练样本、减小网络规模,还有规范化。规范化即在代价函数上添加额外的惩罚,惩罚不涉及偏置。比如L2规范化,添加权重惩罚\(\frac{λ}{2n}\sum\limits_w{w^2}, λ>0\),参数λ越大越倾向小的权重;同时,规范化反向传播给权重偏导数添上\(\frac{λ}{n}w\),梯度下降变成\(w=(1-\frac{αλ}{n}w)-\frac{α}{m}\sum\limits_{x}\frac{\partial{C_x}}{\partial{w}}\)(n是训练集大小,m是minibatch大小)。

规范化的本质是引入一些随机性,Dropout也算一种规范化。在小批量数据上的“前向反向”传播开始前,各隐藏层都随机关闭一定比例的神经元,在剩余子集中做一遍“前向反向”传播;最终选出在不同随机子集中都更健壮的特征。因为权重和偏置是在隐藏层部分神经元关闭的情况下学到的,而实际运行时隐藏层所有神经元都打开,使隐藏层的输出变大。为了补偿这个,将(隐藏层的输出)x(隐藏层神经元参与训练的比例)。

权重初始化

正态分布独立变量的和:如果\(X \sim N(μ_X, σ_X^2), Y \sim N(μ_Y, σ_Y^2), Z=X+Y\),那么\(Z \sim N(μ_X+μ_Y, δ_X^2+δ_Y^2)\)

考虑某个隐藏神经元,其\(z=\sum_j{w_jx_j}+b\),激活函数是sigmoid。若我们用\(w \sim N(0,1)\)初始化权重,简单想\(x_j=1, b=0\)时,z就是\(n_{in}\)个输入\(w \sim N(0,1)\)相加,\(z \sim N(0,n_{in})\)。z的方差较大、取值范围较大,容易过饱和、学习缓慢。若我们将正态分布初始值除以\(\sqrt{n_{in}}\),即标准差缩小到\(\frac{1}{\sqrt{n_{in}}}\),则\(w \sim N(0, \frac{1}{n_{in}})\)\(z \sim N(0,1)\),避免了过饱和问题。

若激活函数是ReLU,因为其左半是“死区”,平均算\(n_{in}/2\)个有效输入,要将正态分布初始值除以\(\sqrt{n_{in}/2}\)

合适的权重初始化可以让学习速度更快。偏置初始化则没有影响,任意值都行。

交叉熵、规范化、权重初始化改进后的代码,见network2.py

含动量的梯度下降

梯度不直接更新权重,引入“速度”的概念,梯度(加速度)影响速度、速度影响权重(距离)。这样如果在某个梯度方向持续更新,该方向上速度叠加,权重更新得更快。

设置超参数
  • 训练集上代价不再下降,而是震荡或增加,学习率太大
  • 验证集上分类准确率n回合不提升,提前终止训练回合,或尝试减小学习率
  • 规范化参数先设为0,选个学习率,再选规范化参数,再调学习率
  • 小批量数据的大小设置相对独立

深度网络的不稳定梯度问题

因为反向传播就是链式相乘,网络越深链条越长,前面层因乘积中大量的项导致不稳定的梯度(消失或激增)。

RNN中的长短期记忆LSTM,就为了解决不稳定梯度问题。

参考