深度学习-一篇入门

【原理】一篇入门之-什么是BP反向传播算法

作者 : 老饼 发表日期 : 2023-05-08 11:42:27 更新日期 : 2024-12-04 01:51:25
本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com



BP反向传播算法是一种常用于计算前馈型神经网络(例如MLP、CNN等)的梯度的算法

本文讲解BP反向传播算法的原理、算法流程,以及展示一个使用BP算法计算梯度的例子

通过本文,可以了解BP算法是什么,如何使用BP算法来计算前馈型神经网络的梯度,以及代码实现






      01. 什么是BP算法     




本节讲解什么是BP算法,以及相关公式




     BP算法是什么    


BP算法(反向传播算法,Back Propagation Algorithm)是一种用于计算前馈式神经网络的梯度的方法
 例如MLP、CNN等就是前馈型神经网络,由于BP算法最早用于MLP的梯度计算,因此MLP往往也被称为BP神经网络
 BP算法是如何计算梯度的
简单来说,BP算法按如下方式计算前馈模型的参数梯度
  BP算法是如何计算梯度的
设前馈式神经网络共有K个计算层,BP算法从第K层(即最后一层)开始计算
它先算出第K层输出的梯度,再将输出的梯度传播到每个参数,从而得到第K层的参数梯度
 在计算完第K层的参数梯度后,将输出梯度后馈到第K-1层,得到K-1层的输出梯度
如法泡制,在算得第K-1层的参数梯度后,又将梯度进一步后馈到K-2层,......如此反复,直到第一层

总的来说,BP算法就是从最后一层开始往前逐层计算,通过输出梯度来算得当层的参数梯度以及前层的输出梯度





      BP算法的计算公式     


BP算法就是从最后一层的输出梯度开始,不断将梯度向前层传播,并同时计算出本层的梯度
 前馈神经网络的梯度传播
 使用BP算法计算梯度时,主要涉及三条公式,如下:
1. 最后一层输出的梯度公式                                                                          
最后一层的输出公式直接由损失函数求得                                               
                                           
  解说:它是较为简单的,例如损失函数为时          
可容易算得:
                         

2. 第层的输出梯度如何传播到参数上                                                           
在算得第k层输出的梯度后,用如下公式来计算该层参数的梯度:            
                       
         其中,是第k层的输出梯度 , 而是该层输出对该层参数的梯度
                  解说:该公式只需要将可以看作复合函数,根据链式法则即可得到:
              


3. 第k层的输出梯度如何后馈到k-1层的输出上                                               
在算得第k层输出的梯度后,用如下公式来计算k-1层的输出梯度             
              
                其中,是第k层的输出梯度,而是第k层输出对k-1层第i个输出的梯度
                 解说:该公式只需要将L可以看作复合函数,根据链式法则即可得到:
                
由上述原理可知,使用BP算法计算前馈型神经网络的梯度时
 需要预计算如下梯度:
1. :损失函数对于最后一层输出的梯度             
         2. :每层网络的输出关于前层网络第i个输出的梯度 
3. :每层网络的输出关于该层各个参数的梯度   
说明:
(1) 由于k-1层的输出就是第k层的输入,更多时候会将写为
                  虽然只是形式的改变,但这样可以使得每层需要计算的梯度公式都是层独立的 
              (2) 不管是还是,都可能要用到的值,因此需要预计算每层的输入
     由于每层的输入就是上层的输出,因此实际就是预计算每层的输出   








   02. BP算法的流程    





本节讲解BP算法的具体流程,以及算法流程图





    BP算法的流程     


BP算法采用后馈式来计算前馈网络的单个样本的参数梯度
 BP算法流程图如下:
BP算法流程图
 它的计算流程如下:
一、前馈                                                                                             
通过前馈计算,获得每层的输入与输出:                                
 将第一层的输入
进行前馈,得到:      
注意,同时也就得到了各层的输入                         
 
二、后馈                                                                                               
 
