本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com
BP反向传播算法是一种常用于计算前馈型神经网络(例如MLP、CNN等)的梯度的算法
本文讲解BP反向传播算法的原理、算法流程,以及展示一个使用BP算法计算梯度的例子
通过本文,可以了解BP算法是什么,如何使用BP算法来计算前馈型神经网络的梯度,以及代码实现
本节讲解什么是BP算法,以及相关公式
BP算法是什么
BP算法(反向传播算法,Back Propagation Algorithm)是一种用于计算前馈式神经网络的梯度的方法
例如MLP、CNN等就是前馈型神经网络,由于BP算法最早用于MLP的梯度计算,因此MLP往往也被称为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) 不管是还是,都可能要用到的值,因此需要预计算每层的输入
由于每层的输入就是上层的输出,因此实际就是预计算每层的输出
本节讲解BP算法的具体流程,以及算法流程图
BP算法的流程
BP算法采用后馈式来计算前馈网络的单个样本的参数梯度
BP算法流程图如下:
它的计算流程如下:
一、前馈
通过前馈计算,获得每层的输入与输出:
将第一层的输入进行前馈,得到:
注意,同时也就得到了各层的输入
二、后馈
1. 初始化最后一层的输出梯度
直接计算损失函数对于最后一层输出的梯度:
2. 逐层后馈
从最后一层开始逐层前馈,对于第k层有:
2.1. 计算当前层参数的梯度:
2.2. 计算当前层输入的梯度:
备注:第k层输入入的梯度,也就是第k-1层的输出梯度
即:
总的来说,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) # 打印每层的参数梯度
代码运行结果如下:
在本代码中,给tanh(wx)层定义为一个类,并实现了前馈、后馈函数
这样做的好处是方便tanh(wx)层的重复利用
但为了方便学习,笔者没有对代码过度工程化,仅是简单展示BP算法是如何计算梯度的
好了,以上就是BP算法的算法流程以及代码实现了~
End