手写BP(反向传播)算法

BP算法为深度学习中参数更新的重要角色,一般基于loss对参数的偏导进行更新。

一些根据均方误差,每层默认激活函数sigmoid(不同激活函数,则更新公式不一样)

假设网络如图所示:

则更新公式为:

以上列举了最后2层的参数更新方式,第一层的更新公式类似,即上一层的误差来自于下一层所有的神经元,e的更新就是不断建立在旧的e上(这里g可以当做初始的e)

下面上代码:

1,BP算法

# 手写BP算法
import numpy as np

# 先更新参数,再继续传播
# layers:包括从输入层到输出层,每层参数为:连接权重w,阈值b,输出y。类型为np.array
# 对于输入层,w和b随便是啥,反正不用,只需y即原始输入
# 基于激活函数sigmoid
# loss为均方误差
def bp(layers,labels,lr=0.001):
#     翻转layers,反向传播
    reversed_layers=layers[::-1]
#     输出层
    output_w,output_b,output_y=reversed_layers[0]
    g=np.array([output_y[j]*(1-output_y[j])*(labels[j]-output_y[j]) for j in range(len(labels))])
#     最后一层更新较为特殊,先进行更新
    delta_w=np.empty(shape=(output_w.shape[0],output_w.shape[1]))
#     上一层输出y
    last_y=reversed_layers[1][2]
    for h in range(output_w.shape[0]):
        for j in range(output_w.shape[1]):
            delta_w[h,j]=lr*g[j]*last_y[h]
    delta_b=-lr*g
    new_w=output_w+delta_w
    new_b=output_b+delta_b
    reversed_layers[0][0]=new_w
    reversed_layers[0][1]=new_b

    #     从倒数第二层到第二层进行更新,每次取3层进行计算,由公式知,需用到上一层输出即下一层权重
    for i in range(1,len(reversed_layers)-1):
#         下一层w
        next_w=reversed_layers[i-1][0]
        out_w,out_b,out_y=reversed_layers[i]
#         上一层y
        last_y=reversed_layers[i+1][2]
#         更新辅助量,意思即上一层每个神经元的误差都由下一层所有神经元的误差反向传播,体现在这里内循环
        e=np.empty(shape=(len(out_b),1))
        for h in range(len(out_b)):
            temp=0
            for j in range(next_w.shape[1]):
                temp+=next_w[h,j]*g[j]
            e[h]=out_y[h]*(1-out_y[h])*temp
        delta_w=np.empty(shape=(out_w.shape[0],out_w.shape[1]))
        for h in range(out_w.shape[0]):
            for j in range(out_w.shape[1]):
                delta_w[h,j]=lr*e[j]*last_y[h]
        delta_b=-lr*e
        out_new_w=out_w+delta_w
        out_new_b=out_b+delta_b
        reversed_layers[i][0]=out_new_w
        reversed_layers[i][1]=out_new_b
        g=np.copy(e)
    return layers

以上假设每个神经元的输出为一个实数y值

2,构建测试

构建平面上的点(x,y),将y是否大于0作为划分,进行训练。只使用了一层网络,sigmoid激活

X=[]
Y=[]
for i in range(-100,100):
    for j in range(-100,100):
        X.append([[i],[j]])
        if j>=0:
            Y.append([1])
        else:
            Y.append([0])
X=np.array(X)
Y=np.array(Y)

3,划分训练,验证集

indexs=np.random.choice(range(40000),size=30000)

x_train=np.array([X[i] for i in indexs])
y_train=np.array([Y[i] for i in indexs])

