BP神经网络(python实现)

注:

本文仅实现了BP框架搭建,并未实例进行(后续可能会添加)

在本文的代码实现中,是从神经网络的组件开始搭建,而并不是使用Linear层,因此可以尝试看代码配合原理理解,代码已附带,若有需要也可以评论区回复领取

BP神经网络原理:

BP神经网络的学习属于有监督学习,需要一组已知期望输出的学习样本集。训练时先使用随机值作为权值(即权重,取值在前向传播部分有介绍),输入学习样本得到网络的输出。然后根据输出值与期望输出计算误差,再由误差根据某种准则逐层修改权值,使误差减小。如此反复,直到误差不再下降,网络就完成训练。

BP神经网络组成:

bp神经网络由输入层、隐藏层、输出层组成

其中x_i表示第个i输入节点的数据,而最后的y_i^\prime则表示预测值,而训练过程则分为了前向传播与反向传播两个过程,而该图像中的圆则表示神经网络节点。

网络层数:

在一般的问题中,单隐层结构就能够满足需求,理论上来说单个隐含层的网络可以通过适当增加神经元节点的个数实现任意非线性映射,但如果样本过多,可以选择增加一个隐含层减少网络规模。

但当隐含层的数目超过一定值,反而会使网络的性能降低,因此采用含有一个隐含层的三层BP网络可以完成任意n维到m维的映射。所以建议使用单隐层结构

输入层神经元个数:

对于输入节点的考虑,就比如《人工神经网络》一书中的案例“基于BP神经网络的瓦斯压力快速反演”,选取输入层神经元个数时,选取的是对于煤层瓦斯压力影响最主要的四个因素。但当我们无法进行判断主要因素同时又拥有多个输入参数是,我们可以采取主成分分析法(PCA)进行分析,选取出主要的影响因素确定输入节点个数

隐含层神经元个数:

隐含层的单元数直接影响网络的非线性性能,它与解决问题的复杂性有关

一般对于三层前向网络隐含层节点数有如下经验公式

j=\sqrt{n+m}+a

其中:m为输出层节点个数;n为输入层节点个数;a1\thicksim10的常数

前向传播:

 

数据在输入后乘以权重再加上偏置得到输出结果,再将结果作为输入在激活函数中计算,再将计算结果作为下一个节点的输入传递下去,而最终的输出结果则是我们想实现的预测结果

其中\omega为该连接上的权重,b为该节点的偏置,f为激活函数

注:此处为了绘图便捷,仅绘制了单一神经元,但在计算的过程中应当是多个输入计算然后加权和

举例说明:

可参考此博客的讲解部分:

BP神经网络原理-CSDN博客

权重:

神经元之间的连接强度由权重表示,权重的大小表示可能性的大小/也可理解为控制信号的重要性通常设定什么样的权重初始值,经常关系到神经网络收敛的快慢以及学习是否成功。权重衰减可以有效抑制过拟合、提高泛化能力,一般将初始值设置为较小的值,同时为了确保神经网络不同的权重的意义不丧失,所以必须随机生成初始值。BP网络训练开始之前,对网络的权重和偏置值进行初始化,权重取[-1,1]之间的一个随机数,此处取值是基于经验的取值

同时另一经验公式为:

\left ( \frac{-2.4}{F},\frac{2.4}{F} \right )或者\left ( -\frac{3}{\sqrt{F}} ,\frac{3}{\sqrt{F}}\right )

其中F为权值输入端连接的神经元个数

偏置:

是为了正确分类样本/控制神经元被激活的容易程度,偏置依据经验取[0,1]间的一个随机数

激活函数:

激活函数:起非线性映射的作用,将神经元的输出幅度限制在一定范围内,一般是限制再(-1,1)或者(0,1),一般选取激活函数为sigmoid函数,对于激活函数的介绍可以参考这篇博客:通用化BP神经网络-激活函数_bp神经网络激活函数-CSDN博客

反向传播:

反向传播的实质为根据误差调整权值

网络误差:

当网络输出与期望输出不等时,会存在输出误差E:

E=\frac{1}{2}\sum_{k=1}^{l}(d_k-o_k)^2

其中为d_k期望输出,而为o_k网络输出

注:此处的计算公式又称为损失函数

因此网络误差实则是各层权值的函数,因此调整权值可改变误差。而调整权值的目的是使误差不断减小,因此应使权值得调整量与误差的梯度下降成正比,即:

\Delta \omega = -\eta \frac{\partial E}{\partial \omega}

其中为\omega权重,而\eta为学习率,负号则表示梯度下降

权重更新公式可参考:神经网络之BP神经网络-CSDN博客

python实现部分:

此处并不会将其实现部分写的过于详细,在我的python文件中有注释,能够帮助理解,故而此部分主要讲解不易于理解的部分,如果想要详细的python讲解部分可以去查阅一下其他博客(后续可能会录制视频上传b站)

反向传播部分:

在反向传播部分主要是在进行通过误差调整权重,而权重的调整公式如下(为便于理解,将配合python代码讲解)

计算输出层/隐含层误差:

在此处的代码中:


        output_deltas=self.output_nodes * [0.0]
        for i in range(self.output_nodes):
            error = label[i] - self.output_values[i]  
            output_deltas[i] = error * sigmoid_derivative(self.output_values[i])
        # 计算隐含层误差
        hidden_deltas=self.hidden_nodes * [0.0]
        for i in range(self.hidden_nodes):
            error = 0.0
            for j in range(self.output_nodes):
                error += output_deltas[j] * self.hidden_weights[i][j]
            hidden_deltas[j] = error * sigmoid_derivative(self.hidden_values[i])

