反向传播的计算过程

反向传播

对于神经网络的训练,需要从损失函数得到每个参数的梯度,它指明参数迭代的方向,从而能够使损失值一步步降低。这一方法利用了求导的链式法则,这里先脱离神经网络的概念,单纯的从公式推导的角度说明为什么更新参数需要反向传播和反向传播的过程。

这里有这样一个计算结构,先给出一个定义,将单个计算单元所得的损失值写作C,
计算结构
首先只考虑一个计算单元,即红色框中的单元。
z = x 1 w 1 + x 2 w 2 + b z=x_1w_1+x_2w_2+b z=x1w1+x2w2+b,求参数 w i w_i wi的梯度,应为 ∂ C ∂ w i = ∂ C ∂ z ∂ z ∂ w i \frac{\partial C}{\partial w_i}=\frac{\partial C}{\partial z}\frac{\partial z}{\partial w_i} wiC=zCwiz,这里的计算很简单,不再展开。

继续将整个计算结构算入在内,仍然计算 ∂ C ∂ w i = ∂ C ∂ z ∂ z ∂ w i \frac{\partial C}{\partial w_i}=\frac{\partial C}{\partial z}\frac{\partial z}{\partial w_i} wiC=zCwiz,此时 ∂ C ∂ z \frac{\partial C}{\partial z} zC计算要复杂一些,应为
∂ C ∂ z = ∂ C ∂ a ∂ a ∂ z \frac{\partial C}{\partial z}=\frac{\partial C}{\partial a}\frac{\partial a}{\partial z} zC=aCza ∂ a ∂ z \frac{\partial a}{\partial z} za就是sigmoid函数求导,无需讨论,下面针对前面一个偏微分做介绍。

