神经网络,顾名思义是计算机科学家们在研究高效算法的过程中借鉴生物大脑的一些特性而形成的产物。
前馈神经网络(Feedforward Neural Network)也被称为多层感知机(Multilayer Perceptron),其定义了一个映射y=(x;θ),通过迭代学习来调整θ的值,以此来使映射y能更好地近似真实函数。
其中,在学习过程中,前馈神经网络使用最广泛的学习算法是误差反向传播(Back Propagation)算法。该算法最初于1963年被提出。

BP算法基本原理

nnstructure
神经网络之所以称之为网络,是因为通常许多不同函数连接在一个链上共同来表示,就像f3(f2(f1(x)))) 。这种情况下,我们称f1为输入层(input layer),最后一层f3为输出层(output layer),其余层为隐藏层。
BP算法的基本原理是利用输出后计算的结果与真实结果之间的误差,来估计输出层的直接前导层的误差,接着利用该误差再计算前一层的误差,以此来获得所有各层的误差估计。

激活函数

激活函数用于计算隐藏层的值,必须处处可导,常用两种非线性函数:Sigmoid函数与双曲正切函数。

Sigmoid函数(logistics函数)

\[Sigmoid(x)=\frac{1}{(1+e^{-x})}\]
Sigmoid
输入:\[net=x_1\omega_1+x_2\omega_2+...+x_n\omega_n\]
输出:\[y=f(net)=\frac{1}{1+e^{-net}}\]
对输出求导:\[f'(x)=\frac{e^{-x}}{{1+e^{-x}}^2}=\frac{1}{1+e^{-x}}-\frac{1}{{1+e^{-x}}^2}=y(1-y)\]

双曲正切函数

注意,网上不少资料在这里的公式都是错误的,详细的正确公式定义详见维基百科

\[f(x)=\frac{1-e^{-2x}}{1+e^{-2x}}\]
tanhFunc-1
输入:\[net=x_1\omega_1+x_2\omega_2+...+x_n\omega_n\]
输出:\[y=f(net)=\frac{1-e^{-net}}{1+e^{-net}}\]
对输出求导:\[f'(x)=\frac{2e^{-2x}(1+e^{-2x})+2e^{-2x}(1-e^{-2x})}{(1+e^{-2x})^2}=\frac{4e^{-2x}}{(1+e^{-2x})^2}=1-(\frac{1-e^{-2x}}{1+e^{-2x}})^2=1-f^2(x)\]

BP网络学习算法

在迭代学习的过程中,神经网络通过将误差逐层反向传播,不断地修改网络之间的权重和偏置,以此来使输出层的预测输出更接近实际的输出值。
一般来说步骤如下:

  • 将网络中的权重和偏置随机赋值(0-1之间)
  • 正向传播求输出层的输出值
  • 计算输出值与真实值之间的误差,并修改与前一层之间的权重与偏置
  • 计算上一层的误差,以此对每一个产生误差的神经元的权重与偏置进行修改
  • 重复迭代,以达到权重和偏置的最佳值(误差减小到预设的程度)

算法推导

首先,假设网络输入层有n个节点,隐藏层有p个节点,输出层有q个节点。
则有:
\[x=(x_1,x_2, ... ,x_n)\tag{Input}\] \[h_i=(h_i1,h_i2, ... ,h_ip)\tag{Hidden input}\] \[h_o=(h_o1,h_o2, ... ,h_op)\tag{Hidden output}\] \[y_i=(y_i1,y_i2, ... ,y_iq)\tag{Output input}\] \[y_o=(y_o1,y_o2, ... ,y_oq)\tag{Output output}\] \[d_o=(d_1.d_2, ... ,d_q)\tag{Expected output}\]
\[f=Sigmoid(x)=\frac{1}{(1+e^{-x})}\tag{Activate function}\] \[e=\frac{1}{2}\sum_{o=1}^q (d_o(k)-y_{o_o}(k))^2 \tag{Loss function}\]
第一步,先对每个权重赋一个随机值,区间在(-1,1)内,设定计算精度ε,误差低于ε则停止训练;也可以设置最大训练次数N,来使训练停止。
第二步,选取第k个样本输入和对应的期望输出:
\[x(k)=(x_1(k),x_2(k),...,x_n(k))\] \[d_o(k)=(d_1(k),d_2(k),...,d_n(k))\]
第三步,计算各层节点的输入输出:
\[h_{i_h}(k)=\sum_{i=1}^p \omega_{ih}x_i(k)-b_h\tag{h=1,2,...,p}\] \[h{o_h}(k)=f(h{i_h}(k))\tag{h=1,2,...,p}\] \[y_{i_o}(k)=\sum_{h=1}^p \omega_{ho}h_{o_h}(k)-b_o\tag{o=1,2,...,q}\] \[y_{o_o}(k)=f(y_{i_o}(k))\tag{o=1,2,...,q}\]
第四步,根据损失函数来计算各权重的偏导数,以此来调节各节点之间的权重,首先通过输出层来求前一层的权重偏导数:
\[\frac{\partial E}{\partial \omega_{h_o}}=\frac{\partial E}{\partial y_{i_o}}*\frac{\partial y_{i_o}}{\partial \omega_{h_o}}\]其中:\[\frac{\partial E}{\partial y_{i_o}}=-(d_o(k)-y_{o_o}(k))*f'(y_{i_o}(k))\]令\[\frac{\partial E}{\partial y_{i_o}}=-(d_o(k)-y_{o_o}(k))*f'(y_{i_o}(k))=\delta_o(k)\]\[\frac{\partial y_{i_o}}{\partial \omega_{h)o}}=h_{o_h}(k)\]\[\frac{\partial E}{\partial \omega_{h_o}}=-(d_o(k)-y_{o_o}(k))*f'(y_{i_o}(k))*h_{o_h}(k)=\delta_o(k)*h_{o_h}(k)\]同理\[\frac{\partial E}{\partial b_o}=-(d_o(k)-y_{o_o}(k))*f'(y_{i_o}(k))=\delta_o(k)\]
接下来求输入层到隐层的权重偏导数:
\[\frac{\partial E}{\partial \omega_{i_h}}=\frac{\partial E}{\partial h_{i_h}(k)}*\frac{\partial h_{i_h}(k)}{\partial \omega_{i_h}}\]其中:\[\frac{\partial h_{i_h}(k)}{\partial \omega_{i_h}}=x_i(k)\]\[\frac{\partial E}{\partial h_{i_h}(k)}=\frac{\partial \frac{1}{2}*\sum_{o=1}^q (d_o(k)-f(y_{i_o}(k))^2}{\partial h_{i_h}(k)}\]\[=\frac{\partial \frac{1}{2}*\sum_{o=1}^q (d_o(k)-f(\sum_{o=1}^p \omega_{h_o}h_{o_h}(k)+b_o)^2}{\partial h_{o_h}(k)}\]\[=-\sum_{o=1}^q (d_o(k)-f(y_{i_o}(k))*f'(y_{i_o}(k)*\omega_{h_o}*f'(h_{i_h}(k))\]\[=-\sum_{o=1}^q \delta_o(k)*f'(h_{i_h}(k))\]令\[-\sum_{o=1}^q \delta_o(k)*f'(h_{i_h}(k))=\delta_h(k)\]因此\[\frac{\partial E}{\partial \omega_{i_h}}=\delta_h(k)*x_i(k)\]同理\[\frac{\partial E}{\partial b_h}=\delta_h(k)\]
这里我只求了仅含一层隐藏层的BP网络,若是多层的话,则类似可以得到:\[\frac{\partial E}{\partial \omega_N}=\delta_Nh_{N-1}(k)\]\[\frac{\partial E}{\partial b_N}=\delta_N\]
第五步,以所求得的偏导数来调节各节点之间的权重,其中μ是学习率:\[\omega_{h_o}=\omega_{h_o}+\Delta\omega_{h_o}=\omega_{h_o}+\mu*\delta_o(k)*h_{o_h}(k)\]\[\omega_{i_h}=\omega_{i_h}+\Delta\omega_{i_h}=\omega_{i_h}+\mu*\delta_h(k)*x_i(k)\]

算法步骤

输入:样本输入x[m],样本输出y[m],学习率μ,阈值ε
初始化:权重矩阵ω,偏置向量b,梯度矩阵∇ω,∇b
foreach x do:
    forward input->hidden->output
    cal error_output
    cal Δω、Δb
    update :
        ∇ω[i]=∇ω[i]-Δω
        ∇b[i]=∇b[i]-Δω
    if error_output < ε :
        stop train

简而言之,每一个节点j的误差可以从它后面一层的所有节点m的误差计算得到 :\[E_j=O_j(1-O_j)\sum_k \omega_{jk}E_k\]其中输出层的误差计算公式为 :\[E=y_i(1-y_i)(d_i-y_i)\]调整权重时,每一层与后一层的权重调整公式为:\[\omega_{ij}=\omega_{ij}+\mu*O_i*E_j\]以上三个公式的Oj均为j节点的输出值。

完结撒花!★,°:.☆(~▽~)/$:*.°...


*Reference:
【整理】 BP神经网络讲解——最好的版本
BP神经网络推导过程详解
《计算智能》
Backpropagation Wiki
*