反向传播神经网络推导中给出了复杂的BP公式。从头看这篇多年的博客的公式,我都有点被绕晕了。现在在这里我可以从矩阵计算的角度去演示一个全连接神经网络的计算过程,这样更简洁明了。
如上图,是一个简单的分类全连接神经网络,每一根线表示权重相乘。如果没看透这个计算关系,那么我们很容易这样设计程序:每一个节点都制作一个class类,每个类节点要与其他类节点有连接关系,要传递数据,要计算梯度误差。这样做出来的神经网络灵活性很强,但是真的是太复杂了!不靠谱!
假设我们只研究两层数据之间的关系,每根线代表一个权重乘法。设权重为
w1ij
,前一层数据是
x1i
,后一层数据为
x2j
,那么后一层每一个数据就是
x2i=∑ix1iw1ij+b1i
。为了形象表示数据从左向右传递的过程,我写成
[x11x12...]⎡⎣⎢⎢w11jw12j...⎤⎦⎥⎥+b11
如果我们扩展计算整个全连接层,就可以得到简单的矩阵计算:
[x11x12...]⎡⎣⎢w111w121...w112w122......⎤⎦⎥+[b11b12...]=[x21x22...]→X1W1+B1=X2
所以,一个全连接层,实际上就是一个矩阵乘法和一个矩阵加法,数据都是向量,权重参数可以用矩阵表示,计算过程能够编码为一个layer类,即一个神经网络层。相应的,激活层其实就是对数据向量,逐个元素进行计算,即
Activation(X2)=[f(x21)f(x22)...]
这两种操作都可以向量化,在GPU中跑的飞快;CPU使用MKL和BLAS库也可以大大加速计算。
下面关键就是如何计算梯度来修正权重,梯度下降法的定义是 θnew=θold−∂F∂θ 。矩阵求导的公式(注意上标2并不是平方,是序号)是:
∂X2∂W1=(X1)T∂E∂B1=I
设损失函数为E。
好了,假设我随便做一个神经网络,包含一层全连接层
fc1:Xfc1=XinputWfc1+Bfc1
,一层sigmoid激活层
sig:Xsig=sigmoid(Xfc1)
,一层全连接层,最后只输出一个结果
fc2:y=Xfc2=XsigWfc2+Bfc2
,损失函数是Euclidean损失函数
E=12∑(y′i−yi)2
,求和是因为我们可能一次性计算一个批次的数据,即一个batch,把多次计算的误差全部加起来。
输出层误差梯度为: δ4=∂E∂Xfc2=∑(y′i−yi)
fc2层要计算W、B的梯度,和一个向上传递的梯度:
δ3=∂E∂Wfc2=∂E∂Xfc2∂Xfc2∂Wfc2=XTsig∑(y′i−yi)=XTsigδ4∂E∂Bfc2=∑(y′i−yi)=δ4δup3=δ4WTfc2
sig层不包含W、B,只上传梯度:
δup2=∂Xsig∂Xfc1δup3=Xsig.∗(1−Xsig).∗δup3
.*是元素相乘。这只是一个过渡层。
fc1层函数梯度:
δ1=∂E∂Wfc1=∂E∂Xfc2∂Xfc2∂Xsig∂Xsig∂Xfc1∂Xfc1∂Wfc1=XTinputδup2∂E∂Bfc1=∂E∂Xfc2∂Xfc2∂Xsig∂Xsig∂Xfc1∂Xfc1∂Bfc1=δup2
归纳总结一下:
* 设当前层序号为n,输入数据为 Xn ,下一层向上传递的梯度为 δupn+1 ,则当前层W更新梯度为 XTnδupn+1 ,B更新梯度为 δupn+1 ,向上传递的梯度为 δupn+1WTn
* 如果当前层不包含W,B,例如激活层,则上传向上传递梯度即可
* 输出层的梯度由损失函数定义。