1. 初始化最后一层的输出梯度                                                     
 直接计算损失函数对于最后一层输出的梯度:                      
  
2. 逐层后馈                                                                              
从最后一层开始逐层前馈,对于第k层有:                          
2.1. 计算当前层参数的梯度:                                            
 
            
 
2.2. 计算当前层输入的梯度:                                              
 
       
    备注:第k层输入入的梯度,也就是第k-1层的输出梯度
即:   
                                  
总的来说,BP算法就是从最后一层开始,每层算出当层的参数梯度和输入梯度(即前层输出梯度),直到第一层







   03. BP算法-代码实现    





本节展示如何BP算法计算前馈网络梯度的代码实现





    BP算法的代码实现    


为便于理解,下面仅以一个简单的前馈模型作为示例,展示如何使用BP算法计算梯度
 BP算法例子的模型结构
如上所示,设前馈模型共有3层,每层的计算公式都为:
 
模型的损失函数使用平方差函数,如下:
 
 则可算得模型的梯度传播如下:
一、损失函数传播给最后一层输出的梯度为:                                  
 
二、各层的梯度传播                                                                               
1. 参数的梯度                                                                           
 各层对自身的参数梯度为:                              
                                  
 从而参数的梯度为:                                   
                               
2. 输入的梯度                                                                           
 各层向输入(即前层的输出)传播的梯度为:               
 
 从而输入的梯度为:                                  
                                 
假设模型参数为:,样本为: 
要计算模型参数的梯度,只需利用上述梯度传播公式
依照BP算法的流程,先计算出最后一层输出的梯度,再逐层后馈即可
 具体代码实现如下:  
# 本代码用于展示如何使用BP算法计算前馈模型的梯度
# 本代码来自《老饼讲解-深度学习》 www.bbbdata.com
import numpy as np
# 将层定义为类对象
class Layer:
    def __init__(self, w):
        self.w  = w                                    # 该层的参数
        self.y  = None                                 # 该层的输出
        self.x  = None                                 # 该层的输入
        self.dw = None                                 # 该层的参数梯度
        self.dx = None                                 # 该层的输入梯度
    def forward(self,x):
        self.x = x                                     # 更新输入 
        self.y = np.tanh(self.w*x)                     # 计算输出
    def backward(self,dy):                             
        self.dw = dy*(1-self.y**2)*self.x              # 计算参数的梯度 
        self.dx = dy*(1-self.y**2)*self.w              # 计算输入的梯度

# 初始化
layers = [Layer(0.1),Layer(0.2),Layer(0.3)]            # 初始化前馈网络
x = 3                                                  # 样本的x
y = 3                                                  # 样本的y

# --------BP算法计算梯度-----------------
# 先通过前馈来计算出每层的输出
for i in range(len(layers)):                           # 逐层前馈   
    layers[i].forward(x)                               # 计算当前层的输出
    x = layers[i].y                                    # 当将前层的输出作为下层输入
												       
# 后馈计算梯度                                         
y_pred = layers[-1].y                                  # 最后一层的输出就是预测值
dy     = 2*(y_pred - y)                                # 最后一层输出的梯度
for i in range(len(layers)-1,-1,-1):                   # 从最后一层开始计算梯度 
    layers[i].backward(dy)                             # 计算当前层的梯度
    dy = layers[i].dx                                  # 将当前层传播给输入的梯度,作为下层输出的梯度

# 打印结果
for i in range(len(layers)):  
    print('第',str(i),'层参数梯度:',layers[i].dw)      # 打印每层的参数梯度
代码运行结果如下:
 BP算法代码运行结果
在本代码中,给tanh(wx)层定义为一个类,并实现了前馈、后馈函数
这样做的好处是方便tanh(wx)层的重复利用
 但为了方便学习,笔者没有对代码过度工程化,仅是简单展示BP算法是如何计算梯度的






好了,以上就是BP算法的算法流程以及代码实现了~








 End 





联系老饼