BP神经网络算法学习及代码实现(含Python源码)[通俗易懂]

BP神经网络算法学习及代码实现(含Python源码)[通俗易懂]目录1.写在前面2.BP神经网络推导2.1前向传播2.2反向传播2.2.1求解梯度矩阵2.2.2梯度下降法2.2.3反向传播公式推导输出层误差推导隐藏层误差参数变化率参数更新3.代码实现3.1过程解释3.1.1导入库3.1.2定义sigmoid函数3.1.3导入数据集3.1.4初始化权重和偏倚3.1.5开始训练3.2完整代码3.3预测结果1.写在前面BP神经网络算法作为作为机器学习最基础的算法,非常适合入门。透彻掌握其原理将对于今后的机器学习有很大的帮助。2.BP神经网络推导2.1前向传播前向传播

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

1.写在前面

BP神经网络算法作为作为机器学习最基础的算法,非常适合入门。透彻掌握其原理将对于今后的机器学习有很大的帮助。

2.BP神经网络推导

2.1前向传播

前向传播过程可以表示为:
O [ l ] = σ ( w [ l ] I [ l − 1 ] + b [ l ] ) O^{[l]}=\sigma\left(w^{[l]} I^{[l-1]}+b^{[l]}\right) O[l]=σ(w[l]I[l1]+b[l])

2.2反向传播

2.2.1求解梯度矩阵

假设函数 f : R n × 1 → R f:R^{n \times 1} \rightarrow R f:Rn×1R 将输入的列向量(shape: n × 1 n \times 1 n×1 )映射为一个实数。那么,函数 f f f 的梯度定义为:

∇ x f ( x ) = [ ∂ f ( x ) ∂ x 1 ∂ f ( x ) ∂ x 2 ⋮ ∂ f ( x ) ∂ x n ] \nabla_{x} f(x)=\left[\begin{array}{c}\frac{\partial f(x)}{\partial x_{1}} \\ \frac{\partial f(x)}{\partial x_{2}} \\ \vdots \\ \frac{\partial f(x)}{\partial x_{n}}\end{array}\right] xf(x)=x1f(x)x2f(x)xnf(x)

同理,假设函数 f : R m × n → R f: R^{m \times n} \rightarrow R f:Rm×nR 将输入的矩阵(shape: m × n m \times n m×n )映射为一个实数。函数 f f f 的梯度定义为:

∇ A f ( A ) = [ ∂ f ( A ) ∂ A 11 ∂ f ( A ) ∂ A 12 … ∂ f ( A ) ∂ A 13 ∂ f ( A ) ∂ A 21 ∂ f ( A ) ∂ A 22 … ∂ f ( A ) ∂ A 2 n ⋮ ⋮ ⋱ ⋮ ∂ f ( A ) ∂ A m 1 ∂ f ( A ) ∂ A m 2 … ∂ f ( A ) ∂ A m n ] \nabla_{A} f(A)=\left[\begin{array}{cccc}\frac{\partial f(A)}{\partial A_{11}} & \frac{\partial f(A)}{\partial A_{12}} & \dots & \frac{\partial f(A)}{\partial A_{13}} \\ \frac{\partial f(A)}{\partial A_{21}} & \frac{\partial f(A)}{\partial A_{22}} & \dots & \frac{\partial f(A)}{\partial A_{2 n}} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{\partial f(A)}{\partial A_{m 1}} & \frac{\partial f(A)}{\partial A_{m 2}} & \dots & \frac{\partial f(A)}{\partial A_{m n}}\end{array}\right] Af(A)=A11f(A)A21f(A)Am1f(A)A12f(A)A22f(A)Am2f(A)A13f(A)A2nf(A)Amnf(A)

可以简化为:

( ∇ A f ( A ) ) i j = ∂ f ( A ) ∂ A i j \left(\nabla_{A} f(A)\right)_{i j}=\frac{\partial f(A)}{\partial A_{i j}} (Af(A))ij=Aijf(A)

2.2.2梯度下降法

​ 从几何意义,梯度矩阵代表了函数增加最快的方向,沿着梯度相反的方向可以更快找到最小值。

在这里插入图片描述
​ 反向传播的过程就是利用梯度下降法原理,逐步找到成本函数的最小值,得到最终的模型参数。

2.2.3反向传播公式推导

输出层误差

δ j [ L ] = ∂ L ∂ a j [ L ] σ ′ ( z j [ L ] ) \delta_{j}^{[L]}=\frac{\partial L}{\partial a_{j}^{[L]}} \sigma^{\prime}\left(z_{j}^{[L]}\right) δj[L]=aj[L]Lσ(zj[L])

