文章目录

  • 一、基本概念
    • (一)单一神经元数学模型
    • (二)多层BPNN的定义
  • 二、运算过程
  • 三、训练过程
    • (一)代价函数
    • (二)梯度推导
    • (三)迭代训练
  • 四、归一化
  • 五、应用示例
    • (一)数据准备
    • (二)定义网络
    • (三)训练拟合

一、基本概念

所谓神经网络,本质上也是用训练样本构造出一个回归模型,与经典回归模型所不同的是它的函数形式并不简洁,而是多重函数的嵌套,因此它无法给出数据之间关系的直观描述,而给人一种黑盒的感觉。但其价值在于,理论上可以拟合多输入多输出的任意非线性函数,故而在回归、聚类等多领域都可应用。而神经网络中又以反向传播神经网络最为经典,此文将给出此类神经网络的基本原理和使用方法。

(一)单一神经元数学模型

根据生物神经元细胞的构造抽象出单一神经元数学模型,如图1所示:

图1 单一神经元模型

多个外界输入(x1∼xnx_1 \sim x_nx1​∼xn​)经分别加权后求和,并做一定偏移(bbb),输入到一个非线性激励函数(fff)中,最后得到该神经元的输出值(ooo)。其数学表达式为:
o=f(∑i=1nwixi+b)=f(wTx+b)o = f \left ({\sum_{i=1}^n w_i x_i} + b \right ) = f(\boldsymbol w^T \boldsymbol x + b) o=f(i=1∑n​wi​xi​+b)=f(wTx+b)由于进入到每个神经元各外部输入都是经过一个线性组合,即组成一个线性函数后再作为自变量进入到该神经元的的激励函数,如果激励也是一个线性函数,那就成了线性函数的线性函数,最终还是线性函数,如此一来,无论多少个神经元组成的神经网络也只能化归成一个线性函数。因此激励函数必须是非线性的,才能通过多层神经元组合连接,加权嵌套出任意非线性函数,实现复杂问题处理。激励函数一般选用以下三种类型:

名称 特征 表达式 图像
Sigmoid函数 正值,可微,类阶跃 f(x)=11+e−xf(x)={1 \over 1+e^{-x}}f(x)=1+e−x1​
双曲正切函数 零均值,可微,类阶跃 f(x)=tanh⁡(x)f(x)=\tanh(x)f(x)=tanh(x)
高斯函数 正值,可微,类脉冲 f(x)=exp⁡(−x2σ2)f(x)=\exp(-{x^2 \over \sigma^2})f(x)=exp(−σ2x2​)

由各激励函数图像可见,只在0附近会产生显著的响应差异,所以要在激励函数的输入中引入偏移量(bbb),从而相当于对响应函数做了适当平移。

(二)多层BPNN的定义

当多个神经元形成多层交叉连接时,即形成神经网络。前一层神经元的输出成为后一层神经元的输入,最后一层直接给出整个网络输出的神经元称为输出层,输出层前面的各层称为隐含层。当一个神经网络有一个以上的隐含层时,称为深度学习网络。此文以一个含有两个隐含层(共三层神经元)的深度学习网络为例做阐述,其构造如图2所示,其运算和训练过程可推广至更多层网络。

图2 深度学习网络

该网络的入参是一个nnn维向量x=[x1x2⋯xn]\boldsymbol x = \left [ \begin{matrix} x_1 & x_2 & \cdots & x_n \end{matrix} \right]x=[x1​​x2​​⋯​xn​​],出参是一个mmm维向量y=[y1y2⋯ym]\boldsymbol y = \left [ \begin{matrix} y_1 & y_2 & \cdots & y_m \end{matrix} \right]y=[y1​​y2​​⋯​ym​​]。从左至右,各层神经元的输入权重矩阵、偏移向量、入参向量、出参向量分别是:
w(1)=[w11(1)w21(1)⋯wn1(1)w12(1)w22(1)⋯wn2(1)⋮⋮⋮⋮w1k(1)w2k(1)⋯wnk(1)]b(1)=[b1(1)b2(1)⋮bk(1)]z(1)=[z1(1)z2(1)⋮zk(1)]o(1)=[o1(1)o2(1)⋮ok(1)]\boldsymbol w^{(1)} = \left [ \begin{matrix} w_{11}^{(1)} & w_{21}^{(1)} & \cdots & w_{n1}^{(1)} \\ w_{12}^{(1)} & w_{22}^{(1)} & \cdots & w_{n2}^{(1)} \\ \vdots & \vdots & \vdots & \vdots \\ w_{1k}^{(1)} & w_{2k}^{(1)} & \cdots & w_{nk}^{(1)} \\ \end{matrix} \right] \quad \boldsymbol b^{(1)} = \left [ \begin{matrix} b_{1}^{(1)} \\ b_{2}^{(1)} \\ \vdots \\ b_{k}^{(1)} \end{matrix} \right] \quad \boldsymbol z^{(1)} = \left [ \begin{matrix} z_{1}^{(1)} \\ z_{2}^{(1)} \\ \vdots \\ z_{k}^{(1)} \end{matrix} \right] \quad \boldsymbol o^{(1)} = \left [ \begin{matrix} o_{1}^{(1)} \\ o_{2}^{(1)} \\ \vdots \\ o_{k}^{(1)} \end{matrix} \right] w(1)=⎣⎢⎢⎢⎢⎡​w11(1)​w12(1)​⋮w1k(1)​​w21(1)​w22(1)​⋮w2k(1)​​⋯⋯⋮⋯​wn1(1)​wn2(1)​⋮wnk(1)​​⎦⎥⎥⎥⎥⎤​b(1)=⎣⎢⎢⎢⎢⎡​b1(1)​b2(1)​⋮bk(1)​​⎦⎥⎥⎥⎥⎤​z(1)=⎣⎢⎢⎢⎢⎡​z1(1)​z2(1)​⋮zk(1)​​⎦⎥⎥⎥⎥⎤​o(1)=⎣⎢⎢⎢⎢⎡​o1(1)​o2(1)​⋮ok(1)​​⎦⎥⎥⎥⎥⎤​w(2)=[w11(2)w21(2)⋯wk1(2)w12(2)w22(2)⋯wk2(2)⋮⋮⋮⋮w1k(2)w2k(2)⋯wkl(2)]b(2)=[b1(2)b2(2)⋮bl(2)]z(2)=[z1(2)z2(2)⋮zl(2)]o(2)=[o1(2)o2(2)⋮ol(2)]\boldsymbol w^{(2)} = \left [ \begin{matrix} w_{11}^{(2)} & w_{21}^{(2)} & \cdots & w_{k1}^{(2)} \\ w_{12}^{(2)} & w_{22}^{(2)} & \cdots & w_{k2}^{(2)} \\ \vdots & \vdots & \vdots & \vdots \\ w_{1k}^{(2)} & w_{2k}^{(2)} & \cdots & w_{kl}^{(2)} \\ \end{matrix} \right] \quad \boldsymbol b^{(2)} = \left [ \begin{matrix} b_1^{(2)} \\ b_2^{(2)} \\ \vdots \\ b_l^{(2)} \end{matrix} \right] \quad \boldsymbol z^{(2)} = \left [ \begin{matrix} z_1^{(2)} \\ z_2^{(2)} \\ \vdots \\ z_l^{(2)} \end{matrix} \right] \quad \boldsymbol o^{(2)} = \left [ \begin{matrix} o_1^{(2)} \\ o_2^{(2)} \\ \vdots \\ o_l^{(2)} \end{matrix} \right] w(2)=⎣⎢⎢⎢⎢⎡​w11(2)​w12(2)​⋮w1k(2)​​w21(2)​w22(2)​⋮w2k(2)​​⋯⋯⋮⋯​wk1(2)​wk2(2)​⋮wkl(2)​​⎦⎥⎥⎥⎥⎤​b(2)=⎣⎢⎢⎢⎢⎡​b1(2)​b2(2)​⋮bl(2)​​⎦⎥⎥⎥⎥⎤​z(2)=⎣⎢⎢⎢⎢⎡​z1(2)​z2(2)​⋮zl(2)​​⎦⎥⎥⎥⎥⎤​o(2)=⎣⎢⎢⎢⎢⎡​o1(2)​o2(2)​⋮ol(2)​​⎦⎥⎥⎥⎥⎤​w(3)=[w11(3)w21(3)⋯wl1(3)w12(3)w22(3)⋯wl2(3)⋮⋮⋮⋮w1k(3)w2k(3)⋯wlm(3)]b(3)=[b1(3)b2(3)⋮bm(3)]z(3)=[z1(3)z2(3)⋮zm(3)]o(3)=[o1(3)o2(3)⋮om(3)]\boldsymbol w^{(3)} = \left [ \begin{matrix} w_{11}^{(3)} & w_{21}^{(3)} & \cdots & w_{l1}^{(3)} \\ w_{12}^{(3)} & w_{22}^{(3)} & \cdots & w_{l2}^{(3)} \\ \vdots & \vdots & \vdots & \vdots \\ w_{1k}^{(3)} & w_{2k}^{(3)} & \cdots & w_{lm}^{(3)} \\ \end{matrix} \right] \quad \boldsymbol b^{(3)} = \left [ \begin{matrix} b_1^{(3)} \\ b_2^{(3)} \\ \vdots \\ b_m^{(3)} \end{matrix} \right] \quad \boldsymbol z^{(3)} = \left [ \begin{matrix} z_1^{(3)} \\ z_2^{(3)} \\ \vdots \\ z_m^{(3)} \end{matrix} \right] \quad \boldsymbol o^{(3)} = \left [ \begin{matrix} o_1^{(3)} \\ o_2^{(3)} \\ \vdots \\ o_m^{(3)} \end{matrix} \right] w(3)=⎣⎢⎢⎢⎢⎡​w11(3)​w12(3)​⋮w1k(3)​​w21(3)​w22(3)​⋮w2k(3)​​⋯⋯⋮⋯​wl1(3)​wl2(3)​⋮wlm(3)​​⎦⎥⎥⎥⎥⎤​b(3)=⎣⎢⎢⎢⎢⎡​b1(3)​b2(3)​⋮bm(3)​​⎦⎥⎥⎥⎥⎤​z(3)=⎣⎢⎢⎢⎢⎡​z1(3)​z2(3)​⋮zm(3)​​⎦⎥⎥⎥⎥⎤​o(3)=⎣⎢⎢⎢⎢⎡​o1(3)​o2(3)​⋮om(3)​​⎦⎥⎥⎥⎥⎤​其中wjk(i)w_{jk}^{(i)}wjk(i)​表示第iii层神经元前第jjj个输入源传达该层第kkk个神经元上的强度权重;bj(i)b_j^{(i)}bj(i)​表示第iii层第jjj个神经元的输入偏移量;zj(i)z_j^{(i)}zj(i)​表示第iii层第jjj个神经元的激励函数入参;oj(i)o_j^{(i)}oj(i)​表示第iii层第jjj个神经元的激励函数出参。最后一层神经元的出参也就是整个神经网络的输出向量,即o(3)=y^=[y^1y^2⋯y^m]\boldsymbol o^{(3)} = \hat \boldsymbol y = \left [ \begin{matrix} \hat y_1 & \hat y_2 & \cdots & \hat y_m \end{matrix} \right]o(3)=y^​=[y^​1​​y^​2​​⋯​y^​m​​]。第一层神经元的输入源即为整个神经网络的输入向量,即x=[x1x2⋯xn]\boldsymbol x = \left [ \begin{matrix} x_1 & x_2 & \cdots & x_n \end{matrix} \right]x=[x1​​x2​​⋯​xn​​]。

