链式法则求导的原则和梯度回传

在这里插入图片描述

一、链式法则简化的原则

注意:
(1)本文将求导的链式法则和深度学习中梯度回传看作是一个概念;
(2)本文链式法则的原则首先建立在基本算术算子:加法、减法、乘法、除法、幂运算和简单函数基础之上;

  • 复杂运算的简化;定义基本算子为加、减、乘、除、幂以及其他[简单函数](如sin,tan……)(https://zhuanlan.zhihu.com/p/63265208);假如一个运算包含多个算子,那么精简的原则就是怎么把复杂的多算子运算,转换为单一的算子的一次运算,或者其他的一元简单函数,这种精简包含两个特征
    (1)多算子简化为单一算子或者单一的简单函数;
    (2)单一算子或简单函数必须仅运行1次;
    (3)如果简化为加、减、乘、除,简化后的自变量最多两个,至少1个;
    (4)如果简化为一个简单函数,则必须要求该简单函数为一元函数;
    注意:二元/一元加减乘除幂构成的运算,其实也是简单函数,这里单独把加减乘除运算拉出来,是因为他们构成的简单函数有可能是二元的,即有两个输入;
    案例1
    f ( w , x ) = 1 1 + e − ( w 0 x 0 + w 1 x 1 + w 2 ) f(w,x)=\frac{1}{1+e^{-(w_0x_0+w_1x_1+w_2)}} f(w,x)=1+e(w0x0+w1x1+w2)1
    表1上述运算式出现的基础算子个数
31311

运算中,包含加、减、乘、除、幂,按原则,需要把运算降为一种算子的运算,那么究竟怎么精简:原则:每一步都是将复杂运算精简为一种算子或简单函数,按照这个原则,一直进行下去,直到不能再简化。
(1)如果简化为除法运算,即需要把分母看作一个整体,即:
{ f ( u ) = 1 u u = 1 + e − ( w 0 x 0 + w 1 x 1 + w 2 ) (1) \begin{cases} f(u)=\frac{1}{u} \tag{1} \\ u= 1+e^{-(w_0x_0+w_1x_1+w_2)} \\ \end{cases} {f(u)=u1u=1+e(w0x0+w1x1+w2)(1)
此时只保留了一种运算,且有一个变元;如果将整体的幂运算简化为一个变元呢:
{ f ( u ) = 1 u + 1 u = e − ( w 0 x 0 + w 1 x 1 + w 2 ) (2) \begin{cases} f(u)=\frac{1}{u+1} \tag{2} \\ u= e^{-(w_0x_0+w_1x_1+w_2)} \\ \end{cases} {f(u)=u+11u=e(w0x0+w1x1+w2)(2)
从公式(2)可以看出,简化后的运算,除了包含加法外,还包含了除法,因此,不是最佳简化;
其他简化,也都不能实现方程(1)的结果,即最终只有一个算子;

案例2
f = x y x ⋅ e y + 1 f=\frac{xy}{x·e^y+1} f=xey+1xy
如果仅保留一个算子,要实现最佳简化,很明显也是保留除法算子,因此,简化为:
{ f ( u , v ) = u v u = x y v = x ⋅ e y + 1 \begin{cases} f(u,v)=\frac{u}{v}\\ u=xy\\ v=x·e^y+1 \end{cases} f(u,v)=vuu=xyv=xey+1

二、最佳简化是否唯一

最佳简化不唯一,因为加法运算存在加法结合率,即不同的结合方式,具有相同的计算结果,但是,不同的结合方式,是不同的简化方法;
f = x y + x ⋅ e y + x y (3) f={xy}+{x·e^y} + \frac{x}{y} \tag{3} f=xy+xey+yx(3)
公式(3)可以简化为两个变元的加法,即:
{ f ( u , v ) = u + v u = x y v = x ⋅ e y + x y \begin{cases} f(u,v)=u+v\\ u=xy\\ v=x·e^y+\frac{x}{y} \end{cases} f(u,v)=u+vu=xyv=xey+yx
但是,根据加法结合率,也可以把前两项进行结合,构建如下的链式计算:
{ f ( u , v ) = u + v u = x y + x ⋅ e y v = x y \begin{cases} f(u,v)=u+v\\ u=xy+x·e^y\\ v=\frac{x}{y} \end{cases} f(u,v)=u+vu=xy+xeyv=yx
这两种分解,从算法的角度,无任何差异,所以,链式简化并不唯一;

三、梯度backward与链式法则求导

有了上述链式法则后,下一步要做的就是递归的进行简化,比如,公式(3)中的 u u u v v v,再继续对其进行链式法则进行简化,原则仍然是简化为一个算子,这其实也是梯度backward的思想来源,即把一个复杂的运算式的求导,转换为一系列的仅一个算子的求导,根据链式法则,复杂运算式的求导等于这一系列简单算子的导数之乘,所谓的梯度回传,就是梯度的计算有一个先后顺序,这个顺序就是我们简化复杂式的顺序;
案例一,简化为基本算子的情况,以除法为例:
f ( w 0 , w 1 , x ) = 1 1 + e − ( w 0 x 0 + w 1 x 1 + w 2 ) f(w_0,w_1,x)=\frac{1}{1+e^{-(w_0x_0+w_1x_1+w_2)}} f(w0,w1,x)=1+e(w0x0+w1x1+w2)1

第一步简化:引入变元u,简化为一个除法运算
{ f ( u ) = 1 u u = 1 + e − ( w 0 x 0 + w 1 x 1 + w 2 ) \begin{cases} f(u)=\frac{1}{u}\\ u= 1+e^{-(w_0x_0+w_1x_1+w_2)} \\ \end{cases} {f(u)=u1u=1+e(w0x0+w1x1+w2)
第二步简化:对 u u u进行简化,引入v,把 u u u简化为一个加法算子
{ f ( u ) = 1 u u = 1 + v v = e − ( w 0 x 0 + w 1 x 1 + w 2 ) \begin{cases} f(u)=\frac{1}{u} \\ u= 1+v \qquad\\ v = e^{-(w_0x_0+w_1x_1+w_2)} \end{cases} f(u)=u1u=1+vv=e(w0x0+w1x1+w2)
第三步简化:对 v v v进行简化,引入 x x x,把 v v v简化为一个乘幂运算
{ f ( u ) = 1 u u = 1 + v v = e x x = − ( w 0 x 0 + w 1 x 1 + w 2 ) \begin{cases} f(u)=\frac{1}{u} \\ u= 1+v \qquad\\ v = e^{x}\\ x=-(w_0x_0+w_1x_1+w_2) \end{cases} f(u)=u1u=1+vv=exx=(w0x0+w1x1+w2)
第四步简化:对 x x x进行简化,引入 y y y,把 x x x简化为一个减法运算
{ f ( u ) = 1 u u = 1 + v v = e x x = 0 − y y = w 0 x 0 + w 1 x 1 + w 2 \begin{cases} f(u)=\frac{1}{u} \\ u= 1+v \qquad\\ v = e^{x}\\ x=0-y\\ y = w_0x_0+w_1x_1+w_2 \end{cases} f(u)=u1u=1+vv=exx=0yy=w0x0+w1x1+w2
第五步简化:对 y y y进行简化,引入 z z z,把 y y y简化为一个加法运算
{ f ( u ) = 1 u u = 1 + v v = e x x = 0 − y y = z + w 2 z = w 0 x 0 + w 1 x 1 \begin{cases} f(u)=\frac{1}{u} \\ u= 1+v \qquad\\ v = e^{x}\\ x=0-y\\ y =z+w_2\\ z=w_0x_0+w_1x_1 \end{cases} f(u)=u1u=1+vv=exx=0yy=z+w2z=w0x0+w1x1
第六步简化:对 z z z进行简化,引入 m m m n n n z z z简化为一个加法运算
{ f ( u ) = 1 u u ( v ) = 1 + v v ( x ) = e x x ( y ) = 0 − y y ( z ) = z + w 2 z ( m , n ) = m + n m ( w 0 , x 0 ) = w 0 x 0 n ( w 1 , x 1 ) = w 1 x 1 \begin{cases} f(u)=\frac{1}{u} \\ u(v)= 1+v \qquad\\ v(x) = e^{x}\\ x(y)=0-y\\ y(z) =z+w_2\\ z(m,n)=m+n\\ m(w_0,x_0)=w_0x_0\\ n(w_1,x_1)=w_1x_1 \end{cases} f(u)=u1u(v)=1+vv(x)=exx(y)=0yy(z)=z+w2z(m,n)=m+nm(w0,x0)=w0x0n(w1,x1)=w1x1
因为简化的变元 m m m n n n只有一个基础运算算子,所以,链式分解到此结束。
如果对 f ( w , x ) f(w,x) f(w,x)求导,则根据链式法则,对每一步简化的运算进行求导,然后进行乘积运算;假设求 f f f关于 w 0 w_0 w0的偏导,即:
∂ f ∂ w 0 \frac{ \partial f}{ \partial w_0} w0f
根据链式法则:
∂ f ( w 0 , w 1 , x ) ∂ w 0 = ∂ f ( u ) ∂ u ⋅ ∂ u ( v ) ∂ v ⋅ ∂ v ( x ) ∂ x ⋅ ∂ x ( y ) ∂ y ⋅ ∂ y ( z ) ∂ z ⋅ [ ∂ z ∂ m ⋅ ∂ m ( w 0 , x 0 ) ∂ w 0 + ∂ z ∂ n ⋅ ∂ n ( w 1 , x 1 ) ∂ w 0 ] \frac{ \partial f(w_0,w_1,x)}{ \partial w_0}=\frac{ \partial f(u)}{ \partial u} · \frac{ \partial u(v)}{ \partial v} · \frac{ \partial v(x)}{ \partial x} · \frac{ \partial x(y)}{ \partial y} · \frac{ \partial y(z)}{ \partial z} · [\frac{ \partial z}{ \partial m} · \frac{ \partial m(w_0,x_0)}{ \partial w_0} + \frac{ \partial z}{ \partial n} · \frac{ \partial n(w_1,x_1)}{ \partial w_0}] w0f(w0,w1,x)=uf(u)vu(v)xv(x)yx(y)zy(z)[mzw0m(w0,x0)+nzw0n(w1,x1)]
由于 ∂ n ( w 1 , x 1 ) ∂ w 0 = 0 \frac{ \partial n(w_1,x_1)}{ \partial w_0}=0 w0n(w1,x1)=0,因此上述式子可以简化为:
∂ f ( w 0 , w 1 , x ) ∂ w 0 = ∂ f ( u ) ∂ u ⋅ ∂ u ( v ) ∂ v ⋅ ∂ v ( x ) ∂ x ⋅ ∂ x ( y ) ∂ y ⋅ ∂ y ( z ) ∂ z ⋅ ∂ z ∂ m ⋅ ∂ m ( w 0 , x 0 ) ∂ w 0 (4) \frac{ \partial f(w_0,w_1,x)}{ \partial w_0}=\frac{ \partial f(u)}{ \partial u} · \frac{ \partial u(v)}{ \partial v} · \frac{ \partial v(x)}{ \partial x} · \frac{ \partial x(y)}{ \partial y} · \frac{ \partial y(z)}{ \partial z} · \frac{ \partial z}{ \partial m} · \frac{ \partial m(w_0,x_0)}{ \partial w_0} \tag{4} w0f(w0,w1,x)=uf(u)vu(v)xv(x)yx(y)zy(z)mzw0m(w0,x0)(4)
通过上面的案例,可以看出一个复杂运算的求导可以按照链式法则,转换为一系列基础算子构成的运算的求导,且各个子运算相互独立,可以并行的进行计算,提升了梯度计算的效率;
下面将用数据验证上述链式法则:
{ ∂ f ( u ) ∂ u = 1 − u 2 ∂ u ( v ) ∂ v = 1 ∂ v ( x ) ∂ x = e x ∂ x ( y ) ∂ y = − 1 ∂ y ( z ) ∂ z = 1 ∂ z ∂ m = 1 ∂ m ( w 0 , x 0 ) ∂ w 0 = x 0 \begin{cases} \frac{ \partial f(u)}{ \partial u} =\frac{1}{-u^2} \\ \frac{ \partial u(v)}{ \partial v}=1\\ \frac{ \partial v(x)}{ \partial x} =e^x\\ \frac{ \partial x(y)}{ \partial y} =-1\\ \frac{ \partial y(z)}{ \partial z}=1 \\ \frac{ \partial z}{ \partial m}=1 \\ \frac{ \partial m(w_0,x_0)}{ \partial w_0}=x_0 \end{cases} uf(u)=u21vu(v)=1xv(x)=exyx(y)=1zy(z)=1mz=1w0m(w0,x0)=x0
∂ f ( w 0 , w 1 , x ) ∂ w 0 = 1 − u 2 ⋅ 1 ⋅ e x ⋅ ( − 1 ) ⋅ 1 ⋅ 1 ⋅ x 0 = x 0 e x u 2 (5) \frac{ \partial f(w_0,w_1,x)}{ \partial w_0}=\frac{1}{-u^2}·1·e^x·(-1)·1·1·x_0\\ =\frac{x_0e^x}{u^2} \tag{5} w0f(w0,w1,x)=u211ex(1)11x0=u2x0ex(5)
因为$u=1+e^{-(w_0x_0+w_1x_1+w_2)} $,带入公式(5)中,得到:
∂ f ( w 0 , w 1 , x ) ∂ w 0 = 1 − u 2 ⋅ 1 ⋅ e x ⋅ ( − 1 ) ⋅ 1 ⋅ 1 ⋅ x 0 = x 0 e − ( w 0 x 0 + w 1 x 1 + w 2 ) ( 1 + e − ( w 0 x 0 + w 1 x 1 + w 2 ) ) 2 (5) \frac{ \partial f(w_0,w_1,x)}{ \partial w_0}=\frac{1}{-u^2}·1·e^x·(-1)·1·1·x_0\\ =\frac{x_0e^{-(w_0x_0+w_1x_1+w_2)}}{(1+e^{-(w_0x_0+w_1x_1+w_2)})^2} \tag{5} w0f(w0,w1,x)=u211ex(1)11x0=(1+e(w0x0+w1x1+w2))2x0e(w0x0+w1x1+w2)(5)
下面来验证,假设 x 0 = 1 , x 1 = 3 , w 0 = 4 , w 1 = 1 , w 2 = 3 x_0=1,x_1=3,w_0=4,w_1=1,w_2=3 x0=1,x1=3,w0=4,w1=1,w2=3,求 ∂ f ( w 0 , w 1 , x ) ∂ w 0 \frac{ \partial f(w_0,w_1,x)}{ \partial w_0} w0f(w0,w1,x)的值。下面用百度的paddle.autograd.backward函数来对比手动推导的导函数值和调用函数计算的导函数值。

import numpy as np
import paddle
from paddle import to_tensor as TT

x_0 = 1
x_1 = 3
w_0 = 4
w_1 = 1
w_2 = 3

x_0 = TT(x_0, dtype='float32', stop_gradient=True)
x_1 = TT(x_1, dtype='float32')
w_0 = TT(w_0, dtype='float32', stop_gradient=False)
w_1 = TT(w_1, dtype='float32')
w_2 = TT(w_2, dtype='float32')
# 导函数
f_ = (  x_0*paddle.exp(-(w_0*x_0+w_1*x_1+w_2))  )/(1+paddle.exp(-(w_0*x_0+w_1*x_1+w_2)))**2
print(f"手动计算的导函数值:{f_.numpy()[0]}")
# 原函数
f = 1/(1+paddle.exp(-(w_0*x_0+w_1*x_1+w_2)))
paddle.autograd.backward(f, TT(1, dtype='float32'))
print(f"程序计算的导函数值:{w_0.grad.numpy()[0]}")

输出为:
手动计算的导函数值:4.539580913842656e-05
程序计算的导函数值:4.539580550044775e-05

从对比结果来看,除了一些截断误差外,二者一致。

案例二,简化为简单函数的情况,以tanh函数为例:
f ( x , y , z ) = t a n h ( x y + z 2 ) f(x,y,z)=tanh(xy+z^2) f(x,y,z)=tanh(xy+z2)
第一步,简化为仅有一个自变量的简单函数,引入 u u u:
{ f ( u ) = t a n h ( u ) u ( x , y , z ) = x y + z 2 \begin{cases} f(u)=tanh(u) \\ u(x,y,z)=xy+z^2 \\ \end{cases} {f(u)=tanh(u)u(x,y,z)=xy+z2
第二步,简化u,引入 w w w, v v v,把u简化为两个自变量的和:
{ f ( u ) = t a n h ( u ) u ( w , v ) = v + w w ( x , y ) = x y v ( z ) = z 2 \begin{cases} f(u)=tanh(u) \\ u(w,v)=v+w \\ w(x,y)=xy\\ v(z)=z^2 \end{cases} f(u)=tanh(u)u(w,v)=v+ww(x,y)=xyv(z)=z2
简化后, w w w已经是一个基本算子的一次运算,因此, w w w无法再简化; v v v是一个一元简单函数了,也无法再简化,因此,简化结束;下面以求 ∂ f ( x , y , z ) ∂ x \frac{ \partial f(x,y,z)}{ \partial x} xf(x,y,z)为例,来验证正确性:
{ ∂ f ( x , y , z ) ∂ x = ∂ f ( u ) ∂ u ⋅ ∂ u ( w , v ) ∂ w ⋅ ∂ w ( x , y ) ∂ x = { 1 − 2 [ t a n h ( u ) ] 2 } y = { 1 − 2 [ t a n h ( x y + z 2 ) ] 2 } y \begin{cases} \frac{ \partial f(x,y,z)}{ \partial x}= \frac{ \partial f(u)}{ \partial u}· \frac{ \partial u(w,v)}{ \partial w}· \frac{ \partial w(x,y)}{ \partial x}\\ = \{1 - 2[tanh(u)]^2\}y\\ =\{1 - 2[tanh(xy+z^2)]^2\}y \end{cases} xf(x,y,z)=uf(u)wu(w,v)xw(x,y)={12[tanh(u)]2}y={12[tanh(xy+z2)]2}y
假设 x = 0.1 , y = 0.2 , z = 0.3 x=0.1,y=0.2,z=0.3 x=0.1,y=0.2,z=0.3,求导函数的值:

import sys,os,math,time
from numpy import *
import numpy as np
import paddle
from paddle import to_tensor as TT
x=0.1
y=0.2
z=0.3
x = TT(x, dtype='float32', stop_gradient=False)
y = TT(y, dtype='float32')
z = TT(z, dtype='float32', stop_gradient=True)

# 导函数
u = x*y + z**2
f_ = y * ( 1-paddle.tanh(u)**2)
print(f"手动计算的导函数值:{f_.numpy()[0]}")
# 原函数
f = paddle.tanh(x*y+z**2)
paddle.autograd.backward(f, TT(1, dtype='float32'))
print(f"程序计算的导函数值:{x.grad.numpy()[0]}")
运算结果为:
手动计算的导函数值:0.197599396109581
程序计算的导函数值:0.197599396109581
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值