L表示输出层层数。以下用 ∂ L \partial L L 表示 ∂ L ( a [ L ] , y ) \partial L\left(a^{[L]}, y\right) L(a[L],y)

推导

计算输出层的误差 δ j [ L ] = ∂ L ∂ z j [ L ] \delta_{j}^{[L]}=\frac{\partial L}{\partial z_{j}^{[L]}} δj[L]=zj[L]L ,根据链式法则

δ j [ L ] = ∑ k ∂ L ∂ a k [ L ] ∂ a k [ L ] ∂ z j [ L ] \delta_{j}^{[L]}=\sum_{k} \frac{\partial L}{\partial a_{k}^{[L]}} \frac{\partial a_{k}^{[L]}}{\partial z_{j}^{[L]}} δj[L]=kak[L]Lzj[L]ak[L]

输出层不一定只有一个神经元,可能有多个神经元。成本函数是每个输出神经元的损失函数之和,每个输出神经元的误差与其它神经元没有关系,所以只有 k = j k=j k=j 的时候值不是0。

k ≠ j k\neq j k=j 时, ∂ L ∂ z j [ L ] = 0 \frac{\partial L}{\partial z_{j}^{[L]}}=0 zj[L]L=0 ,简化误差 δ j [ L ] \delta_{j}^{[L]} δj[L] ,得到

δ j [ L ] = ∂ L ∂ a j [ L ] ∂ a j [ L ] ∂ z j [ L ] \delta_{j}^{[L]}=\frac{\partial L}{\partial a_{j}^{[L]}} \frac{\partial a_{j}^{[L]}}{\partial z_{j}^{[L]}} δj[L]=aj[L]Lzj[L]aj[L]

σ \sigma σ 表示激活函数,由 a j [ L ] = σ ( z j [ L ] ) a_{j}^{[L]}=\sigma\left(z_{j}^{[L]}\right) aj[L]=σ(zj[L]),计算出 ∂ a j [ L ] ∂ z j [ L ] = σ ′ ( z j [ L ] ) \frac{\partial a_{j}^{[L]}}{\partial z_{j}^{[L]}}=\sigma^{\prime}\left(z_{j}^{[L]}\right) zj[L]aj[L]=σ(zj[L]) ,代入最后得到

δ j [ L ] = ∂ L ∂ a j [ L ] σ ′ ( z j [ L ] ) \delta_{j}^{[L]}=\frac{\partial L}{\partial a_{j}^{[L]}} \sigma^{\prime}\left(z_{j}^{[L]}\right) δj[L]=aj[L]Lσ(zj[L])

隐藏层误差

δ j [ l ] = ∑ k w k j [ l + 1 ] δ k [ l + 1 ] σ ′ ( z j [ l ] ) \begin{array}{c} \delta_{j}^{[l]}=\sum_{k} w_{k j}^{[l+1]} \delta_{k}^{[l+1]} \sigma^{\prime}\left(z_{j}^{[l]}\right) \end{array} δj[l]=kwkj[l+1]δk[l+1]σ(zj[l])

推导

z k [ l + 1 ] = ∑ j w k j [ l + 1 ] a j [ l ] + b k [ l + 1 ] = ∑ j w k j [ l + 1 ] σ ( z j [ l ] ) + b k [ l + 1 ] z_{k}^{[l+1]}=\sum_{j} w_{k j}^{[l+1]} a_{j}^{[l]}+b_{k}^{[l+1]}=\sum_{j} w_{k j}^{[l+1]} \sigma\left(z_{j}^{[l]}\right)+b_{k}^{[l+1]} zk[l+1]=jwkj[l+1]aj[l]+bk[l+1]=jwkj[l+1]σ(zj[l])+bk[l+1]

z j [ l ] z_{j}^{[l]} zj[l] 求偏导

∂ z k [ l + 1 ] ∂ z j [ l ] = w k j [ l + 1 ] σ ′ ( z j [ l ] ) \frac{\partial z_{k}^{[l+1]}}{\partial z_{j}^{[l]}}=w_{k j}^{[l+1]} \sigma^{\prime}\left(z_{j}^{[l]}\right) zj[l]zk[l+1]=wkj[l+1]σ(zj[l])

根据链式法则