1.error与output_deltas[i]这两个计算公式,首先error此处得到了输出层各个节点的输出值与期望值的差,然后output_deltas此处计算公式为:

error = o_{i} - y_{i}

\delta _{k}^{o}= error * f'({o_{i}})

此处的\delta _{k}^{o}表示为误差信号,此处的f{}'表示激活函数的导数,o_{i}表示输出层的输出值

注:因为将权值计算公式拆开,所以此处不好理解,后续会提到

2.对于隐含层到输出层的误差与上述的计算公式又有所不同,此处的误差信号\delta _{j}^{y}计算公式为:

\sum_{k=1}^{l} \delta _{k}^{o}\omega _{jk}

此处的\omega _{jk}表示为从隐含层到输出层的权重值,\delta _{k}^{o}则表示为输出层的误差信号

更新权重:

# 更新隐含层到输出层的权重
        for i in range(self.hidden_nodes):
            for j in range(self.output_nodes):
                change = output_deltas[j] * self.hidden_values[i]
                self.hidden_weights[i][j] += learn * change
                self.output_bias[j] += learn * change
        # 更新输入层到隐含层的权重
        for i in range(self.input_nodes):
            for j in range(self.hidden_nodes):
                change = hidden_deltas[j] * self.input_values[i]
                self.input_weights[i][j] += learn * change
                self.input_bias[j] += learn * change

1.在更新隐含层到输出层的权重时,更新权重的最终公式为:变化的权重 = 学习率\eta * 误差信号\delta _{k}^{o} * 隐含层的输出值

2.在更新输入层到隐含层的权重时,更新权重的最终公式为:变化的权重 = 学习率 * 误差信号\delta _{j}^{y} * 输入层的输出值

偏置更新:

由于我的python代码主要来自于网络上寻找的部分,故而在偏置更新部分与博客大部分的理论公式相冲突,就比如在隐含层到输出层的偏置更新中公式为:变化的偏置\Delta b = 学习率\eta * 误差e_{k}(此处的误差为输出层第k个节点处,期望输出值与实际输出值的差)

但在此篇博客中BP算法,用梯度下降法更新权值W与偏置项b-CSDN博客其偏置的更新公式为:变化的偏置\Delta b = 学习率\eta * 误差信号 \delta _{k}^{o} 

所以大家在偏置更新这段代码编写过程中可以依据自己的选择进行更改,作者选择的是第二种方法

优化BP算法:

在误差的权空间中,考虑到误差曲面形状的固有性,易形成局部最小而达不到全局最优,因此可以通过添加动量项,自适应调节学习率,引入陡度因子来改进算法

在本文附带的python代码中就通过增加动量项来改进

在python代码中对应:

其中correct等效于\alpha,而乘的那部分等效于\Delta W(t-1)

  • 17
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,以下是一个简单的BP神经网络Python实现,供您参考: ```python import numpy as np class NeuralNetwork: def __init__(self, input_size, hidden_size, output_size): self.input_size = input_size self.hidden_size = hidden_size self.output_size = output_size self.learning_rate = 0.1 # 初始化权重和偏置 self.weights1 = np.random.randn(self.input_size, self.hidden_size) self.bias1 = np.zeros((1, self.hidden_size)) self.weights2 = np.random.randn(self.hidden_size, self.output_size) self.bias2 = np.zeros((1, self.output_size)) def sigmoid(self, z): return 1 / (1 + np.exp(-z)) def sigmoid_derivative(self, z): return self.sigmoid(z) * (1 - self.sigmoid(z)) def feedforward(self, X): # 计算隐藏层输入 self.z1 = np.dot(X, self.weights1) + self.bias1 # 计算隐藏层输出 self.a1 = self.sigmoid(self.z1) # 计算输出层输入 self.z2 = np.dot(self.a1, self.weights2) + self.bias2 # 计算输出层输出 self.a2 = self.sigmoid(self.z2) return self.a2 def backward(self, X, y, output): # 计算输出层误差 self.output_error = y - output # 计算输出层梯度 self.output_gradient = self.output_error * self.sigmoid_derivative(self.z2) # 计算隐藏层误差 self.hidden_error = np.dot(self.output_gradient, self.weights2.T) # 计算隐藏层梯度 self.hidden_gradient = self.hidden_error * self.sigmoid_derivative(self.z1) # 更新权重和偏置 self.weights2 += self.learning_rate * np.dot(self.a1.T, self.output_gradient) self.bias2 += self.learning_rate * np.sum(self.output_gradient, axis=0, keepdims=True) self.weights1 += self.learning_rate * np.dot(X.T, self.hidden_gradient) self.bias1 += self.learning_rate * np.sum(self.hidden_gradient, axis=0) def train(self, X, y): output = self.feedforward(X) self.backward(X, y, output) def predict(self, X): return self.feedforward(X) ``` 使用示例: ```python X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) y = np.array([[0], [1], [1], [0]]) nn = NeuralNetwork(2, 3, 1) for i in range(10000): nn.train(X, y) print(nn.predict(X)) ``` 这个实现中,我们使用了 sigmoid 作为激活函数,并且采用了随机梯度下降法来更新权重和偏置。当然,这只是一个简单的实现,实际应用中可能需要更复杂的网络结构和优化算法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值