欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

利用计算图的正向传播和反向传播高效地计算导数

程序员文章站 2022-05-21 22:24:03
...

# 1.计算图的正向传播与反向传播

利用计算图的正向传播和反向传播高效地计算导数

计算图

计算图就是一种表示计算过程的数据结构图。计算图通过节点表示某种运算,用带箭头的边表示运算的方向,箭头上标有与运算相关的数据。上图就是一张计算图。

正向传播

上图中黑色箭头表示的就是正向传播。将输入数据x通过某种运算f转化为输出数据y,再传给下一个节点(或输出为最后结果)。

反向传播

上图中灰色箭头表示的就是反向传播。沿着与正向传播相反的方向,将输入数据E乘以局部导数∂y/∂x后传给下一个节点(或输出为最后结果)。

# 2.高效地计算导数

举个例子

式子z = (x+y)^2可以看作是由z = t^2和t = x+y构成的。将其用如下计算图表示出来

利用计算图的正向传播和反向传播高效地计算导数

根据链式法则可以作进一步转换,得到下图

利用计算图的正向传播和反向传播高效地计算导数

我们可以发现通过先进行一次正向传播再进行一次反向传播,我们可以得到各个变量的导数值。

这个求导过程是高效的。原因如下

1.中间变量可以重复利用。(如∂z/∂t在计算∂z/∂x和∂z/∂y时都有用到)

2.节点负责封装解析性求局部导数的过程。(没有像数值微分那样用定义求导,而是用解析性公式求导。如x^2的导数是2x)

 

下面用python实现上述例子的求解导数过程:

class AddLayer:
	def __init__(self):
		pass   # 表示什么也不运行
	
	def forward(self, x, y):  # 前向函数,入口参数为加法的两个参数
		return x+y
		
	def backward(self, dout): # 反向函数,入口参数为上游传来的导数dout
		return dout,dout  # x+y关于x的偏导数是1,关于y的偏导数也是1,所以输出的dx,dy应该为dout*1,dout*1
		
class SquareLayer:
	def __init__(self):
		self.x = None     # 由于反向传播时,会用到正向传播的入口参数,所以设置一个成员变量来保存该入口参数
		
	def forward(self,x):      # 前向函数
		self.x = x
		return self.x**2
	
	def backward(self,dout):  # 反向函数,入口参数为上游传来的导数dout
		return dout*2*self.x  # x^2关于x的导数是2x,所以输出的dx应该为dout*2*x

# 测试		
addlayer = AddLayer()         # 创建加法层实例对象和平方层实例对象
squarelayer = SquareLayer()

t = addlayer.forward(3,4)     # 设置x,y为3,4,进行整个计算图的正向传播
z = squarelayer.forward(t)

dz = 1  # z关于z的导数是1
dt = squarelayer.backward(dz) # 进行整个计算图的反向传播
dx,dy = addlayer.backward(dt)

print(dx,dy)                  # 打印出计算结果(z关于x和y的偏导数)

运行结果如下

利用计算图的正向传播和反向传播高效地计算导数

综上所述,这个高效的求导数方法就被如此实现了。

 

# 本博客参考了《深度学习入门——基于Python的理论与实现》(斋藤康毅著,陆宇杰译),特在此声明。

 

 

相关标签: python与深度学习