δ j [ l ] = ∂ L ∂ z j [ l ] = ∂ L ∂ z k [ l + 1 ] ∂ z k [ l + 1 ] ∂ z j [ l ] = ∑ k w k j [ l + 1 ] δ k [ l + 1 ] σ ′ ( z j [ l ] ) \delta_{j}^{[l]}=\frac{\partial L}{\partial z_{j}^{[l]}}=\frac{\partial L}{\partial z_{k}^{[l+1]}}\frac{\partial z_{k}^{[l+1]}}{\partial z_{j}^{[l]}}=\sum_{k} w_{k j}^{[l+1]} \delta_{k}^{[l+1]} \sigma^{\prime}\left(z_{j}^{[l]}\right) δj[l]=zj[l]L=zk[l+1]Lzj[l]zk[l+1]=kwkj[l+1]δk[l+1]σ(zj[l])

参数变化率

∂ L ∂ b j [ l ] = δ j [ l ] ∂ L ∂ w j k [ l ] = a k [ l − 1 ] δ j [ l ] \begin{array}{c} \frac{\partial L}{\partial b_{j}^{[l]}}=\delta_{j}^{[l]} \\ \frac{\partial L}{\partial w_{j k}^{[l]}}=a_{k}^{[l-1]} \delta_{j}^{[l]} \end{array} bj[l]L=δj[l]wjk[l]L=ak[l1]δj[l]

推导

z j [ l ] = ∑ k w j k [ l ] a k [ l − 1 ] + b k [ l ] z_{j}^{[l]}=\sum_{k} w_{j k}^{[l]} a_{k}^{[l-1]}+b_{k}^{[l]} zj[l]=kwjk[l]ak[l1]+bk[l]

L 对 b j [ l ] b_{j}^{[l]} bj[l] 求偏导,根据链式法则得到

∂ L ∂ b j [ l ] = ∂ L ∂ z j [ l ] ∂ z j [ l ] b j [ l ] = ∂ L ∂ z j [ l ] ∗ 1 = δ j [ l ] \frac{\partial L}{\partial b_{j}^{[l]}}=\frac{\partial L}{\partial z_{j}^{[l]}} \frac{\partial z_{j}^{[l]}}{b_{j}^{[l]}}= \frac{\partial L}{\partial z_{j}^{[l]}} * 1 = \delta_{j}^{[l]} bj[l]L=zj[l]Lbj[l]zj[l]=zj[l]L1=δj[l]

L 对 w j k [ l ] w_{j k}^{[l]} wjk[l] 求偏导,根据链式法则得到

∂ L ∂ w j k [ l ] = ∂ L ∂ z j [ l ] ∂ z j [ l ] w j k [ l ] = a k [ l − 1 ] δ j [ l ] \frac{\partial L}{\partial w_{j k}^{[l]}}=\frac{\partial L}{\partial z_{j}^{[l]}} \frac{\partial z_{j}^{[l]}}{w_{j k}^{[l]}}=a_{k}^{[l-1]} \delta_{j}^{[l]} wjk[l]L=zj[l]Lwjk[l]zj[l]=ak[l1]δj[l]

在这里插入图片描述

参数更新

根据梯度下降法原理,朝着梯度的反方向更新参数
b j [ l ] ← b j [ l ] − α ∂ L ∂ b j [ l ] w j k [ l ] ← w j k [ l ] − α ∂ L ∂ w j k [ l ] \begin{array}{c} b_{j}^{[l]} \leftarrow b_{j}^{[l]}-\alpha \frac{\partial L}{\partial b_{j}^{[l]}} \\ w_{j k}^{[l]} \leftarrow w_{j k}^{[l]}-\alpha \frac{\partial L}{\partial w_{j k}^{[l]}} \end{array} bj[l]bj[l]αbj[l]Lwjk[l]wjk[l]αwjk[l]L

3.代码实现

3.1过程解释

本次代码实现决定考虑四层神经网络,即含有两个隐层。其中,有三个输入单元,一个输出单元,第一层隐层含有四个节点,第二层隐层含有二个节点。
并且,激励函数采用sigmoid函数。
在这里插入图片描述

3.1.1导入库

import numpy as np

3.1.2定义sigmoid函数

def sigmoid(x, deriv = False):
    if(deriv == True):
        return x*(1-x)
    else:
        return 1/(1+np.exp(-x))

当deriv为默认值False时,进行sigmoid函数的运算;
当deriv为True时,对函数求导运算。(其中,求导运算时输入的x值应为函数值而非自变量)

3.1.3导入数据集

#input dataset
X = np.array([[0,0,1],
             [0,1,1],
             [1,0,1],
             [1,1,1]])
 
