1 非线性假设
对于我们之前学习过的无论是线性回归还是逻辑回归都有一个这样的缺点,就是当特征太多时,计算的负荷会特别大。尤其是数据并不具有线性关系并且无法直观观察其具有的多项式性质,如下面:
我们在之前的章节中使用过多项式回归,对回归模型中添加多项式项,但是我们仅仅对两个特征添加多项式项就会使得程序运行的效率大大下降,假如我们有100个特征,我们希望使用这100个特征去构建我们的非线性的预测模型,结果的数量级会是令人震惊的,仅仅引入二次项就会使得100个特征变成5000个特征,这对于我们之前使用的方法来说需要计算的特征太多了。
2 神经元模型
神经网络(neural networks)方面的研究很早就已经出现,今天“神经网络”已是一个相当大的、多学科交叉的学科领域。各相关学科对神经网络的定义多种多样,这里我们介绍目前使用最广泛的一种,即神经网络是由具有适应性的简单单元组成的广发并行互连的网络,它的组织能够模拟生物神经系统对真实世界物体所作出的交互反应。 神经网络中最基本的成分是神经元(neuron)模型,即上述定义中的“简单单元”。在生物神经网络中,每个神经元与其他神经元相连,当它“兴奋”时,就会向相连的神经元发送化学物质,从而改变这些神经元内的电位;如果某神经元的电位超过了一个“阈值”(threshold),那么它就会被激活,即“兴奋”起来,向其他神经元发送化学物质。
科学家将上述情形抽象为下图中的简单模型,这就是沿用至今的“M-P神经元模型”。在这个模型中,神经元接收到来自n个神经元传过来的输入信号,这些输入信号通过带有权重的连接(connection)进行传递,神经元接受到的总输入值将与神经元的阈值进行比较,然后通过“激活函数”(activation function)处理以产生神经元的输出。
理想中的激活函数是下图中的阶跃函数,它将输入值映射为输出值“0”或“1”,显然“0”和“1”对应神经元的抑制和兴奋,但是阶跃函数具有不连续、不光滑等不太好的性质,因此实际应用中常用Sigmoid函数作为激活函数,因为它把可能在较大范围内变化的输入值挤压到(0,1)输出值范围内,因此有时也称为“挤压函数”(squashing function)。下面我们从逻辑回归模型作为自己学习模型的神经元示例,在神经网络中,参数又可被称为权重(weight),下图中
x
i
x_i
xi就是来自第i个神经元的输入,每个箭头也代表了一定的权重,最后传入当前神经元使用Sigmoid函数进行输出,注意这里在上面每一个神经元都有一个阈值,实质上输出是
y
=
f
(
∑
i
=
1
n
ω
i
x
i
−
θ
)
y=f(\sum^n_{i=1}\omega_ix_i-\theta)
y=f(∑i=1nωixi−θ),下图中我们将阈值转换成了一个偏置神经元作为输入,它的输入为
θ
\theta
θ,权重为
−
1
-1
−1,需要明白上下两种表示方式是一样的就好了,上图是西瓜书上对神经元的定义,下图是吴恩达机器学习对神经元的定义:
3 神经网络与前向传播算法
我们根据上面的神经元设计出类似的神经网络:
其中
x
1
,
x
2
,
x
3
x_1,x_2,x_3
x1,x2,x3是输入单元(input units),我们将原始数据输入给它们。
a
1
,
a
2
,
a
3
a_1,a_2,a_3
a1,a2,a3是中间单元,它们负责将数据进行处理,然后呈递到下一层。最后是输出单元,它负责计算
h
θ
(
x
)
h_\theta(x)
hθ(x)。神经网络模型是许多逻辑单元按照不同层级组织起来的网络,每一层的输出变量都是下一层的输入变量。上图就是一个3层的神经网络,第一层称为输入层(Input Layer),最后一层称为输出层(Outout Layer),中间一层称为隐藏层(Hidden Layers)。我们为每一层都添加一个偏置单元(bias unit)。
下面我们引入一些标记法来帮助描述模型: a i ( j ) a_i^{(j)} ai(j)代表第j层的第i个激活单元。 θ ( j ) \theta^{(j)} θ(j)代表从第j层映射到第j+1层时的权重的矩阵,例如 θ ( 1 ) \theta^{(1)} θ(1)代表从第一层映射到第二层的权重的矩阵。其尺寸为:以第j+1层的激活单元数量为行数,以第j层的激活单元加一为列数的矩阵。例如:上图所示的神经网络中 θ ( 1 ) \theta^{(1)} θ(1)的尺寸为3*4。
对于上图所示的模型,激活单元和输出分别表达为: a 1 ( 2 ) = g ( θ 10 ( 1 ) x 0 + θ 11 ( 1 ) x 1 + θ 12 ( 1 ) x 2 + θ 13 ( 1 ) x 3 ) a 2 ( 2 ) = g ( θ 20 ( 1 ) x 0 + θ 21 ( 1 ) x 1 + θ 22 ( 1 ) x 2 + θ 23 ( 1 ) x 3 ) a 3 ( 2 ) = g ( θ 30 ( 1 ) x 0 + θ 31 ( 1 ) x 1 + θ 32 ( 1 ) x 2 + θ 33 ( 1 ) x 3 ) h θ ( x ) = g ( θ 10 ( 2 ) a 0 ( 2 ) + θ 11 ( 2 ) a 1 ( 2 ) + θ 12 ( 2 ) a 2 ( 2 ) + θ 13 ( 2 ) a 3 ( 2 ) ) a_1^{(2)}=g(\theta^{(1)}_{10}x_0+\theta^{(1)}_{11}x_1+\theta^{(1)}_{12}x_2+\theta^{(1)}_{13}x_3) \\ a_2^{(2)}=g(\theta^{(1)}_{20}x_0+\theta^{(1)}_{21}x_1+\theta^{(1)}_{22}x_2+\theta^{(1)}_{23}x_3) \\ a_3^{(2)}=g(\theta^{(1)}_{30}x_0+\theta^{(1)}_{31}x_1+\theta^{(1)}_{32}x_2+\theta^{(1)}_{33}x_3) \\ h_\theta(x)=g(\theta^{(2)}_{10}a_0^{(2)}+\theta^{(2)}_{11}a_1^{(2)}+\theta^{(2)}_{12}a_2^{(2)}+\theta^{(2)}_{13}a_3^{(2)}) a1(2)=g(θ10(1)x0+θ11(1)x1+θ12(1)x2+θ13(1)x3)a2(2)=g(θ20(1)x0+θ21(1)x1+θ22(1)x2+θ23(1)x3)a3(2)=g(θ30(1)x0+θ31(1)x1+θ32(1)x2+θ33(1)x3)hθ(x)=g(θ10(2)a0(2)+θ11(2)a1(2)+θ12(2)a2(2)+θ13(2)a3(2))上面进行的讨论中只是将特征矩阵中的一行(一个训练实例)喂给了神经网络,我们需要将整个训练集都喂给我们的神经网络算法来学习模型。
我们可以知道:每一个a都是由上一层所有的 x x x和每一个 x x x所对应的决定的。(我们把这样的从左向右的算法称为前向传播算法(forward propagation))
下面我们使用吴恩达机器学习课程中的手写识别数据集来编码:
这里的astype方法如下:
一维情况下实质上就是使用逻辑回归,这里没有附带权值:
从一维升高到多维时可以看出我们需要进行的计算量大了好几个量级:
最后使用多维逻辑回归得到上面的结果。
使用课程中给定的权值我们仅仅执行前向传播得到的准确率要比多特征使用逻辑回归准确率还高,这样就可以看出神经网络的威力。
多类分类
当我们有不止两种分类时(也就是y=1,2,3…),比如以下这种情况时,该怎么办?如果我们要训练一个神经网络算法来识别路人、汽车、摩托车和卡车,在输出层我们应该有4个值。例如,第一个值为1或0用于预测是否是行人,第二个值用于判断是否为汽车。
输入向量有
x
x
x有三个维度,两个中间层,输出层4个神经元分别用来表示4类,也就是每一个数据在输出层都会出现
[
a
b
c
d
]
T
[a\quad b\quad c \quad d]^T
[abcd]T,且
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d中仅有一个为1,表示当前类。下面是该神经网络的可能结构示例:
神经网络算法的输出结果为四种可能情形之一:
4 神经网络与反向传播算法
代价函数
首先引入一些便于稍后讨论的新标记方法:
假设神经网络的训练样本有m个,每个包含一组输入 x x x和一组输出信号 y y y, L L L表示神经网络层数, S I S_I SI表示每层的 n e u r o n neuron neuron个数( S l S_l Sl表示输出层神经元个数), S L S_L SL代表最后一层中处理单元的个数。
将神经网络的分类定义为两种情况:二类分类和多类分类,二类分类:
S
L
=
0
,
y
=
0
o
r
1
S_L=0,y=0 or 1
SL=0,y=0or1;K类分类:
S
L
=
k
,
y
i
=
1
S_L=k,y_i=1
SL=k,yi=1表示分到第
i
i
i类;(k>2)
我们回顾逻辑回归问题中我们的代价函数为:
J
(
θ
)
=
−
1
m
[
∑
i
=
1
m
y
(
i
)
log
h
θ
(
x
(
i
)
)
+
(
1
−
y
(
i
)
)
log
(
1
−
h
θ
(
x
(
i
)
)
)
]
+
λ
2
m
∑
j
=
1
n
θ
j
2
J(\theta)=-\frac{1}{m}[\sum^m_{i=1}y^{(i)}\log{h_\theta(x^{(i)})}+(1-y^{(i)})\log{(1-h_\theta(x^{(i)}))}]+\frac{\lambda}{2m}\sum^n_{j=1}\theta^2_j
J(θ)=−m1[i=1∑my(i)loghθ(x(i))+(1−y(i))log(1−hθ(x(i)))]+2mλj=1∑nθj2在逻辑回归中,我们只有一个输出变量,又称标量(scalar),也只有一个因变量
y
y
y,但是在神经网络中,我们可以有很多输出变量,我们的
h
θ
(
x
)
h_\theta(x)
hθ(x)是一个维度为K的向量,并且我们训练集中的因变量也是同样维度的一个向量,因此我们的代价函数会比逻辑回归更加复杂一些,为:
h
θ
(
x
)
∈
R
K
(
h
θ
(
x
)
)
i
=
i
t
h
o
u
t
p
u
t
h_\theta(x) \in \mathbb{R}^K \quad (h_\theta(x))_i=i^{th}output
hθ(x)∈RK(hθ(x))i=ithoutput
J
(
θ
)
=
−
1
m
[
∑
i
=
1
m
∑
k
=
1
k
y
k
(
i
)
log
h
θ
(
x
(
i
)
)
k
+
(
1
−
y
k
(
i
)
)
log
(
1
−
h
θ
(
x
(
i
)
)
)
k
]
+
λ
2
m
∑
l
=
1
L
−
1
∑
i
=
1
s
l
∑
j
=
1
s
l
+
1
(
θ
j
i
(
l
)
)
2
J(\theta)=-\frac{1}{m}[\sum^m_{i=1}\sum^k_{k=1}y_k^{(i)}\log{h_\theta(x^{(i)})}_k+(1-y_k^{(i)})\log{(1-h_\theta(x^{(i)}))}_k]+\frac{\lambda}{2m}\sum^{L-1}_{l=1}\sum^{s_l}_{i=1}\sum^{s_l+1}_{j=1}(\theta^{(l)}_{ji})^2
J(θ)=−m1[i=1∑mk=1∑kyk(i)loghθ(x(i))k+(1−yk(i))log(1−hθ(x(i)))k]+2mλl=1∑L−1i=1∑slj=1∑sl+1(θji(l))2这个看起来复杂很多的代价函数背后的思想还是一样的,我们希望通过代价函数来观察算法预测结果与真实情况的误差有多大,唯一不同的是,对于每一行特征,我们都会给出K个预测,基本上我们可以利用循环,对每一行特征都预测K个结果,然后在利用循环在K个预测中选择可能性最高的一个,将其与
y
y
y中的实际数据进行比较。
正则化的那一项只是排除了每一层 θ 0 \theta_0 θ0后,每一层的 θ \theta θ矩阵的和。最里层的循环j循环所有的行(由 s l + 1 s_l+1 sl+1层的激活单元数决定),循环i则循环所有的列,由该层( s l s_l sl层)的激活单元数所决定。即: h θ ( x ) h_\theta(x) hθ(x)与真实值之间的距离为每个样本-每个类输出的加和,对参数进行regularization的bias项处理所有参数的平方和。
反向传播算法
之前我们在计算神经网络预测结果的时候我们采用了一种正向传播方法,我们从第一层开始正向一层一层进行计算,直到最后一层的 h θ ( x ) h_\theta(x) hθ(x)。
现在,为了计算代价函数的偏导数 ∂ J ( θ ) ∂ θ i j ( l ) \frac{\partial J(\theta)}{\partial \theta^{(l)}_{ij}} ∂θij(l)∂J(θ),我们需要采用一种反向传播算法,也就是首先计算最后一层的误差,然后再一层一层反向求出各层的误差,直到倒数第二层。以一个例子来说明反向传播算法。
假设我们的训练集只有一个样本 ( x ( 1 ) , y ( 1 ) ) (x^{(1)},y^{(1)}) (x(1),y(1)),我们的神经网络是一个四层的神经网络,其中 K = 4 , S L = 4 , L = 4 K=4,S_L=4,L=4 K=4,SL=4,L=4,反向传播算法:
我们从最后一层的误差开始计算,误差是激活单元的预测 ( a ( 4 ) ) (a^{(4)}) (a(4))与实际值 ( y k ) (y^k) (yk)之间的误差,(k=1:k)。我们用 δ \delta δ来表示误差,则 δ ( 4 ) = a ( 4 ) − y \delta^{(4)}=a^{(4)}-y δ(4)=a(4)−y我们利用这个误差值来计算前一层的误差: δ ( 3 ) = ( θ ( 3 ) ) T δ ( 4 ) ∗ g ′ ( z ( 3 ) ) \delta^{(3)}=(\theta^{(3)})^T\delta^{(4)}*g'(z^{(3)}) δ(3)=(θ(3))Tδ(4)∗g′(z(3))其中 g ′ ( z ( 3 ) ) g'(z^{(3)}) g′(z(3))是S形函数的导数, g ′ ( z ( 3 ) ) = a ( 3 ) ∗ ( 1 − a ( 3 ) ) g'(z^{(3)})=a^{(3)}*(1-a^{(3)}) g′(z(3))=a(3)∗(1−a(3))。而 ( θ ( 3 ) ) T δ ( 4 ) (\theta^{(3)})^T\delta^{(4)} (θ(3))Tδ(4)则是权重导致的误差的和。下一步是继续计算第二层的误差: δ ( 2 ) = ( θ ( 2 ) ) T δ ( 3 ) ∗ g ′ ( z ( 2 ) ) \delta^{(2)}=(\theta^{(2)})^T\delta^{(3)}*g'(z^{(2)}) δ(2)=(θ(2))Tδ(3)∗g′(z(2))因为第一次是输入变量,不存在误差。我们有了所有的误差的表达式后,便可以计算代价函数的偏导数了,假设 λ = 0 \lambda=0 λ=0,即我们不做任何正则化处理时有: ∂ J ( θ ) ∂ θ i j ( l ) = a j ( l ) δ i l + 1 \frac{\partial J(\theta)}{\partial \theta^{(l)}_{ij}}=a^{(l)}_j\delta_i^{l+1} ∂θij(l)∂J(θ)=aj(l)δil+1
重要的是清楚地知道上面式子中上下标的含义:
l l l代表目前所计算的是第几层。
j
j
j代表目前计算层中激活单元的下标,也将是下一层的第
j
j
j个输入变量的下标。
梯度检验
当我们对一个较为复杂的模型(例如神经网络)使用梯度下降法时,可能会存在一些不容易察觉的错误,意味着,虽然代价看上去在不断减少,但最终的结果可能并不是最优解。
为了避免这样的问题,我们采取一种叫做梯度的数值检验(Numerical Gradient Checking)方法。这种方法的思想是通过估计梯度值来检验我们计算的导数值是否真的是我们要求的。
对梯度的估计采用的方法是在代价函数上沿着切线的方向选择离两个非常近的点然后计算两个点的平均点用以估计梯度。即对于某个特定的 θ \theta θ,我们计算出在 θ − ϵ \theta-\epsilon θ−ϵ处和 θ + ϵ \theta+\epsilon θ+ϵ的代价值( ϵ \epsilon ϵ是一个非常小的值,通常选取0.001),然后求两个代价的平均,用以估计在 θ \theta θ处的代价值。
随机初始化
任何优化算法都需要一些初始参数。到目前为止我们都是初始所有参数为0,这样的初始方法对于逻辑回归来说是可行的,但是对于神经网络来说是不可行的。如果我们令所有的初始参数都为0,这将意味着我们第二层的所有激活单元都会有相同的值。同理,如果我们初始所有的参数都为一个非0的数,结果也是一样的。
综合起来
使用神经网络时的步骤:
网络结构:第一件要做的事是选择网络结构,即决定选择多少层以及决定每层分别有多少个单元。
第一层的单元数即我们训练集的特征数量。
最后一层的单元数是我们训练集的结果的类的数量。
如果隐藏层数大于1,确保每个隐藏层的单元个数相同,通常情况下隐藏层单元的个数越多越好。
我们真正要决定的是隐藏层的层数和每个中间层的单元数。
训练神经网络:
1)参数的随机初始化
2)利用正向传播方法计算所有的
h
θ
(
x
)
h_\theta(x)
hθ(x)
3)编写计算代价函数
J
J
J的代码
4)利用反向传播方法计算所有偏导数
5)利用数值检验方法检验这些偏导数
6)使用优化算法来最小化代价函数
下面按照这个步骤去实现神经网络,还是使用吴恩达机器学习中的手写识别数据集:
神经网络中反向传递的过程确实是一个很复杂的过程,要搞清其中的矩阵大小及转置与其数学原理是一件十分不容易的事情,希望可以伴随着更深入的学习逐步对神经网络有更深的认识,大家加油!