计算过程: ∂ C ∂ a = ∂ C ∂ z ′ ∂ z ′ ∂ a + ∂ C ∂ z ′ ′ ∂ z ′ ′ ∂ a \frac{\partial C}{\partial a}=\frac{\partial C}{\partial z'}\frac{\partial z'}{\partial a}+\frac{\partial C}{\partial z''}\frac{\partial z''}{\partial a} aC=zCaz+zCaz,其中 ∂ C ∂ z ′ = w 3 , ∂ C ∂ z ′ ′ = w 4 \frac{\partial C}{\partial z'}=w_3,\frac{\partial C}{\partial z''}=w_4 zC=w3,zC=w4,剩余的两项无法求得。此时 ∂ C ∂ z = σ ′ ( z ) ( w 3 ∗ ∂ C ∂ z ′ + w 4 ∗ ∂ C ∂ z ′ ′ ) \frac{\partial C}{\partial z}=\sigma'(z)(w_3*\frac{\partial C}{\partial z'}+w_4*\frac{\partial C}{\partial z''}) zC=σ(z)(w3zC+w4zC)

分两种情况讨论如何计算剩余的两项

  1. 假定 z ′ , z ′ ′ z',z'' z,z在sigmoid激活函数后为输出 y 1 , y 2 y_1,y_2 y1,y2(上图就是如此),则 ∂ C ∂ z ′ = ∂ C ∂ y 1 ∂ y 1 ∂ z ′ \frac{\partial C}{\partial z'}=\frac{\partial C}{\partial y_1}\frac{\partial y_1}{\partial z'} zC=y1Czy1 ∂ C ∂ z ′ = ∂ C ∂ y 2 ∂ y 2 ∂ z ′ ′ \frac{\partial C}{\partial z'}=\frac{\partial C}{\partial y_2}\frac{\partial y_2}{\partial z''} zC=y2Czy2,所有的值都是可以计算的,最终可以顺利地计算出参数 w i w_i wi的梯度 ∂ C ∂ w i \frac{\partial C}{\partial w_i} wiC
  2. z ′ , z ′ ′ z',z'' z,z在sigmoid激活函数后不为输出,就可以像加入 z ′ , z ′ ′ z',z'' z,z一样将 z ′ ′ ′ , z ′ ′ ′ ′ . . . z''',z''''... z,z...加入公式后继续延伸计算,直到输出层,通过正向传播后最终一定会存在输出,通过输出就可以计算出最后一层的梯度从而推出倒数第二层的梯度,直至算出各个参数的梯度,计算路径如下图所示。
    BP计算路径

这个过程刚好与前向传播相反,称为反向传播。

计算过程

这里针对斯坦福大学cs231n课程中给出的两个例子做推导。

直接计算

f ( x , y , z ) = ( x + y ) z f(x,y,z)=(x+y)z f(x,y,z)=(x+y)z,计算这个式子的梯度是很简单的,因为表达式已经给出并且十分简单,可以通过链式法则直接求出各变量的梯度值
这里引入一个中间变量即计算图中的非叶节点 q = x + y q=x+y q=x+y,那么 f ( x , y , z ) = f ( q , z ) = q z f(x,y,z)=f(q,z)=qz f(x,y,z)=f(q,z)=qz所以有

  1. ∂ f ∂ z = q = x + y = 3 \frac{\partial f}{\partial z}=q=x+y=3 zf=q=x+y=3
  2. ∂ f ∂ y = ∂ f ∂ q ∂ q ∂ y = z ∗ 1 = − 4 \frac{\partial f}{\partial y}=\frac{\partial f}{\partial q}\frac{\partial q}{\partial y}=z*1=-4 yf=qfyq=z1=4
  3. ∂ f ∂ x = ∂ f ∂ q ∂ q ∂ x = z ∗ 1 = − 4 \frac{\partial f}{\partial x}=\frac{\partial f}{\partial q}\frac{\partial q}{\partial x}=z*1=-4 xf=qfxq=z1=4

三个变量的梯度求解完毕。

使用计算图辅助

计算图在我的另一篇文章Pytorch自动求导中使用过一次,它的作用是帮助理解变量之间的计算关系,使用计算图可以将变量与算子的逻辑关系理清,这样在计算正向传播或反向传播时就会更加清晰。

首先对上面 f ( x , y , z ) = ( x + y ) z f(x,y,z)=(x+y)z f(x,y,z)=(x+y)z 绘制计算图并完成正向传播和反向传播,尝试着从这个最简单的例子中找寻规律
简单计算图
在正向传播最后,即刚开始反向传播时,对应一个梯度值为1,这一项的求解是固定的,可以将其写作是 ∂ f ∂ f = 1 \frac{\partial f}{\partial f}=1 ff=1,并作为一个将要传递的梯度值传递,按照正常的计算方式,这个梯度值是乘法算子由输出方向往输入方向回传,课程中把它标注为gradients,为了区分后面的局部梯度可以把它称为回传梯度(为了作说明给定的名称,并非官方名称)。

下面计算 ∂ f ∂ q \frac{\partial f}{\partial q} qf,q与z通过作为输入,通过乘法算子连接后输出,这个运算单元的操作为 f = q z f=qz f=qz,计算为 ∂ f ∂ q = z = − 4 \frac{\partial f}{\partial q}=z=-4 qf=z=4,在课程视频中,将这个结果称为计算单元的局部梯度(local gradient),将回传梯度与局部梯度相乘就得到了一个运算单元的输出对其输入值的梯度。

运算单元
目前为止,可通过直接推导与计算图两种方式理解梯度的反向传播,接下来还是利用cs231n中的稍复杂的例子来一步步计算。

式子 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+w2}} f(w,x)=1+ew0x0+w1x1+w21,如果直接考虑求解 ∂ f ∂ x \frac{\partial f}{\partial x} xf ∂ f ∂ w 0 \frac{\partial f}{\partial w_0} w0f…变量的梯度,会十分复杂,这时利用计算图可以简化,通过计算图来理解梯度反向传播过程:
计算图求Bp
f ( w , x ) f(w,x) f(w,x)分解为计算图,并给出正向传播值,如上图绿色字体,下面按红色编号分别计算梯度传递。首先要明确的一点是,我们最终求解的目的是找到参数的梯度并更新它们的值,在此之前对1-8的求解都是服务于最终求解各参数的梯度,为了方便表示,每一个计算单元用 O i O_i Oi表示,i对应上图中红色编号

