手写前向传播和反向传播
""" 前向传播 """ #定义这些值,逐步求出最终的L a=2.0 print("a:",a) b=-3.0 print("b:",b) c=10.0 print("c:",c) f=-2.0 e=a*b print("e:",e) d=e+c print("d:",d) L=d*f print(L)
a: 2.0 b: -3.0 c: 10.0 e: -6.0 d: 4.0 -8.0
""" 反向传播 """ """ 我们要算的是L对每个节点的导数,也就是每个节点的grad。规则只有三条局部导数: 1. 乘法 y=u*v: dy/du=v, dy/dv=u (交换一个对方的值) 2. 加法 y=u+v: dy/du=1, dy/dv=1 (梯度原样穿过) 3. 然后用链式法则:某节点.grad = 上游.grad * 本节点的局部导数 """ """ 起点:L对自已求导, dL/dL=1.0 开始反向: 1. L = d*f (乘法,对方的值就是局部导数) dL/dd = dL/dL * f = 1.0*(-2.0)=-2.0 dL/df = dL/dL * d = 1.0*4.0=4.0 2. d = e+c (加法,梯度原样向两边传) dL/de = dL/dd*1 = -2.0 dL/dc = dL/dd*1 = -2.0 3. e = a*b (乘法,再次交换对方的值) dL/da = dL/de * b = -2.0 * (-3.0) = 6.0 dL/db = dL/de * a = -2.0 * 2.0 = -4.0 """
下面这张计算图把上面的算术整合在一起——每个方框里是前向传播得到的值,下面一行是反向传播算出的梯度。蓝色是输入(叶子节点),绿色是运算结果:

例子二
#例子二, 一个神经元+tanh """ 前向传播 """ x1=2.0 x2=0.0 w1=-3.0 w2=1.0 b=6.88137 y1=x1*w1 y2=x2*w2 print(y1) print(y2) y3=y1+y2 print(y3) n=y3+b print(n) import numpy as np o=np.tanh(n) print(o)
-6.0 0.0 -6.0 0.8813700000000004 0.7071049876722272
""" 反向传播 """ #因为: o=tanh(n), 根据公式得出do/dn=1-o**2 #起点: o.grad=1, 因为o=0.7,所以o**2=0.5,于是do/dn=0.5 """ n=x1w1x2w2+b,加法把梯度原样分给两边 x1w1x2w2.grad=0.5 b.grad=0.5 x1w1x2w2 = x1w1+x2w2, 加法再分一次 x1w1.grad=0.5 x2w2.grad=0.5 x1w1 = x1*w1, 乘法交换对方的值 x1.grad = x1w1.grad * w1 = 0.5*-3.0=-1.5 w1.grad = x1w1.grad * x1 = 0.5*2.0=1.0 x2w2 = x2*w2, 乘法交换对方的值 x2.grad = x2w2.grad * w2 = 0.5*1.0=0.5 w2.gard = x2w2.grad * x2 = 0.5*0.0=0.0 """
几条要记住的规则: 反向传播其实就是几条局部导数规则反复套用:
加法节点y=u+v是梯度路由器,把上游梯度原样复制给每个输入(本地导数都是1)。乘法节点y=u*v把上游梯度乘以另一个输入的值再传回去。tanh节点的本地导数是1-tanh**2(n),即1-o**2。每个节点只需知道自已的局部导数,再乘上从右边传来的上游梯度,就完成了它那一段链式法则。

浙公网安备 33010602011771号