1 前言
神经网络的学习本质上就是通过大量训练进行网络中的参数更新。这么说有点笼统,下面使用一个简单的例子介绍下神经网络训练的过程。
2 神经网络
经常看到神经网络的图片是下面这样的(百度百科):
初看可能摸不到头脑,实际上只是进行了简单的矩阵乘法。
- 绿色的输入层是一个1*4的矩阵
- 图中的每一个圆代表一个神经元,其中存储一个数
- 输入层和隐层之间的连线每一条线代表了一个参数 w i j w_{ij} wij,参数和输入层的数相乘并与其他指向相同输出神经元的乘积相加得到隐层中那个神经元的值
- 红色的隐层是上一步中说到的运算结果,其实就是下面这个乘法:
( x 1 x 2 x 3 x 4 ) T × ( w 11 w 12 w 13 w 14 w 15 w 21 w 22 w 23 w 24 w 25 w 31 w 32 w 33 w 34 w 35 w 41 w 42 w 43 w 44 w 45 ) = ( x 1 w 11 + x 2 w 21 + x 3 w 31 + x 4 w 41 x 1 w 12 + x 2 w 22 + x 3 w 32 + x 4 w 42 x 1 w 13 + x 2 w 23 + x 3 w 33 + x 4 w 43 x 1 w 14 + x 2 w 24 + x 3 w 34 + x 4 w 44 x 1 w 15 + x 2 w 25 + x 3 w 35 + x 4 w 45 ) T \begin{pmatrix} x_1 \\ x_2 \\ x_3 \\ x_4 \end{pmatrix}^T × \begin{pmatrix} w_{11} & w_{12} & w_{13} & w_{14} & w_{15} \\ w_{21} & w_{22} & w_{23} & w_{24} & w_{25} \\ w_{31} & w_{32} & w_{33} & w_{34} & w_{35} \\ w_{41} & w_{42} & w_{43} & w_{44} & w_{45} \end{pmatrix}= \begin{pmatrix} x_1w_{11} + x_2w_{21} + x_3w_{31} + x_4w_{41} \\ x_1w_{12} + x_2w_{22} + x_3w_{32} + x_4w_{42} \\ x_1w_{13} + x_2w_{23} + x_3w_{33} + x_4w_{43} \\ x_1w_{14} + x_2w_{24} + x_3w_{34} + x_4w_{44} \\ x_1w_{15} + x_2w_{25} + x_3w_{35} + x_4w_{45} \end{pmatrix}^T x1x2x3x4 T× w11w21w31w41w12w22w32w42w13w23w33w43w14w24w34w44w15w25w35w45 = x1w11+x2w21+x3w31+x4w41x1w12+x2w22+x3w32+x4w42x1w13+x2w23+x3w33+x4w43x1w14+x2w24+x3w34+x4w44x1w15+x2w25+x3w35+x4w45 T
类似地,隐层到输出层也是这个步骤。这种两个相邻网络层之间每个神经元间都会有参数进行相乘并求和的网络层一般叫做全连接层,也是最基本的神经网络层。
我们训练神经网络,实际上是通过拟合提供的训练数据,尽可能找到每一个最优的参数 w w w的值,让输出层的取值符合我们的预期。
之后我们以一个更简单的例子说明神经网络是如何更新参数的。
3 训练过程
3.1 梯度下降
加入经过对参数
w
w
w的大量搜索,我们可以绘制出在训练数据集上全部的
w
w
w的值对应的预测结果和真实值的差距(后面这个差距会有更专业的说法)图像如下:
很明显,我们要找到的
w
w
w值就是这个点对应的
w
w
w:
貌似看起来寻找最优参数并不是什么难事,但这是一种非常理想化的假设。因为
w
w
w是一个实数,我们不可能遍历到全部的
w
w
w值。如何通过有限的计算寻找
w
w
w值成为了关键,梯度下降就是有效方法。
首先,我们训练前, w w w需要有一个随机初始值,我们的目标是通过初始值尽可能找到离上面图中差距最小点最近的位置。
比如现在我们取到的
w
w
w在这:
再次提醒一点,除了这个点所在的位置,这条曲线我们是不可见的。那我们应该如何移动这个蓝色的点呢?
我们可以对 w w w求导数(这里先直接记住预测值和真实值的差距函数我们是已知的)。在这一点,导数为负数,所以我们可以知道增大 w w w值就可以让差距变小了。
个人理解,所谓梯度,其实就是导数。梯度下降就是根据导数让参数向差距下降的方向移动。
现在的问题是,让 w w w增大多少呢?
3.2 学习率
学习率是梯度下降过程中一个重要的参数,它决定了参数每次变化的幅度,需要由我们自己来设置。
w
w
w更新值为:
w
n
e
w
=
w
o
l
d
−
g
r
a
d
×
l
r
w_{new}=w_{old}-grad×lr
wnew=wold−grad×lr
其中,
g
r
a
d
grad
grad是旧点位置的导数,
l
r
lr
lr是学习率。
很显然,学习率过大过小都不行,过大就会是如下的情况,难于收敛(并且更严重的情况是可能导致取值点离开了全局最小值附近到了另一个极小值点附近):
过小就是下面的情况,使参数收敛过于缓慢:
对于学习率的取值没有通用方式,对于不同的情况,最合适的学习率一般都不同。通常我们会随着迭代次数的增多逐渐减小学习率,这样既提高了速度,又不会太影响准度。
3.3 损失函数
到这,我们终于能对这个预测值和真实值的差距进行解释了。
我们需要一个度量来表示出我们当前这个参数求得的预测值和这个训练数据实际的真实值的差距,这个度量的函数就叫做损失函数。
损失函数可以很简单,比如 l o s s ( ) = ∣ y ^ − y ∣ loss()=|\hat{y}-y| loss()=∣y^−y∣,或者 l o s s ( ) = ( y ^ − y ) 2 loss()=(\hat{y}-y)^2 loss()=(y^−y)2。损失函数也会影响神经网络的训练效果,和学习率一样,也需要根据实际情况来设计。
3.4 前向和反向传播
前向传播就是从输入经过计算得到输出的过程,反向传播就是通过损失函数计算输出和真实值的损失值,并从后向前依次对每层参数求导进行梯度下降。
接下来我们以前面说的例子更加细致的对整个过程进行说明。
4 案例说明
我们简化上面复杂的神经网络层,让整个网络只有一个输入层和一个输出层,且都只有一个神经元,并在输出前加一个偏置量。说这么多,其实就是把上面那个矩阵乘法简化为:
x
∗
w
+
b
=
y
^
x*w+b=\hat{y}
x∗w+b=y^
y
^
\hat{y}
y^是预测值,我们用
y
y
y表示实际值。我们现在的目标是想要找到合适的
w
w
w和
b
b
b来使这个
y
^
\hat{y}
y^更加贴近
y
y
y符合我们的预期。
现在我们假设真实的参数就是 y = 2 x − 3 y=2x-3 y=2x−3。
在当前案例中,我们就使用
l
o
s
s
(
)
=
(
y
^
−
y
)
2
loss()=(\hat{y}-y)^2
loss()=(y^−y)2作为损失函数,学习率定为0.1,
w
w
w和
b
b
b的初始值都为1。
为了待会儿方便计算和表示,
y
^
=
w
x
+
b
\hat{y}=wx+b
y^=wx+b可以表示为下面的计算图1:
其中,绿圆为参数,是我们要寻找最优值的变量,灰色的是输入输出值,蓝色的是运算方式。
m
m
m是临时中间值,等于
w
x
wx
wx。
现在我们有数据集如下:
x | y |
---|---|
2 | 1 |
-1 | -5 |
我们先用第一对数据。经过前向传播的计算,我们可以得到如下的这些值:
在当前参数下,我们的预测值为3,真实值为1,损失值为4。下面我们需要进行反向传播求得
l
o
s
s
loss
loss对两个参数的导数。
首先,我们对
y
^
\hat{y}
y^进行求导,已知
l
o
s
s
(
)
=
(
y
^
−
y
)
2
loss()=(\hat{y}-y)^2
loss()=(y^−y)2,很容易能够得到:
∂
l
o
s
s
∂
y
^
=
2
(
y
^
−
y
)
=
4
\frac{\partial{loss}}{\partial{\hat{y}}}=2(\hat{y}-y)=4
∂y^∂loss=2(y^−y)=4
接着继续向前,通过链式法则对b进行求导,这里我们已知
y
^
=
m
+
b
\hat{y}=m+b
y^=m+b:
∂
l
o
s
s
∂
b
=
∂
l
o
s
s
∂
y
^
∗
∂
y
^
∂
b
=
4
∗
1
=
4
\frac{\partial{loss}}{\partial{b}}=\frac{\partial{loss}}{\partial{\hat{y}}}*\frac{\partial{\hat{y}}}{\partial{b}}=4*1=4
∂b∂loss=∂y^∂loss∗∂b∂y^=4∗1=4
根据我们设置的学习率,可以得到更新的参数
b
b
b值:
b
n
e
w
=
b
o
l
d
−
g
r
a
d
×
l
r
=
1
−
4
∗
0.1
=
0.6
b_{new}=b_{old}-grad×lr=1-4*0.1=0.6
bnew=bold−grad×lr=1−4∗0.1=0.6
很显然,
b
b
b向准确值-3更加靠近了。我们继续反向传播,现在我们需要对
m
m
m进行求导:
∂
l
o
s
s
∂
m
=
∂
l
o
s
s
∂
y
^
∗
∂
y
^
∂
m
=
4
∗
1
=
4
\frac{\partial{loss}}{\partial{m}}=\frac{\partial{loss}}{\partial{\hat{y}}}*\frac{\partial{\hat{y}}}{\partial{m}}=4*1=4
∂m∂loss=∂y^∂loss∗∂m∂y^=4∗1=4
下面继续对w求导,这里我们已知
m
=
w
x
m=wx
m=wx:
∂
l
o
s
s
∂
w
=
∂
l
o
s
s
∂
m
∗
∂
m
∂
w
=
4
∗
x
=
4
∗
2
=
8
\frac{\partial{loss}}{\partial{w}}=\frac{\partial{loss}}{\partial{m}}*\frac{\partial{m}}{\partial{w}}=4*x=4*2=8
∂w∂loss=∂m∂loss∗∂w∂m=4∗x=4∗2=8
根据我们设置的学习率,可以得到更新的参数
w
w
w值:
w
n
e
w
=
w
o
l
d
−
g
r
a
d
×
l
r
=
1
−
8
∗
0.1
=
0.2
w_{new}=w_{old}-grad×lr=1-8*0.1=0.2
wnew=wold−grad×lr=1−8∗0.1=0.2
至此,反向传播结束,也是一次完整的梯度下降的过程。你可能会奇怪这不是更加远离真实值了吗?实际上,针对第一条数据,在
w
w
w更新前后,
y
^
\hat{y}
y^从2.6变为了1,预测值其实是更加准确了。
但 y = 0.2 x + 0.6 y=0.2x+0.6 y=0.2x+0.6不是我们所期望的,这也是过拟合的雏形,即对训练集数据拟合非常好,但是对其他数据就很一般了。通常过拟合出现于数据集规模小且训练次数过多的情况下。
类似地,我们继续把第二条数据给这个简单的网络进行训练,会得到如下结果:
这个结果就更加符合我们的预期了,所以大量的数据集也是网络预测效果更好的必要条件。
5 总结
神经网络整个训练过程就是使用训练集的输入前向传播得到预测值,通过和真实值使用损失函数计算损失值,之后进行反向传播,从最后一层开始向前,逐步求出损失值对参数的导数,然后使用链式法则依次可以求出前面一层的导数,得到了导数就可以结合学习率进行梯度下降来更新参数了。