符号 算子 局部梯度 前一单元回传梯度 最终梯度值
1 ∂ f ∂ f = 1 \frac{\partial f}{\partial f}=1 ff=1 1
2 1 x \frac{1}{x} x1 ∂ O 2 ∂ x = − 1 x 2 = − 1 1.3 7 2 = − 0.53 \frac{\partial O_2}{\partial x}=-\frac{1}{x^2}=-\frac{1}{1.37^2}=-0.53 xO2=x21=1.3721=0.53 1 − 0.53 ∗ 1 = − 0.53 -0.53*1=-0.53 0.531=0.53
3 x + 1 x+1 x+1 ∂ O 3 ∂ x = 1 \frac{\partial O_3}{\partial x}=1 xO3=1 -0.53 − 0.53 ∗ 1 = − 0.53 -0.53*1=-0.53 0.531=0.53
4 e x e^x ex ∂ O 4 ∂ x = e − 1 \frac{\partial O_4}{\partial x}=e^{-1} xO4=e1 -0.53 − 0.53 ∗ e − 1 = − 0.20 -0.53*e^{-1}=-0.20 0.53e1=0.20
5 − x -x x ∂ O 5 ∂ x = − 1 \frac{\partial O_5}{\partial x}=-1 xO5=1 -0.20 − 0.20 ∗ − 1 = 0.20 -0.20*-1=0.20 0.201=0.20
w 2 w_2 w2 w 2 + q 1 w_2+q_1 w2+q1(使用 q 1 q_1 q1代替7,8正向传播结果) ∂ ( w 2 + q 1 ) ∂ w 1 = 1 \frac{\partial (w_2+q_1)}{\partial w_1}=1 w1(w2+q1)=1 0.20 1 ∗ 0.20 = 0.20 1*0.20=0.20 10.20=0.20
6 w 2 + q 1 w_2+q_1 w2+q1 ∂ ( w 2 + q 1 ) ∂ q 1 = 1 \frac{\partial (w_2+q_1)}{\partial q_1}=1 q1(w2+q1)=1 0.20 1 ∗ 0.20 = 0.20 1*0.20=0.20 10.20=0.20
7 q 2 + q 3 q_2+q_3 q2+q3( q 2 q_2 q2代替 w 0 w_0 w0 x 0 x_0 x0正向传播结果, q 3 q_3 q3代替 w 1 w_1 w1 x 1 x_1 x1正向传播结果) ∂ O 7 ∂ q 2 = 1 \frac{\partial O_7}{\partial q_2}=1 q2O7=1 0.20 1 ∗ 0.20 = 0.20 1*0.20=0.20 10.20=0.20
8 q 2 + q 3 q_2+q_3 q2+q3 ∂ O 8 ∂ q 3 = 1 \frac{\partial O_8}{\partial q_3}=1 q3O8=1 0.20 1 ∗ 0.20 = 0.20 1*0.20=0.20 10.20=0.20
w 0 w_0 w0 w 0 ∗ x 0 w_0*x_0 w0x0 ∂ w 0 ∗ x 0 ∂ w 0 = x 0 = − 1 \frac{\partial w_0*x_0}{\partial w_0}=x_0=-1 w0w0x0=x0=1 0.20 − 1 ∗ 0.20 = − 0.20 -1*0.20=-0.20 10.20=0.20
x 0 x_0 x0 w 0 ∗ x 0 w_0*x_0 w0x0 ∂ w 0 ∗ x 0 ∂ x 0 = w 0 = 2 \frac{\partial w_0*x_0}{\partial x_0}=w_0=2 x0w0x0=w0=2 0.20 2 ∗ 0.20 = 0.40 2*0.20=0.40 20.20=0.40
w 1 w_1 w1 w 1 ∗ x 1 w_1*x_1 w1x1 ∂ w 1 ∗ x 1 ∂ w 1 = x 1 = − 2 \frac{\partial w_1*x_1}{\partial w_1}=x_1=-2 w1w1x1=x1=2 0.20 − 2 ∗ 0.20 = − 0.40 -2*0.20=-0.40 20.20=0.40
x 1 x_1 x1 w 1 ∗ x 1 w_1*x_1 w1x1 ∂ w 1 ∗ x 1 ∂ x 1 = w 1 = − 3 \frac{\partial w_1*x_1}{\partial x_1}=w_1=-3 x1w1x1=w1=3 0.20 − 3 ∗ 0.20 = − 0.60 -3*0.20=-0.60 30.20=0.60