二、运算过程

基于上述定义,该神经网络的运算过程可表示为:
z(1)=w(1)⊗x+b(1),o(1)=f(z(1))\boldsymbol z^{(1)} =\boldsymbol w^{(1)} \otimes \boldsymbol x +\boldsymbol b^{(1)}, \quad \boldsymbol o^{(1)} = f(\boldsymbol z^{(1)}) z(1)=w(1)⊗x+b(1),o(1)=f(z(1))z(2)=w(2)⊗o(1)+b(2),o(2)=f(z(2))\boldsymbol z^{(2)} =\boldsymbol w^{(2)} \otimes \boldsymbol o^{(1)} +\boldsymbol b^{(2)}, \quad \boldsymbol o^{(2)} = f(\boldsymbol z^{(2)}) z(2)=w(2)⊗o(1)+b(2),o(2)=f(z(2))z(3)=w(3)⊗o(2)+b(3),o(3)=f(z(3))\boldsymbol z^{(3)} =\boldsymbol w^{(3)} \otimes \boldsymbol o^{(2)} +\boldsymbol b^{(3)}, \quad \boldsymbol o^{(3)} = f(\boldsymbol z^{(3)}) z(3)=w(3)⊗o(2)+b(3),o(3)=f(z(3))对于其中运算符“⊗”“\otimes”“⊗”,当将其定义为做内积时,就是普通BP网络;当将其定义为右边向量与左边矩阵各行向量求欧氏距时,就成了径向基网络(Radial Basis Funtion, RBF),一种有监督聚类网络,此时如果神经元的激励函数是高斯函数,则相当于其权重向量越是离输入向量距离近的神经元越会被激活。

三、训练过程

目前看来,神经网络运算的原理并不复杂,在实际应用中也确实不会对运算资源带来多大消耗。非常消耗计算资源的是网络的训练过程,其原理也相对复杂,需要使得其可以每次训练中找到不足的原因,并做出改进,直到可以对每个输入都能给出完美输出。本章将阐述如何定量输出与预想结果的不足,如何迭代改进各神经元的权重和偏移量使得神经网络最终输出预想结果。

(一)代价函数

此处定义代价函数为一个训练样本集中各样本训练误差的均值,用来定量描述每组训练中神经网络表现不佳的程度。设一个样本量为ttt的训练样本集合,其入参向量集为{x1,x2,⋯,xt}\{ \boldsymbol x_1, \boldsymbol x_2, \cdots , \boldsymbol x_t \}{x1​,x2​,⋯,xt​},其中每个入参是一个nnn维向量;对应的出参向量集为{y1,y2,⋯,yt}\{ \boldsymbol y_1, \boldsymbol y_2, \cdots , \boldsymbol y_t \}{y1​,y2​,⋯,yt​},其中每个出参是一个mmm维向量。则代价函数为:
J(w(1),w(2),w(3),b(1),b(2),b(3))=1t∑i=1tLiJ(\boldsymbol w^{(1)} , \boldsymbol w^{(2)} , \boldsymbol w^{(3)} , \boldsymbol b^{(1)} , \boldsymbol b^{(2)} , \boldsymbol b^{(3)}) = {1 \over t} \sum_{i=1}^t L_i J(w(1),w(2),w(3),b(1),b(2),b(3))=t1​i=1∑t​Li​其中LiL_iLi​表示第iii个训练样本的训练误差,为方便后续计算,将其定义为该样本出参向量与预测向量欧氏距平方的一半,即
Li(w(1),w(2),w(3),b(1),b(2),b(3))=12∥yi−y^i∥2=12∑j=1m(yij−y^ij)2L_i (\boldsymbol w^{(1)} , \boldsymbol w^{(2)} , \boldsymbol w^{(3)} , \boldsymbol b^{(1)} , \boldsymbol b^{(2)} , \boldsymbol b^{(3)}) = {1 \over 2} \| \boldsymbol y_i - \hat \boldsymbol y_i \|^2 = {1 \over 2} \sum_{j=1}^m (y_{ij} - \hat y_{ij})^2 Li​(w(1),w(2),w(3),b(1),b(2),b(3))=21​∥yi​−y^​i​∥2=21​j=1∑m​(yij​−y^​ij​)2由于代价函数就是一个由各层神经元各权重和各偏移量构成的多元函数,训练神经网络的目的在于找到一组使得该代价函数取得最小值的各层神经元权重矩阵和偏移向量。这里要引入梯度下降法。梯度就是由多元函数各自变量的偏导数组成的向量,一个自变量的偏导数反应了该自变量对函数值增长的影响度,所以一个多元函数的梯度指向的就是在多维空间中该函数值增长最快的方向,而反方向就是函数值下降最快的方向,沿此方向移动自变量,即所谓梯度下降。根据每次迭代训练计算代价函数梯度时所选用的训练样本集合不同,分为三种梯度下降方式:

  1. 批量梯度下降(Batch Gradient Descent,BGD)。即每次训练迭代时,使用当前训练样本集合的全集来计算代价函数梯度。
  2. 随机梯度下降(Stochastic Gradient Descent,SGD)。即每次训练迭代时,仅使用当前训练样本集合中的一个随机样本来计算代价函数梯度。
  3. 小批量梯度下降(Mini-Batch Gradient Descent, MBGD)。即每次训练迭代时,使用当前训练样本集合中的一个随机子集样本来计算代价函数梯度。此训练方案是上述两种方案的折中,一般效果最好。

