-
2021-07-20 16:48:30
文章目录
前言
BP神经网络(Back propagation neural network)全称为多层前馈神经网络,其用于解决非线性问题。整个神经网络的步骤为:输入层接收外界的输入,隐藏层和输出层的神经元对输入的特征或信号通过权重矩阵进行加工,最终输出结果。过程中最重要的是获得加工所要的权重,本质上说神经网络的学习过程就是在学习神经元与神经元之间连接的权重。
提示:以下是本篇文章正文内容,下面案例可供参考
第一步:正向传播激活神经网络
A.变量说明
关于BP神经网络的正向传播,我们以上图为例对变量进行一下说明:
- x i ( i n ) , a i ( i n ) x^{(in)}_i ,a^{(in)}_i xi(in),ai(in) : 二者均表示输入层的第 i i i个输入单元(这样做是为了和后面的进行统一,二者相等的原因是输入层的神经元不起激活作用);
- w m , h ( h ) w^{(h)}_{m,h} wm,h(h) :输入层的第 m m m个单元与隐藏层的的第 h h h个单元之间的权重(⚠️权重的标识很重要,而且不同的参考书用的也不同,读者一定要弄清楚)
- z i ( h ) z^{(h)}_i zi(h) : 隐藏层的第 i i i的净输入单元, z i ( h ) = w 1 , i ( h ) a 1 ( i n ) + . . . + w m , i ( h ) a m ( i n ) z^{(h)}_i =w^{(h)}_{1,i}a^{(in)}_1+ ...+w^{(h)}_{m,i}a^{(in)}_m zi(h)=w1,i(h)a1(in)+...+wm,i(h)am(in);
- a i ( h ) a^{(h)}_i ai(h) : 隐藏层的第 i i i的隐藏单元, a i ( h ) = ϕ ( z i ( h ) ) a^{(h)}_i = \phi (z^{(h)}_i) ai(h)=ϕ(zi(h));
- w h , t ( o ) w^{(o)}_{h,t} wh,t(o) :隐藏层的第 h h h个单元与输出层的的第 t t t个单元之间的权重(⚠️权重的标识很重要,而且不同的参考书用的也不同,读者一定要弄清楚)
- z i ( o u t ) z^{(out)}_i zi(out) : 输出层的第 i i i的净输入单元, z i ( o u t ) = w 1 , i ( o u t ) a 1 ( h ) + . . . + w h , i ( o u t ) a h ( h ) z^{(out)}_i =w^{(out)}_{1,i}a^{(h)}_1+ ...+w^{(out)}_{h,i}a^{(h)}_h zi(out)=w1,i(out)a1(h)+...+wh,i(out)ah(h);
- a i ( o u t ) a^{(out)}_i ai(out) : 输出层的第 i i i的输出单元, a i ( o u t ) = ϕ ( z i ( o u t ) ) a^{(out)}_i = \phi (z^{(out)}_i) ai(out)=ϕ(zi(out));
- y [ i ] , a [ i ] y^{[i]},a^{[i]} y[i],a[i]: 分别代表一组数据的实际结果中的第 i i i个(上角标视为索引),经过神经网络输出的第 i i i个;
- 关于激活函数,我们采用 sigmoid 函数 ϕ ( x ) = 1 1 + e − x \phi (x) = \cfrac{1}{1 + e^{-x}} ϕ(x)=1+e−x1 ;
B.正向传播
熟悉完上面的量的意义之后,我们通过线性代数的知识导出BP神经网络的正向传播过程:
首先,输入层的各个单元 A ( i n ) \bm A^{(in)} A(in)(不妨设阶数为:n*m )和通过指向隐藏层的权重矩阵 W ( h ) \bm W^{(h)} W(h)(阶数:m * h)做点乘,得到隐藏层的净输入向量: Z ( h ) \bm Z^{(h)} Z(h)(阶数: n * h);
其次,将隐藏层的净输入向量进行激活,得到 A ( h ) \bm A^{(h)} A(h), A ( h ) = ϕ ( Z ( h ) ) \bm A^{(h)} = \phi(\bm Z^{(h)}) A(h)=ϕ(Z(h))(阶数:n * h);
再其次: a ( h ) \bm a^{(h)} a(h)通过指向输出层的权重矩阵 W ( o u t ) \bm W^{(out)} W(out)(阶数:h * t),得到输出层的净输入向量: Z ( o u t ) \bm Z^{(out)} Z(out)(n * t);
最后,将输出层的净输入向量进行激活,得到得到 A ( o u t ) \bm A^{(out)} A(out), A ( o u t ) = ϕ ( Z ( o u t ) \bm A^{(out)} = \phi(\bm Z^{(out}) A(out)=ϕ(Z(out)(阶数:n * t),即我们的输出结果。
这其中涉及的矩阵运算罗列如下:
- Z ( h ) = A ( i n ) W ( h ) \bm Z^{(h)} = \bm A^{(in)}\bm W^{(h)} Z(h)=A(in)W(h);
- Z ( o u t ) = A ( h ) W ( o u t ) \bm Z^{(out)} = \bm A^{(h)}\bm W^{(out)} Z(out)=A(h)W(out);
BP神经网络的正向传播过程相对比较简单,弄清楚需要对上面变量的标识熟练掌控以及要有线性代数的基础,另外上面👆的表述并没牵扯到偏置单元,下面我们介绍下偏置单元的内容
C.偏置单元
关于偏置单元的设置,通常输入层的激发单元(神经元)组成的是输入单元和偏置单元,隐藏层的激发单元(神经元)组成的是隐藏单元和偏置单元。但是为方便起见,将偏置单元更改为单独的偏置向量,单独的偏置向量的设定和之前的以权重变量作为偏置(也就是偏置单元设定为1)的操作二者相同,只是形式不同。
采用这样形式的优点:- 代码更加高效并且便于阅读,权重矩阵的维度可完全用层与层之间的神经元个数表示,而无偏置单元的参与;
- 这样的操作也在常用的深度学习库中被采用;
如上图所示:输入层(Input layer)中共有3+1个输入单元,1个为偏置单元。隐藏层(Hidden layer)中共有4 + 1个隐藏单元,1个为偏置单元。采用偏置单元更改为单独的偏置向量的形式,尚若我们有100组数据,一组数据对应3个特征值,那么我们就只需考虑输入层(Input layer)和隐藏层(Hidden layer)之间的权重矩阵为: 一个 3 * 4的权重矩阵 + 一个 1 * 4 的偏置向量;
可能读者有疑问🤔️两个向量个维度不相同,那么计算不会报错吗?不会,因为numpy具有广博机制,一个权重矩阵的列数和第二个单元的行数相同,就会采用广播机制,见下面👇代码示例。
print(np.array([[1,2],[1,2]]),'\n') print(np.array([3,2]),'\n') print(np.array([[1,2],[1,2]])+np.array([3,2]),'\n') #s输出结果如下,可见计算正常进行 [[1 2] [1 2]] [3 2] [[4 4] [4 4]]
第二步:误差逆向传播
1.损失函数
我们通过第一步进行了BP神经网络的正向传播,得到输出结果向量,输出结果向量和实际的向量之间存在差异,如何用数学的形式进行表示?这就需要损失函数的发挥作用。为简单起见,我们首先从1组输入数据(阶数:1 * m)入手再过渡到多组数据(阶数:n * m)的形式,由易到难。
损失函数的一般形式(线性回归形式):
J ( W ) = 1 2 ∥ a o u t − y ∥ 2 2 = 1 2 ∑ i = 1 m ( y [ i ] − a [ i ] ) 2 J(\bm W)= \bf\tfrac{1}{2}{\lVert a^{out}-y \rVert ^{2}_{2}= \tfrac{1}{2}\displaystyle\sum_{i=1}^m(y^{[i]}-a^{[i]})^2} J(W)=21∥aout−y∥22=21i=1∑m(y[i]−a[i])2损失函数的一般形式(逻辑回归形式):
J ( W ) = 1 2 ∥ a o u t − y ∥ 2 2 = − ∑ i = 1 m [ y [ i ] l o g ( a [ i ] + ( 1 − y [ i ] ) l o g ( 1 − a [ i ] ) ] J(\bm W)= \bf\tfrac{1}{2}{\lVert a^{out - y} \rVert ^{2}_{2}} = -\displaystyle\sum_{i=1}^m[y^{[i]}log(a^{[i]}+(1-y^{[i]})log(1-a^{[i]})] J(W)=21∥aout−y∥22=−i=1∑m[y[i]log(a[i]+(1−y[i])log(1−a[i])]注: y [ i ] , z [ i ] y^{[i]},z^{[i]} y[i],z[i]: 分别代表一组数据的实际结果中的第 i i i个(上角标视为索引),经过神经网络输出的第 i i i个(上角标视为索引);
可以看到损失函数是 a o u t a^{out} aout的函数,而 a o u t a^{out} aout又是通过已知的输入变量(固定)通过层与层之间的权重矩阵得到,所以损失函数是整个神经网络的权重矩阵的函数。此时我们明确了损失函数的自变量是权重,按照梯度下降的思想,要想损失函数最小化,就要找到损失函数关于各个权重的梯度(偏导数),然后在其相反的方向上进行变化。
2.哈达玛积(Hadamard product)
为了更好的表达误差,我们采用一种运算方式,可以让我们的结果更加简洁:哈达玛积
这种运算方式定义如下:
[ a b ] ⊙ [ c d ] = [ a c b d ] \begin{bmatrix} a \\ b \end{bmatrix}\odot\begin{bmatrix} c \\ d \end{bmatrix} = \begin{bmatrix} ac \\ bd \end{bmatrix} [ab]⊙[cd]=[acbd]
即:
[ 1 3 ] ⊙ [ 2 4 ] = [ 2 12 ] \begin{bmatrix} 1 \\ 3 \end{bmatrix}\odot\begin{bmatrix} 2 \\ 4 \end{bmatrix} = \begin{bmatrix} 2 \\ 12 \end{bmatrix} [13]⊙[24]=[212]哈达玛乘积如何由numpy实现?
np.array([1,2,3]) * np.array([1,2,3]) #输出结果为: array([1, 4, 9]) #所以,在numpy中,实现哈达玛乘积只需将两个大小相同的向量直接相乘
3.理解误差逆向传播
上面👆我们介绍了损失函数和一种运算方式,我们提到想要通过计算损失函数关于权重的梯度也就是偏导数来实现梯度下降,那么我们该如何下手?也就是如何得到以下二式?
∂ J ( W ) ∂ w m , h ( h ) \dfrac{\partial{J(\bm W)}}{\partial{w^{(h)}_{m,h}}} ∂wm,h(h)∂J(W)
∂ J ( W ) ∂ w h , t ( o u t ) \dfrac{\partial{J(\bm W)}}{\partial{w^{(out)}_{h,t}}} ∂wh,t(out)∂J(W)
上面我们提到损失函数是 a o u t a^{out} aout的函数, a o u t a^{out} aout是权重变量的函数。通过最开始神经网络的描述,我们直观的感觉这里面层层环绕,一层接着一层,直接获得上面两个式子看来不太容易。我们不妨一层层一层看,离我们损失函数最近(也就是直接影响损失函数)的是 a i o u t a^{out}_i aiout,其次是 z i ( o u t ) z^{(out)}_i zi(out)(输出层的净输入单元,尚未激活),最后才是权重 w h , t ( o u t ) w^{(out)}_{h,t} wh,t(out)。考虑到这种关系,我们不妨先求损失函数关于 z i ( o u t ) z^{(out)}_i zi(out)偏导数,然后再求解关于其 w h , t ( o u t ) w^{(out)}_{h,t} wh,t(out)的偏导数。
这里的计算本质上是链式求导法则,计算机代数中自动微分(Automatic Differentiation)可以很好的解决这样的问题,但是我们作为初学者,要用代码一行一行的将其表示出来。
基于上面的分析,我们从输出层考虑:
δ i o u t = ∂ J ( W ) ∂ a i ( o u t ) ∂ a i ( o u t ) ∂ z i ( o u t ) \delta^{out}_i = \dfrac{\partial{J(\bm W)}}{\partial{a^{(out)}_{i}}}\dfrac{\partial{a^{(out)}_{i}}}{\partial{z^{(out)}_{i}}} δiout=∂ai(out)∂J(W)∂zi(out)∂ai(out)
这其中 δ i o u t \delta^{out}_i δiout称为误差项,而 a i ( o u t ) = ϕ ( z i ( o u t ) ) a^{(out)}_{i} = \phi (z^{(out)}_i) ai(out)=ϕ(zi(out)),所以:
∂ a i ( o u t ) ∂ z i ( o u t ) = ϕ ′ ( z i ( o u t ) ) \dfrac{\partial{a^{(out)}_{i}}}{\partial{z^{(out)}_{i}}}=\phi^{'} (z^{(out)}_i) ∂zi(out)∂ai(out)=ϕ′(zi(out))
即:
δ i o u t = ∂ J ( W ) ∂ a i ( o u t ) ϕ ′ ( z i ( o u t ) ) \delta^{out}_i=\dfrac{\partial{J(\bm W)}}{\partial{a^{(out)}_{i}}}\phi^{'} (z^{(out)}_i) δiout=∂ai(out)∂J(W)ϕ′(zi(out))
利用哈达玛积并且写成向量的形式更加简洁:
δ o u t = ∂ J ( W ) ∂ a ( o u t ) ⊙ ϕ ′ ( Z ( o u t ) ) \bm\delta^{out} = \dfrac{\partial{J(\bm W)}}{\partial{\bm a^{(out)}}}\odot\bm\phi^{'} (\bm{Z^{(out)}}) δout=∂a(out)∂J(W)⊙ϕ′(Z(out))
又因为我们知道损失函数关于 a ( o u t ) \bm a^{(out)} a(out)的函数表达式,求偏导可得 ∂ J ( W ) ∂ a ( o u t ) = a o u t − y \dfrac{\partial{J(\bm W)}}{\partial{\bm a^{(out)}}}= \bm{a^{out}-y} ∂a(out)∂J(W)=aout−y所以:
δ o u t = ( a o u t − y ) ⊙ ϕ ′ ( Z ( o u t ) ) \bm\delta^{out} = (\bm{a^{out}-y})\odot\bm\phi^{'} (\bm{Z^{(out)}}) δout=(aout−y)⊙ϕ′(Z(out))至此,我们得到 δ o u t \bm\delta^{out} δout也就是损失函数关于 z ( o u t ) \bm z^{(out)} z(out)的偏导数,但是我们最终的目的是其关于权重的偏导数,考虑到 Z ( o u t ) \bm Z^{(out)} Z(out) = A h W o u t \bm{A^{h}W^{out}} AhWout,所以:
∂ J ( W ) ∂ W ( o u t ) = ( A h ) T δ o u t = ( A h ) T ( a o u t − y ) ⊙ ϕ ′ ( Z ( o u t ) ) \dfrac{\partial{J(\bm W)}}{\partial{\bm{W^{(out)}}}} = \bm{(A^{h}) ^T}\bm\delta^{out}=\bm{(A^{h})^T}(\bm{a^{out}-y})\odot\bm\phi^{'} (\bm{Z^{(out)}}) ∂W(out)∂J(W)=(Ah)Tδout=(Ah)T(aout−y)⊙ϕ′(Z(out))
这里我们可以类比求偏导的方式求 Z ( o u t ) \bm Z^{(out)} Z(out) 对 W o u t W^{out} Wout的偏导数,按理说应该是 A h \bm A^h Ah,那为啥是 ( A h ) T \bm{(A^h)^T} (Ah)T😯?思考一下🤔,如果纠一下细节,确实是 ( A h ) T \bm{(A^h)^T} (Ah)T!这是因为矩阵的乘法所导致: Z ( o u t ) \bm Z^{(out)} Z(out) = A h W o u t \bm{A^{h}W^{out}} AhWout,我们可以举个具体的例子:影响 W o u t \bm W^{out} Wout第一行第二个的元素其实是 A h \bm A^{h} Ah中的第二行第一个元素,也就是 A ( m , n ) \bm A(m,n) A(m,n)----> W ( n , m ) \bm W(n,m) W(n,m),所以 A \bm A A矩阵要转置(这里不理解也无所谓,可以选择记住或者跳过😄)。
下面我们谈谈如何求 ∂ J ( W ) ∂ w m , h ( h ) \dfrac{\partial{J(\bm W)}}{\partial{w^{(h)}_{m,h}}} ∂wm,h(h)∂J(W)(建议先把上面关于 ∂ J ( W ) ∂ w h , t ( o u t ) \dfrac{\partial{J(\bm W)}}{\partial{w^{(out)}_{h,t}}} ∂wh,t(out)∂J(W)弄清楚一些):
我们上面成功的推导出来了 ∂ J ( W ) ∂ w m , h ( h ) \dfrac{\partial{J(\bm W)}}{\partial{w^{(h)}_{m,h}}} ∂wm,h(h)∂J(W),并且用我们定义了一个叫做误差项的量,我们算法的名称叫做误差逆向传播算法,故名思义,就是将误差进行逆向传播。既然我们已经成功的计算出来了 δ o u t \bm\delta^{out} δout,那么我们可不可以通过 δ o u t \bm\delta^{out} δout计算 δ h \bm\delta^{h} δh,然后按照同样的步骤计算 ∂ J ( W ) ∂ w h , t ( o u t ) \dfrac{\partial{J(\bm W)}}{\partial{w^{(out)}_{h,t}}} ∂wh,t(out)∂J(W)?答案是🉑️,下面我们具体推导说明。
我们已经求得:
δ i o u t = ∂ J ( W ) ∂ z i ( o u t ) = ∂ J ( W ) ∂ a i ( o u t ) ϕ ′ ( z i ( o u t ) ) \delta^{out}_i=\colorbox{aqua}{$\dfrac{\partial{J(\bm W)}}{\partial{z^{(out)}_{i}}}$}=\dfrac{\partial{J(\bm W)}}{\partial{a^{(out)}_{i}}}\phi^{'} (z^{(out)}_i) δiout=∂zi(out)∂J(W)=∂ai(out)∂J(W)ϕ′(zi(out))
目标是(为一般起见我们用i,j进行区分):
δ j h = ∂ J ( W ) ∂ z j ( h ) \delta^{h}_j = \dfrac{\partial{J(\bm W)}}{\partial{z^{(h)}_{j}}} δjh=∂zj(h)∂J(W)
如果将目标与已求联系,在求偏导过程中引入 z i ( o u t ) z^{(out)}_i zi(out)(已用蓝色框标明):
δ j h = ∂ J ( W ) ∂ z i ( o u t ) ∂ z i ( o u t ) ∂ z j ( h ) = δ i o u t ∂ z i ( o u t ) ∂ z j ( h ) \delta^{h}_j = \colorbox{aqua}{$\dfrac{\partial{J(\bm W)}}{\partial{z^{(out)}_{i}}}$} \dfrac{\partial z^{(out)}_i}{\partial{z^{(h)}_j}}=\delta^{out}_i\colorbox{yellow}{$\dfrac{\partial z^{(out)}_i}{\partial{z^{(h)}_j}}$} δjh=∂zi(out)∂J(W)∂zj(h)∂zi(out)=δiout∂zj(h)∂zi(out)
δ i o u t \delta^{out}_i δiout为已知项,现在则需要求:
∂ z i ( o u t ) ∂ z j ( h ) \colorbox{yellow}{$\dfrac{\partial z^{(out)}_i}{\partial{z^{(h)}_j}}$} ∂zj(h)∂zi(out)那 z i ( o u t ) z^{(out)}_i zi(out)又与 z i ( h ) z^{(h)}_i zi(h)有什么关系🤔️?
回答这个问题并不难,但是需要你对正向传播激活神经网络有充分的了解。 z i ( h ) z^{(h)}_i zi(h)通过激活函数得到 a i ( h ) a^{(h)}_i ai(h),而 a i ( h ) a^{(h)}_i ai(h)又通过和权重的结合得到 z i ( o u t ) z^{(out)}_i zi(out)(可以回头看看第一步:正向传播)。
所以:
∂ z i ( o u t ) ∂ z j ( h ) = ∂ z i ( o u t ) ∂ a j ( h ) ∂ a j ( h ) ∂ z j ( h ) = w j , i ( o u t ) ϕ ′ ( z j ( h ) ) \colorbox{yellow}{$\dfrac{\partial z^{(out)}_i}{\partial{z^{(h)}_j}}=\dfrac{\partial z^{(out)}_i}{\partial{a^{(h)}_j}}\dfrac{\partial a^{(h)}_j}{\partial{z^{(h)}_j}}=w^{(out)}_{j,i}\phi^{'}(z^{(h)}_j$)} ∂zj(h)∂zi(out)=∂aj(h)∂zi(out)∂zj(h)∂aj(h)=wj,i(out)ϕ′(zj(h))整理以上式子有:
δ j h = δ j o u t w j , i ( o u t ) ϕ ′ ( z j ( h ) ) \delta^{h}_j =\delta^{out}_j w^{(out)}_{j,i}\phi^{'}(z^{(h)}_j) δjh=δjoutwj,i(out)ϕ′(zj(h))
用向量以及哈达玛运算符的形式表达:
δ h = δ o u t ( w ( o u t ) ) T ⊙ ϕ ′ ( z ( h ) ) \bm{\delta^{h} =\delta^{out} (w^{(out)})^T\odot\phi^{'}(z^{(h)})} δh=δout(w(out))T⊙ϕ′(z(h))
至此,我们得到 δ h \bm\delta^{h} δh也就是损失函数关于 z ( h ) \bm z^{(h)} z(h)的偏导数,但是我们最终的目的是其关于权重的偏导数,考虑到 Z ( h ) \bm Z^{(h)} Z(h) = A o u t W h \bm{A^{out}W^{h}} AoutWh,所以:∂ J ( W ) ∂ W ( h ) = ( A i n ) T δ h \dfrac{\partial{J(\bm W)}}{\partial{\bm{W^{(h)}}}} = \bm{(A^{in}) ^T}\bm\delta^{h} ∂W(h)∂J(W)=(Ain)Tδh
得到这个结果的方式和我们算 ∂ J ( W ) ∂ W ( o u t ) \dfrac{\partial{J(\bm W)}}{\partial{\bm{W^{(out)}}}} ∂W(out)∂J(W)相同,可以翻上去回看。
4.关于偏置项的梯度
上面我们已经推导出损失函数关于层与层之间权重的偏导数,但是不要忘记了偏置向量的作用。我们在一开始介绍到:在实际操作中偏置单元更改为单独的偏置向量,那么损失函数关于偏执向量的梯度如何求解?
我先把结果写下:
∂ J ( W ) ∂ b ( o u t ) = δ o u t \dfrac{\partial{J(\bm W)}}{\partial{\bm{b^{(out)}}}} = \bm\delta^{out} ∂b(out)∂J(W)=δout
∂ J ( W ) ∂ b ( h ) = δ h \dfrac{\partial{J(\bm W)}}{\partial{\bm{b^{(h)}}}} = \bm\delta^{h} ∂b(h)∂J(W)=δh
即:
∂ J ( W ) ∂ b = δ \dfrac{\partial{J(\bm W)}}{\partial{\bm{b}}} = \bm\delta ∂b∂J(W)=δ可以看到损失函数关于偏置向量的偏导数是其所在层的误差向量,这里我做一下简单的说明(其实大家可以当作练习自己推导一番,和上面的推导类似并且要简单,检验下自己是否学会)
我们从就从输出层下手进行推导,我们已知:
δ i o u t = ∂ J ( W ) ∂ a i ( o u t ) ∂ a i ( o u t ) ∂ z i ( o u t ) \delta^{out}_i = \dfrac{\partial{J(\bm W)}}{\partial{a^{(out)}_{i}}}\dfrac{\partial{a^{(out)}_{i}}}{\partial{z^{(out)}_{i}}} δiout=∂ai(out)∂J(W)∂zi(out)∂ai(out)
又 z i ( o u t ) z^{(out)}_{i} zi(out)又取决于 b i ( o u t ) b^{(out)}_{i} bi(out),所以再次在上面的式子中设法添加 b i ( o u t ) b^{(out)}_{i} bi(out)项:
∂ J ( W ) ∂ b i ( o u t ) = ∂ J ( W ) ∂ a i ( o u t ) ∂ a i ( o u t ) ∂ z i ( o u t ) ∂ z i ( o u t ) ∂ b i ( o u t ) = δ i o u t ∂ z i ( o u t ) ∂ b i ( o u t ) \dfrac{\partial{J(\bm W)}}{\partial{b^{(out)}_{i}}}=\dfrac{\partial{J(\bm W)}}{\partial{a^{(out)}_{i}}}\dfrac{\partial{a^{(out)}_{i}}}{\partial{z^{(out)}_{i}}}\dfrac{\partial{z^{(out)}_i}}{\partial{b^{(out)}_{i}}}=\delta^{out}_i \dfrac{\partial{z^{(out)}_i}}{\partial{b^{(out)}_{i}}} ∂bi(out)∂J(W)=∂ai(out)∂J(W)∂zi(out)∂ai(out)∂bi(out)∂zi(out)=δiout∂bi(out)∂zi(out)
而 ∂ z i ( o u t ) ∂ b i ( o u t ) = 1 \dfrac{\partial{z^{(out)}_i}}{\partial{b^{(out)}_{i}}}=1 ∂bi(out)∂zi(out)=1,所以:
∂ J ( W ) ∂ b i ( o u t ) = δ i o u t \dfrac{\partial{J(\bm W)}}{\partial{b^{(out)}_{i}}}=\delta^{out}_i ∂bi(out)∂J(W)=δiout
于是得证,至于其他的层可以类比得到最终结果:∂ J ( W ) ∂ b = δ \dfrac{\partial{J(\bm W)}}{\partial{\bm{b}}} = \bm\delta ∂b∂J(W)=δ
5.误差逆向传播公式总结
∂ J ( W ) ∂ W ( o u t ) = ( A h ) T δ o u t \dfrac{\partial{J(\bm W)}}{\partial{\bm{W^{(out)}}}} = \bm{(A^{h}) ^T}\bm\delta^{out} ∂W(out)∂J(W)=(Ah)Tδout
∂ J ( W ) ∂ W ( h ) = ( A i n ) T δ h \dfrac{\partial{J(\bm W)}}{\partial{\bm{W^{(h)}}}} = \bm{(A^{in}) ^T}\bm\delta^{h} ∂W(h)∂J(W)=(Ain)Tδh
∂ J ( W ) ∂ b = δ \dfrac{\partial{J(\bm W)}}{\partial{\bm{b}}} = \bm\delta ∂b∂J(W)=δ
至此我们已经把误差逆向传播算法所需的公式推导并且总结完毕,充分理解好这些公式将有助于对这个算法的认识。其实,我们回过头来看,这里面用的最最核心的就是链式求导法则,所以我觉得要真正的弄明白这个算法:首先要对整个神经网络的传播过程以及各个符号有充分的认识并掌握熟练,其次要会利用链式求导法则进行梯度的求解,最后就是用代码实现
5.梯度检测
代码如下(示例):
未完待续!6.初始化的重要性
代码如下(示例):
未完待续!7.代码实现
具体代码请参考:我的另一篇文章(包含代码注释)
总结
以上是我在学习BP神经网络的个人理解,上面的推导数学形式偏多,所以本文章适合数学基础相对较好的而且强烈想弄清楚BP算法的一些公式由来的人参考。另外上文可能有错误,还望各位多多包含,希望找到的朋友可以在评论区留言告诉我好让我及时更改🙏🙏🙏。
更多相关内容 -
BP神经网络原理
2018-06-04 21:07:38BP神经网络原理,BP神经网络原理,BP神经网络原理,BP神经网络原理 -
深度学习(神经网络) —— BP神经网络原理推导及python实现
2020-12-20 16:41:03深度学习(神经网络) —— BP神经网络原理推导及python实现摘要(一)BP神经网络简介1、神经网络权值调整的一般形式为:2、BP神经网络中关于学习信号的求取方法:(二)BP神经网络原理推导1、变量说明2、BP算法推导... -
BP神经网络原理及Python实现代码
2021-01-20 04:10:44本文主要讲如何不依赖TenserFlow等高级API实现一个简单的神经网络来做分类,所有的代码都在下面;在构造的数据(通过程序构造)上做了验证,经过1个小时的训练分类的准确率可以达到97%。 完整的结构化代码见于:链接... -
BP神经网络基本原理简介
2018-08-15 15:11:57BP神经网络的详细介绍,包括基本算法及理论,方便初学者学习掌握 -
BP神经网络原理及应用.doc
2022-06-20 10:54:09BP神经网络原理及应用.doc -
BP神经网络原理及实现
2020-06-27 21:08:21BP神经网络原理 经典的BP神经网络通常由三层组成: 输入层, 隐含层与输出层.通常输入层神经元的个数与特征数相关,输出层的个数与类别数相同, 隐含层的层数与神经元数均可以自定义. 每个神经元代表对数据的一次...BP神经网络原理
经典的BP神经网络通常由三层组成: 输入层, 隐含层与输出层.通常输入层神经元的个数与特征数相关,输出层的个数与类别数相同, 隐含层的层数与神经元数均可以自定义.
每个神经元代表对数据的一次处理:
每个隐含层和输出层神经元输出与输入的函数关系为:其中Wij表示神经元i与神经元j之间连接的权重,Oj代表神经元j的输出, sigmod是一个特殊的函数用于将任意实数映射到(0,1)区间.
上文中的sigmod函数称为神经元的激励函数(activation function), 除了sigmod函数1/1+e^-IL外, 常用还有tanh和ReLU函数.
我们用一个完成训练的神经网络处理回归问题, 每个样本拥有n个输入.相应地,神经网络拥有n个输入神经元和1个输出神经元.
实际应用中我们通常在输入层额外增加一个偏置神经元, 提供一个可控的输入修正;或者为每个隐含层神经元设置一个偏置参数.
我们将n个特征依次送入输入神经元, 隐含层神经元获得输入层的输出并计算自己输出值, 输出层的神经元根据隐含层输出计算出回归值.
上述过程一般称为前馈(Feed-Forward)过程, 该过程中神经网络的输入输出与多维函数无异.
现在我们的问题是如何训练这个神经网络.
作为监督学习算法,BP神经网络的训练过程即是根据前馈得到的预测值和参考值比较, 根据误差调整连接权重Wij的过程.
训练过程称为反向传播过程(BackPropagation), 数据流正好与前馈过程相反.
首先我们随机初始化连接权重Wij, 对某一训练样本进行一次前馈过程得到各神经元的输出.
首先计算输出层的误差:
Ej=sigmod′(Oj)∗(Tj−Oj)=Oj(1−Oj)(Tj−Oj)
其中Ej代表神经元j的误差,Oj表示神经元j的输出, Tj表示当前训练样本的参考输出, sigmod′(x)是上文sigmod函数的一阶导数.
计算隐含层误差:Ej=sigmod′(Oj)∗∑kEkWjk=Oj(1−Oj)∑kEkWjk
隐含层输出不存在参考值, 使用下一层误差的加权和代替(Tj−Oj).
计算完误差后就可以更新Wij和θj:
Wij=Wij+λEjOi
其中λ是一个称为学习率的参数,一般在(0,0.1)区间上取值.
实际上为了加快学习的效率我们引入称为矫正矩阵的机制, 矫正矩阵记录上一次反向传播过程中的EjOi值, 这样Wj更新公式变为:
Wij=Wij+λEjOi+μCij
μ是一个称为矫正率的参数.随后更新矫正矩阵:
Cij=EjOi
每一个训练样本都会更新一次整个网络的参数.我们需要额外设置训练终止的条件.
最简单的训练终止条件为设置最大迭代次数, 如将数据集迭代1000次后终止训练.
单纯的设置最大迭代次数不能保证训练结果的精确度, 更好的办法是使用损失函数(loss function)作为终止训练的依据.
损失函数可以选用输出层各节点的方差:
L=∑j(Tj−Oj)2
为了避免神经网络进行无意义的迭代, 我们通常在训练数据集中抽出一部分用作校验.当预测误差高于阈值时提前终止训练.
Python实现BP神经网络
首先实现几个工具函数:
def rand(a, b): return (b - a) * random.random() + a def make_matrix(m, n, fill=0.0): # 创造一个指定大小的矩阵 mat = [] for i in range(m): mat.append([fill] * n) return mat
定义sigmod函数和它的导数:
def sigmoid(x): return 1.0 / (1.0 + math.exp(-x)) def sigmod_derivate(x): return x * (1 - x)
定义BPNeuralNetwork类, 使用三个列表维护输入层,隐含层和输出层神经元, 列表中的元素代表对应神经元当前的输出值.使用两个二维列表以邻接矩阵的形式维护输入层与隐含层, 隐含层与输出层之间的连接权值, 通过同样的形式保存矫正矩阵.
定义setup方法初始化神经网络:
def setup(self, ni, nh, no): self.input_n = ni + 1 # 因为需要多加一个偏置神经元,提供一个可控的输入修正 self.hidden_n = nh self.output_n = no # 初始化神经元 self.input_cells = self.input_n * [1.0] self.hidden_cells = self.hidden_n * [1.0] self.output_cells = self.output_n * [1.0] # 初始化权重矩阵 self.input_weights = make_matrix(self.input_n, self.hidden_n) self.output_weights = make_matrix(self.hidden_n, self.output_n) # 权重矩阵随机激活 for i in range(self.input_n): for h in range(self.hidden_n): self.input_weights[i][h] = rand(-0.2, 0.2) for h in range(self.hidden_n): for o in range(self.output_n): self.output_weights[h][o] = rand(-0.2, 0.2) # 初始化矫正矩阵 self.input_correction = make_matrix(self.input_n, self.hidden_n) self.output_correction = make_matrix(self.hidden_n, self.output_n)
定义predict方法进行一次前馈, 并返回输出:
def predict(self, inputs): # 激活输入层 for i in range(self.input_n - 1): self.input_cells[i] = inputs[i] # 激活隐藏层 for j in range(self.hidden_n): total = 0.0 for i in range(self.input_n): total += self.input_cells[i] * self.input_weights[i][j] self.hidden_cells[j] = sigmoid(total) for k in range(self.output_n): total = 0.0 for j in range(self.hidden_n): total += self.hidden_cells[j] * self.output_weights[j][k] self.output_cells[k] = sigmoid(total) return self.output_cells[:]
定义back_propagate方法定义一次反向传播和更新权值的过程, 并返回最终预测误差:
def back_propagate(self, case, label, learn, correct): # 前馈 self.predict(case) # 获取输出层误差 output_deltas = [0.0] * self.output_n for o in range(self.output_n): error = label[o] - self.output_cells[o] output_deltas[o] = sigmod_derivate(self.output_cells[o]) * error # 获取隐藏层误差 hidden_deltas = [0.0] * self.hidden_n for h in range(self.hidden_n): error = 0.0 for o in range(self.output_n): error += output_deltas[o] * self.output_weights[h][o] hidden_deltas[h] = sigmod_derivate(self.hidden_cells[h]) * error # 更新输出权重 for h in range(self.hidden_n): for o in range(self.output_n): # Wij=Wij+λEjOi+μCij change = output_deltas[o] * self.hidden_cells[h] self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o] # 更新输入权重 for i in range(self.input_n): for h in range(self.hidden_n): # Wij=Wij+λEjOi+μCij change = hidden_deltas[h] * self.input_cells[i] self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h] self.input_correction[i][h] = change # 获取全局误差 error = 0.0 for o in range(len(label)): error += 0.5 * (label[o] - self.output_cells[o]) ** 2 return error
定义train方法控制迭代, 该方法可以修改最大迭代次数, 学习率λ, 矫正率μ三个参数.
def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1): for i in range(limit): error = 0.0 for i in range(len(cases)): label = labels[i] case = cases[i] error += self.back_propagate(case, label, learn, correct)
编写test方法,演示如何使用神经网络学习异或逻辑:
def test(self): cases = [ [0, 0], [0, 1], [1, 0], [1, 1], ] labels = [[0], [1], [1], [0]] self.setup(2, 5, 1) # 设置各层的神经元数量 self.train(cases, labels, 10000, 0.5, 0.1) for case in cases: print(self.predict(case))
运行结果:
总结
BP神经网络的理解主要难点在于各层误差和权重的更新上面,涉及到一系列数学公式,只要把数学公式弄懂就会理解代码为什么这样做.
-
BP 神经网络原理
2016-11-21 15:19:12BP 神经网络原理 -
BP神经网络原理.docx
2021-10-06 20:24:54BP神经网络的理解与记录 -
BP神经网络原理.doc
2022-05-10 18:52:53BP神经网络原理.doc -
BP神经网络原理与异或实例分析
2021-08-31 21:01:34文章目录系列文章目录BP神经网络原理介绍一、BP神经网络算法原理是什么?二、激活函数1.激活函数作用三、BP神经网络异或实例分析1.问题:2.分析:3.代码总结 BP神经网络原理介绍 BP神经网络算法(Back-...
BP神经网络原理介绍
BP神经网络算法(Back-Propagation Neural Network)
一、BP神经网络算法原理是什么?
人类大脑是有多个相互链接的神经元组成,通过视觉、触觉、嗅觉等输入信号,经过人类大脑神经元的多次处理,我们人类可以做出某种判定(或反应),受人类大脑神经元的启发,我们提出了神经网络算法模型。
如下图所示:
输入层x(x1~xn)相当于我们的感知信号(视觉、嗅觉、触觉等)。
隐含层就是神经元的处理。
经过神经元的处理得到某种判定(反应)就是结果输出层。
在介绍BP神经网络实例之前,理解一下神经网络得灵魂_激活函数。二、激活函数
1.激活函数作用
激活函数作用:在于如何来激活输入信号得总和,对于下图感知机来说:
如上图感知机,输入x1,x2,偏置值b,来计算信号总和a通过阈值计算预测值。
a=w1+w2+b
y=h(a)
感知机的激活函数**(阶跃函数)**:以阈值为界,一旦输入超出阈值,就切换输出。
在神经网络中,激活函数都是非线性函数(如果激活函数是线性函数,无论如何加深层数,效果都将等同“无隐含层的神经网路”),最常用的非线性激活函数有:sigmoid函数,如果不懂激活该函数作用请参考:深度学习之魂之神经网络激活函数。三、BP神经网络异或实例分析
1.问题:
相同为0不同为1。
例如:
输入:【0,0】、【0,1】、【1,0】、【1,1】经过异或判定,输出0或1;2.分析:
通过BP神经网络实现首先明确输入参数2个。偏置值参数1个。则输入层参数共3个,假设隐藏层有4个,输出层只有一个,输入层与隐藏层权值为V【V0-V3】,隐藏层与输出层权值W[W0-W3]。通过权值可以计算y0-y3,如下图所示:
如上图所示,每个输入层和隐藏层都有一个权重,则共有12个权重值V,和4和权重值W。则输出O可表示为:
3.代码
import numpy as np # import matplotlib.pyplot as plt # 导入数据 X = np.array([[1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1] ]) # 标签 Y = np.array([[0, 1, 1, 0]]) # 输入层和隐藏层权值V、隐藏层和输出层之间的权值W初始化,取值范围-1到1 V = np.random.random((3, 4)) * 2 - 1 W = np.random.random((4, 1)) * 2 - 1 print(V, W) # 学习率设置 lr = 0.11 # 定义sigmoid激活函数 def sigmoid(x): return 1 / (1 + np.exp(-x)) #定义sigmoid函数导数 def dsigmoid(x): return x * (1 - x) #设置更新权值,更新权值理论推导可参照《神经网络导论》 def update(): global X, Y, W, V, lr L1 = sigmoid(np.dot(X, V)) # 隐藏层的输出(4,4) L2 = sigmoid(np.dot(L1, W)) # 输出层输出(4,1) L2_delta = (Y.T - L2) * dsigmoid(L2) L1_delta = L2_delta.dot(W.T) * dsigmoid(L1) W_C = lr * L1.dot(L2_delta) V_C = lr * X.T.dot(L1_delta) W = W + W_C V = V + V_C for i in range(20000): update() # 更新权值 if i % 500 == 0: L1 = sigmoid(np.dot(X, V)) # 隐藏层输出(4,4) L2 = sigmoid(np.dot(L1, W)) # 输出层输出(4,1) print('Error:', np.mean(np.abs(Y.T - L2))) L1 = sigmoid(np.dot(X, V)) # 隐藏层输出(4,4) L2 = sigmoid(np.dot(L1, W)) # 输出层输出(4,1) print(L2)
运行结果:
该处使用的url网络请求的数据。
总结
学习了BP神经网络的后向反馈机制,通过实例更加清晰神经网络使用,神经网络推到还需进一步了解。 -
python:BP神经网络原理实现
2022-04-24 22:03:02利用python对前馈神经网络进行原理编写,也就是误差逆传播算法的实现,对于想要理解神经的很有帮助 -
神经网络学习笔记1——BP神经网络原理到编程实现(matlab,python)
2022-01-13 22:10:46神经网络学习笔记1——BP神经网络原理到编程实现(matlab,python)先表达一下歉意吧
不好意思拖了这么久才整理,弄完考试的事情就在研究老师给安排的新任务,一时间还有点摸不到头脑,就直接把百度网盘链接放在视频下面了但是最近才发现那个链接发出来了看不到,所以现在有时间了就来重新整理一下!
(发了之后看好多人管我要,我还奇怪,原来是没法出去o(╥﹏╥)o)
目录
下面是视频地址和代码数据
BP神经网络原理及编程实现
BP神经网络原理及编程实现_哔哩哔哩_bilibili
python,matlab代码,还有数据集放在这里了
链接:https://pan.baidu.com/s/1-onLcVrPR7csFWJkxhIMsg
提取码:j3z6感觉有帮助的话,可以点个赞支持一下,真的每次被赞的时候还是挺开心的哈哈(*^▽^*)
1.bp神经网络原理
bp神经网络主要由三部分组成,分别是前向传播,反向传播,测试模型。其中前向传播主要是计算模型当前的预测结果,反向传播是对模型进行修正得到能达到预测效果的模型,测试模型是看我们最后通过反向传播得到的模型能否识别出我们想要的分类,好下面我来分别介绍他们的原理~
1.1前向传播
我就拿一个三层的网络来举例说明,同时也是我后面讲得第一个项目,这样理论实践相结合,比较方便理解。
这是一个基础的三层神经网络,按顺序分别是输入层,隐藏层,输出层,我们先按照一个节点来看,这样方便理解,我把传播的公式放在下面
其中a1作为输入的一个特征,在这里我们把特征数字化表示成一个数字
w代表权重,其实按我理解,就是a1占多少分量,这个特征对我们识别目标帮助有多大
b是一个偏差,方便我们对a2也就是下一个节点的值进行调整
z代表节点激活,这里用到了归激活函数sigmoid,对节点进行激活,按照我的理解sigmoid是一个归一化函数他要把归一化到0-1之间,方便收敛和标签值比较,判断误差,这样通过缩小误差来达到模型的预测功能。
最后的得到的z3是前向传播的结果也就是我们模型预测结果分类标签。
1.2反向传播
得到了前向传播的结果,相当于是一组用来训练的数据,我们要通过这次前向传播得到的结果和我们的分类标签相减得到误差。
我们希望缩小误差让我们的模型有更好的训练效果,这时候就要用到反向传播们这里用的是梯度下降法,让误差按照梯度的方向减小,最后训练打到我们预期的效果。
还是用我们刚才的神经网络
这里是做的一个链式求导,可以左右对应着看,因为E对b直接进行求导得不到结果,通过链式展开,配合着左边的公式,其中每一部都是可以求出来的,结果如红字表示,这里感觉文字不太方便表述,要是实在还不清楚,就看一下最前面分享的视频,里面讲解的还是比较细致的
然后求w1,w2同理
这样我们就能通过反向传播的到,b1,b2,w1,w2的变化率,然后和最开始随机得到的w和b进行运算就得到了我们通过这一组数据训练后的结果,在这里最重要的就是权重和偏差的数值了!
1.3 测试模型
理解了前面两个测试模型其实就很好理解了,这里其实主要也是前向传播,我们用训练好的模型对测试集进行目标分类,在通过
正确预测/训练集总数 * 100% = 模型的准确率
2.两个项目的matlab实现和python实现
对于这个程序要是看不懂的地方我记得在视频中有逐行对应的讲解,这里我们就大致标注一下
2.1语音数据分类预测
matlab实现如下
%% 清空环境变量 clc clear %% 训练数据预测数据提取及归一化 %1.下载四类语音信号 load data1 c1 load data2 c2 load data3 c3 load data4 c4 %2.四个特征信号矩阵合成一个矩阵 data(1:500,:)=c1(1:500,:); data(501:1000,:)=c2(1:500,:); data(1001:1500,:)=c3(1:500,:); data(1501:2000,:)=c4(1:500,:); %3.从1到2000间随机排序 k=rand(1,2000);%生成0-1之中2000个随机数 [m,n]=sort(k);%对k进行从从小到大的排序,序结果放入m(1*2000)向量,n(1*2000)为原数据对应的索引号 %4.输入输出数据 input=data(:,2:25);%取出data中2到25列数据构成新矩阵input output1 =data(:,1);%取出data中第一列数据构成矩阵output1 %5.把输出从1维变成4维 for i=1:2000 switch output1(i) case 1 output(i,:)=[1 0 0 0]; case 2 output(i,:)=[0 1 0 0]; case 3 output(i,:)=[0 0 1 0]; case 4 output(i,:)=[0 0 0 1]; end end %6.随机提取1500个样本为训练样本,500个样本为预测样本 input_train=input(n(1:1500),:)'; output_train=output(n(1:1500),:)'; input_test=input(n(1501:2000),:)'; output_test=output(n(1501:2000),:)'; %7.输入数据归一化 [inputn,inputps]=mapminmax(input_train);%归一化到[-1,1]之间,inputps用来作下一次同样的归一化 %%8.网络结构初始化,设置节点输入层,隐藏层,输出层 innum=24; midnum=25;%选择多少个节点才比较好? outnum=4; %9.权值初始化 w1=rands(midnum,innum);%随机给定隐藏层和是输入层间的初始神经元权重 W1= net. iw{1, 1}; b1=rands(midnum,1);%中间各层神经元阈值 B1 = net.b{1}; w2=rands(midnum,outnum);%中间层到输出层的权值 W2 = net.lw{2,1}; b2=rands(outnum,1);%输出层各神经元阈值 B2 = net. b{2} %10.权重,偏差重新赋值 w2_1=w2;w2_2=w2_1;%把w2中的值分别配给w1w2 w1_1=w1;w1_2=w1_1; b1_1=b1;b1_2=b1_1; b2_1=b2;b2_2=b2_1; %11.学习率 xite=0.1 %权值阈值更新 alfa=0.01; %学习速率,这里设置为0.01 %%12 网络训练 %(1)大循环 for ii=1:10 E(ii)=0; for i=1:1:1500 %% (2)网络预测输出 x=inputn(:,i); % (3)隐含层输出 for j=1:1:midnum I(j)=inputn(:,i)'*w1(j,:)'+b1(j); Iout(j)=1/(1+exp(-I(j))); end % (4)输出层输出 yn=w2'*Iout'+b2; %%(5) 权值阀值修正 %计算误差 e=output_train(:,i)-yn; E(ii)=E(ii)+sum(abs(e)); %计算权值变化率 dw2=e*Iout; db2=e'; for j=1:1:midnum S=1/(1+exp(-I(j))); FI(j)=S*(1-S); end for k=1:1:innum for j=1:1:midnum dw1(k,j)=FI(j)*x(k)*(e(1)*w2(j,1)+e(2)*w2(j,2)+e(3)*w2(j,3)+e(4)*w2(j,4)); db1(j)=FI(j)*(e(1)*w2(j,1)+e(2)*w2(j,2)+e(3)*w2(j,3)+e(4)*w2(j,4)); end end %(6)权值阈值更新,学习率在这用 w1=w1_1+xite*dw1'; b1=b1_1+xite*db1'; w2=w2_1+xite*dw2'; b2=b2_1+xite*db2'; w1_2=w1_1;w1_1=w1; w2_2=w2_1;w2_1=w2; b1_2=b1_1;b1_1=b1; b2_2=b2_1;b2_1=b2; end end %%13 语音特征信号分类 inputn_test=mapminmax('apply',input_test,inputps); for ii=1:1 for i=1:500%1500 %隐含层输出 for j=1:1:midnum I(j)=inputn_test(:,i)'*w1(j,:)'+b1(j); Iout(j)=1/(1+exp(-I(j))); end fore(:,i)=w2'*Iout'+b2; end end %% 14结果分析 %(1)根据网络输出找出数据属于哪类 for i=1:500 output_fore(i)=find(fore(:,i)==max(fore(:,i))); end %(2)BP网络预测误差 error=output_fore-output1(n(1501:2000))'; %画出预测语音种类和实际语音种类的分类图 figure(1) plot(output_fore,'r') hold on plot(output1(n(1501:2000))','b') legend('预测语音类别','实际语音类别') %画出误差图 figure(2) plot(error) title('BP网络分类误差','fontsize',12) xlabel('语音信号','fontsize',12) ylabel('分类误差','fontsize',12) %print -dtiff -r600 1-4 k=zeros(1,4); %找出判断错误的分类属于哪一类 for i=1:500 if error(i)~=0 %~表示非也就是error不等于0是 [b,c]=max(output_test(:,i)); switch c case 1 k(1)=k(1)+1; case 2 k(2)=k(2)+1; case 3 k(3)=k(3)+1; case 4 k(4)=k(4)+1; end end end %找出每类的个体和 kk=zeros(1,4); for i=1:500 [b,c]=max(output_test(:,i)); switch c case 1 kk(1)=kk(1)+1; case 2 kk(2)=kk(2)+1; case 3 kk(3)=kk(3)+1; case 4 kk(4)=kk(4)+1; end end %正确率 rightridio=(kk-k)./kk
这个我在程序中进行了比较详细的备注,数据集在前面网盘链接中可以获取,这个项目的python文件是我完全仿照matlab中写了,流程基本一样,不太具有再利用价值,所以可以看下视频中的思路和讲解。
2.2 蝴蝶花分类预测
这里需要注意的是这里数据集是已经打乱好的数据,python中不在是墨守成规的仿照matlab文件,而是写好了一个框架,虽然是打乱的数据集,但是也配备了随机抽取数据的函数,方便大家拿了就用!!
这里用的改进的bp神经网路,动量下降法进行加速收敛大致步骤原理公式如下
Step 1 :初始化数据,设定各层节点数和学习效率等值。Step 2 :输入层 FA 输入样品,计算出隐层 FB 活动。b(ki)=logsig(a*V(:,ki)+Pi(ki))Step 3 :计算出输出层 FC 活动。c(kj)=logsig(b*W(:,kj)+Tau(kj))Step 4 :网络输出和期望输出相比较,计算出输出层 FC 的错误。d=c.*(1-c).*(ck-c)Step 5 :反传,计算出隐层 FB 的错误。e=b.*(1-b).*(d*W')Step 6 :修改 FC 层和 FB 之间的权值 wij 。DeltaW(ki,kj)=Alpha*b(ki)*d(kj)+Gamma*DeltaWOld(ki,kj)W=W+DeltaWStep 7 :修改 FA 层和 FB 之间的权值 vhj 。DeltaV(kh,ki)=Beta*a(kh)*e(ki)V=V+DeltaVStep 8 :修改偏差。重复 Step 2 ~ Step 8 ,直到输出层 FC 的错误足够小。2.2.1matlab程序如下
clc %清屏 clear all; %删除 workplace 变量 close all; %关掉显示图形窗口 format long % Initial % parameters for the NN structure h=4; i=3; j=3; Alpha=0.9; Beta=0.5; Gamma=0.85; Tor=0.0005; Maxepoch=2000; Accuracy=0; Ntrain=115; Ntest=35; %随机赋值 [-1, +1] V=2*(rand(h,i)-0.5); W=2*(rand(i,j)-0.5); Pi=2*(rand(1,i)-0.5); Tau=2*(rand(1,j)-0.5); DeltaWOld(i,j)=0; DeltaVOld(h,i)=0; DeltaPiOld(i)=0; DeltaTauOld(j)=0; % the learning process Epoch=1; Error=10; %加载数据 load data.dat Odesired=data(:,2); % normalize the input data to rang [-1 +1] datanew=data(:,3:6); maxv=max(max(datanew)); minv=min(min(datanew)); datanorm=2*((datanew-minv)/(maxv-minv)-0.5); while Error>Tor Err(Epoch)=0; for k=1:Ntrain % k = the index of tranning set a=datanorm(k,:); % set the desired output ck[j] if data(k,2)==0 ck=[1 0 0]; elseif data(k,2)==1 ck=[0 1 0]; else ck=[0 0 1]; end; % calculate the hidden nodes activation for ki=1:i b(ki)=logsig(a*V(:,ki)+Pi(ki)); end; % calculate the output nodes activation for kj=1:j c(kj)=logsig(b*W(:,kj)+Tau(kj)); end; % calculate error in output Layer FC d=c.*(1-c).*(ck-c); % calculate error in hidden layer FB e=b.*(1-b).*(d*W'); % adjust weights Wij between FB and FC for ki=1:i for kj=1:j DeltaW(ki,kj)=Alpha*b(ki)*d(kj)+Gamma*DeltaWOld(ki,kj); end end; W=W+DeltaW; DeltaWOld=DeltaW; % adjust weights Vij between FA and FB for kh=1:h for ki=1:i DeltaV(kh,ki)=Beta*a(kh)*e(ki); end end; V=V+DeltaV; DeltaVold=DeltaV; % adjust thresholds Pi and Tau DeltaPi=Beta*e+Gamma*DeltaPiOld; Pi=Pi+DeltaPi; DeltaPiold=DeltaPi; DeltaTau=Alpha*d+Gamma*DeltaTauOld; Tau=Tau+DeltaTau; DeltaTauold=DeltaTau; % the error is the max of d(1),d(2),d(3) Err(Epoch)=Err(Epoch)+0.5*(d(1)*d(1)+d(2)*d(2)+d(3)*d(3)); end %for k=1:Ntrain Err(Epoch)=Err(Epoch)/Ntrain; Error=Err(Epoch); % the training stops when iterate is too much if Epoch > Maxepoch break; end Epoch = Epoch +1; % update the iterate number end % test data for k=1:Ntest % k = the index of test set a=datanorm(Ntrain+k,:); % calculate the hidden nodes activation for ki=1:i b(ki)=logsig(a*V(:,ki)+Pi(ki)); end; % calculate the output of test sets for kj=1:j c(kj)=logsig(b*W(:,kj)+Tau(kj)); end; % transfer the output to one field format if (c(1)> 0.9) Otest(k)=0; elseif (c(2)> 0.9) Otest(k)=1; elseif (c(3)> 0.9) Otest(k)=2; else Otest(k)=3; end; % calculate the accuracy of test sets if Otest(k)==Odesired(Ntrain+k) Accuracy=Accuracy+1; end; end; % k=1:Ntest % plot the error plot(Err); % plot the NN output and desired output during test N=1:Ntest; figure; plot(N,Otest,'b-',N,Odesired(116:150),'r-'); % display the accuracy Accuracy = 100*Accuracy/Ntest; t=['正确率: ' num2str(Accuracy) '%' ]; disp(t);
2.2.2 python实现和框架如下
每一部分具体的功能都进行了标注
import math import random import numpy from sklearn import preprocessing import time import xlwt import matplotlib.pyplot as plt random.seed(0) def read_data(dir_str): ''' 读取txt文件中的数据 数据内容:科学计数法保存的多行多列数据 输入:txt文件的路径 输出:小数格式的数组,行列与txt文件中相同 ''' data_temp = [] with open(dir_str) as fdata: while True: line=fdata.readline() if not line: break data_temp.append([float(i) for i in line.split()]) return numpy.array(data_temp) def randome_init_train_test(data, n_tr): ''' 随机划分训练集和测试集 ''' # sklearn提供一个将数据集切分成训练集和测试集的函数train_test_split train_index = numpy.random.choice(data.shape[0], size=n_tr, replace=False, p=None) train_data = data[train_index] test_index = numpy.delete(numpy.arange(data.shape[0]),train_index) # 删除train_index对应索引的行数 test_data = data[test_index] return train_data, test_data def min_max_normalization(np_array): ''' 离差标准化,(Xi-min(X))/(max(X)-min(X)) ''' min_max_scaler = preprocessing.MinMaxScaler() ret = min_max_scaler.fit_transform(np_array) return ret def label_to_value(label): ''' 标签转换为对应输出值 (由于输出层结构,需要修改输出数据结构)''' switch = { 0.0: [1,0,0], 1.0: [0,1,0], 2.0: [0,0,1] } return switch[label] def value_to_label(value): ''' 神经网络输出值转换为对应标签 ''' return value.index(max(value)) def rand(min, max): ''' 随机取[a, b]范围内的值 ''' return (max - min) * random.random() + min def make_matrix(m, n, fill=0.0): # 生成多维矩阵 mat = [] for i in range(m): mat.append([fill] * n) return mat def sigmoid(x): return 1.0 / (1.0 + math.exp(-x)) def sigmoid_derivative(x): return x * (1 - x) class BPNeuralNetwork: def __init__(self): # 设置在BP神经网络中用到的参数 self.input_n = 0 self.hidden_n = 0 self.output_n = 0 self.input_values = [] # [1.0] * self.input_n self.hidden_values = [] # [1.0] * self.hidden_n self.output_values = [] # [1.0] * self.output_n self.input_weights = [] self.output_weights = [] self.input_correction = [] # dw1 self.output_correction = [] # dw2 self.input_bias = [] self.output_bias = [] def setup(self, ni, nh, no): # 参数设置 self.input_n = ni self.hidden_n = nh self.output_n = no # init self.input_values = [1.0] * self.input_n # 输入层神经元输出(输入特征) self.hidden_values = [1.0] * self.hidden_n # 中间层神经元输出 self.output_values = [1.0] * self.output_n # 隐藏层神经元输出(预测结果) self.input_weights = make_matrix(self.input_n, self.hidden_n) self.output_weights = make_matrix(self.hidden_n, self.output_n) # 初始随机赋值,在范围[-1, +1]内 for i in range(self.input_n): for h in range(self.hidden_n): self.input_weights[i][h] = rand(-1, 1) for h in range(self.hidden_n): for o in range(self.output_n): self.output_weights[h][o] = rand(-1, 1) self.input_correction = make_matrix(self.input_n, self.hidden_n) self.output_correction = make_matrix(self.hidden_n, self.output_n) self.input_bias = [0.0] * self.input_n self.output_bias = [0.0] * self.output_n def predict(self, inputs): # 前向传播(在train中套在反向传播的train前面) # 输入层计算 for i in range(self.input_n - 1): self.input_values[i] = inputs[i] # 隐藏层计算 for j in range(self.hidden_n): total = 0.0 for i in range(self.input_n): total += self.input_values[i] * self.input_weights[i][j] self.hidden_values[j] = sigmoid(total + self.input_bias[i]) # 输出层计算 for k in range(self.output_n): total = 0.0 for j in range(self.hidden_n): total += self.hidden_values[j] * self.output_weights[j][k] self.output_values[k] = sigmoid(total + self.output_bias[j]) return self.output_values[:] def back_propagate(self, case, label, learn, correct): # 前向预测 self.predict(case) # 计算输出层的误差 w2 output_deltas = [0.0] * self.output_n for o in range(self.output_n): error = label[o] - self.output_values[o] output_deltas[o] = sigmoid_derivative(self.output_values[o]) * error # 计算隐藏层的误差 w1 hidden_deltas = [0.0] * self.hidden_n for h in range(self.hidden_n): error = 0.0 for o in range(self.output_n): error += output_deltas[o] * self.output_weights[h][o] hidden_deltas[h] = sigmoid_derivative(self.hidden_values[h]) * error # 更新隐藏-输出层权重 b2 for h in range(self.hidden_n): for o in range(self.output_n): change = output_deltas[o] * self.hidden_values[h] self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o] self.output_correction[h][o] = change self.output_bias[o] += learn * change # 更新输入-隐藏层权重 b1 for i in range(self.input_n): for h in range(self.hidden_n): change = hidden_deltas[h] * self.input_values[i] self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h] self.input_correction[i][h] = change self.input_bias[h] += learn * change # 计算样本的均方误差 error = 0.0 for o in range(len(label)): error += 0.5 * (label[o] - self.output_values[o]) ** 2 return error def train(self, datas, labels, epochs=5000, learn=0.05, correct=0.1, stop_error=0.001): for j in range(epochs): error = 0.0 for i in range(len(datas)): label = labels[i] data = datas[i] error += self.back_propagate(data, label, learn, correct) if error <= stop_error: return j+1 return epochs def save_excel(datas, output_file): # 将数据保存到新的excel表格里 # 因为xls文件支持最大数据行数为65536,所以大文件输出成几个小文件,每个小文件有MAX_EXCEL_ROWS行数据 MAX_EXCEL_ROWS = 60000 for no in range(0, datas.__len__()//MAX_EXCEL_ROWS + 1): sheet_name = 'sheet' + str(no+1) output_file_name = output_file.split('.')[0] + str(no+1) + '.' + output_file.split('.')[-1] print('输出文件:', output_file_name) excel = xlwt.Workbook() sh = excel.add_sheet(sheet_name) for i, data in enumerate(datas[no*MAX_EXCEL_ROWS:(no+1)*MAX_EXCEL_ROWS]): for j, d in enumerate(data): sh.write(i, j, d) try: excel.save(output_file_name) except: xxx = input('输出异常!!请检查输出路径是否异常或文件是否已存在(需删除已存在文件)。然后输入任意键即可...') no = no - 1 print('结束gool luck') if __name__ == '__main__': n_tr = 115 input_nodes = 4 hidden_nodes = 3 # output_nodes = 3 epochs = 1000 learn_rate = 0.5 # 学习率 momentum_rate = 0.09 # 动量参数 correct_rate = 0.1 # 矫正率 data = read_data(r"D:\优化大作业\BPNN_Butterfly classification\data.txt") normal_data = min_max_normalization(data[:, 2:]) # 变量归一化 data = numpy.concatenate((data[:, 1:2],normal_data),axis=1) # 取出输出的结果和标准化的数据拼接在一起 tr, te = randome_init_train_test(data, n_tr) # 随机划分训练集和测试集 tr_in = tr[:, 1:] tr_out = [label_to_value(v[0]) for v in tr[:, :1]] # 由于输出层使用3个节点,需要修改输出数据结构 n_true = 0 # 统计正确预测数量 nn = BPNeuralNetwork() nn.setup(input_nodes, hidden_nodes, output_nodes) # 设置BP神经网络框架 st = time.perf_counter() epoch = nn.train(tr_in, tr_out, epochs, learn_rate, correct_rate) # train(self, datas, labels, epochs=5000, learn=0.05, correct=0.1, stop_error=0.001) print('epoch:', epoch, '\nTrain_time:', time.perf_counter() - st) pre = [] for t in te: t_in = t[1:] label = value_to_label(nn.predict(t_in)) if label == t[0]: n_true += 1 # print(t, label) pre.append([label]) # 输出统计结果 accuracy = n_true/(data.shape[0]-n_tr) print('accuracy:', accuracy) print(nn.input_bias, nn.output_bias) # numpy.savetxt(r'bpnn_param\input_weights.txt', (nn.input_weights), fmt='%s') # numpy.savetxt(r'bpnn_param\output_weights.txt', (nn.output_weights), fmt='%s') # numpy.savetxt(r'bpnn_param\input_correction.txt', (nn.input_correction), fmt='%s') # numpy.savetxt(r'bpnn_param\output_correction.txt', (nn.output_correction), fmt='%s') # 将数据保存到新的excel表格里 te_pre = numpy.concatenate((te, numpy.array(pre)), axis=1) save_excel(te_pre, 'test_result.xls') # 绘制准确率曲线 x_axis_data = [i for i in range(35)] y_axis_data1 = te[:, :1] # 真实结果 y_axis_data2 = pre # 预测结果 plt.xlabel('测试集数', fontproperties='SimHei') plt.ylabel('预测分类', fontproperties='SimHei') plt.plot(x_axis_data, y_axis_data1, color='blue', label='真实结果' ) plt.plot(x_axis_data, y_axis_data2, color='red', label='预测结果') plt.show()
3.心得分享
经过自己的学习和研究,感觉可以先看懂公式,然后把程序导进去,用好断点,一步一步步进或者调试模式,看看得到的程序的结果和应该得出的结果得维数是不是相同,数据是否合理,研究透之后可以多自己写一写练一练,基本就没啥问题啦~
up也是刚开始学习,有什么问题可以多多交流,互相学习~
-
【数学建模】BP神经网络贝叶斯正则化算法
2021-06-27 23:32:35采用贝叶斯正则化算法提高 BP 网络的推广能力。在本例中,我们采用两种训练方法,即 L-M 优化算法(trainlm)和贝叶斯正则化算法(trainbr),用以训练 BP 网络,使其能够拟合某一附加有白噪声的正弦样本数据。其中... -
BP神经网络原理研究与实现.pdf
2021-09-26 09:28:10BP神经网络原理研究与实现.pdf -
BP神经网络原理及应用归纳.pdf
2022-01-10 20:14:09BP神经网络原理及应用归纳.pdf -
BP神经网络原理推导
2019-01-13 14:48:12本文会完成BP神经网络的推导过程,先介绍BP神经网络的历史,然后介绍BP神经网络的结构,然后再开始推导,最后介绍BP神经网络的优缺点以及几个优化的方法。 -
BP神经网络原理详解
2017-05-08 16:06:17BP神经网络原理详解 -
基于BP神经网络原理的人造岩心配比设计模型.pdf
2021-09-25 23:42:34基于BP神经网络原理的人造岩心配比设计模型.pdf -
BP神经网络,bp神经网络预测模型,Python
2021-09-10 16:41:58包含BP神经网络的原理算法模型,并使用BP神经网络对数据进行分类 -
BP神经网络原理及应用.pdf
2021-10-14 16:49:09BP神经网络原理及应用.pdf -
(完整版)BP神经网络原理及应用.doc
2020-11-01 15:53:01BP 神经网络原理及应用 人工神经网络简介 1.1 生物神经元模型 神经系统的基本构造是神经元神经细胞 它是处理人体内各部分之间相互信息传递的基本单元据神经生物学家研究的结果表明人的大脑一般有 1010 1011 个神经元... -
BP神经网络原理学习教案.pptx
2021-09-29 23:44:03BP神经网络原理学习教案.pptx -
BP神经网络原理介绍+示例代码.rar
2021-07-07 14:58:32简单介绍BP神经网络算法的原理,并提供教学版代码,以供参考 -
BP神经网络原理及MATLAB仿真.zip
2021-10-16 15:43:13BP神经网络原理及MATLAB仿真