上篇文章中我们讲解了卷积神经网络的基本原理,包括几个基本层的定义、运算规则等。本文主要写卷积神经网络如何进行一次完整的训练,包括前向传播和反向传播,并自己手写一个卷积神经网络。
卷积神经网络的前向传播
首先我们来看一个最简单的卷积神经网络:
1.输入层---->卷积层
以上一节的例子为例,输入是一个4*4 的image,经过两个2*2的卷积核进行卷积运算后,变成两个3*3的feature_map
以卷积核filter1为例(stride = 1 ):
计算第一个卷积层神经元o11的输入:
\begin{equation}
\begin{aligned}
\ net_{o_{11}}&= conv (input,filter)\\
&= i_{11} \times h_{11} + i_{12} \times h_{12} +i_{21} \times h_{21} + i_{22} \times h_{22}\\
&=1 \times 1 + 0 \times (-1) +1 \times 1 + 1 \times (-1)=1
\end{aligned}
\end{equation}
神经元o11的输出:(此处使用Relu激活函数)
\begin{equation}
\begin{aligned}
out_{o_{11}} &= activators(net_{o_{11}}) \\
&=max(0,net_{o_{11}}) = 1
\end{aligned}
\end{equation}
其他神经元计算方式相同
2.卷积层---->池化层
计算池化层m11 的输入(取窗口为 2 * 2),池化层没有激活函数
\begin{equation}
\begin{aligned}
net_{m_{11}} &= max(o_{11},o_{12},o_{21},o_{23}) = 1\\
&out_{m_{11}} = net_{m_{11}} = 1
\end{aligned}
\end{equation}
3.池化层---->全连接层
池化层的输出到flatten层把所有元素“拍平”,然后到全连接层。
4.全连接层---->输出层
全连接层到输出层就是正常的神经元与神经元之间的邻接相连,通过softmax函数计算后输出到output,得到不同类别的概率值。
卷积神经网络的反向传播
传统的神经网络是全连接形式的,如果进行反向传播,只需要由下一层对前一层不断的求偏导,即求链式偏导就可以求出每一层的误差敏感项,用来更新权重即可,而卷积神经网络有两个特殊的层:卷积层和池化层。池化层输出时不需要经过激活函数,是一个滑动窗口的最大值,一个常数,那么它的偏导是1。而我们要通过池化层反推上一层的误差敏感项时,由于池化层相当于对上一层做了一个压缩,所以这个部分与传统的反向传播方式不同。从卷积后的feature_map反向传播到输入时,由于前向传播时是通过卷积核做卷机运算得到的feature_map,所以反向传播与传统的也不一样,需要更新卷积核的参数。下面我们介绍一下池化层和卷积层是如何做反向传播的。
在介绍之前,首先回顾一下传统的反向传播方法:
1.通过前向传播计算每一层的输入值$net_{i,j}$ (如卷积后的feature_map的第一个神经元的输入:neto11)
2.反向传播计算每个神经元的误差项$\delta_{i,j}$ ,$\delta_{i,j} = \frac{\partial E}{\partial net_{i,j}}$,其中E为损失函数计算得到的总体误差,可以用平方差,交叉熵等表示。
3.计算每个神经元权重$w_{i,j}$ 的梯度,$\eta_{i,j} = \frac{\partial E}{\partial net_{i,j}} \cdot \frac{\partial net_{i,j}}{\partial w_{i,j}} = \delta_{i,j} \cdot a_i$
4.更新权重 $w_{i,j} = w_{i,j}-\lambda \cdot \eta_{i,j}$(其中$\lambda$为学习率)
卷积层的反向传播
由前向传播可得: