BP神经网络(python实现)

本文介绍了BP神经网络的构建原理,包括有监督学习过程、权重初始化、前向传播、反向传播及权重更新公式。重点讲解了单隐层网络的选择策略和优化方法,如动量项和自适应学习率。附带的Python代码示例有助于理解神经网络的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:

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

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

GitHub:fkdyc/Python_machine_learning (github.com)

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神经网络原理-优快云博客

权重:

神经元之间的连接强度由权重表示,权重的大小表示可能性的大小/也可理解为控制信号的重要性通常设定什么样的权重初始值,经常关系到神经网络收敛的快慢以及学习是否成功。权重衰减可以有效抑制过拟合、提高泛化能力,一般将初始值设置为较小的值,同时为了确保神经网络不同的权重的意义不丧失,所以必须随机生成初始值。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神经网络激活函数-优快云博客

反向传播:

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

网络误差:

当网络输出与期望输出不等时,会存在输出误差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神经网络-优快云博客

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-优快云博客其偏置的更新公式为:变化的偏置\Delta b = 学习率\eta * 误差信号 \delta _{k}^{o} 

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

优化BP算法:

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

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

在python代码中对应:

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

### BP神经网络Python实现 反向传播(Backpropagation,简称BP)算法是一种用于训练多层感知器的经典方法。以下是基于两层和深层神经网络模型的一个简单实现示例。 #### 1. 数据准备与初始化 为了构建一个简单的BP神经网络,我们需要定义权重矩阵、偏置项以及激活函数。这里采用ReLU作为隐藏层的激活函数,Sigmoid作为输出层的激活函数[^2]: ```python import numpy as np def initialize_parameters(layer_dims): """ 初始化参数。 参数: layer_dims -- 列表形式表示每一层节点数 返回: parameters -- 字典形式存储W和b """ np.random.seed(1) parameters = {} L = len(layer_dims) for l in range(1, L): parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1]) * 0.01 parameters['b' + str(l)] = np.zeros((layer_dims[l], 1)) return parameters ``` #### 2. 前向传播计算 前向传播过程中会依次计算每层的线性和非线性部分,并保存中间结果以便后续梯度计算。 ```python def forward_propagation(X, parameters): """ 实现前向传播过程。 参数: X -- 输入数据集 parameters -- 权重和偏差字典 返回: AL -- 输出层的结果 caches -- 缓存列表,包含Z和A的信息 """ caches = [] A = X L = len(parameters) // 2 for l in range(1, L): W = parameters["W" + str(l)] b = parameters["b" + str(l)] Z = np.dot(W, A) + b A = relu(Z) cache = ((A, W, b), Z) caches.append(cache) WL = parameters["W" + str(L)] bL = parameters["b" + str(L)] ZL = np.dot(WL, A) + bL AL = sigmoid(ZL) caches.append(((AL, WL, bL), ZL)) return AL, caches ``` #### 3. 计算成本函数 使用交叉熵损失来衡量预测值与真实标签之间的差异。 ```python def compute_cost(AL, Y, lambd=0, parameters=None): """ 计算代价函数。 参数: AL -- 预测概率向量 Y -- 真实标签向量 lambd -- 正则化超参,默认为0 parameters -- 如果启用正则化,则需传入此参数 返回: cost -- 成本值 """ m = Y.shape[1] logprobs = np.multiply(-np.log(AL),Y) + np.multiply(-np.log(1 - AL), 1 - Y) cross_entropy_cost = 1./m * np.sum(logprobs) if lambd != 0 and parameters is not None: L = len(parameters) // 2 regularization_term = 0 for l in range(1, L+1): regularization_term += np.sum(np.square(parameters[f"W{l}"])) L2_regularization_cost = (lambd / (2*m)) * regularization_term total_cost = cross_entropy_cost + L2_regularization_cost else: total_cost = cross_entropy_cost return total_cost ``` #### 4. 反向传播更新参数 利用链式法则逐层求导并调整权值与偏移量。 ```python def backward_propagation_with_regularization(AL, Y, caches, lambd=0): """ 执行带正则化的反向传播操作。 参数: AL -- 最终输出层的数据 Y -- 对应的真实标签 caches -- 存储着各层间关系的缓存结构体 lambd -- 控制强度大小的系数 返回: grads -- 含有所有梯度信息的字典型变量集合 """ grads = {} L = len(caches) m = AL.shape[1] Y = Y.reshape(AL.shape) dAL = -(np.divide(Y, AL) - np.divide(1-Y, 1-AL)) current_cache = caches[-1] grads["dA"+str(L)], grads["dW"+str(L)], grads["db"+str(L)] = linear_activation_backward(dAL, current_cache, activation="sigmoid", lambd=lambd) for l in reversed(range(L-1)): current_cache = caches[l] da_prev_temp, dw_temp, db_temp = linear_activation_backward(grads["dA" + str(l+2)], current_cache, activation='relu', lambd=lambd) grads["dA" + str(l+1)] = da_prev_temp grads["dW" + str(l+1)] = dw_temp grads["db" + str(l+1)] = db_temp return grads ``` #### 5. 更新规则应用 最后一步就是按照学习率逐步修正各个连接上的数值直至收敛为止。 ```python def update_parameters(parameters, grads, learning_rate): """ 使用梯度下降法更新参数。 参数: parameters -- 当前迭代轮次下的全部待优化对象 grads -- 上一轮得到的所有方向变化指示符 learning_rate -- 调控步伐长度的关键因子 返回: updated_params -- 经过本轮修改后的最新版配置方案 """ L = len(parameters)//2 for l in range(L): parameters["W" + str(l+1)] -= learning_rate * grads["dW" + str(l+1)] parameters["b" + str(l+1)] -= learning_rate * grads["db" + str(l+1)] return parameters ``` 以上即是一个完整的BP神经网络框架搭建流程概述[^1]^。 --- ###
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值