使用数学公式推导反向传播算法
网络结构
此图仅用来参考,说明神经网络结构
符号说明
符号 | 含义 |
---|---|
ω i j ( l ) \omega_{ij}^{(l)} ωij(l) | 网络中第 l − 1 l-1 l−1 层第 j j j 个节点到神经网络中第 l l l 层第 i i i 个节点之间的权重 |
b i ( l ) b_i^{(l)} bi(l) | 网络中第 l l l 层第 i i i 个节点的偏执 |
σ ( ) \sigma() σ() | 神经元激活函数,这里使用sigmoid激活函数 |
α i ( l ) \alpha_i^{(l)} αi(l) | 网络中第 l l l 层第 i i i 个节点的输入 |
β i ( l ) \beta_i^{(l)} βi(l) | 网络中第 l l l 层第 i i i 个节点的输出 |
l l l | 神经网络的层号,这里从 1 开始(即输入层的编号为1),共有 L L L 层(即输出层的编号为 L L L) |
k ( l ) k^{(l)} k(l) | 网络中第 l l l 层的神经元个数 |
x ( j ) x^{(j)} x(j) | 输入层第 j j j 个输入 |
y ⌢ ( j ) \overset\frown y^{(j)} y⌢(j) | 输出层第 j j j 个输出 |
y ( j ) y^{(j)} y(j) | 样本真实值得第 j j j 个值 |
E E E | 损失函数,这里使用均方误差 |
一些符号可能与网上其他博客有所出入
前向传播
给定训练集,前向传播的流程如下:
输入层->隐藏层:
α
i
(
2
)
=
∑
j
k
(
1
)
ω
i
j
(
2
)
x
(
j
)
+
b
i
(
2
)
β
i
(
2
)
=
σ
(
α
i
(
2
)
)
\alpha_i^{(2)}=\sum_j^{k^{(1)}}\omega_{ij}^{(2)}x^{(j)}+b_i^{(2)}\\ \beta_i^{(2)}=\sigma(\alpha_i^{(2)})
αi(2)=j∑k(1)ωij(2)x(j)+bi(2)βi(2)=σ(αi(2))
隐藏层->隐藏层:
α
i
(
l
)
=
∑
j
k
(
l
−
1
)
ω
i
j
(
l
)
β
j
(
l
−
1
)
+
b
i
(
l
)
β
i
(
l
)
=
σ
(
α
i
l
)
\alpha_i^{(l)}=\sum_j^{k^{(l-1)}}\omega_{ij}^{(l)}\beta_j^{(l-1)}+b_i^{(l)}\\ \beta_i^{(l)}=\sigma(\alpha_i^l)
αi(l)=j∑k(l−1)ωij(l)βj(l−1)+bi(l)βi(l)=σ(αil)
隐藏层->输出层:
α
i
(
L
)
=
∑
j
k
(
L
−
1
)
ω
i
j
(
L
)
β
j
(
L
−
1
)
+
b
i
(
L
)
y
⌢
(
i
)
=
σ
(
α
i
L
)
E
=
1
2
∑
j
k
(
L
)
(
y
⌢
(
j
)
−
y
(
j
)
)
2
\alpha_i^{(L)}=\sum_j^{k^{(L-1)}}\omega_{ij}^{(L)}\beta_j^{(L-1)}+b_i^{(L)}\\ \overset\frown y^{(i)}=\sigma(\alpha_i^L)\\ E=\frac12\sum_j^{k^{(L)}}{(\overset\frown y^{(j)}-y^{(j)})}^2
αi(L)=j∑k(L−1)ωij(L)βj(L−1)+bi(L)y⌢(i)=σ(αiL)E=21j∑k(L)(y⌢(j)−y(j))2
反向传播
反向传播与的核心思想只有一个——链式求导
反向传播要干什么:对待更新的各个参数求导,并将其用于用于梯度下降等优化方法更新网络参数
要对哪些参数求导:
ω
i
j
(
l
)
\omega_{ij}^{(l)}
ωij(l) 和
b
i
(
l
)
b_i^{(l)}
bi(l) 。
反向传播的流程如下:
首先对
ω
i
j
(
l
)
\omega_{ij}^{(l)}
ωij(l) 求导,我们的目标是求
∂
E
∂
ω
i
j
(
l
)
\frac{\partial E}{\partial\omega_{ij}^{(l)}}
∂ωij(l)∂E,其公式如下:
∂
E
∂
ω
i
j
(
l
)
=
∑
m
k
(
l
+
1
)
∑
n
k
(
l
+
2
)
⋯
∑
h
k
(
L
)
∂
E
∂
y
⌢
(
h
)
∂
y
⌢
(
h
)
∂
α
h
(
L
)
∂
α
h
(
L
)
∂
β
m
(
L
−
1
)
⋯
∂
β
n
(
l
+
2
)
∂
α
n
(
l
+
2
)
∂
α
n
(
l
+
2
)
∂
β
m
(
l
+
1
)
∂
β
m
(
l
+
1
)
∂
α
m
(
l
+
1
)
∂
α
m
(
l
+
1
)
∂
β
i
(
l
)
∂
β
i
(
l
)
∂
α
i
(
l
)
∂
α
i
(
l
)
∂
ω
i
j
(
l
)
{\textstyle\frac{\partial E}{\partial\omega_{ij}^{(l)}}}=\sum_m^{k^{(l+1)}}\sum_n^{k^{(l+2)}}\cdots\sum_h^{k^{(L)}}{\textstyle\frac{\partial E}{\partial\overset\frown y^{(h)}}}{\textstyle\frac{\partial\overset\frown y^{(h)}}{\partial\alpha_h^{(L)}}}{\textstyle\frac{\partial\alpha_h^{(L)}}{\partial\beta_m^{(L-1)}}}{\textstyle\cdots}{\textstyle\frac{\partial\beta_n^{(l+2)}}{\partial\alpha_n^{(l+2)}}}{\textstyle\frac{\partial\alpha_n^{(l+2)}}{\partial\beta_m^{(l+1)}}}{\textstyle\frac{\partial\beta_m^{(l+1)}}{\partial\alpha_m^{(l+1)}}}{\textstyle\frac{\partial\alpha_m^{(l+1)}}{\partial\beta_i^{(l)}}}{\textstyle\frac{\partial\beta_i^{(l)}}{\partial\alpha_i^{(l)}}}{\textstyle\frac{\partial\alpha_i^{(l)}}{\partial\omega_{ij}^{(l)}}}\\
∂ωij(l)∂E=m∑k(l+1)n∑k(l+2)⋯h∑k(L)∂y⌢(h)∂E∂αh(L)∂y⌢(h)∂βm(L−1)∂αh(L)⋯∂αn(l+2)∂βn(l+2)∂βm(l+1)∂αn(l+2)∂αm(l+1)∂βm(l+1)∂βi(l)∂αm(l+1)∂αi(l)∂βi(l)∂ωij(l)∂αi(l)
这个公式有点复杂,但思路很简单,就是利用链式求导从输出层一直求导到
ω
i
j
(
l
)
\omega_{ij}^{(l)}
ωij(l)所在的层。下面对该公式进行化简,令
δ
i
(
l
)
=
∂
E
∂
α
i
(
l
)
=
∑
m
k
(
l
+
1
)
∑
n
k
(
l
+
2
)
⋯
∑
h
k
(
L
)
∂
E
∂
y
⌢
(
h
)
∂
y
⌢
(
h
)
∂
α
h
(
L
)
∂
α
h
(
L
)
∂
β
m
(
L
−
1
)
⋯
∂
β
n
(
l
+
2
)
∂
α
n
(
l
+
2
)
∂
α
n
(
l
+
2
)
∂
β
m
(
l
+
1
)
∂
β
m
(
l
+
1
)
∂
α
m
(
l
+
1
)
∂
α
m
(
l
+
1
)
∂
β
i
(
l
)
∂
β
i
(
l
)
∂
α
i
(
l
)
\delta_i^{(l)}={\textstyle\frac{\partial E}{\partial\alpha_i^{(l)}}}=\sum_m^{k^{(l+1)}}\sum_n^{k^{(l+2)}}\cdots\sum_h^{k^{(L)}}{\textstyle\frac{\partial E}{\partial\overset\frown y^{(h)}}}{\textstyle\frac{\partial\overset\frown y^{(h)}}{\partial\alpha_h^{(L)}}}{\textstyle\frac{\partial\alpha_h^{(L)}}{\partial\beta_m^{(L-1)}}}{\textstyle\cdots}{\textstyle\frac{\partial\beta_n^{(l+2)}}{\partial\alpha_n^{(l+2)}}}{\textstyle\frac{\partial\alpha_n^{(l+2)}}{\partial\beta_m^{(l+1)}}}{\textstyle\frac{\partial\beta_m^{(l+1)}}{\partial\alpha_m^{(l+1)}}}{\textstyle\frac{\partial\alpha_m^{(l+1)}}{\partial\beta_i^{(l)}}}{\textstyle\frac{\partial\beta_i^{(l)}}{\partial\alpha_i^{(l)}}}\\
δi(l)=∂αi(l)∂E=m∑k(l+1)n∑k(l+2)⋯h∑k(L)∂y⌢(h)∂E∂αh(L)∂y⌢(h)∂βm(L−1)∂αh(L)⋯∂αn(l+2)∂βn(l+2)∂βm(l+1)∂αn(l+2)∂αm(l+1)∂βm(l+1)∂βi(l)∂αm(l+1)∂αi(l)∂βi(l)
则有
∂
E
∂
ω
i
j
(
l
)
=
δ
i
(
l
)
∂
α
i
(
l
)
∂
ω
i
j
(
l
)
=
δ
i
(
l
)
β
j
(
l
−
1
)
{\textstyle\frac{\partial E}{\partial\omega_{ij}^{(l)}}}=\delta_i^{(l)}{\textstyle\frac{\partial\alpha_i^{(l)}}{\partial\omega_{ij}^{(l)}}}=\delta_i^{(l)}\beta_j^{(l-1)}
∂ωij(l)∂E=δi(l)∂ωij(l)∂αi(l)=δi(l)βj(l−1)
引入
δ
i
(
l
)
\delta_i^{(l)}
δi(l)有什么用呢,接下来介绍反向传播算法最关键的部分:
δ
(
l
)
\delta^{(l)}
δ(l)与
δ
(
l
+
1
)
\delta^{(l+1)}
δ(l+1)之间的关系,由上面的公式很容易得到
δ
i
(
l
)
=
∑
m
k
(
l
+
1
)
δ
m
(
l
+
1
)
∂
α
m
(
l
+
1
)
∂
β
i
(
l
)
∂
β
i
(
l
)
∂
α
i
(
l
)
=
∑
m
k
(
l
+
1
)
δ
m
(
l
+
1
)
ω
m
i
(
l
+
1
)
σ
′
(
α
i
(
l
)
)
δ
i
(
L
)
=
∂
E
∂
α
i
(
L
)
=
∂
E
∂
y
⌢
(
i
)
∂
y
⌢
(
i
)
∂
α
i
(
L
)
=
(
y
⌢
(
i
)
−
y
(
i
)
)
σ
′
(
α
i
(
L
)
)
\delta_i^{(l)}\;=\sum_m^{k^{(l+1)}}\;\delta_m^{(l+1)}{\textstyle\frac{\partial\alpha_m^{(l+1)}}{\partial\beta_i^{(l)}}}{\textstyle\frac{\partial\beta_i^{(l)}}{\partial\alpha_i^{(l)}}=\sum_m^{k^{(l+1)}}\;\delta_m^{(l+1)}\omega_{mi}^{(l+1)}\sigma'(\alpha_i^{(l)})}\\\\\\\\ \delta_i^{(L)}\;={\textstyle\frac{\partial E}{\partial\alpha_i^{(L)}}}=\textstyle\frac{\partial E}{\partial\overset\frown y^{(i)}}{\textstyle\frac{\partial\overset\frown y^{(i)}}{\partial\alpha_i^{(L)}}=(\overset\frown y^{(i)}-y^{(i)})\sigma'(\alpha_i^{(L)})}\\\\
δi(l)=m∑k(l+1)δm(l+1)∂βi(l)∂αm(l+1)∂αi(l)∂βi(l)=∑mk(l+1)δm(l+1)ωmi(l+1)σ′(αi(l))δi(L)=∂αi(L)∂E=∂y⌢(i)∂E∂αi(L)∂y⌢(i)=(y⌢(i)−y(i))σ′(αi(L))
这样反向传播的精髓就有了:可以通过第
l
+
1
l+1
l+1 层求得的计算成果便捷地对第
l
l
l 层进行计算。
以上对
ω
i
j
(
l
)
\omega_{ij}^{(l)}
ωij(l) 进行了求导,对
b
i
(
l
)
b_{i}^{(l)}
bi(l)的求导过程类似,不再赘述,仅给出公式
∂
E
∂
b
i
(
l
)
=
δ
i
(
l
)
∂
a
i
(
l
)
∂
b
i
(
l
)
=
δ
i
(
l
)
\textstyle\frac{\partial E}{\partial b_i^{(l)}}=\delta_i^{(l)}\frac{\partial a_i^{(l)}}{\partial b_i^{(l)}}=\delta_i^{(l)}
∂bi(l)∂E=δi(l)∂bi(l)∂ai(l)=δi(l)
公式总结
上面对反向传播的数学推导进行了介绍,接下来对其公式进行总结,方便记忆。
公式简介 | 具体公式 |
---|---|
ω i j ( l ) \omega_{ij}^{(l)} ωij(l) 求导公式 | ∂ E ∂ ω i j ( l ) = δ i ( l ) ∂ α i ( l ) ∂ ω i j ( l ) = δ i ( l ) β j ( l − 1 ) {\textstyle\frac{\partial E}{\partial\omega_{ij}^{(l)}}}=\delta_i^{(l)}{\textstyle\frac{\partial\alpha_i^{(l)}}{\partial\omega_{ij}^{(l)}}}=\delta_i^{(l)}\beta_j^{(l-1)} ∂ωij(l)∂E=δi(l)∂ωij(l)∂αi(l)=δi(l)βj(l−1) |
b i ( l ) b_{i}^{(l)} bi(l) 求导公式 | ∂ E ∂ b i ( l ) = δ i ( l ) ∂ a i ( l ) ∂ b i ( l ) = δ i ( l ) \textstyle\frac{\partial E}{\partial b_i^{(l)}}=\delta_i^{(l)}\frac{\partial a_i^{(l)}}{\partial b_i^{(l)}}=\delta_i^{(l)} ∂bi(l)∂E=δi(l)∂bi(l)∂ai(l)=δi(l) |
δ \delta δ 的含义 | δ i ( l ) = ∂ E ∂ α i ( l ) \delta_i^{(l)}={\textstyle\frac{\partial E}{\partial\alpha_i^{(l)}}} δi(l)=∂αi(l)∂E |
通过 δ i ( l + 1 ) \delta_i^{(l+1)} δi(l+1) 求 δ i ( l ) \delta_i^{(l)} δi(l) | δ i ( l ) = ∑ m k ( l + 1 ) δ m ( l + 1 ) ω m i ( l + 1 ) σ ′ ( α i ( l ) ) \delta_i^{(l)}\;=\sum_m^{k^{(l+1)}}\;\delta_m^{(l+1)}\omega_{mi}^{(l+1)}\sigma'(\alpha_i^{(l)}) δi(l)=∑mk(l+1)δm(l+1)ωmi(l+1)σ′(αi(l)) |
最初的 δ \delta δ , δ ( L ) \delta^{(L)} δ(L) | δ i ( L ) = ( y ⌢ ( i ) − y ( i ) ) σ ′ ( α i ( L ) ) \delta_i^{(L)}\;=(\overset\frown y^{(i)}-y^{(i)})\sigma'(\alpha_i^{(L)}) δi(L)=(y⌢(i)−y(i))σ′(αi(L)) |
使用计算图实现反向传播算法
不少文献和博客提到了计算图,计算图带给我们另一种描述神经网络和反向传播的方式。计算图将反向传播算法中复杂的计算过成分解成一个一个的简单的步骤,使得复杂的问题变得简单易懂。
什么是计算图(熟悉计算图的同学直接跳过)
计算图,顾名思义就是将计算的过程用图像表示出来通过多个节点和边表示,这里以参考文献[1]中例子说明计算图的工作流程。
小明在超市买了2个100元一个的苹果,消费税是10%,请计算支付金额,并且计算苹果价格的上涨将在多大程度上影响最终支付的金额(求支付金额关于价格的导数)。
下面使用计算图解决这个问题
上面的计算图很简单,苹果的价格(100元)首先流入一个
×
\times
× 节点,与苹果的个数(2个)相乘,得到两个苹果的价格,然后再流入一个
×
\times
× 节点,与消费税(1.1)相乘,得到最终最终支付的金额220元。可以发现这是个从左到右的计算过程,称之为计算图的前向传播。
接下来考虑第二个问题,第二个问题是求支付金额关于价格的导数。这里使用计算图的反向传播求导。
如何进行反向传播?首先看一个例子,设存在
y
=
f
(
x
)
y = f(x)
y=f(x) ,则其反向传播流程如下图所示:
如图所示,反向传播的计算顺序是从右到左的,首先将信号E乘以节点的局部导数
(
∂
y
∂
x
)
(\frac{\partial y}{\partial x})
(∂x∂y),然后将结果传递给下一个节点。这里所说的局部导数是指正向传播中
y
=
f
(
x
)
y = f(x)
y=f(x) 的导数,也就是y关于x的导数
(
∂
y
∂
x
)
(\frac{\partial y}{\partial x})
(∂x∂y)。
计算图的反向传播是基于链式法则进行的,下面看一个更复杂的例子,设存在
z
=
f
(
x
,
y
)
=
(
x
+
y
)
2
z =f(x,y)= (x + y)^2
z=f(x,y)=(x+y)2 ,求
(
∂
z
∂
x
)
(\frac{\partial z}{\partial x})
(∂x∂z),则其反向传播流程如下图所示:
设
t
=
x
+
y
t = x+y
t=x+y正常情况下,我们可以通过链式法则求
(
∂
z
∂
x
)
(\frac{\partial z}{\partial x})
(∂x∂z):
∂
z
∂
x
=
∂
z
∂
t
∂
t
∂
x
\frac{\partial z}{\partial x}=\frac{\partial z}{\partial t}\frac{\partial t}{\partial x}
∂x∂z=∂t∂z∂x∂t ,计算图的计算流程与链式法则是一致的,在反向传播的过程中(从右向左的浅色路线),“**2”节点传入的是
(
∂
z
∂
z
)
(\frac{\partial z}{\partial z})
(∂z∂z) 即1,传出
∂
z
∂
z
∂
z
∂
t
\frac{\partial z}{\partial z}\frac{\partial z}{\partial t}
∂z∂z∂t∂z ,接下来数据流入
+
+
+ 节点,输出
∂
z
∂
z
∂
z
∂
t
∂
t
∂
x
=
∂
z
∂
t
∂
t
∂
x
\frac{\partial z}{\partial z}\frac{\partial z}{\partial t}\frac{\partial t}{\partial x}=\frac{\partial z}{\partial t}\frac{\partial t}{\partial x}
∂z∂z∂t∂z∂x∂t=∂t∂z∂x∂t 与一般链式法则求得的结果一模一样。
以上就是计算图求导(反向传播)的过程。将上述内容应用到求支付金额关于价格的导数这个问题上,结果如下所示
计算图成功地算出了支付金额关于价格的导数值2.2 。
Tips:计算图反向传播是有规律的,不同的节点甚至可以根据正向传播输入计算反向传播输出,例如乘法节点:
了解这些规律有利于编写相应代码。其他节点的计算规律此处不在列举,有兴趣的朋友可以自己推导或参照相关文献。
计算图的介绍到此为止,这里仅对计算图做了简要介绍,详情查看引文[1]第五章或者其他博客。
计算图的特点
对上述内容进行分析,可以发现计算图有几个特点:
-
局部计算
-
保存中间计算结果
-
可通过反向传播高效地计算导数
这些特点决定了计算图可以用来简单高效地实现反向传播算法。每个节点的计算仅与该节点在正反向传播中的输入有关(局部计算),所以我们可以将多个简单的计算图拼接成一个复杂的计算图而不影响正反向传播的计算。也就是说只要实现神经网络中几个基本的“部件”的计算图,就可以实现整个神经网络,并且能够通过反向传播高效地计算导数。
基础部件
计算图介绍完毕,接下来使用计算图构建神经网络的 “部件”。
- 仿射(Affine)层
神经网络的前向传播中用到了如下公式
α i ( l ) = ∑ j k ( l − 1 ) ω i j ( l ) β j ( l − 1 ) + b i ( l ) \alpha_i^{(l)}=\sum_j^{k^{(l-1)}}\omega_{ij}^{(l)}\beta_j^{(l-1)}+b_i^{(l)}\\ αi(l)=j∑k(l−1)ωij(l)βj(l−1)+bi(l)
通常情况下,为了算法的高效性,我们会在实现算法时使用矩阵运算,相应的公式如下
α ( l ) = ω ( l ) β ( l − 1 ) + b ( l ) \alpha^{(l)}=\omega^{(l)}\beta^{(l-1)}+b^{(l)}\\ α(l)=ω(l)β(l−1)+b(l)
也就是说需要构造描述 Y = W ⋅ X + B Y=W \cdot X+B Y=W⋅X+B 的计算图,这个运算在几何领域被称为仿射变换,所以这里把处理仿射变换的层称之为仿射层,其结构如下图所示
(dot表示矩阵乘法) - 激活层
这里激活函数选择Sigmoid,Sigmoid函数的公式如下所示
y = 1 1 + e − x y=\frac1{1+e^{-x}} y=1+e−x1
它的计算图如下所示
其中可以对输出做进一步整理:
∂ L ∂ y y 2 e − x = ( 1 1 + e − x ) 2 ( 1 + e − x ) e − x 1 + e − x = 1 1 + e − x e − x 1 + e − x = y ( 1 − y ) \frac{\partial L}{\partial y}y^2e^{-x}={(\frac1{1+e^{-x}})}^2\frac{(1+e^{-x})e^{-x}}{1+e^{-x}}=\frac1{1+e^{-x}}\frac{e^{-x}}{1+e^{-x}}=y(1-y)\; ∂y∂Ly2e−x=(1+e−x1)21+e−x(1+e−x)e−x=1+e−x11+e−xe−x=y(1−y)
我们可以画出计算图的简化版
- MSE层
神经网络是没有损失函数层的,但是反向传播的时候一开始需要对损失函数求导,所以需要画出损失函数的计算图。参考文献[1]中给出了Softmax-with-loss层的计算图,推导过程比较复杂,不再介绍。这里就用MSE作为损失函数,绘制计算图。损失函数公式如下
E = 1 2 ∑ j k ( L ) ( y ⌢ ( j ) − y ( j ) ) 2 E=\frac12\sum_j^{k^{(L)}}{(\overset\frown y^{(j)}-y^{(j)})}^2 E=21j∑k(L)(y⌢(j)−y(j))2
同样,实现算法时使用矩阵运算,需要构造 E = 1 2 ( X − Y ) 2 E=\frac12(X-Y)^2 E=21(X−Y)2 的计算图(此图为本人在processon上画的,画风与之前的不一样,请见谅)
至此,构造神经网络的三个基本部件已经实现完毕。
组合
通过像组装乐高积木一样组装上一节中实现的层,可以构建神经网络。
一个最简单的神经网络由全连接层、激活层和损失函数组成,我们可以用上述的基础部件(规模较小的计算图)组成一个复杂的神经网络的结构(一个规模较大的计算图),从而实现对神经网络、正向传播和反向传播的描述。
参考文献
[1]《深度学习入门基于Python的理论与实现》[日]斋藤康毅
[2] 一文搞懂反向传播算法
[3] 读懂反向传播算法(bp算法)