栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

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

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

一、链式法则简化的原则

注意:
(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−(w0​x0​+w1​x1​+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)=u1​u=1+e−(w0​x0​+w1​x1​+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+11​u=e−(w0​x0​+w1​x1​+w2​)​(2)
从公式(2)可以看出,简化后的运算,除了包含加法外,还包含了除法,因此,不是最佳简化;
其他简化,也都不能实现方程(1)的结果,即最终只有一个算子;

案例2
f = x y x ⋅ e y + 1 f=frac{xy}{x·e^y+1} f=x⋅ey+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)=vu​u=xyv=x⋅ey+1​

二、最佳简化是否唯一

最佳简化不唯一,因为加法运算存在加法结合率,即不同的结合方式,具有相同的计算结果,但是,不同的结合方式,是不同的简化方法;
f = x y + x ⋅ e y + x y (3) f={xy}+{x·e^y} + frac{x}{y} tag{3} f=xy+x⋅ey+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=x⋅ey+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+x⋅eyv=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−(w0​x0​+w1​x1​+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)=u1​u=1+e−(w0​x0​+w1​x1​+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)=u1​u=1+vv=e−(w0​x0​+w1​x1​+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)=u1​u=1+vv=exx=−(w0​x0​+w1​x1​+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)=u1​u=1+vv=exx=0−yy=w0​x0​+w1​x1​+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)=u1​u=1+vv=exx=0−yy=z+w2​z=w0​x0​+w1​x1​​
第六步简化:对 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)=u1​u(v)=1+vv(x)=exx(y)=0−yy(z)=z+w2​z(m,n)=m+nm(w0​,x0​)=w0​x0​n(w1​,x1​)=w1​x1​​
因为简化的变元 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} ∂w0​∂f​
根据链式法则:
∂ 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}] ∂w0​∂f(w0​,w1​,x)​=∂u∂f(u)​⋅∂v∂u(v)​⋅∂x∂v(x)​⋅∂y∂x(y)​⋅∂z∂y(z)​⋅[∂m∂z​⋅∂w0​∂m(w0​,x0​)​+∂n∂z​⋅∂w0​∂n(w1​,x1​)​]
由于 ∂ n ( w 1 , x 1 ) ∂ w 0 = 0 frac{ partial n(w_1,x_1)}{ partial w_0}=0 ∂w0​∂n(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} ∂w0​∂f(w0​,w1​,x)​=∂u∂f(u)​⋅∂v∂u(v)​⋅∂x∂v(x)​⋅∂y∂x(y)​⋅∂z∂y(z)​⋅∂m∂z​⋅∂w0​∂m(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} ⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧​∂u∂f(u)​=−u21​∂v∂u(v)​=1∂x∂v(x)​=ex∂y∂x(y)​=−1∂z∂y(z)​=1∂m∂z​=1∂w0​∂m(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} ∂w0​∂f(w0​,w1​,x)​=−u21​⋅1⋅ex⋅(−1)⋅1⋅1⋅x0​=u2x0​ex​(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} ∂w0​∂f(w0​,w1​,x)​=−u21​⋅1⋅ex⋅(−1)⋅1⋅1⋅x0​=(1+e−(w0​x0​+w1​x1​+w2​))2x0​e−(w0​x0​+w1​x1​+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} ∂w0​∂f(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} ∂x∂f(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} ⎩⎪⎨⎪⎧​∂x∂f(x,y,z)​=∂u∂f(u)​⋅∂w∂u(w,v)​⋅∂x∂w(x,y)​={1−2[tanh(u)]2}y={1−2[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
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1005098.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号