#output dataset
y = np.array([[0,1,1,0]]).T

3.1.4初始化权重和偏倚

权重weight01表示从第0层(即输入层)到第1层(第一隐层)的权重矩阵;
偏倚bias1表示第1层(第一隐层)的偏倚矩阵。

#初始化权重
weight01 = 2*np.random.random((3,4)) - 1 
weight12 = 2*np.random.random((4,2)) - 1
weight23 = 2*np.random.random((2,1)) - 1

#初始化偏倚
b1 = 2*np.random.random((1,4)) - 1 
b2 = 2*np.random.random((1,2)) - 1
b3 = 2*np.random.random((1,1)) - 1
bias1=np.array([b1[0],b1[0],b1[0],b1[0]])
bias2=np.array([b2[0],b2[0],b2[0],b2[0]])
bias3=np.array([b3[0],b3[0],b3[0],b3[0]])

3.1.5开始训练

I0表示第0层输入,O0表示第0层输出。

for j in range(60000):
    I0 = X
    O0=I0
    I1=np.dot(O0,weight01)+bias1
    O1=sigmoid(I1)
    I2=np.dot(O1,weight12)+bias2
    O2=sigmoid(I2)
    I3=np.dot(O2,weight23)+bias3
    O3=sigmoid(I3)

    f3_error = y-O3       
    
    f3_delta = f3_error*sigmoid(O3,deriv = True)
 
    f2_error = f3_delta.dot(weight23.T)
 
    f2_delta = f2_error*sigmoid(O2,deriv = True)
 
    f1_error = f2_delta.dot(weight12.T)     
 
    f1_delta = f1_error*sigmoid(O1,deriv = True)


    weight23 += O2.T.dot(f3_delta) #调整权重
    weight12 += O1.T.dot(f2_delta)
    weight01 += O0.T.dot(f1_delta)

    bias3 += f3_delta #调整偏倚
    bias2 += f2_delta
    bias1 += f1_delta

3.2完整代码

import numpy as np

#定义sigmoid函数 
def sigmoid(x, deriv = False):
    if(deriv == True):
        return x*(1-x)
    else:
        return 1/(1+np.exp(-x))
 
#input dataset
X = np.array([[0,0,1],
             [0,1,1],
             [1,0,1],
             [1,1,1]])
 
#output dataset
y = np.array([[0,1,1,0]]).T

#初始化权重
weight01 = 2*np.random.random((3,4)) - 1 
weight12 = 2*np.random.random((4,2)) - 1
weight23 = 2*np.random.random((2,1)) - 1

#初始化偏倚
b1 = 2*np.random.random((1,4)) - 1 
b2 = 2*np.random.random((1,2)) - 1
b3 = 2*np.random.random((1,1)) - 1
bias1=np.array([b1[0],b1[0],b1[0],b1[0]])
bias2=np.array([b2[0],b2[0],b2[0],b2[0]])
bias3=np.array([b3[0],b3[0],b3[0],b3[0]])

#开始训练
for j in range(60000):
    I0 = X
    O0=I0
    I1=np.dot(O0,weight01)+bias1
    O1=sigmoid(I1)
    I2=np.dot(O1,weight12)+bias2
    O2=sigmoid(I2)
    I3=np.dot(O2,weight23)+bias3
    O3=sigmoid(I3)

    f3_error = y-O3       
 
    if(j%10000) == 0:
        print ("Error:"+str(np.mean(f3_error)))

    
    

    f3_delta = f3_error*sigmoid(O3,deriv = True)
 
    f2_error = f3_delta.dot(weight23.T)
 
    f2_delta = f2_error*sigmoid(O2,deriv = True)
 
    f1_error = f2_delta.dot(weight12.T)     
 
    f1_delta = f1_error*sigmoid(O1,deriv = True)


    weight23 += O2.T.dot(f3_delta) #调整权重
    weight12 += O1.T.dot(f2_delta)
    weight01 += O0.T.dot(f1_delta)

    bias3 += f3_delta #调整偏倚
    bias2 += f2_delta
    bias1 += f1_delta
    
print ("outout after Training:")
print (O3)

3.3预测结果

迭代次数为1000时,预测结果为:
在这里插入图片描述
迭代次数为5000时,预测结果为:
在这里插入图片描述
迭代次数为10000时,预测结果为:
在这里插入图片描述
迭代次数为60000时,预测结果为:
在这里插入图片描述
可见,已经十分逼近预期结果。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/194908.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号