如果将 x 0 , x 1 x0,x1 x0,x1看作是输入值,需要更新的参数则是 w 0 , w 1 , w 2 w_0,w_1,w_2 w0,w1,w2,它们的梯度计算完成,就可以利用熟悉的梯度下降法优化参数,例如
θ i = θ i − 1 − η ∇ L ( θ i − 1 ) \theta^i=\theta^{i-1}-\eta \nabla L(\theta^{i-1}) θi=θi1ηL(θi1) η \eta η为学习率

从这个过程里,还是能够找出一些规律帮助简化计算

  • 对加法算子,算子输出对两个输入的局部梯度均为1,因此最终梯度=回传梯度*1=回传梯度
  • 对乘法算子,算子输出 O O O对两个输入 i 1 , i 2 i_1,i_2 i1,i2的局部梯度是对方的正向传播值,即 ∇ i 1 \nabla i_1 i1=回传梯度* i 2 i_2 i2, ∇ i 2 \nabla i_2 i2=回传梯度* i 1 i_1 i1

另外还有两个技巧是以上两个计算中没有使用过的

  • 对于取最大值的操作算子,它的梯度会传给其中一个在正向传播中值最大的那个输入。因为在取最大值操作中,最高值的局部梯度是1,其余均为0
  • 对于sigmoid函数 σ ( x ) = 1 1 + e − x \sigma(x)=\frac{1}{1+e^{-x}} σ(x)=1+ex1,有 d σ d x = σ ( 1 − σ ) \frac{d\sigma}{dx}=\sigma(1-\sigma) dxdσ=σ(1σ)

实际推导

给出一个简单神经网络,对其损失函数做手动BP推导
给定输出标记值分别为0.01和0.99,激活函数使用sigmoid,网络结构如下
示例

前向传播

  1. 隐藏层输入
    h 1 i n = 0.05 ∗ 0.15 + 0.1 ∗ 0.2 + 0.35 = 0.3775 h_{1_{in}}=0.05*0.15+0.1*0.2+0.35=0.3775 h1in=0.050.15+0.10.2+0.35=0.3775
    h 2 i n = 0.05 ∗ 0.2 + 0.1 ∗ 0.3 + 0.35 = 0.39 h_{2_{in}}=0.05*0.2+0.1*0.3+0.35=0.39 h2in=0.050.2+0.10.3+0.35=0.39
    隐藏层输出
    h 1 o u t = 1 1 + e − 0.3775 = 0.5933 h_{1_{out}}=\frac{1}{1+e^{-0.3775}}=0.5933 h1out=1+e0.37751=0.5933
    h 2 o u t = 1 1 + e − 0.39 = 0.5963 h_{2_{out}}=\frac{1}{1+e^{-0.39}}=0.5963 h2out=1+e0.391=0.5963
  2. 输出层输入
    O 1 i n = 0.5933 ∗ 0.4 + 0.5963 ∗ 0.45 + 0.6 = 1.1057 O_{1_{in}}=0.5933*0.4+0.5963*0.45+0.6=1.1057 O1in=0.59330.4+0.59630.45+0.6=1.1057
    O 2 i n = 0.5933 ∗ 0.5 + 0.5963 ∗ 0.55 + 0.6 = 1.2246 O_{2_{in}}=0.5933*0.5+0.5963*0.55+0.6=1.2246 O2in=0.59330.5+0.59630.55+0.6=1.2246
    输出层输出
    O 1 o u t = 1 1 + e − 1.1057 = 0.7513 O_{1_{out}}=\frac{1}{1+e^{-1.1057}}=0.7513 O1out=1+e1.10571=0.7513
    O 2 o u t = 1 1 + e − 1.2246 = 0.7729 O_{2_{out}}=\frac{1}{1+e^{-1.2246}}=0.7729 O2out=1+e1.22461=0.7729