(二)梯度推导

由上述看来,训练神经网络的关键是先求得其代价函数的梯度,才能沿梯度下降方向一步步优化网络参数。由于和的导数就等于导数的和,即
ddx[f1(x)+f2(x)]=ddxf1(x)+ddxf2(x){d \over dx}[f_1(x)+f_2(x)] = {d \over dx}f_1(x) + {d \over dx}f_2(x) dxd​[f1​(x)+f2​(x)]=dxd​f1​(x)+dxd​f2​(x)由于梯度是各偏导数组成的向量,因此同样适用上述性质,因而
∇J=1t∑i=1t∇Li\nabla J = {1 \over t} \sum_{i=1}^t \nabla L_i ∇J=t1​i=1∑t​∇Li​对于其中任一个训练样本,其误差函数的梯度为:
∇L={∂L∂w(1)∂L∂w(2)∂L∂w(3)∂L∂b(1)∂L∂b(2)∂L∂b(3)}\nabla L = \left \{ \begin{matrix} {\partial L \over \partial \boldsymbol w^{(1)}} & {\partial L \over \partial \boldsymbol w^{(2)}} & {\partial L \over \partial \boldsymbol w^{(3)}} & {\partial L \over \partial \boldsymbol b^{(1)}} & {\partial L \over \partial \boldsymbol b^{(2)}} & {\partial L \over \partial \boldsymbol b^{(3)}} \end{matrix} \right \} ∇L={∂w(1)∂L​​∂w(2)∂L​​∂w(3)∂L​​∂b(1)∂L​​∂b(2)∂L​​∂b(3)∂L​​}上式之所以用大括号是因为其中每个元素又是一个矩阵或向量。首先考虑各权重矩阵的偏导。以第一层神经元为例,由前述神经网络运算过程可知,w1q(1)∼wnq(1)w_{1q}^{(1)} \sim w_{nq}^{(1)}w1q(1)​∼wnq(1)​都仅是zq(1)z_q^{(1)}zq(1)​的自变量,而zq(1)z_q^{(1)}zq(1)​又是LLL的自变量。对于嵌套函数f[g(x)]f[g(x)]f[g(x)],其导数为
dfdx=dfdgdgdx{df \over dx} = {df \over dg} {dg \over dx} dxdf​=dgdf​dxdg​换成偏导数同样适用此法则,因此
∂L∂wpq(1)=∂L∂zq(1)∂zq(1)∂wpq(1){\partial L \over \partial w_{pq}^{(1)}} = {\partial L \over \partial z_q^{(1)}} {\partial z_q^{(1)} \over \partial w_{pq}^{(1)}} ∂wpq(1)​∂L​=∂zq(1)​∂L​∂wpq(1)​∂zq(1)​​以此类推有
∂L∂w(1)=[∂L∂w11(1)∂L∂w21(1)⋯∂L∂wn1(1)∂L∂w12(1)∂L∂w22(1)⋯∂L∂wn2(1)⋮⋮⋮⋮∂L∂w1k(1)∂L∂w2k(1)⋯∂L∂wnk(1)]=[∂L∂z1(1)∂z1(1)∂w11(1)∂L∂z1(1)∂z1(1)∂w21(1)⋯∂L∂z1(1)∂z1(1)∂wn1(1)∂L∂z2(1)∂z2(1)∂w12(1)∂L∂z2(1)∂z2(1)∂w22(1)⋯∂L∂z2(1)∂z2(1)∂wn2(1)⋮⋮⋮⋮∂L∂zk(1)∂zk(1)∂w1k(1)∂L∂zk(1)∂zk(1)∂w2k(1)⋯∂L∂zk(1)∂zk(1)∂wnk(1)]{\partial L \over \partial \boldsymbol w^{(1)}} = \left [ \begin{matrix} {\partial L \over \partial w_{11}^{(1)}} & {\partial L \over \partial w_{21}^{(1)}} & \cdots & {\partial L \over \partial w_{n1}^{(1)}} \\ {\partial L \over \partial w_{12}^{(1)}} & {\partial L \over \partial w_{22}^{(1)}} & \cdots & {\partial L \over \partial w_{n2}^{(1)}} \\ \vdots & \vdots & \vdots & \vdots \\ {\partial L \over \partial w_{1k}^{(1)}} & {\partial L \over \partial w_{2k}^{(1)}} & \cdots & {\partial L \over \partial w_{nk}^{(1)}} \\ \end{matrix} \right] = \left [ \begin{matrix} {\partial L \over \partial z_1^{(1)}} {\partial z_1^{(1)} \over \partial w_{11}^{(1)}} & {\partial L \over \partial z_1^{(1)}} {\partial z_1^{(1)} \over \partial w_{21}^{(1)}} & \cdots & {\partial L \over \partial z_1^{(1)}} {\partial z_1^{(1)} \over \partial w_{n1}^{(1)}} \\ {\partial L \over \partial z_2^{(1)}} {\partial z_2^{(1)} \over \partial w_{12}^{(1)}} & {\partial L \over \partial z_2^{(1)}} {\partial z_2^{(1)} \over \partial w_{22}^{(1)}} & \cdots & {\partial L \over \partial z_2^{(1)}} {\partial z_2^{(1)} \over \partial w_{n2}^{(1)}} \\ \vdots & \vdots & \vdots & \vdots \\ {\partial L \over \partial z_k^{(1)}} {\partial z_k^{(1)} \over \partial w_{1k}^{(1)}} & {\partial L \over \partial z_k^{(1)}} {\partial z_k^{(1)} \over \partial w_{2k}^{(1)}} & \cdots & {\partial L \over \partial z_k^{(1)}} {\partial z_k^{(1)} \over \partial w_{nk}^{(1)}} \\ \end{matrix} \right] ∂w(1)∂L​=⎣⎢⎢⎢⎢⎢⎡​∂w11(1)​∂L​∂w12(1)​∂L​⋮∂w1k(1)​∂L​​∂w21(1)​∂L​∂w22(1)​∂L​⋮∂w2k(1)​∂L​​⋯⋯⋮⋯​∂wn1(1)​∂L​∂wn2(1)​∂L​⋮∂wnk(1)​∂L​​⎦⎥⎥⎥⎥⎥⎤​=⎣⎢⎢⎢⎢⎢⎢⎢⎡​∂z1(1)​∂L​∂w11(1)​∂z1(1)​​∂z2(1)​∂L​∂w12(1)​∂z2(1)​​⋮∂zk(1)​∂L​∂w1k(1)​∂zk(1)​​​∂z1(1)​∂L​∂w21(1)​∂z1(1)​​∂z2(1)​∂L​∂w22(1)​∂z2(1)​​⋮∂zk(1)​∂L​∂w2k(1)​∂zk(1)​​​⋯⋯⋮⋯​∂z1(1)​∂L​∂wn1(1)​∂z1(1)​​∂z2(1)​∂L​∂wn2(1)​∂z2(1)​​⋮∂zk(1)​∂L​∂wnk(1)​∂zk(1)​​​⎦⎥⎥⎥⎥⎥⎥⎥⎤​由前述神经网络运算过程可知,其中
∂z1(1)∂wp1(1)=∂z2(1)∂wp2(1)=⋯=∂zk(1)∂wpk(1)=xp{\partial z_1^{(1)} \over \partial w_{p1}^{(1)}} = {\partial z_2^{(1)} \over \partial w_{p2}^{(1)}} = \cdots = {\partial z_k^{(1)} \over \partial w_{pk}^{(1)}} = x_p ∂wp1(1)​∂z1(1)​​=∂wp2(1)​∂z2(1)​​=⋯=∂wpk(1)​∂zk(1)​​=xp​所以
∂L∂w(1)=[∂L∂z1(1)∂L∂z2(1)⋮∂L∂zk(1)][∂zq(1)∂w1q(1)∂zq(1)∂w2q(1)⋯∂zq(1)∂wnq(1)]=[∂L∂z1(1)∂L∂z2(1)⋮∂L∂zk(1)][x1x2⋯xn]=δ(1)xT{\partial L \over \partial \boldsymbol w^{(1)}} = \left [ \begin{matrix} {\partial L \over \partial z_1^{(1)}} \\ {\partial L \over \partial z_2^{(1)}} \\ \vdots \\ {\partial L \over \partial z_k^{(1)}} \end{matrix} \right] \left [ \begin{matrix} {\partial z_q^{(1)} \over \partial w_{1q}^{(1)}} & {\partial z_q^{(1)} \over \partial w_{2q}^{(1)}} & \cdots & {\partial z_q^{(1)} \over \partial w_{nq}^{(1)}} \end{matrix} \right] = \left [ \begin{matrix} {\partial L \over \partial z_1^{(1)}} \\ {\partial L \over \partial z_2^{(1)}} \\ \vdots \\ {\partial L \over \partial z_k^{(1)}} \end{matrix} \right] \left [ \begin{matrix} x_1 & x_2 & \cdots & x_n \end{matrix} \right] = \boldsymbol \delta^{(1)} \boldsymbol x^T ∂w(1)∂L​=⎣⎢⎢⎢⎢⎢⎡​∂z1(1)​∂L​∂z2(1)​∂L​⋮∂zk(1)​∂L​​⎦⎥⎥⎥⎥⎥⎤​[∂w1q(1)​∂zq(1)​​​∂w2q(1)​∂zq(1)​​​⋯​∂wnq(1)​∂zq(1)​​​]=⎣⎢⎢⎢⎢⎢⎡​∂z1(1)​∂L​∂z2(1)​∂L​⋮∂zk(1)​∂L​​⎦⎥⎥⎥⎥⎥⎤​[x1​​x2​​⋯​xn​​]=δ(1)xT同理可得
∂L∂w(2)=δ(2)(o(1))T{\partial L \over \partial \boldsymbol w^{(2)}} = \boldsymbol \delta^{(2)} (\boldsymbol o^{(1)})^T ∂w(2)∂L​=δ(2)(o(1))T∂L∂w(3)=δ(3)(o(2))T{\partial L \over \partial \boldsymbol w^{(3)}} = \boldsymbol \delta^{(3)} (\boldsymbol o^{(2)})^T ∂w(3)∂L​=δ(3)(o(2))T再考虑各偏移向量的偏导数。以第一层神经元为例,由前述神经网络运算过程可知,bq(1)b_q^{(1)}bq(1)​仅是zq(1)z_q^{(1)}zq(1)​的自变量,所以有
∂L∂bq(1)=∂L∂zq(1)∂zq(1)∂bq(1)=∂L∂zq(1){\partial L \over \partial b_q^{(1)}} = {\partial L \over \partial z_q^{(1)}} {\partial z_q^{(1)} \over \partial b_q^{(1)}} = {\partial L \over \partial z_q^{(1)}} ∂bq(1)​∂L​=∂zq(1)​∂L​∂bq(1)​∂zq(1)​​=∂zq(1)​∂L​因为其中∂zq(1)∂bq(1)=1{\partial z_q^{(1)} \over \partial b_q^{(1)}} = 1∂bq(1)​∂zq(1)​​=1。以此类推有
∂L∂b(1)=[∂L∂b1(1)∂L∂b2(1)⋮∂L∂bk(1)]=[∂L∂z1(1)∂L∂z2(1)⋮∂L∂zk(1)]=δ(1){\partial L \over \partial \boldsymbol b^{(1)}} = \left [ \begin{matrix} {\partial L \over \partial b_1^{(1)}} \\ {\partial L \over \partial b_2^{(1)}} \\ \vdots \\ {\partial L \over \partial b_k^{(1)}} \end{matrix} \right] = \left [ \begin{matrix} {\partial L \over \partial z_1^{(1)}} \\ {\partial L \over \partial z_2^{(1)}} \\ \vdots \\ {\partial L \over \partial z_k^{(1)}} \end{matrix} \right] = \boldsymbol \delta^{(1)} ∂b(1)∂L​=⎣⎢⎢⎢⎢⎢⎡​∂b1(1)​∂L​∂b2(1)​∂L​⋮∂bk(1)​∂L​​⎦⎥⎥⎥⎥⎥⎤​=⎣⎢⎢⎢⎢⎢⎡​∂z1(1)​∂L​∂z2(1)​∂L​⋮∂zk(1)​∂L​​⎦⎥⎥⎥⎥⎥⎤​=δ(1)同理可得
∂L∂b(2)=δ(2){\partial L \over \partial \boldsymbol b^{(2)}} = \boldsymbol \delta^{(2)} ∂b(2)∂L​=δ(2)∂L∂b(3)=δ(3){\partial L \over \partial \boldsymbol b^{(3)}} = \boldsymbol \delta^{(3)} ∂b(3)∂L​=δ(3)综上所述,任一组训练样本的误差函数梯度为:
∇L={δ(1)xTδ(2)(o(1))Tδ(3)(o(2))Tδ(1)δ(2)δ(3)}\nabla L = \left \{ \begin{matrix} \boldsymbol \delta^{(1)} \boldsymbol x^T & \boldsymbol \delta^{(2)} (\boldsymbol o^{(1)})^T & \boldsymbol \delta^{(3)} (\boldsymbol o^{(2)})^T & \boldsymbol \delta^{(1)} & \boldsymbol \delta^{(2)} & \boldsymbol \delta^{(3)} \end{matrix} \right \} ∇L={δ(1)xT​δ(2)(o(1))T​δ(3)(o(2))T​δ(1)​δ(2)​δ(3)​}此时可以发现计算梯度的关键是计算出δ(i)\boldsymbol \delta^{(i)}δ(i),它被称为第iii层神经元的灵敏度,反映了第iii层神经元对网络总输出误差的影响程度。仍以第一层神经元为例,由于z1(1)z_1^{(1)}z1(1)​仅是一个激励函数的入参,即仅是o1(1)o_1^{(1)}o1(1)​的自变量,而o1(1)o_1^{(1)}o1(1)​是下一层所有神经元激励函数入参z(2)\boldsymbol z^{(2)}z(2)的自变量。对于一个多元嵌套函数,其偏导计算公式为:
∂∂x1f[g1(x1,x2),g2(x1,x2)]=∂f∂g1∂g1∂x1+∂f∂g2∂g2∂x1{\partial \over \partial x_1}f[g_1(x_1,x_2),g_2(x_1,x_2)] = {\partial f \over \partial g_1}{\partial g_1 \over \partial x_1} + {\partial f \over \partial g_2}{\partial g_2 \over \partial x_1} ∂x1​∂​f[g1​(x1​,x2​),g2​(x1​,x2​)]=∂g1​∂f​∂x1​∂g1​​+∂g2​∂f​∂x1​∂g2​​所以有
∂L∂z1(1)=∂L∂o1(1)∂o1(1)∂z1(1)=∂L∂o1(1)f′(z1(1))=f′(z1(1))[∂L∂z1(2)∂z1(2)∂o1(1)+∂L∂z2(2)∂z2(2)∂o1(1)+⋯+∂L∂zl(2)∂zl(2)∂o1(1)]{\partial L \over \partial z_1^{(1)}} = {\partial L \over \partial o_1^{(1)}} {\partial o_1^{(1)} \over \partial z_1^{(1)}} = {\partial L \over \partial o_1^{(1)}} f'(z_1^{(1)}) = f'(z_1^{(1)}) \left [ {\partial L \over \partial z_1^{(2)}} {\partial z_1^{(2)} \over \partial o_1^{(1)}} + {\partial L \over \partial z_2^{(2)}} {\partial z_2^{(2)} \over \partial o_1^{(1)}} + \cdots + {\partial L \over \partial z_l^{(2)}} {\partial z_l^{(2)} \over \partial o_1^{(1)}}\right] ∂z1(1)​∂L​=∂o1(1)​∂L​∂z1(1)​∂o1(1)​​=∂o1(1)​∂L​f′(z1(1)​)=f′(z1(1)​)[∂z1(2)​∂L​∂o1(1)​∂z1(2)​​+∂z2(2)​∂L​∂o1(1)​∂z2(2)​​+⋯+∂zl(2)​∂L​∂o1(1)​∂zl(2)​​]以此类推
δ(1)=∂L∂z(1)=[∂L∂z1(1)∂L∂z2(1)⋮∂L∂zk(1)]=[f′(z1(1))f′(z2(1))⋱f′(zk(1))][∂z1(2)∂o1(1)∂z2(2)∂o1(1)⋯∂zl(2)∂o1(1)∂z1(2)∂o2(1)∂z2(2)∂o2(1)⋯∂zl(2)∂o2(1)⋮⋮⋮⋮∂z1(2)∂ok(1)∂z2(2)∂ok(1)⋯∂zl(2)∂ok(1)][∂L∂z1(2)∂L∂z2(2)⋮∂L∂zl(2)]\boldsymbol \delta^{(1)} = {\partial L \over \partial \boldsymbol z^{(1)}} = \left [ \begin{matrix} {\partial L \over \partial z_1^{(1)}} \\ {\partial L \over \partial z_2^{(1)}} \\ \vdots \\ {\partial L \over \partial z_k^{(1)}} \end{matrix} \right] = \left [ \begin{matrix} f'(z_1^{(1)}) & & & \\ & f'(z_2^{(1)}) & & \\ & & \ddots & \\ & & & f'(z_k^{(1)}) \end{matrix} \right] \left [ \begin{matrix} {\partial z_1^{(2)} \over \partial o_1^{(1)}} & {\partial z_2^{(2)} \over \partial o_1^{(1)}} & \cdots & {\partial z_l^{(2)} \over \partial o_1^{(1)}} \\ {\partial z_1^{(2)} \over \partial o_2^{(1)}} & {\partial z_2^{(2)} \over \partial o_2^{(1)}} & \cdots & {\partial z_l^{(2)} \over \partial o_2^{(1)}} \\ \vdots & \vdots & \vdots & \vdots \\ {\partial z_1^{(2)} \over \partial o_k^{(1)}} & {\partial z_2^{(2)} \over \partial o_k^{(1)}} & \cdots & {\partial z_l^{(2)} \over \partial o_k^{(1)}} \\ \end{matrix} \right] \left [ \begin{matrix} {\partial L \over \partial z_1^{(2)}} \\ {\partial L \over \partial z_2^{(2)}} \\ \vdots \\ {\partial L \over \partial z_l^{(2)}} \end{matrix} \right] δ(1)=∂z(1)∂L​=⎣⎢⎢⎢⎢⎢⎡​∂z1(1)​∂L​∂z2(1)​∂L​⋮∂zk(1)​∂L​​⎦⎥⎥⎥⎥⎥⎤​=⎣⎢⎢⎢⎡​f′(z1(1)​)​f′(z2(1)​)​⋱​f′(zk(1)​)​⎦⎥⎥⎥⎤​⎣⎢⎢⎢⎢⎢⎢⎢⎡​∂o1(1)​∂z1(2)​​∂o2(1)​∂z1(2)​​⋮∂ok(1)​∂z1(2)​​​∂o1(1)​∂z2(2)​​∂o2(1)​∂z2(2)​​⋮∂ok(1)​∂z2(2)​​​⋯⋯⋮⋯​∂o1(1)​∂zl(2)​​∂o2(1)​∂zl(2)​​⋮∂ok(1)​∂zl(2)​​​⎦⎥⎥⎥⎥⎥⎥⎥⎤​⎣⎢⎢⎢⎢⎢⎡​∂z1(2)​∂L​∂z2(2)​∂L​⋮∂zl(2)​∂L​​⎦⎥⎥⎥⎥⎥⎤​即
δ(1)=∂o(1)∂z(1)∂z(2)∂o(1)∂L∂z(2)=diag[f′(z(1))](w(2))Tδ(2)\boldsymbol \delta^{(1)} = {\partial \boldsymbol o^{(1)} \over \partial \boldsymbol z^{(1)}} {\partial \boldsymbol z^{(2)} \over \partial \boldsymbol o^{(1)}} {\partial \boldsymbol L \over \partial \boldsymbol z^{(2)}} = diag[f'(\boldsymbol z^{(1)})] (\boldsymbol w^{(2)})^T \boldsymbol \delta^{(2)} δ(1)=∂z(1)∂o(1)​∂o(1)∂z(2)​∂z(2)∂L​=diag[f′(z(1))](w(2))Tδ(2)同理可得
δ(2)=∂o(2)∂z(2)∂z(3)∂o(2)∂L∂z(3)=diag[f′(z(2))](w(3))Tδ(3)\boldsymbol \delta^{(2)} = {\partial \boldsymbol o^{(2)} \over \partial \boldsymbol z^{(2)}} {\partial \boldsymbol z^{(3)} \over \partial \boldsymbol o^{(2)}} {\partial \boldsymbol L \over \partial \boldsymbol z^{(3)}} = diag[f'(\boldsymbol z^{(2)})] (\boldsymbol w^{(3)})^T \boldsymbol \delta^{(3)} δ(2)=∂z(2)∂o(2)​∂o(2)∂z(3)​∂z(3)∂L​=diag[f′(z(2))](w(3))Tδ(3)可见每一层神经元的灵敏度都是前一层神经元灵敏度的自变量(即入参),这也就体现了误差从输出层反向传播到输入层,故而称这种网络为反向传播网络。正是因为这个特性,使得无论有多少层神经元,只要知道了最后一层输出层神经元的灵敏度,就可以很方便的迭代出各层神经元的灵敏度。在本文所举例的三层BPNN中,输出层的灵敏度为:
δ(3)=∂L∂z(3)=[∂L∂z1(3)∂L∂z2(3)⋮∂L∂zm(3)]\boldsymbol \delta^{(3)} = {\partial \boldsymbol L \over \partial \boldsymbol z^{(3)}} = \left [ \begin{matrix} {\partial L \over \partial z_1^{(3)}} \\ {\partial L \over \partial z_2^{(3)}} \\ \vdots \\ {\partial L \over \partial z_m^{(3)}} \end{matrix} \right] δ(3)=∂z(3)∂L​=⎣⎢⎢⎢⎢⎢⎡​∂z1(3)​∂L​∂z2(3)​∂L​⋮∂zm(3)​∂L​​⎦⎥⎥⎥⎥⎥⎤​其中,由于z1(3)z_1^{(3)}z1(3)​仅是o1(3)o_1^{(3)}o1(3)​(即y^1\hat y_1y^​1​)的自变量,而y^1\hat y_1y^​1​又仅是误差函数LLL的自变量,所以有
∂L∂z1(3)=∂L∂o1(3)∂o1(3)∂z1(3)=∂L∂y^1f′(z1(3))=(y1−y^1)f′(z1(3)){\partial L \over \partial z_1^{(3)}} = {\partial L \over \partial o_1^{(3)}} {\partial o_1^{(3)} \over \partial z_1^{(3)}} = {\partial L \over \partial \hat y_1} f'(z_1^{(3)}) = (y_1-\hat y_1) f'(z_1^{(3)}) ∂z1(3)​∂L​=∂o1(3)​∂L​∂z1(3)​∂o1(3)​​=∂y^​1​∂L​f′(z1(3)​)=(y1​−y^​1​)f′(z1(3)​)以此类推
δ(3)=∂L∂z(3)=[∂L∂z1(3)∂L∂z2(3)⋮∂L∂zm(3)]=[f′(z1(3))f′(z2(3))⋱f′(zm(3))][y1−y^1y2−y^2⋮ym−y^m]=diag[f′(z(3))](y−y^)\boldsymbol \delta^{(3)} = {\partial \boldsymbol L \over \partial \boldsymbol z^{(3)}} = \left [ \begin{matrix} {\partial L \over \partial z_1^{(3)}} \\ {\partial L \over \partial z_2^{(3)}} \\ \vdots \\ {\partial L \over \partial z_m^{(3)}} \end{matrix} \right] = \left [ \begin{matrix} f'(z_1^{(3)}) & & & \\ & f'(z_2^{(3)}) & & \\ & & \ddots & \\ & & & f'(z_m^{(3)}) \end{matrix} \right] \left [ \begin{matrix} y_1-\hat y_1 \\ y_2-\hat y_2 \\ \vdots \\ y_m-\hat y_m \end{matrix} \right] = diag[f'(\boldsymbol z^{(3)})] (\boldsymbol y-\hat \boldsymbol y) δ(3)=∂z(3)∂L​=⎣⎢⎢⎢⎢⎢⎡​∂z1(3)​∂L​∂z2(3)​∂L​⋮∂zm(3)​∂L​​⎦⎥⎥⎥⎥⎥⎤​=⎣⎢⎢⎢⎡​f′(z1(3)​)​f′(z2(3)​)​⋱​f′(zm(3)​)​⎦⎥⎥⎥⎤​⎣⎢⎢⎢⎡​y1​−y^​1​y2​−y^​2​⋮ym​−y^​m​​⎦⎥⎥⎥⎤​=diag[f′(z(3))](y−y^​)至此,误差函数的梯度公式可以进一步细化为
∇L={∂L∂w(1)∂L∂w(2)∂L∂w(3)∂L∂b(1)∂L∂b(2)∂L∂b(3)}={δ(1)xTδ(2)(o(1))Tδ(3)(o(2))Tδ(1)δ(2)δ(3)}={diag[f′(z(1))](w(2))Tdiag[f′(z(2))](w(3))Tdiag[f′(z(3))](y−y^)xTdiag[f′(z(2))](w(3))Tdiag[f′(z(3))](y−y^)(o(1))Tdiag[f′(z(3))](y−y^)(o(2))Tdiag[f′(z(1))](w(2))Tdiag[f′(z(2))](w(3))Tdiag[f′(z(3))](y−y^)diag[f′(z(2))](w(3))Tdiag[f′(z(3))](y−y^)diag[f′(z(3))](y−y^)}\nabla L = \left \{ \begin{matrix} {\partial L \over \partial \boldsymbol w^{(1)}} \\ {\partial L \over \partial \boldsymbol w^{(2)}} \\ {\partial L \over \partial \boldsymbol w^{(3)}} \\ {\partial L \over \partial \boldsymbol b^{(1)}} \\ {\partial L \over \partial \boldsymbol b^{(2)}} \\ {\partial L \over \partial \boldsymbol b^{(3)}} \end{matrix} \right \} = \left \{ \begin{matrix} \boldsymbol \delta^{(1)} \boldsymbol x^T \\ \boldsymbol \delta^{(2)} (\boldsymbol o^{(1)})^T \\ \boldsymbol \delta^{(3)} (\boldsymbol o^{(2)})^T \\ \boldsymbol \delta^{(1)} \\ \boldsymbol \delta^{(2)} \\ \boldsymbol \delta^{(3)} \end{matrix} \right \} = \left \{ \begin{matrix} diag[f'(\boldsymbol z^{(1)})] (\boldsymbol w^{(2)})^T diag[f'(\boldsymbol z^{(2)})] (\boldsymbol w^{(3)})^T diag[f'(\boldsymbol z^{(3)})] (\boldsymbol y-\hat \boldsymbol y) \boldsymbol x^T \\ diag[f'(\boldsymbol z^{(2)})] (\boldsymbol w^{(3)})^T diag[f'(\boldsymbol z^{(3)})] (\boldsymbol y-\hat \boldsymbol y) (\boldsymbol o^{(1)})^T \\ diag[f'(\boldsymbol z^{(3)})] (\boldsymbol y-\hat \boldsymbol y) (\boldsymbol o^{(2)})^T \\ diag[f'(\boldsymbol z^{(1)})] (\boldsymbol w^{(2)})^T diag[f'(\boldsymbol z^{(2)})] (\boldsymbol w^{(3)})^T diag[f'(\boldsymbol z^{(3)})] (\boldsymbol y-\hat \boldsymbol y) \\ diag[f'(\boldsymbol z^{(2)})] (\boldsymbol w^{(3)})^T diag[f'(\boldsymbol z^{(3)})] (\boldsymbol y-\hat \boldsymbol y) \\ diag[f'(\boldsymbol z^{(3)})] (\boldsymbol y-\hat \boldsymbol y) \end{matrix} \right \} ∇L=⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧​∂w(1)∂L​∂w(2)∂L​∂w(3)∂L​∂b(1)∂L​∂b(2)∂L​∂b(3)∂L​​⎭⎪⎪⎪⎪⎪⎪⎪⎬⎪⎪⎪⎪⎪⎪⎪⎫​=⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧​δ(1)xTδ(2)(o(1))Tδ(3)(o(2))Tδ(1)δ(2)δ(3)​⎭⎪⎪⎪⎪⎪⎪⎪⎬⎪⎪⎪⎪⎪⎪⎪⎫​=⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧​diag[f′(z(1))](w(2))Tdiag[f′(z(2))](w(3))Tdiag[f′(z(3))](y−y^​)xTdiag[f′(z(2))](w(3))Tdiag[f′(z(3))](y−y^​)(o(1))Tdiag[f′(z(3))](y−y^​)(o(2))Tdiag[f′(z(1))](w(2))Tdiag[f′(z(2))](w(3))Tdiag[f′(z(3))](y−y^​)diag[f′(z(2))](w(3))Tdiag[f′(z(3))](y−y^​)diag[f′(z(3))](y−y^​)​⎭⎪⎪⎪⎪⎪⎪⎬⎪⎪⎪⎪⎪⎪⎫​设其中激活函数选用Sigmoid函数,即f(x)=11+e−xf(x) = {1 \over 1+e^{-x}}f(x)=1+e−x1​,所以有
f′(x)=(1+e−x)−2e−x=f(x)[1−f(x)]f'(x) = (1+e^{-x})^{-2} e^{-x} = f(x)[1-f(x)] f′(x)=(1+e−x)−2e−x=f(x)[1−f(x)]故而
f′(z(i))=diag[f(z(i))][1−f(z(i))]=diag(o(i))(1−o(i))f'(\boldsymbol z^{(i)}) = diag[f(\boldsymbol z^{(i)})][1-f(\boldsymbol z^{(i)})] = diag(\boldsymbol o^{(i)})(1-\boldsymbol o^{(i)}) f′(z(i))=diag[f(z(i))][1−f(z(i))]=diag(o(i))(1−o(i))至此,训练样本集合中的每个训练样本的误差函数梯度(∇L\nabla L∇L)都可以计算出来了,将其都代入前述代价函数梯度公式(即算数平均值)即得到代价函数梯度。

(三)迭代训练

由于梯度描述的是函数值增长最快的方向,因此为找到使代价函数取得最小值的点,则需沿梯度反方向前进,即向所谓梯度下降方向对所有权重和偏移量做一小步调整,此调整的大小称为迭代步长。然后再重新计算当前梯度,再做反向调整,周而复始直到梯度值足够小为止。

四、归一化

归一化是一个线性变化过程。即用向量的每一个值除以这些向量的一种共同线性范数(一般可取模长、最大坐标、或是最大与最小坐标间距等),还可再做一些相对平移使其中心化。即通过线性变换将每个量都统一成同一单位(或无量纲值,如占比),从而可以互相进行比较。这就如同要比较一个学生俩次成绩的好坏,不应该比较分数,而应该比较名次一样。
由各响应函数的图像可见,神经网络的各输出值范围只能是在[0,1][0,1][0,1]或[−1,1][-1,1][−1,1]之间,故而要用于处理实际问题时也必须对出参做归一化处理。对训练样本出参可做归一化处理如下:
y。=y−min⁡(y)max⁡(y)−min⁡(y)\boldsymbol y^。= {\boldsymbol y - \min(\boldsymbol y) \over \max(\boldsymbol y) - \min(\boldsymbol y)} y。=max(y)−min(y)y−min(y)​其中y\boldsymbol yy为一个训练样本出参向量,y。\boldsymbol y^。y。为归一化后的该训练样本出参向量。当已知待估计结果的值域后,如上限和下限向量分别为m\boldsymbol mm和n\boldsymbol nn,则对神经网络估计出的结果须做反归一化处理:
y^=y^。(m−n)+n\hat \boldsymbol y = \hat \boldsymbol y^。(\boldsymbol m - \boldsymbol n) + \boldsymbol n y^​=y^​。(m−n)+n其中y^。\hat \boldsymbol y^。y^​。为整个神经网络输出的估计向量,本身就是归一化的。

五、应用示例

此例中根据给定的平面直角坐标系上的一系列点来训练一个BPNN,从而拟合出一条曲线。使用python语言实现。

(一)数据准备

import numpy as np
import matplotlib.pyplot as plt# 定义一个函数
def f(x):return np.sin(x) + 0.5*x# 利用上述函数创建出一系列离散点,并绘制出图像
x = np.linspace(-2*np.pi, 2*np.pi, 50)
y = f(x)
plt.figure(2)
plt.plot(x, y, 'b.', label='points')
plt.legend(loc=2)


上图就是此例中要拟合的离散点,也是训练数据。

(二)定义网络

根据前述神经网络原理,可定义一个BPNN类如下:

import numpy as np
import numpy.matlib as matlib
import matplotlib.pyplot as plt
import numpy.random as randomclass BPANN:'''1、定义一个各层神经元个数分别为neures=[k1, k2, ...]的神经网络,其中k1为作为输入的神经元个数。2、初始化入参W为由各神经元层之间的连接权重矩阵构成的数组;初始化入参B为由各神经元层的偏移向量构成的数组。均默认为随机数。3、每个神经元的激活函数默认为是Sigmoid函数。'''def __init__(self, neures, W=None, B=None):self.neures = neuresself.W = []         # The weight matrixes of each neures layerself.B = []         # The bias vectors of each neures layer# Construct the matrixes of weight and bias of each neures layerfor i in range(1, len(neures)):# A matrix of random floats follow standard normal distributionself.W.append(matlib.randn(neures[i], neures[i-1]))self.B.append(matlib.randn(neures[i], 1))# Activation function (Sigmoid function) of each neuresdef actFun(self, x):       # The input x should be considered as a vectorreturn 1/(1+np.exp(-x))# Use the ANN to get the outputdef work(self, X, lowerLimit=0, upperLimit=1):# Check the type of input parametersif not all(isinstance(x, float) or isinstance(x, int) for x in X):raise Exception("the type of parameters must be float or int list")else:self.Z = []         # The input vectors of each neures layer self.O = []         # The output vectors of each neures layer# The input vector of first neure layerself.Z.append(self.W[0]*np.matrix(X).T + self.B[0])# The output vector of first neure layerself.O.append(self.actFun(self.Z[-1]))for (w, b) in zip(self.W[1:], self.B[1:]):self.Z.append(w*self.O[-1] + b)self.O.append(self.actFun(self.Z[-1]))# Renormalize and change the type of output to a listreturn list((self.O[-1]*(upperLimit-lowerLimit)+lowerLimit).A1)# Train the ANN    def train(self, sampleX, sampleY, step, iterations=100, size=0):'''sampleX:所有不重复训练样本的入参,一个一维列表。sampleY:所有不重复训练样本的出参,一个与上述入参同长度的一维列表。step:每次训练迭代的步长,一个(0, 10)的数值。iterations:训练迭代倍数,乘以样本子集个数即为训练迭代次数,是一个大于零的整数,默认是30倍。size:每次迭代从样本全集中抽取的样本子集大小,最小值是1,最大值不超过样本全集大小(若超过或值为0则取样本全集大小)的整数。'''# The indices list of the samples in the universal setself.uniSetIndices = list(range(0,len(sampleX)))random.shuffle(self.uniSetIndices)  # Disrupt the sort of samples# The list of the indices lists of the samples in each subsetself.subsetsIndices = []if size > 0 and size <= len(sampleX):i = 0while i < len(sampleX):self.subsetsIndices.append(self.uniSetIndices[i: i+size])i = i + sizeelse:self.subsetsIndices = [self.uniSetIndices]# Normalize the sampleYSYM = np.matrix(sampleY)
# Use each colume's max data to consturct a matrix that has the same shapemaxData = matlib.ones((SYM.shape[0], 1)) * np.amax(SYM, axis=0)
# Use each colume's min data to consturct a matrix that has the same shape minData = matlib.ones((SYM.shape[0], 1)) * np.amin(SYM, axis=0)
# Normal = (original - min)/(max - min)sampleY = np.nan_to_num(np.divide((SYM - minData),(maxData - minData)), nan=1).tolist()# The list of loss expectation of each iteration's eache subsetJ = []# The number of neures layers include the input layerlayerNum = len(self.neures)while iterations > 0:                # Dispose each subsetfor sI in self.subsetsIndices:# The partial derivative matrixes of each neures layer's weightself.dW = []# The partial derivative vectors of each neures layer's biasself.dB = []# Construct all weight and bias matrixex filled by zerofor i in range(1, layerNum):self.dW.append(matlib.zeros((self.neures[i], self.neures[i-1])))self.dB.append(matlib.zeros((self.neures[i], 1)))# Dispose each sample in the subsetL = 0             # The loss of all samples in the subsetsubSize = len(sI) # The size of the subsetfor i in sI:# The sensitivity vectors of each neures layerDelta = []# Get the current state of the ANNY = np.matrix(self.work(sampleX[i])).T# Recalculate the loss which is defined as sum of squared errorsL = L + 0.5*np.sum(np.square(Y-np.matrix(sampleY[i]).T))# Get the sensitivity vectors of the last neure layer Delta.append(np.diag(np.multiply(self.O[-1],(1-self.O[-1])).A1) * (Y-np.matrix(sampleY[i]).T))for j in range(layerNum-2, 0, -1):# Delta_j = diag[f'(Z_j)]*W_j.T*Delta_(j-1)Delta.append(np.diag(np.multiply(self.O[j-1],(1-self.O[j-1])).A1) * self.W[j].T*Delta[-1])Delta.reverse()# Sum gradients at each sample in the subsetself.dW[0] = self.dW[0] + Delta[0]*np.matrix(sampleX[i])self.dB[0] = self.dB[0] + Delta[0]for j in range(1, layerNum-1):self.dW[j] = self.dW[j] + Delta[j]*self.O[j-1].Tself.dB[j] = self.dB[j] + Delta[j]# Append the loss expectation of the subsetJ.append(L/subSize)# Adjust the weight and bias of each neures layerfor i in range(0, layerNum-1):self.W[i] = self.W[i] - step*self.dW[i]/subSizeself.B[i] = self.B[i] - step*self.dB[i]/subSize# Complete an iterationiterations = iterations - 1 # Plot the curve of loss declineplt.figure('loss curve')plt.plot(range(0, len(J)), J)

(三)训练拟合

使用前面生成的离散数据训练此BPNN。在此构造了一个单输入单输出,包括6层神经元的神经网络。

import DataMining.BPANN as ANN
import matplotlib.pyplot as plt# Transform data type for ANN training
x0 = []
y0 = []
for (i, j) in zip(x, y):x0.append([i])y0.append([j])# Construct and train an ANN
neures = [1, 60, 160, 250, 160, 60, 1]
ann = ANN.BPANN(neures)
ann.train(x0, y0, 0.8, 150, 20)
y1 = []
for i in x0:y1.append(ann.work(i, -np.pi, np.pi)[0])
plt.figure(2)
plt.plot(x, y1, 'r', label='regression ANN')
plt.legend(loc=2)

训练迭代过程中损失函数的变换如下:

最后的拟合结果为:

详解反向传播神经网络 (Back Propagation Neural Network, BPNN)相关推荐

  1. Java Back Propagation Neural Network(JAVA反向传播神经网络)

    EDITOR: KJ021320 BLOG: http://blog.csdn.net/kj021320 TEAM: I.S.T.O 好久没写东西了,随便记一下~~如果不记录忘记了真是很浪费啊~ 下面 ...

  2. BP神经网络(Back Propagation Neural Network)Matlab简单实现

    BP神经网络(Back Propagation Neural Network)Matlab简单实现 前言 简单了解反向传播(Backwarod Propagation)机制(链式法则) 实例分析 前向 ...

  3. 花书+吴恩达深度学习(三)反向传播算法 Back Propagation

    目录 0. 前言 1. 从 Logistic Regression 中理解反向传播 2. 两层神经网络中单个样本的反向传播 3. 两层神经网络中多个样本的反向传播 如果这篇文章对你有一点小小的帮助,请 ...

  4. 前向传播算法(Forward propagation)与反向传播算法(Back propagation)

    虽然学深度学习有一段时间了,但是对于一些算法的具体实现还是模糊不清,用了很久也不是很了解.因此特意先对深度学习中的相关基础概念做一下总结.先看看前向传播算法(Forward propagation)与 ...

  5. 图片1---前馈神经网络+反向传播神经网络

    声明:  记录自己认为好的图片------以方便后续的使用或者是改进 来源论文:  <一种金融市场预测的深度学习模型: FEPA> (1)前馈神经网络 (2)反向传播神经网络的大致的架构- ...

  6. 反式自动微分autodiff是什么?反向传播(Back Propagation)是什么?它是如何工作的?反向传播与反式自动微分autodiff有什么区别?

    反式自动微分autodiff是什么?反向传播(Back Propagation)是什么?它是如何工作的?反向传播与反式自动微分autodiff有什么区别? 目录

  7. 反向传播神经网络 demo源码

    反向传播神经网络(有代码) 找了很多资料,觉得就这篇博客讲的最好,原文如下,有非常详细的推导过程.http://www.hankcs.com/ml/back-propagation-neural-ne ...

  8. Arduino前馈反向传播神经网络

    本文介绍了为Arduino Uno微控制器板开发的人工神经网络. 这里描述的网络是前馈反向传播网络,可能是最常见的类型. 它被认为是有监督或无监督学习的良好通用网络. 该项目的代码以Arduino S ...

  9. 反向传播神经网络(BPNN)的实现(Python,附源码及数据集)

    文章目录 一.理论基础 1.前向传播 2.反向传播 3.激活函数 4.神经网络结构 二.BP神经网络的实现 1.训练过程(BPNN.py) 2.测试过程(test.py) 3.测试结果 4.参考源码及 ...

  10. cs231n笔记5—反向传播/神经网络

    反向传播 神经网络 反向传播 问题陈述:这节的核心问题是,给定函数f(x),其中x是输入数据的向量,需要计算函数f关于x的梯度,也就是∇f(x). 反向传播是利用链式法则递归计算表达式的梯度的方法.理 ...

最新文章

  1. 鸿蒙系统全面解析,诞生背景、技术细节生态圈一文看懂
  2. RxSwift 小记 Error Handling Operators(catchError,retry)
  3. java读取src路径下的txt文件_Java程序使用Maven后无法运行?
  4. DES和RSA算法的java实现
  5. 面试官:GET和POST两种基本请求方法有什么区别
  6. java –cp ./:_成为Java流专家–第2部分:中级操作
  7. 用Python一次性把论文作图与数据处理全部搞定!
  8. npm安装vue-cli时报错解决方法
  9. java新手的第一个小东西,或许小东西都算不上=。 =
  10. 【图像分割】基于形态学重建和过滤改进FCM算法(FRFCM)的图像分割【含Matlab源码 085期】
  11. 黑马程序员视频加源码
  12. 计算机视觉有哪些商业用途​
  13. 智能人物画像综合分析系统——Day1
  14. linux访问网络图片,linux网络图形监控方法
  15. Lambda表达式的几种简化形式
  16. C++友元和友元函数
  17. 找自幂数的逐步优化算法
  18. 位图BITMAP结构
  19. 基于 Vue JS、Element UI、Nuxt JS的项目PC端前端手册
  20. 网页图片批量获取 ※(依据国防科大招生网新版更新)

热门文章

  1. keil5——安装教程附资源包
  2. 服务器网络修复工具,常用LSP修复工具盘点 让你轻轻松松上网
  3. 几种常用的PID控制算法
  4. 【Delta并联机器人Simscape仿真(正运动学、逆运动学)】
  5. RFC2544时延测试——信而泰网络测试仪实操
  6. 大数据培训(第三季)——Linux基础-徐培成-专题视频课程
  7. Unity运行时输出场景物体及角色为fbx文件
  8. 图书馆图书借阅登记微信小程序管理软件系统开发制作
  9. HTML5+CSS3项目实战之河马牙医首页、百度首页、Mac桌面、简书首页、登录注册页面、苏宁易购首页
  10. 3DMAX的渲染后期丨怎么在PS里P出真实感的灯光?