这篇博文将详细介绍神经网络以及它的python实现,主要参考书是《Python神经网络编程》。
神经网络是由现代神经科学的基础理论上发展出来的,旨在反应人脑结构及功能的一种数学模型。在如今的年代,计算机可以以相当的速度,在一秒钟内进行4位数甚至十位数的相加,其运算的速度是远远超于人脑的运算速度的。但是在之前的时候,让计算机准确的识别一幅照片是非常困难的,但是人脑可以迅速的识别一幅照片,这提示着计算机仍然不够智能,需要我们设计出一种新的算法来使得计算机变得更加智能。这时,根据人脑神经元结构,科学家们设计了人工神经网络算法。
下图是人脑神经元照片。
观察人脑的神经元之间的信息传递,我们可以知道信息是从一个神经元传送个其他的神经元,经过其他神经元,再最后输出信息。经过这一过程的启发,我们可以设计三层神经元网络。如下图:
需要特别的指出,在具体的人脑信息处理过程中,树突收集其他神经元的电信号,将其组合成形成更强的电信号。如果信号足够强,其超过了某一阈值,神经元就会发射信号,沿着轴突,到达末端,将信号传递给下一个神经元的树突。那此过程对应于人工神经网络就是激活函数。其中激活函数有许多种,常见的有阈值函数,分段线性函数,sigmoid函数等等。此次我们主要使用sigmoid函数,简称为S函数或逻辑函数。
插如函数:
y
=
1
1
+
e
x
y = \frac { 1 } { 1 + e ^ { x } }
y=1+ex1
将其抽象出来,可能更有利于我们的后续理解,如下图:
现在我们再来看之前的三层神经网络模型图。其中
w
i
j
w_{ij}
wij表示权重,表示本层的神经元传递到第二层的神经元的权重为
w
i
j
w_{ij}
wij。所以我们可以调整节点之间的神经元不断地对神经网络进行优化。这也叫就是为啥我们在运用人工智能算法总是不断地在调参。接下来,我们考虑如何用计算机实现神经元之间地运算。这便用到了矩阵乘法。
插入函数:
[
W
11
W
21
W
12
W
22
]
\left[ \begin{array} { l l } W _ { 11 } & W _ { 21 } \\ W _ { 12 } & W _ { 22 } \end{array} \right]
[W11W12W21W22]
结合上面地三层网络模型,我们可知输出地结果为二行一列地矩阵,其中第一行第一列地结果是input_1经过第一层地传播传递到第二层第一个节点地结果。其余类似。所以面对n层的网络模型,我们只需让计算机进行矩阵乘法运算即可。
我们经常用神经网络训练一个预测模型或分类模型。在我们得到一个训练集后,我们得对神经网络模型进行训练。其训练得过程实质就是确定各层节点之间的权重。这个过程,我们运用的是最速下降法。这个方法就是不断利用微分,不断地寻求局部最优解。就好像现在你站在山谷的某处,你要到达山谷的最低处,显而易见,你应该一步步地朝向地势下降地方向走,直到到达局部地最低点。 在训练神经网络模型时,我们输入训练集地数据后,经过神经网络,我们得到一个输出值
o
n
o_{n}
on,然而其有一个现实值
t
n
t_{n}
tn,我们地优化目标就是不断缩小输出值与现实值之间地差值。
我们得到这个差值后,要用后向传播算法,将误差分配给从输出层到输入层之间地神经元节点。
我们用E来表示误差值,则 插入函数(),表示了当权重变化时候,误差值E是如何变化的。这也就是我们希望使用梯度下降的方法到达最小值的方向。我们结合模型可以发现,节点n的输出on仅仅取决于连接到这个节点的链接,所以该式子可以继续化简为:插入函数()
接下来我们继续运用链式法则,将该式子变化为:
∂
E
∂
w
j
k
=
∂
(
t
k
−
O
k
)
2
∂
W
j
k
\frac { \partial E } { \partial w_ { j k} } = \frac { \partial \left( t _ { k } - O _ { k } \right) ^ { 2 } } { \partial W _ { j k } }
∂wjk∂E=∂Wjk∂(tk−Ok)2
∂
E
∂
w
j
k
=
∂
E
∂
O
k
⋅
∂
O
k
∂
W
j
k
\frac { \partial E } { \partial w _ { j k } } = \frac { \partial E } { \partial O k } \cdot \frac { \partial O _ { k } } { \partial W _ { j k } }
∂wjk∂E=∂Ok∂E⋅∂Wjk∂Ok
进一步求导得到:
∂
E
∂
W
j
,
k
=
−
2
(
t
k
−
O
k
)
⋅
∂
O
k
∂
W
j
,
k
\frac { \partial E } { \partial W _ { j , k } } = - 2 \left( t _ { k } - O _ { k } \right) \cdot \frac { \partial O _ { k } } { \partial W _ { j , k } }
∂Wj,k∂E=−2(tk−Ok)⋅∂Wj,k∂Ok
接下来对
O
k
O_{k}
Ok进一步得化简,即
∂
E
∂
W
j
k
\frac { \partial E } { \partial W _ { j k } }
∂Wjk∂E=
−
2
(
t
k
−
O
k
)
- 2 \left( t _ { k } - O_{k} \right)
−2(tk−Ok)*
O
k
O_{k}
Ok=
sigmoid
(
Σ
j
W
j
,
k
⋅
O
j
)
\operatorname { sigmoid } \left( \Sigma _ { j } W _ { j , k } \cdot O _ { j } \right)
sigmoid(ΣjWj,k⋅Oj)。
将其代入,得到:KaTeX parse error: Expected '}', got 'EOF' at end of input: …ial W _ { j k }
继续化简得到: =
−
2
(
t
k
−
O
k
)
⋅
sig
moid
(
∑
j
w
j
k
⋅
O
j
)
- 2 ( t _{k} - O_ {k} ) \cdot \operatorname { sig } \operatorname { moid } \left( \sum _ { j } w _ { j k } \cdot O _ { j } \right)
−2(tk−Ok)⋅sigmoid(∑jwjk⋅Oj)
(
1
−
sigmoid
(
Σ
j
w
j
k
⋅
O
j
)
)
⋅
O
j
\left( 1 - \operatorname { sigmoid } \left( \Sigma _ { j } w _ { j k } \cdot O _ { j } \right) \right) \cdot O _ { j }
(1−sigmoid(Σjwjk⋅Oj))⋅Oj
我们先将
∂
E
∂
w
j
,
k
\frac { \partial E } { \partial w _ { j , k } }
∂wj,k∂E化简到此程度。接下来我们思考如何训练网络或如何对权重进行更新。首先,我们要先明白更新权重是为了啥?它是为了减少误差值,即目标值与实际值得差值。换句话来说,我们就是通过更新权重来寻找最小误差值,即走到山谷得最低点。在实际训练网络得过程中,我们通过初始得权重值,是已经得到一个误差值,我们优化的方向是让误差值一次次地减小。先上一张图:
当此时的权值为w1时候,我们下一步就应该向前走去,若此时的权值为W2时,我们就应该向后走,那如何让计算机直到是向前还是向后呢?我们分析可知,这个方向是由目标值减实际值的差值的正负决定的。所以我们更新
W
j
k
W_{jk}
Wjk的式子就是new
w
j
,
k
w _ { j , k }
wj,k =old
w
j
k
−
α
⋅
∂
E
∂
w
j
,
k
w _ { j k } - \alpha \cdot \frac { \partial E } { \partial w _ { j ,k } }
wjk−α⋅∂wj,k∂E
矩阵运算形式为下图:
写成矩阵乘法的形式为
Δ
W
j
,
k
=
α
⋅
E
k
⋅
O
k
(
1
−
O
k
)
⋅
O
j
⊤
\Delta W _ { j , k } = \alpha \cdot E _ { k } \cdot O _ { k } \left( 1 - O _ { k } \right) \cdot O _ { j } ^ { \top }
ΔWj,k=α⋅Ek⋅Ok(1−Ok)⋅Oj⊤
注意是j是前一层神经元,k是后一层的神经元。
得到这个式子后,我们就只需要不断利用最速下降法找到最小点即可。下面的文章,将用python实现三层神经网络,用于手写数字的识别。