之前的学习中了解了一些基本的知识和定义,接下来开始学习核心一点点的东西了。
一、神经网络表示
神经网络的表示方法主要有层数、特征数这些来标记。
在给出的PPT中,层数是通过在右上角加入[i]进行标记。
在我的理解中,对于不同的神经元,我们可以配置不同的参数来提取不同的特征。在每个神经元中,保存如下信息:该神经元的参数
w
、
b
w、b
w、b。通过神经元参数和输入的特征,计算
a
=
σ
(
w
T
x
+
b
)
a=\sigma(w^Tx+b)
a=σ(wTx+b)并作为该神经元的输出提供给下一层神经网络。
所以如这张图所示,我们只有三个特征,但是可以设置更多的神经元进行提取。我们给出的初始数据对应的是输入层,中间会有多个隐藏层,最后给出输出的成为输出层。在计算深度神经网络层数时,一般不将输入层也算进去。
在计算神经网络的过程中,我们会像之前提到的一样,将过程向量化(矩阵化)以加快运算。
对于上图中的计算,我们可以得到:
z
1
[
1
]
=
w
1
[
1
]
T
x
+
b
1
[
1
]
,
a
1
[
1
]
=
σ
(
z
1
[
1
]
)
z
2
[
1
]
=
w
2
[
1
]
T
x
+
b
2
[
1
]
,
a
2
[
1
]
=
σ
(
z
2
[
1
]
)
z
3
[
1
]
=
w
3
[
1
]
T
x
+
b
3
[
1
]
,
a
3
[
1
]
=
σ
(
z
3
[
1
]
)
z
4
[
1
]
=
w
4
[
1
]
T
x
+
b
4
[
1
]
,
a
4
[
1
]
=
σ
(
z
4
[
1
]
)
z^{[1]}_1=w^{[1]T}_1x+b^{[1]}_1,a^{[1]}_1=\sigma(z^{[1]}_1)\\ z^{[1]}_2=w^{[1]T}_2x+b^{[1]}_2,a^{[1]}_2=\sigma(z^{[1]}_2)\\ z^{[1]}_3=w^{[1]T}_3x+b^{[1]}_3,a^{[1]}_3=\sigma(z^{[1]}_3)\\ z^{[1]}_4=w^{[1]T}_4x+b^{[1]}_4,a^{[1]}_4=\sigma(z^{[1]}_4)
z1[1]=w1[1]Tx+b1[1],a1[1]=σ(z1[1])z2[1]=w2[1]Tx+b2[1],a2[1]=σ(z2[1])z3[1]=w3[1]Tx+b3[1],a3[1]=σ(z3[1])z4[1]=w4[1]Tx+b4[1],a4[1]=σ(z4[1])这样的的四个计算结果。很明显,矩阵化可以让整体结构更清晰:
值得注意的是,每个
w
i
[
j
]
w^{[j]}_i
wi[j]对应的维度都应该是
1
∗
s
i
z
e
o
f
(
a
[
j
−
1
]
)
1*sizeof(a^{[j-1]})
1∗sizeof(a[j−1]),即上一层输出层的输出个数。
同理,我们在得到多个训练样本后,自然可以进一步矩阵化,结果如下所示:
之后的层也就是这个步骤的不断重复而已。
二、激活函数的选择
我们之前一直在使用sigmoid函数作为激活函数,同时我在之前就已经提到了sigmoid函数的不足,现在让我们讨论另一些激活函数。
首先是tanh函数。tanh函数其实就是sigmoid函数搬移使其通过零点而已,表现形式为:
t
a
n
h
(
z
)
=
e
z
−
e
−
z
e
z
+
e
−
z
tanh(z)=\frac{e^z-e^{-z}}{e^z+e^{-z}}
tanh(z)=ez+e−zez−e−z该函数使得激活函数的平均值为0,使得下一层学习更加容易。根据吴恩达老师的说法,tanh基本上可以完美代替sigmoid函数(除了二分类领域输出需要保持0~1)。
但是无论是tanh函数还是sigmoid函数,都有一个问题:在z非常大的时候,斜率接近于零,这就大大减缓了学习的速度。
所以在这个领域,我们引入了ReLU函数:
R
e
L
U
(
z
)
=
{
z
if
z
>
0
0
if
z
<
0
ReLU(z)=\begin{cases} z &\text{if } z>0 \\ 0 &\text{if } z<0 \end{cases}
ReLU(z)={z0if z>0if z<0这个函数的优势在于z很大的情况下也可以很快完成学习,就是说梯度很大。所以在隐藏层一般现在都用Relu函数。虽然有一半的区域ReLU的导数为0,但是可以设置足够多的隐藏单元令z>0,所以还是够用的。
另外还定义了
L
e
a
k
y
R
e
L
U
(
z
)
=
m
a
x
(
0.01
z
,
z
)
Leaky \ ReLU(z)=max(0.01z,z)
Leaky ReLU(z)=max(0.01z,z),就是在z<0处有较小的斜率。这些激活函数的图像如下图所示:
这里我要记录一下我对激活函数的思考:以前我一直不知道激活函数有什么用,现在我们除去激活函数,或者使用线性激活函数来看看,很明显,这样的函数就成了特征的线性组合:
a
[
2
]
=
z
[
2
]
=
w
[
2
]
a
[
1
]
+
b
[
2
]
=
w
[
2
]
w
[
1
]
x
+
w
[
2
]
b
[
1
]
+
b
[
2
]
=
w
′
x
+
b
′
a^{[2]}=z^{[2]}=w^{[2]}a^{[1]}+b^{[2]}=w^{[2]}w^{[1]}x+w^{[2]}b^{[1]}+b^{[2]}\\ =w'x+b'
a[2]=z[2]=w[2]a[1]+b[2]=w[2]w[1]x+w[2]b[1]+b[2]=w′x+b′相当于我们得到的输出仅仅是输入的线性组合而已,这个就是最原始的感知机,所有的隐藏层都可以删去了。也因此可以看出,我们确实需要用非线性的激活函数来提取特征。
另外这里加入一点我对ReLU函数的小思考:ReLU函数在z>0的阶段是有线性函数的特征的,但是它的导数并不连续。这里我要引入一个百度用户的思考,很有启发。
1、首先什么是线性的网络,如果把线性网络看成一个大的矩阵M。那么输入样本A和B,则会经过同样的线性变换MA,MB(这里A和B经历的线性变换矩阵M是一样的)。
2、的确对于单一的样本A,经过由relu激活函数所构成神经网络,其过程确实可以等价是经过了一个线性变换M1,但是对于样本B,在经过同样的网络时,由于每个神经元是否激活(0或者Wx+b)与样本A经过时情形不同了(不同样本),因此B所经历的线性变换M2并不等于M1。因此,relu构成的神经网络虽然对每个样本都是线性变换,但是不同样本之间经历的线性变换M并不一样,所以整个样本空间在经过relu构成的网络时其实是经历了非线性变换的。
这里讲明了ReLU函数是非线性变换的本质之一。同时我也在思考,z<0的部分ReLU结果始终是0,是不是也意味着对于不同的输入,神经元会舍弃掉不同的特征点。这个就是ReLU函数的稀疏性,可以舍弃掉部分无意义的特征。这里引用一个知乎用户的思考:
当前,深度学习一个明确的目标是从数据变量中解离出关键因子。原始数据(以自然数据为主)中通常缠绕着高度密集的特征。然而,如果能够解开特征间缠绕的复杂关系,转换为稀疏特征,那么特征就有了鲁棒性(去掉了无关的噪声)。稀疏特征并不需要网络具有很强的处理线性不可分机制。那么在深度网络中,对非线性的依赖程度就可以缩一缩。一旦神经元与神经元之间改为线性激活,网络的非线性部分仅仅来自于神经元部分选择性激活。