损失函数

使用MSE损失函数 L = Σ 1 2 ( y ^ − y ) 2 L=\Sigma\frac{1}{2}(\hat y-y)^2 L=Σ21(y^y)2

L o 1 = 1 2 ( 0.01 − 0.7513 ) 2 = 0.2748 L_{o1}=\frac{1}{2}(0.01-0.7513)^2=0.2748 Lo1=21(0.010.7513)2=0.2748
L o 2 = 1 2 ( 0.99 − 0.7729 ) 2 = 0.0.0236 L_{o2}=\frac{1}{2}(0.99-0.7729)^2=0.0.0236 Lo2=21(0.990.7729)2=0.0.0236
总损失 L = L o 1 + L o 2 = 0.2984 L=L_{o1}+L_{o2}=0.2984 L=Lo1+Lo2=0.2984

反向传播

可以使用计算图来完成梯度更新,对于这个例子,计算图较复杂,不再画出,但是仍然按照计算图的方式分步计算
首先是求解与输出层相连的 w 5 , w 6 , w 7 , w 8 w_5,w_6,w_7,w_8 w5,w6,w7,w8的梯度并更新他们的值,以 w 5 w_5 w5为例,梯度为 ∂ L ∂ w 5 \frac{\partial L}{\partial w_5} w5L,详细推导如下
w5更新
w 6 , w 7 , w 8 w_6,w_7,w_8 w6,w7,w8计算方式相同,最后可得出更新后,分别计算如下
w6,w7,w8更新
w 1 , w 2 , w 3 , w 4 w_1,w_2,w_3,w_4 w1,w2,w3,w4需要经过一个隐藏层,梯度计算起来要麻烦一些,但总体还是按照计算图的思考方式解决,首先来看 w 1 w_1 w1,单独抽取关于它传播,反向传播路径如下图
w1反向传播路径
w 1 w_1 w1的更新过程如下
w1更新
w 2 , w 3 , w 4 w_2,w_3,w_4 w2,w3,w4的梯度计算和更新过程基本相似,这里不再给出。

总结

通过实例计算,更加真实地感知梯度反向传播的过程,并且对cs231课程实例代码之后提到的两个计算注意事项也更有体会,在反向传播过程中,会不断利用前向传播结果和反向路径上的值,因此缓存这些计算过的结果是很有必要的。

  1. 对前向传播变量进行缓存
  2. 在不同分支的梯度要相加

这时再来理解Pytorch自动求导中提到的一些概念,就会更加深刻。

首先根据前向传播和损失函数,pytorch会自动建立计算图,关于计算图中的叶子节点和非叶节点在文章中也有介绍。

我们需要理解的是非叶节点对应的是通过运算创建的Tensor,类似于上面提到的计算单元,这些节点pytorch通过grad_fn属性表示梯度函数,如add、mul等,分别对应grad_fn为 AddBackward与MulBackward对象。

此外pytorch还有一个grad属性,用于保存计算过程中各变量的梯度,这也实现了手动推导中计算路径上可供多次利用的缓存值。

这里只是对结构较为简单的网络的反向传播做了推导和理解,对于一些复杂的网络如CNN各层的反向传播思想,目前还没完全明白,需要在后面的学习过程中继续深入。

附:加入正则化后的梯度推导

L 2 L2 L2正则化为例,对加入正则项的梯度公式进行推导
L2正则化后的BP

猜你喜欢

转载自blog.csdn.net/qq_41533576/article/details/119327671