深度学习-一篇入门

【原理】一篇入门之-MLP神经网络的梯度计算

作者 : 老饼 发表日期 : 2022-11-04 04:42:26 更新日期 : 2024-12-04 02:04:36
本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com



MLP神经网络往往也称为BP神经网络,因为它在计算梯度时使用的是BP算法

本文讲解如何使用BP算法计算MLP神经网络的梯度,并展示具体算法流程与实现例子

通过本文,可以了解MLP神经网络的梯度是如何计算的,以及MLP梯度计算的代码实现






    01. MLP的梯度计算-讲解     





本节讲解MLP神经网络是如何利用BP算法计算参数梯度的





    MLP与MLP的梯度计算方法      


MLP神经网络是一种前馈神经网络,它由多层构成,每层的输出是下层的输入
由于MLP往往使用梯度下降、动量梯度下降等算法进行训练,因此需要计算MLP模型里的参数梯度
 MLP神经网络的梯度计算思路如下:
由于MLP的损失函数为单样本的损失函数的均值,即
 
因此,损失函数的梯度也是单样本梯度的均值:
 
如果将单个样本的损失函数设为,则梯度就是单个样本梯度之和:
 
 这里的是样本个数,则代表MLP神经网络的任意一个权重或阈值
因此,对于MLP,只需计算单个样本损失函数的梯度,再取均值(或取和)就可得到总的梯度
 MLP的梯度计算方法一般如下:
 MLP的梯度计算方法
如图所示,MLP的梯度计算一般先通过BP算法,逐层从后往前计算单样本的梯度
在计算完每层所有样本的梯度时,就进行求和,从而得到该层参数W、B的整体梯度






    MLP的单样本梯度计算    


MLP单样本的梯度一般使用BP算法来计算,BP算法从最后一层开始,后馈式计算梯度
 BP算法的流程
  BP算法先计算当层的输出梯度,再通过输出梯度进一步计算该层的参数梯度
 
BP算法流程
设MLP共有K个计算层,BP算法的过程如下:

1. 计算第K层(即最后一层)的输出梯度                    
2. 计算第K层的参数梯度                                      
3. 计算第K-1层的输出梯度                                   
4. 计算第K-1层的参数梯度                                   
....
 如此类推,直到第一层,即可算得所有层的参数
总的来说,BP算法就是从最后一层开始,先算出当层输出的梯度,再计算当层的参数梯度,直到第一层
 MLP梯度计算公式
 MLP每层的输出梯度、参数梯度公式如下:
 
 一、输出梯度                                                                                           
1. 设单个样本的损失函数为均方差函数MSE:                                
                        
 则最后一层(第K层)的输出梯度为:                                          
                                 
2. 其余层的输出梯度依赖于后层的输出梯度                                    
  第k-1层的输出梯度为:                                    
                                
二、参数梯度                                                                                        
         第k层的权重、阈值的梯度都依赖于第k层输出的梯度,计算公式如下:
                      第k层的权重梯度:  
第k层的阈值梯度:    
 激活函数的梯度
其中,当激活函数T=tanh时,根据tanh函数的导数,有:
 
当没有激活函数时,可以认为激活函数是恒等函数 ,则有:
 








    02. MLP线性层的梯度公式-推导    




本节推导MLP线性层的输出梯度与参数梯度





      MLP最后一层输出的梯度公式-推导     


最后一层的输出梯度公式推导
对于单个样本,不妨设损失函数为均方差函数MSE,则损失函数为:
 
这里M是样本个数,N是MLP的输出个数,是最后一层(第K层)的第i个输出值,是真实值
则对于最后一层的第个输出,它的梯度为: 
         
 按形式进行推广,即可得到最后一层所有输出的梯度为:
            
其余层的输出梯度公式推导
 MLP每层都为线性层,它的前馈公式为:
 ,T为激活函数
由于MLP每层的输出就是后层的输入,因此只需求出线性层的输入梯度
推导过程如下:
1. 线性层第j个输出对第i个输入的梯度为:                                         
   
  2. 按形式进行推广,即可得到线性层所有输出对第i个输入的梯度向量:
                             
3. 因此,损失函数对线性层的第i个输入的梯度为:                             
           
 4. 按形式进行推广,即可得到损失函数对所有输入的梯度向量为:     
 
            






      线性层对输入、权重w、阈值b的梯度     


 MLP每层都为线性层,它的前馈公式为:
 ,T为激活函数
 它的权重、阈值的梯度公式如下
 权重W的梯度 
