BP神经网络

损失函数:损失函数通常用来衡量神经网络在训练过程中预测输出与实际输出之间的差异。

梯度:在BP神经网络中,梯度是指损失函数相对于网络参数的变化率。在训练过程中,通过计算损失函数对于每个参数的梯度,可以确定应该如何调整参数以最小化损失函数。具体而言,梯度描述了损失函数在参数空间中的变化方向和速率。

对于一个具有 L个层的BP神经网络,梯度是一个关于所有权重和偏置项的矩阵,其中每个元素表示损失函数相对于对应参数的偏导数。

假设我们有一个损失函数 C,它是关于神经网络输出和真实标签的函数。在反向传播算法中,我们首先计算输出层的误差,然后通过链式法则依次向前计算每一层的误差,直到达到输入层。然后,我们利用误差计算每个参数的梯度,并使用梯度下降等优化算法来更新参数。

具体而言,梯度可以表示为:

  • BP神经网络训练的主要思想
  • 前向传播(激活函数实现)——输入的信号特征数据先映射到隐含层(通过计算每个神经元的加权和并应用激活函数计算每个神经元的输出),再映射到输出层(默认采用线性传递函数),得到期望输出值。计算损失——将期望输出值和实际测量值做比较,计算误差函数,再将误差反向传播——根据损失函数,链式法则计算每个权重和偏置项对损失函数的梯度,将梯度从输出层向输入层反向传播,以更新网络中所有权重和偏置项。更新参数——使用梯度下降或者其他优化算法,沿着梯度的负方向更新网络参数(调节权值和阈值),以最小化损失函数。重复训练——直到满足设定的目标误差或者最大迭代次数等终止准则,停止训练。
  • 具体算法:
  • 前向传播:
  • 激活函数:
  • 激活函数f(x)一般有对数几率函数sigmoid双曲正切函数tanh等等

  • 反向传播
  • 4.1构造(pycharm)
  • 1)初始化各个变量的情况
  • #? 构造3层BP神经网络架构
    class BP:
        #? 初始化函数:各层结点数、激活结点、权重矩阵、偏差、动量因子
        def __init__(self,num_in,num_hidden,num_out):
            # 输入层、隐藏层、输出层 的结点数
            self.num_in=num_in+1            # 输入层结点数 并增加一个偏置结点(阈值)
            self.num_hidden=num_hidden+1    # 隐藏层结点数 并增加一个偏置结点(阈值)
            self.num_out=num_out            # 输出层结点数
            # 激活BP神经网络的所有结点(向量)
            self.active_in=np.array([-1.0]*self.num_in)
            self.active_hidden=np.array([-1.0]*self.num_hidden)
            self.active_out=np.array([1.0]*self.num_out)
            # 创建权重矩阵
            self.weight_in=makematrix(self.num_in,self.num_hidden)      # in*hidden 的0矩阵
            self.weight_out=makematrix(self.num_hidden,self.num_out)    # hidden*out的0矩阵
            # 对权重矩阵weight赋初值
            for i in range(self.num_in):        # 对weight_in矩阵赋初值
                for j in range(self.num_hidden):
                    self.weight_in[i][j]=random_number(0.1,0.1)
            for i in range(self.num_hidden):    # 对weight_out矩阵赋初值
                for j in range(self.num_out):
                    self.weight_out[i][j]=random_number(0.1,0.1)
            # 偏差
            for j in range(self.num_hidden):
                self.weight_in[0][j]=0.1
            for j in range(self.num_out):
                self.weight_out[0][j]=0.1
            
            # 建立动量因子(矩阵)
            self.ci=makematrix(self.num_in,self.num_hidden)     # num_in*num_hidden 矩阵
            self.co=makematrix(self.num_hidden,self.num_out)    # num_hidden*num_out矩阵

  • 2)前向传播:计算出active_in、active_hidden、active_out:
  • #? 构造3层BP神经网络架构
    class BP:
        #? 信号正向传播
        def update(self,inputs):
            if len(inputs)!=(self.num_in-1):
                raise ValueError("与输入层结点数不符")
            # 数据输入 输入层
            self.active_in[1:self.num_in]=inputs
            # 数据在隐藏层处理
            self.sum_hidden=np.dot(self.weight_in.T,self.active_in.reshape(-1,1))   # 叉乘
                # .T操作是对于array操作后的数组进行转置操作
                # .reshape(x,y)操作是对于array操作后的数组进行重新排列成一个x*y的矩阵,参数为负数表示无限制,如(-1,1)转换成一列的矩阵
            self.active_hidden=sigmoid(self.sum_hidden) # active_hidden[]是处理完输入数据之后处理,作为输出层的输入数据
            self.active_hidden[0]=-1
            # 数据在输出层处理
            self.sum_out=np.dot(self.weight_out.T,self.active_hidden)
            self.active_out=sigmoid(self.sum_out)
            # 返回输出层结果
            return self.active_out

  • 3)反向传播:计算出error_hidden(即eh)、error_out(即  gj)
  • #? 构造3层BP神经网络架构
    class BP:
        #? 误差反向传播
        def errorbackpropagate(self,targets,lr,m):  # lr 学习效率
            if self.num_out==1:
                targets=[targets]
            if len(targets)!=self.num_out:
                raise ValueError("与输出层结点数不符")
            # 误差
            error=(1/2)*np.dot((targets.reshape(-1,1)-self.active_out).T,
                               (targets.reshape(-1,1)-self.active_out))
            
            # 输出层 误差信号
            self.error_out=(targets.reshape(-1,1)-self.active_out)*DS(self.sum_out) # DS(self.active_out)
            # 隐层 误差信号
            self.error_hidden=np.dot(self.weight_out,self.error_out)*DS(self.sum_hidden)    # DS(self.active_hidden)
     
            # 更新权值
            # 隐层
            self.weight_out=self.weight_out+lr*np.dot(self.error_out,self.active_hidden.reshape(1,-1)).T+m*self.co
            self.co=lr*np.dot(self.error_out,self.active_hidden.reshape(1,-1)).T
            # 输入层
            self.weight_in=self.weight_in+lr*np.dot(self.error_hidden,self.active_in.reshape(1,-1)).T+m*self.ci
            self.ci=lr*np.dot(self.error_hidden,self.active_in.reshape(1,-1)).T
     
            return error

  • 4)反向传播更新权值,计算出weight_in、weight_out:
  • 5)迭代训练:
  • #? 构造3层BP神经网络架构
    class BP:
        def train(self,pattern,itera=100,lr=0.2,m=0.1):
            for i in range(itera):
                error=0.0   # 每一次迭代将error置0
                for j in pattern:   # j为传入数组的第一维数据   #! *2?(1次迭代里面重复两次?)
                    inputs=j[0:self.num_in-1]   # 根据输入层结点的个数确定传入结点值的个数
                    targets=j[self.num_in-1:]   # 剩下的结点值作为输出层的值
                    self.update(inputs) # 正向传播 更新了active_out
                    error=error+self.errorbackpropagate(targets,lr,m)   # 误差反向传播 计算总误差
                if i%10==0:
                    print("########################误差 %-.5f ######################第%d次迭代" %(error, i))
  • 6)算法检验:
  • #? 算法检验——预测数据D
    # X 输入数据;D 目标数据
    X = list(np.arange(-1, 1.1, 0.1))   # -1~1.1 步长0.1增加
    D = [-0.96, -0.577, -0.0729, 0.017, -0.641, -0.66, -0.11, 0.1336, -0.201, -0.434, -0.5, 
         -0.393, -0.1647, 0.0988, 0.3072,0.396, 0.3449, 0.1816, -0.0312, -0.2183, -0.3201]
    A = X + D   # 数据合并 方便处理
    patt = np.array([A] * 2)    # 2*42矩阵  #! 为什么*2?
    # 创建神经网络,21个输入节点,13个隐藏层节点,21个输出层节点
    bp = BP(21, 13, 21)
    # 训练神经网络
    bp.train(patt)
    # 测试神经网络
    d = bp.test(patt)
    # 查阅权重值
    bp.weights()
     
    import matplotlib.pyplot as plt
    plt.plot(X, D, label="source data")  # D为真实值
    plt.plot(X, d, label="predict data")  # d为预测值
    plt.legend()
    plt.show()
  • 7)图解总结:
  • 总代码(pycharm)
  • #! BP神经网络(误差逆传播算法)
    #! 三层BP神经网络/三层感知机
     
    #? 激活函数sigmoid(x)、及其导数DS(x)
    import numpy as np
     
    # 双曲正切函数tanh
    def sigmoid(x):
        return np.tanh(x)
    def DS(x):
        return 1 - (np.tanh(x)) ** 2
    # 第90次迭代 误差0.00005
     
    #? 生成区间[a,b]内的随机数
    import random
    def random_number(a,b):
        return (b-a)*random.random()+a  # random.random()随机生成[0,1)内浮点数
     
    #? 生成一个m*n矩阵,并且设置默认零矩阵
    def makematrix(m,n,fill=0.0):
        a = []
        for i in range(m):
            a.append([fill]*n)    # 列表1*n会得到一个新列表,新列表元素为列表1元素重复n次。[fill]*3==[fill fill fill]
        return np.array(a)
     
    #? 构造3层BP神经网络架构
    class BP:
        #? 初始化函数:各层结点数、激活结点、权重矩阵、偏差、动量因子
        def __init__(self,num_in,num_hidden,num_out):
            # 输入层、隐藏层、输出层 的结点数
            self.num_in=num_in+1            # 输入层结点数 并增加一个偏置结点(阈值)
            self.num_hidden=num_hidden+1    # 隐藏层结点数 并增加一个偏置结点(阈值)
            self.num_out=num_out            # 输出层结点数
            # 激活BP神经网络的所有结点(向量)
            self.active_in=np.array([-1.0]*self.num_in)
            self.active_hidden=np.array([-1.0]*self.num_hidden)
            self.active_out=np.array([1.0]*self.num_out)
            # 创建权重矩阵
            self.weight_in=makematrix(self.num_in,self.num_hidden)      # in*hidden 的0矩阵
            self.weight_out=makematrix(self.num_hidden,self.num_out)    # hidden*out的0矩阵
            # 对权重矩阵weight赋初值
            for i in range(self.num_in):        # 对weight_in矩阵赋初值
                for j in range(self.num_hidden):
                    self.weight_in[i][j]=random_number(0.1,0.1)
            for i in range(self.num_hidden):    # 对weight_out矩阵赋初值
                for j in range(self.num_out):
                    self.weight_out[i][j]=random_number(0.1,0.1)
            # 偏差
            for j in range(self.num_hidden):
                self.weight_in[0][j]=0.1
            for j in range(self.num_out):
                self.weight_out[0][j]=0.1
            
            # 建立动量因子(矩阵)
            self.ci=makematrix(self.num_in,self.num_hidden)     # num_in*num_hidden 矩阵
            self.co=makematrix(self.num_hidden,self.num_out)    # num_hidden*num_out矩阵
     
        #? 信号正向传播
        def update(self,inputs):
            if len(inputs)!=(self.num_in-1):
                raise ValueError("与输入层结点数不符")
            # 数据输入 输入层
            self.active_in[1:self.num_in]=inputs
            # 数据在隐藏层处理
            self.sum_hidden=np.dot(self.weight_in.T,self.active_in.reshape(-1,1))   # 叉乘
                # .T操作是对于array操作后的数组进行转置操作
                # .reshape(x,y)操作是对于array操作后的数组进行重新排列成一个x*y的矩阵,参数为负数表示无限制,如(-1,1)转换成一列的矩阵
            self.active_hidden=sigmoid(self.sum_hidden) # active_hidden[]是处理完输入数据之后处理,作为输出层的输入数据
            self.active_hidden[0]=-1
            # 数据在输出层处理
            self.sum_out=np.dot(self.weight_out.T,self.active_hidden)
            self.active_out=sigmoid(self.sum_out)
            # 返回输出层结果
            return self.active_out
        
        #? 误差反向传播
        def errorbackpropagate(self,targets,lr,m):  # lr 学习效率
            if self.num_out==1:
                targets=[targets]
            if len(targets)!=self.num_out:
                raise ValueError("与输出层结点数不符")
            # 误差
            error=(1/2)*np.dot((targets.reshape(-1,1)-self.active_out).T,
                               (targets.reshape(-1,1)-self.active_out))
            
            # 输出层 误差信号
            self.error_out=(targets.reshape(-1,1)-self.active_out)*DS(self.sum_out) # DS(self.active_out)
            # 隐层 误差信号
            self.error_hidden=np.dot(self.weight_out,self.error_out)*DS(self.sum_hidden)    # DS(self.active_hidden)
     
            # 更新权值
            # 隐层
            self.weight_out=self.weight_out+lr*np.dot(self.error_out,self.active_hidden.reshape(1,-1)).T+m*self.co
            self.co=lr*np.dot(self.error_out,self.active_hidden.reshape(1,-1)).T
            # 输入层
            self.weight_in=self.weight_in+lr*np.dot(self.error_hidden,self.active_in.reshape(1,-1)).T+m*self.ci
            self.ci=lr*np.dot(self.error_hidden,self.active_in.reshape(1,-1)).T
     
            return error
     
        #? 测试
        def test(self,patterns):
            for i in patterns:  # i为传入数组的第一维数据
                print(i[0:self.num_in-1],"->",self.update(i[0:self.num_in-1]))
            return self.update(i[0:self.num_in-1])  # 返回测试结果,用于作图
     
        #? 权值
        def weights(self):
            print("输入层的权值:")
            print(self.weight_in)
            print("输出层的权值:")
            print(self.weight_out)
        
        def train(self,pattern,itera=100,lr=0.2,m=0.1):
            for i in range(itera):
                error=0.0   # 每一次迭代将error置0
                for j in pattern:   # j为传入数组的第一维数据
                    inputs=j[0:self.num_in-1]   # 根据输入层结点的个数确定传入结点值的个数
                    targets=j[self.num_in-1:]   # 剩下的结点值作为输出层的值
                    self.update(inputs) # 正向传播 更新了active_out
                    error=error+self.errorbackpropagate(targets,lr,m)   # 误差反向传播 计算总误差
                if i%10==0:
                    print("########################误差 %-.5f ######################第%d次迭代" %(error, i))
     
     
    #? 算法检验——预测数据D
    # X 输入数据;D 目标数据
    X = list(np.arange(-1, 1.1, 0.1))   # -1~1.1 步长0.1增加
    D = [-0.96, -0.577, -0.0729, 0.017, -0.641, -0.66, -0.11, 0.1336, -0.201, -0.434, -0.5, 
         -0.393, -0.1647, 0.0988, 0.3072,0.396, 0.3449, 0.1816, -0.0312, -0.2183, -0.3201]
    A = X + D   # 数据合并 方便处理
    patt = np.array([A] * 2)    # 2*42矩阵
    # 创建神经网络,21个输入节点,13个隐藏层节点,21个输出层节点
    bp = BP(21, 13, 21)
    # 训练神经网络
    bp.train(patt)
    # 测试神经网络
    d = bp.test(patt)
    # 查阅权重值
    bp.weights()
     
     
    import matplotlib.pyplot as plt
    plt.plot(X, D, label="source data")  # D为真实值
    plt.plot(X, d, label="predict data")  # d为预测值
    plt.legend()
    plt.show()

    4.2构造(matlab)

  • %% 此程序为matlab编程实现的BP神经网络
    % 清空环境变量
    clear
    close all
    clc
    
    %%第一步 读取数据
    input=randi([1 20],200,2);  %载入输入数据
    output=input(:,1)+input(:,2);  %载入输出数据
    
    %% 第二步 设置训练数据和预测数据
    input_train = input(1:190,:)';
    output_train =output(1:190,:)';
    input_test = input(191:200,:)';
    output_test =output(191:200,:)';
    %节点个数
    inputnum=2; % 输入层节点数量
    hiddennum=5;% 隐含层节点数量
    outputnum=1; % 输出层节点数量
    %% 第三本 训练样本数据归一化
    [inputn,inputps]=mapminmax(input_train);%归一化到[-1,1]之间,inputps用来作下一次同样的归一化
    [outputn,outputps]=mapminmax(output_train);
    %% 第四步 构建BP神经网络
    net=newff(inputn,outputn,hiddennum,{'tansig','purelin'},'trainlm');% 建立模型,传递函数使用purelin,采用梯度下降法训练
    
    W1= net. iw{1, 1};%输入层到中间层的权值
    B1 = net.b{1};%中间各层神经元阈值
    
    W2 = net.lw{2,1};%中间层到输出层的权值
    B2 = net. b{2};%输出层各神经元阈值
    
    %% 第五步 网络参数配置( 训练次数,学习速率,训练目标最小误差等)
    net.trainParam.epochs=1000;         % 训练次数,这里设置为1000次
    net.trainParam.lr=0.01;                   % 学习速率,这里设置为0.01
    net.trainParam.goal=0.00001;                    % 训练目标最小误差,这里设置为0.00001
    
    %% 第六步 BP神经网络训练
    net=train(net,inputn,outputn);%开始训练,其中inputn,outputn分别为输入输出样本
    
    %% 第七步 测试样本归一化
    inputn_test=mapminmax('apply',input_test,inputps);% 对样本数据进行归一化
    
    %% 第八步 BP神经网络预测
    an=sim(net,inputn_test); %用训练好的模型进行仿真
    
    %% 第九步 预测结果反归一化与误差计算     
    test_simu=mapminmax('reverse',an,outputps); %把仿真得到的数据还原为原始的数量级
    error=test_simu-output_test;      %预测值和真实值的误差
    
    %%第十步 真实值与预测值误差比较
    figure('units','normalized','position',[0.119 0.2 0.38 0.5])
    plot(output_test,'bo-')
    hold on
    plot(test_simu,'r*-')
    hold on
    plot(error,'square','MarkerFaceColor','b')
    legend('期望值','预测值','误差')
    xlabel('数据组数')
    ylabel('样本值')
    title('BP神经网络测试集的预测值与实际值对比图')
    
    [c,l]=size(output_test);
    MAE1=sum(abs(error))/l;
    MSE1=error*error'/l;
    RMSE1=MSE1^(1/2);
    disp(['-----------------------误差计算--------------------------'])
    disp(['隐含层节点数为',num2str(hiddennum),'时的误差结果如下:'])
    disp(['平均绝对误差MAE为:',num2str(MAE1)])
    disp(['均方误差MSE为:       ',num2str(MSE1)])
    disp(['均方根误差RMSE为:  ',num2str(RMSE1)])
    
    % 附
    eval(['web ', char([104	116	116	112	115	58	47	47	98	108	111	103	46	99	115	100	110	46	110	101	116	47	113	113	95	53	55	57	55	49	52	55	49	47	97	114	116	105	99	108	101	47	100	101	116	97	105	108	115	47	49	50	49	55	54	55	48	48	52	32	45	98	114	111	119	115	101	114])])
    
    eval(['web ', char([104,116,116,112,115,58,47,47,109,98,100,46,112,117,98,47,111,47,98,114,101,97,100,47,109,98,100,45,89,90,109,84,109,112,116,118,32,45,98,114,111,119,115,101,114])])
    
    eval(['web ', char([104,116,116,112,115,58,47,47,109,98,100,46,112,117,98,47,111,47,117,112,115,95,100,111,119,110,115,47,119,111,114,107,32,45,98,114,111,119,115,101,114])]) 
    
    
    
    

    总结

  • 本文对BP神经网络的基本概念,以及推导过程。给出了一版通俗易懂的BP算法的讲解过程,其中参照了了本站部分博客的见解,如若有侵权倾向,请联系本人。若有错误,还望不吝赐教。

  • 参照博客为:

    ​​​​​​CJ-leaf

    春风不曾温柔

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值