x_val=np.array([X[i] for i in np.setdiff1d(range(40000),indexs))
y_val=np.array([Y[i] for i in np.setdiff1d(range(40000),indexs))

4,训练。这里只对所有样本训练了一轮。使用随机初始化的w和b,每个样本都会改变w和b

# 使用sigmoid激活函数
def output(input_x,w,b):
    res=0
    t=np.matmul(np.transpose(w),input_x)-b
    return 1./(1+np.power(np.e,-t))

w1=np.random.normal(size=(2,1))
b1=np.array([[0]])
for i in range(len(x_train)):
    y0=x_train[i]
    l=y_train[i]
    input_layers=[]
    w0,b0=(0,0)
    input_layers.append([w0,b0,y0])
    input_layers.append([w1,b1,output(y0,w1,b1)])
    input_layers=bp(input_layers,l)
    w1=input_layers[1][0]
    b1=input_layers[1][1]
    
# w:  [[0.11213777]
#  [1.67425498]]
# b:  [[0.0001581]]
print('w: ',w1)
print('b: ',b1)

5,验证。从分出的验证集选取部分验证即可

for xx in x_val[:50]:
    print(xx.reshape((2,)),output(xx,w1,b1).reshape((1,)))

验证结果如下:

[63 68] [1.]
[-100  -99] [1.39636722e-77]
[-100  -98] [7.44936654e-77]
[63 69] [1.]
[-100  -96] [2.12011171e-75]
[-100  -94] [6.03390049e-74]
[-100  -93] [3.21897678e-73]
[63 74] [1.]
[-100  -91] [9.16130293e-72]
[63 75] [1.]
[63 76] [1.]
[63 77] [1.]
[63 78] [1.]
[-100  -86] [3.95872874e-68]
[-100  -85] [2.11191018e-67]
[63 79] [1.]
[-100  -83] [6.01055872e-66]
[-100  -82] [3.20652436e-65]
[63 82] [1.]
[-100  -80] [9.12586299e-64]
[-100  -79] [4.86848285e-63]
[63 83] [1.]
[-100  -77] [1.38558459e-61]
[-100  -76] [7.39184317e-61]
[63 89] [1.]
[-100  -74] [2.10374039e-59]
[63 91] [1.]
[-100  -72] [5.98730724e-58]
[-100  -71] [3.19412012e-57]
[-100  -70] [1.70400531e-56]
[-100  -69] [9.09056014e-56]
[-100  -68] [4.84964942e-55]
[-100  -67] [2.58720025e-54]
[-100  -66] [1.38022454e-53]
[63 99] [1.]
[-100  -64] [3.92815978e-52]
[-100  -63] [2.09560219e-51]
[  64 -100] [2.53988133e-70]
[-100  -61] [5.9641457e-50]
[ 64 -97] [3.85631522e-68]
[ 64 -96] [2.05727442e-67]
[-100  -58] [9.05539386e-48]
[ 64 -90] [4.74253371e-63]
[ 64 -89] [2.53005596e-62]
[ 64 -87] [7.20061393e-61]
[-100  -52] [2.08749548e-43]
[ 64 -84] [1.09327301e-58]
[-100  -50] [5.94107377e-42]
[ 95 -13] [1.49260907e-05]
[-100  -45] [2.56722211e-38]

6,总结:可以看出,这50个验证样本上都没问题,虽然想到的测试方案有点low,但一时找不到啥好数据。由此验证BP算法的正确性。如有可疑或不足之处,敬请告知。

 

转载于:https://www.cnblogs.com/lunge-blog/p/11616175.html

### BP神经网络模型概述 BP(Backpropagation)神经网络是一种多层前馈型的人工神经网络,其核心在于通过反向传播算法来调整网络内部各层之间的连接权重和偏置项。该类网络通常由输入层、一个或多个隐藏层以及输出层构成。 #### 反向传播算法工作原理 反向传播算法旨在最小化预测值与实际标签之间的差异所定义的损失函数。这一过程分为两个主要阶段: - **正向传递**:数据从输入层依次经过每一层直至到达输出层,在此期间每层节点依据接收到的信息执行加权求和并应用激活函数得到下一层所需的输入信号。 - **反向传播**:一旦获得了最终输出,则计算当前估计结果同真实目标间的差距即误差;接着利用链式法则沿路径回溯至初始位置,以此评估各个参数对于总误的影响程度,并据此修正它们以便更好地拟合训练样本集[^1]。 具体而言,当涉及到具体的数学表达时,为了更新某一层中的某个特定权重 \( w_{ij} \),需要考虑它如何影响整个系统的性能指标—通常是均方根误差或其他形式的成本函数\( E \)[^2]。因此,针对单个实例的数据点,可以通过下面的方式来进行局部梯度下降操作: \[ Δw_{ji}(n)=ηδ_j(n)x_i(n)+αΔw_{ji}(n−1)\] 其中, - \( η \) 表示学习速率; - \( δ_j \) 是关于第 j 层单元产生的误差项; - \( x_i \) 则代表来自上一层次 i 的净输入; - 而最后一部分体现了动量因子的作用,有助于加速收敛速度同时减少震荡现象的发生概率[^3]。 #### 实现方法 以下是基于Python的一个简单实现例子,用于展示如何构建一个多层感知机并通过BP算法对其进行训练的过程: ```python import numpy as np def sigmoid(x): return 1 / (1 + np.exp(-x)) class NeuralNetwork: def __init__(self, layers): self.weights = [] for i in range(len(layers)-1): weight_matrix = 2 * np.random.rand(layers[i], layers[i+1]) - 1 bias_vector = 2 * np.random.rand(1,layers[i+1])-1 self.weights.append((weight_matrix,bias_vector)) def forward(self, X): activations = [X] z_values = [] for W,b in self.weights[:-1]: net_input = np.dot(X,W) + b.T activation = sigmoid(net_input) z_values.append(net_input) activations.append(activation) final_layer_W,final_layer_b=self.weights[-1] last_net_input=np.dot(activations[-1],final_layer_W)+final_layer_b.T output=sigmoid(last_net_input) activations.append(output) z_values.append(last_net_input) return activations,z_values def backward(self, y_true, activations, z_values): deltas = [(y_true -1]*(1-activations[-1])] gradients=[] for l in reversed(range(len(z_values))): delta=deltas[l]*z_values[l]*(1-z_values[l]) if l>0: gradient=(np.atleast_2d(deltas[l]).T @ np.atleast_2d(activations[l])) [email protected][l][0].T*activations[l]*(1-activations[l]) deltas.insert(0,prev_delta) else: gradient=(np.atleast_2d(delta).T @ np.atleast_2d(activations[0])) gradients.append(gradient) return list(reversed(gradients)),list(reversed(deltas))[:len(self.weights)] def update_weights(self,gradients,alpha,momentum): updated_weights=[] for idx,(W,b),(grad_w,_)in enumerate(zip(self.weights,gradients)): new_weight=W+(alpha*(grad_w-momentum*self.prev_grads[idx]))if hasattr(self,'prev_grads')else alpha*grad_w updated_bias=b+(alpha*(momentum*self.prev_dbs[idx]-grad_w))if hasattr(self,'prev_grads')else alpha*(-grad_w) updated_weights.append((new_weight,updated_bias)) self.weights=updated_weights nn = NeuralNetwork([2, 4, 1]) for epoch in range(num_epochs): outputs, zs = nn.forward(training_data) grads,delta = nn.backward(labels,outputs,zs) nn.update_weights(grads,learning_rate,momentum_term) ``` 上述代码片段展示了创建了一个具有两层隐含层结构的MLP,并实现了基本版本的BP算法逻辑。需要注意的是这只是一个简化版的例子,实际应用中还需要加入更多细节处理如批量大小的选择、正则化技术的应用等。 #### 应用场景 BP神经网络广泛应用于模式识别领域内的诸多方面,包括但不限于图像分类、语音识别、自然语言处理等领域内复杂任务建模的任务当中。例如,在手写体数字识别项目里,通过对大量标注过的MNIST数据库图片的学习,能够建立起一套高效准确的手写字母辨识系统。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值