线性层的权重W的梯度推导过程如下:
1. 线性层第j个输出对单个权重参数的梯度为:                             
  
           2. 由上可知,是一个第u个元素为,其余为0的向量
 因此,损失函数对线性层的单个权重的梯度为:                  
                             
3. 按形式进行推广,则损失函数对线性层的整个W的梯度为:            
  
 阈值b的梯度 
 线性层的权重W的梯度推导过程如下:
1. 线性层第j个输出对单个阈值参数的梯度为:                                
      
        2.  由上可知,即是一个第u个元素为,其余为0的向量 
 因此,损失函数对线性层的第u个阈值的梯度为:                    
              
3. 按形式进行推广,则损失函数对线性层的整个的梯度为:              
                  








    03. MLP梯度计算流程    




本节讲解MLP神经网络梯度计算的算法流程




    MLP的梯度计算    


MLP采用BP算法来计算单个样本的参数梯度,再进行求和,就是总梯度
 MLP计算梯度的算法流程图如下:
 
MLP梯度计算的算法流程
一、前馈                                                                                                     
通过前馈计算,获得每层的输入与输出:                                         
 将第一层的输入
进行前馈,得到:           
  备注:通过前馈就得到了各层的输出,同时也就得到了各层的输入     
             进行前馈,是因为在后馈时需要使用各层的输出输入来计算梯度      
 
二、后馈                                                                                                       
 
1. 初始化最后一层的输出梯度                                                             
 直接计算损失函数对于最后一层输出的梯度:                              
                                     
2. 逐层后馈                                                                                     
 从最后一层开始逐层前馈,对于第k层有:                                    
2.1. 计算当前层权重、阈值的梯度:                                           
 计算各个样本的权重、阈值梯度,并求和,得到总梯度          
 设当前层为第k层,其权重、阈值的梯度公式如下:             
              
 
                            备注:阈值中的sum是对每行求和,权重梯度已是求和结果,不需sum,下文详细解说
 
2.2. 计算当前层输入的梯度:                                                       
       

       备注:第k层输入入的梯度,也就是第k-1层的输出梯度 
即:   
                              
总的来说,BP算法就是从最后一层开始,每层算出当层的参数梯度和输入梯度(即前层输出梯度),直到第一层






      关于W的梯度求和      


由于W是一个矩阵,它的单样本梯度公式也是一个矩阵
因此,它并不能直接由梯度公式进行批量计算,而需要一个一个样本计算
即如得到各个样本的权重梯度后,再进行求和
 这样是低效的,因此,W的计算方法如下:
由于对单个样本有:  
 其中是一个列向量,是行向量
 就是的第个元素乘以的第个元素
 MLP的权重梯度如何求和
而对于整体样本,则是一个矩阵,每列代表一个样本,第行代表一个样本
因此,所有样本的梯度之和就是的第行与的第列对应元素相乘再求和
即:                               
从而对整体样本,W的梯度为:                               
 
即:        
因此,W在单样本时,按该公式求得的是单样本的梯度,在多样本时,求得的是梯度之和









    03. MLP的梯度计算-代码实现    





本节展示如何使用代码实现MLP神经网络的梯度计算





     MLP梯度计算-代码实现      


下面按照上述BP算法流程与公式,实现一个MLP神经网络的梯度计算
 具体代码如下:
# 本代码用于展示如何使用BP算法计算MLP神经网络的梯度
# 本代码来自《老饼讲解-深度学习》 www.bbbdata.com
import numpy as np

