softmax函数在神经网络中使用是比较频繁,我们刚刚学习的时候,只是直到网络的最后一层经过softmax层,得到最后的输出,但不知道它的具体公式推导,因此本篇,以一个简单的网络来说明神经网络的前向传播与反向传播。
首先,以简单的单层网络为例。
隐含层对应的输出为:
h1=w1∗i1+w4∗i2+w7∗i3
h
1
=
w
1
∗
i
1
+
w
4
∗
i
2
+
w
7
∗
i
3
h2=w2∗i1+w5∗i2+w8∗i3
h
2
=
w
2
∗
i
1
+
w
5
∗
i
2
+
w
8
∗
i
3
h3=w3∗i1+w6∗i2+w9∗i3
h
3
=
w
3
∗
i
1
+
w
6
∗
i
2
+
w
9
∗
i
3
接下来h1、h2、h3经过softmax层,softmax的公式为:
softmax(xi)=exi/∑jexj
s
o
f
t
m
a
x
(
x
i
)
=
e
x
i
/
∑
j
e
x
j
,将o1,o2,o3带入公式:
o1=eh1/(eh1+eh2+eh3)
o
1
=
e
h
1
/
(
e
h
1
+
e
h
2
+
e
h
3
)
o2=eh2/(eh1+eh2+eh3)
o
2
=
e
h
2
/
(
e
h
1
+
e
h
2
+
e
h
3
)
o3=eh3/(eh1+eh2+eh3)
o
3
=
e
h
3
/
(
e
h
1
+
e
h
2
+
e
h
3
)
我们假设,输出的正确标签label为
yi
y
i
,
yi
y
i
的分布有一个特点,即:label中只有一个为1,其余懂为0,例如,本例中,label的真实标签
yi=[1,0,0]
y
i
=
[
1
,
0
,
0
]
。这个特点有助于在反向传播中的求导计算。
整合前向传播的损失函数为:
根据label的特点,loss可以简化为: LOSS=−yi∗lnoi L O S S = − y i ∗ l n o i ,由于该类的变迁 yi=1 y i = 1 ,因此loss可以进一步简化为: LOSS=−∗lnoi L O S S = − ∗ l n o i 。此时,在对损失函数求导是不是很方便了。
接下来就是进行反向传播,范围传播的目的要更新网络中的参数 wi w i 。因为,我们先以 w1 w 1 为例。
∂loss∂w1= ∂ l o s s ∂ w 1 =
∂loss∂o1∗∂o1∂h1∗∂h1∂w1 ∂ l o s s ∂ o 1 ∗ ∂ o 1 ∂ h 1 ∗ ∂ h 1 ∂ w 1
这里我们将这三部分拆开进行计算:
首先,我们来看 :
第一部分:
∂loss∂o1=−1o1 ∂ l o s s ∂ o 1 = − 1 o 1
最后一部分 :
∂h1∂w1=i1,(h1=w1∗i1+w4∗i2+w7∗i3) ∂ h 1 ∂ w 1 = i 1 , ( h 1 = w 1 ∗ i 1 + w 4 ∗ i 2 + w 7 ∗ i 3 )
中间部分,也是最重要的部分:
∂o1∂h1=∂(eh1/(eh1+eh2+eh3))∂h1= ∂ o 1 ∂ h 1 = ∂ ( e h 1 / ( e h 1 + e h 2 + e h 3 ) ) ∂ h 1 =
eh1∗(eh1+eh2+eh3)−eh12(eh1+eh2+eh3)2= e h 1 ∗ ( e h 1 + e h 2 + e h 3 ) − e h 1 2 ( e h 1 + e h 2 + e h 3 ) 2 =
eh1eh1+eh2+eh3∗eeh1+eh2+eh3−eh1eh1+eh2+eh3= e h 1 e h 1 + e h 2 + e h 3 ∗ e e h 1 + e h 2 + e h 3 − e h 1 e h 1 + e h 2 + e h 3 =
eh1eh1+eh2+eh3∗(1−o1)= e h 1 e h 1 + e h 2 + e h 3 ∗ ( 1 − o 1 ) =
o1∗(1−o1) o 1 ∗ ( 1 − o 1 )
这三部分值都已经求出,因此;
∂loss∂w1=(o1−1)∗i1 ∂ l o s s ∂ w 1 = ( o 1 − 1 ) ∗ i 1
同理,其他参数的偏导数分别为:
∂loss∂w2=(o2−1)∗i1 ∂ l o s s ∂ w 2 = ( o 2 − 1 ) ∗ i 1
∂loss∂w3=(o3−1)∗i1 ∂ l o s s ∂ w 3 = ( o 3 − 1 ) ∗ i 1
∂loss∂w4=(o1−1)∗i2 ∂ l o s s ∂ w 4 = ( o 1 − 1 ) ∗ i 2
∂loss∂w5=(o2−1)∗i2 ∂ l o s s ∂ w 5 = ( o 2 − 1 ) ∗ i 2
∂loss∂w6=(o3−1)∗i2 ∂ l o s s ∂ w 6 = ( o 3 − 1 ) ∗ i 2
∂loss∂w7=(o1−1)∗i3 ∂ l o s s ∂ w 7 = ( o 1 − 1 ) ∗ i 3
∂loss∂w8=(o2−1)∗i3 ∂ l o s s ∂ w 8 = ( o 2 − 1 ) ∗ i 3
∂loss∂w9=(o3−1)∗i3 ∂ l o s s ∂ w 9 = ( o 3 − 1 ) ∗ i 3
因此,最后w1的参数更新为:
其中 η η 为学习速率,同理更新其他的参数。本轮参数更新完毕后,在进行下一轮的前向传播和反向传播,更新参数,直到损失函数收敛。
上面只是以一层简单的网络来推导公式,网络中不含有激活函数。下面以两层网络,并且含有激活函数的网络来说明,当然,这是参考牛人的博客: https://www.cnblogs.com/charlotte77/p/5629865.html。里面的推导非常详细,博客中的激活函数使用sigmoid,损失函数使用均方差。同时,我也用代码实现博客中的推导:
def sigmoid(input):
return 1/(1 + np.exp(-input))
def test_nn():
learing_rate = 0.1
output = np.array([0.01,0.99],np.float32)
x = np.array([0.05,0.1],np.float32)
w1 = np.array([[0.15,0.25],[0.2,0.3]], np.float32)
w2 = np.array([[0.4, 0.5], [0.45, 0.55]], np.float32)
b1 = np.array([0.35,0.35],np.float32)
b2 = np.array([0.6,0.6],np.float32)
for i in range(100000):
net1 = x.dot(w1) + b1
# print(net1)
# hidden layer1
out1 = sigmoid(net1)
# print(h1)
net2 = out1.dot(w2) + b2
out2 = sigmoid(net2)
# print(out2)
out_err = np.sum(np.square(out2 - output)/2)
print('output error is:', out_err)
#back propagation
#∂Loss/∂w2 = ∂loss/out2* ∂out2/∂net2 * ∂net2/∂w2
w2_ = (-(output - out2)*out2*(1 - out2)).reshape(2,1)
w2_1 = w2_*(out1.reshape(1,2))
# w2 update
w2_update = w2 - learing_rate * w2_1.T
w1_ = w2_.T * w2[0]
err_sum = np.sum(w1_)
w1_ = (err_sum * net1*(1-net1)).reshape(2,1)
w1_ = (w1_*x)
# w1 update
w1_update = w1 - learing_rate * w1_
# print(w1_update)
w2 = w2_update
w1 = w1_update
print('result is :',out2,output)
test_nn()
经过100000轮迭代后的损失已经将为:7.95119e-06,已经非常的低了,而此时的输出结果为[ 0.01280433 0.98716486],与正确结果[ 0.01 0.99000001],已经非常接近。
output error is: 7.95119e-06
result is : [ 0.01280433 0.98716486] [ 0.01 0.99000001]
通过这两个例子,对深度学习的前向传播与反向传播就会有一个深入的了解,当然这也是深度学习的基础。只有将基础打好,才能学习与理解更深入的东西。