#----------------自写代码计算MLP神经网络的梯度--------------------
# 将层定义为类对象
class Layer:
    def __init__(self, w,b,activeFcn):
        self.w  = w                                                      # 该层的权重参数
        self.b  = b                                                      # 该层的阈值参数
        self.y  = None                                                   # 该层的输出
        self.x  = None                                                   # 该层的输入
        self.dw = None                                                   # 该层的参数梯度
        self.db = None                                                  
        self.dx = None                                                   # 该层的输入梯度
        self.activeFcn= activeFcn                                        # 该层的激活函数 
    def forward(self,x):                                                
        self.x = x                                                       # 更新输入 
        self.y = self.w@x+self.b                                         # 激活前的值
        if (self.activeFcn=='tanh') :                                    # 如果是tanh激活函数
            self.y = np.tanh(self.y)                                     # 按tanh激活
        elif(self.activeFcn==None) :                                     # 如果没有激活函数,则按原值
            self.y = self.y                                             
    def backward(self,dy):                                               
        if (self.activeFcn=='tanh') :                                    # 如果是tanh激活函数
            dA = (1-self.y**2)                                           # 计算tanh激活函数的梯度
        elif(self.activeFcn==None) :                                     # 如果没有激活函数
            dA =  np.ones(self.y.shape)                                  # 则梯度全为1
        self.dw = (dA*dy)@self.x.T                                       # 计算权重w的梯度 
        self.db = (dA*dy).sum(axis=1)                                    # 计算阈值的梯度
        self.dx = self.w.T@(dy*dA)                                       # 计算输入的梯度      
													                    
# 初始化一个MLP神经网络
w1= np.ones([3,2])*0.1                                                   # 初始化权重w1
b1= np.ones([3,1])*0.1                                                   # 初始化阈值b1
w2= np.ones([3,3])*0.1                                                   # 初始化权重w2
b2= np.ones([3,1])*0.1                                                   # 初始化阈值b2
w3= np.ones([2,3])*0.1                                                   # 初始化权重w3
b3= np.ones([2,1])*0.1                                                   # 初始化阈值b4
layers = [Layer(w1,b1,'tanh'),Layer(w2,b2,'tanh'),Layer(w3,b3,None)]     # 初始化前馈网络
x = np.array([[3.,2.],[4,5]])                                            # 样本的x
y = np.array([[2.,1.],[2.,2.]])                                          # 样本的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)/(y.shape[0]*y.shape[1])                          # 最后一层输出的梯度
for i in range(len(layers)-1,-1,-1):                                     # 从最后一层开始计算梯度 
    layers[i].backward(dy)                                               # 计算当前层的梯度
    dy = layers[i].dx                                                    # 将当前层传播给输入的梯度,作为下层输出的梯度

# 打印结果
print('\n----自写代码计算MLP神经网络的梯度----:')
for i in range(len(layers)):                                           
    print('第',str(i),'层权重w梯度:',layers[i].dw)                       # 打印每层的参数梯度
    print('第',str(i),'层阈值b梯度:',layers[i].db)                       # 打印每层的参数梯度

# ----------------使用pytorch计算梯度-------------------------
import torch 
x = torch.tensor([[3.0,2.0],[4.0,5.0]],dtype=(float))                    # 样本的x
y = torch.tensor([[2.,1.],[2.,2.]],dtype=(float))                        # 样本的y
w1= torch.full((3,2),0.1,dtype=(float),requires_grad=True)               # 初始化权重w1
b1= torch.full((3,1),0.1,dtype=(float),requires_grad=True)               # 初始化阈值b1
w2= torch.full((3,3),0.1,dtype=(float),requires_grad=True)               # 初始化权重w2
b2= torch.full((3,1),0.1,dtype=(float),requires_grad=True)               # 初始化阈值b2
w3= torch.full((2,3),0.1,dtype=(float),requires_grad=True)               # 初始化权重w3
b3= torch.full((2,1),0.1,dtype=(float),requires_grad=True)               # 初始化阈值b3
output = w3@torch.tanh(w2@torch.tanh(w1@x+b1)+b2)+b3                     # 根据x计算输出
loss = sum(sum(((output - y)**2)))/(y.shape[0]*y.shape[1])               # 计算损失值
loss.backward()                                                          # 将loss反向传播
print('\n----pytorch计算MLP神经网络的梯度----:')
print('w1的梯度:',w1.grad)                                               # 打印loss对w的梯度
print('b1的梯度:',b1.grad)                                               # 打印loss对w的梯度
print('w2的梯度:',w2.grad)                                               # 打印loss对w的梯度
print('b2的梯度:',b2.grad)                                               # 打印loss对w的梯度
print('w3的梯度:',w3.grad)                                               # 打印loss对w的梯度
print('b3的梯度:',b3.grad)                                               # 打印loss对w的梯度
代码运行结果如下:
 MLP神经网络梯度计算代码实现 
在本代码中,我们同时使用了pytorch的自动求导
通过结果,可以看到,自写代码实现的MLP神经网络梯度计算结果与pytorch的是一致的






好了,以上就是MLP神经网络的梯度计算方法与代码实现了~








 End 




联系老饼