神经网络和深度学习之——前馈神经网络
深度神经网络,简单来理解就是含有多个隐藏层的网络。一个深度神经网络总会有一个输入层,一个输出层,还有中间多个隐藏层,隐藏层的维数决定了网络的宽度。无论是输入层、隐藏层还是输出层,每一层都是由多个感知器组成,所以深度神经网络又称多层感知机。
前面一章我们详细讲解了神经网络的组成,工作原理,信号在网络中如何流动,以及如何求解每一个输入信号赋予的权重等计算过程;同时我们还构建了一个逻辑回归网模型来解决鸢尾花分类问题,很明显,这种网络很“浅”,但它对于分类鸢尾花数据还是非常有效的,而且不仅仅是鸢尾花,对于有需要的其他二分类问题,该模型也能表现得很好。由于这种模型太“浅”了,我们一般称这种模型为bp网络,而不直接称为神经网络,有些人甚至觉得这种网络还不配叫做神经网络。我无需去争论这些芝麻小事,我们要做的就是从这一章起,迈向深度神经网络,了解什么是深度神经网络,它结构是什么样,它如何工作,以及综合前面三章的内容,用Pytorch搭建一个三层网络实现手写数字分类。
1. 深度前馈网络
1.1 什么是深度前馈网络
深度神经网络,简单来理解就是含有多个隐藏层的网络。一个深度神经网络总会有一个输入层,一个输出层,还有中间多个隐藏层,隐藏层的维数决定了网络的宽度。无论是输入层、隐藏层还是输出层,每一层都是由多个感知器组成,所以深度神经网络又称多层感知机。
前馈(feedforward)也可以称为前向,从信号流向来理解就是输入信号进入网络后,信号流动是单向的,即信号从前一层流向后一层,一直到输出层,其中任意两层之间的连接并没有反馈(feedback),亦即信号没有从后一层又返回到前一层。如果从输入输出关系来理解,则为当输入信号进入后,输入层之后的每一个层都将前一个层的输出作为输入。如下图所示的四层网络,这个图也可以称为有向无环路图。反之,当前馈神经网络中层与层之间的信号有反向流动,或者自输入时,我们则称这种网络为循环神经网络,循环神经网络在自然语言处理方面发挥着极大的作用。
图5.1.1
在深度前馈网络中,链式结构也就是层与层之间的连接方式,层数就代表网络深度。如果我们把每一层看作一个函数,那么深度神经网络就是许多不同非线性函数复合而成,这里与之前典型的线性回归和逻辑回归明显的区别开来。比如,第一层函数为\(f^{(1)}\),第二层函数为\(f^{(2)}\),第三层函数为\(f^{(3)}\),那么这个链式结构就可以表示为:\(f(x)=f^{(3)}(f^{(2)}(f^{(1)}(x)))\),通过多次复合,实现输入到输出的复杂映射,链的全长也就代表模型深度。这种网络结构比较好搭建,应用也十分广泛,比如在图像识别领域占主导地位的卷积神经网络就是深度前馈网络的一种,学习这种网络,是我们通向循环神经网络的奠基石。
1.2 深度学习
维基百科对深度学习的解释是:深度学习(deep learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。由于深度神经网络也是多层非线性变换的载体,所以也有人认为深度学习就是深度神经网络的代名词。这里请注意,我们所说是深度神经网络,而不是深度前馈网络,前馈网络仅仅是深度神经网络的其中一种。
为什么深度学习是多层和非线性变换的结合呢,很显然,我们需要从两个方面来理解。
一,我们从之前的学习中可以知道线性模型仅仅能够解决的是简单的线性分类问题,对于异或逻辑的出现会直接让线性模型出现无法工作的情况,所以非线性变换随此出现。
二,对于上面提及的多层,其实我们指的是多层隐藏层。相对于输入层或输出层的设计直观性,隐藏层的设计即是科学又是艺术,一些经验法则指出,隐藏层并不是越多越好,神经网络的研究人员已经为隐藏层开发了许多设计最优法则,这有助于网络的行为能符合人们的期望。
如果把隐藏层看成一个黑盒,那么你所要关心的只是输出,而不关注隐藏层内部如何对数据特征进行提取,如何优化权重参数和随机变量;如果把隐藏层拆开细究,隐藏层代表的则是数据特征,上一个隐藏层把特征向量经过一系列变换后输入到下一个隐藏层,隐藏层的每一个神经元都带有特征数据向前传播。如下图举例说明,输入一张人物头像,三层隐藏层依次输出的图片信息可视化后,最后由计算机得出图像特征是人脸还是动物。
图5.1.2
你可以这样理解,每一张图片就代表一层隐藏层的输出,这样我们便知道了隐藏层在对数据进行特征提取方面发挥着重要作用。
2. 梯度下降的再次学习
到目前为止,我们了解的梯度下降算法是基于线性模型的残差\(E(w)\),初始化权重\(w\)、可设定的常数学习率\(\eta\)的前提下,结合以下公式来不断更新。
\]
当然,还有种解法,如果残差(代价函数)为凸函数,那么我们只需要设残差(代价函数)的导数为0,便可以求得残差最小值所对应的权重。
\]
这种方法理论上是可行的,但如果遇到代价函数的导数很难求解的时候,问题就卡住了,相信你应该不会硬碰硬的去直接求解函数\(y=(e^x+x^2)^{\frac {1} {e^x+x}}\)的导数吧?。因此,回到上面的\(\frac{\partial E(w)}{\partial w}\),如果遇到导数无法求解的时候,我们是否可以换个方式或者法则来计算呢?数学中,链式法是求解复合函数导数的一种重要方法,让我们来回顾一下。
假设\(E(f(w))\)为\(y=f(w)\)和\(w\)复合而成的函数,那么对于求导数,我们可以有:
\]
对于求微分可写成:
\]
而我们面对的问题,和上面的链式法则定义一致。首先,别忘了残差\(E\)的定义是真实值\(t{k}\)和输出预测值\(O_{k}\)之差的平方,表示为\(E=(t_{k}-O_{k})^2\)
残差对权重的微分也可写作
\]
而预测值\(O_{k}\)又是权重\(w\)的函数,\(O_{k}=O_{k}(w)\)
运用链式法则,我们把残差对权重的微分写成:
\]
其中\(\frac{\partial E}{\partial O}\)的微分是很容易计算的平方函数微分,所以上面的式子可为:
\]
进行到这里,我们可能需要引入深度前馈网络来详细考虑一下这个\(\frac{\partial O_{k}}{\partial w_{j,k}}\)的微分该如何表示。如下某网络中任意一层,其中有四个节点,每个节点为S型感知器(即加入Sigmoid函数),我们知道,无论这一层是输入层、隐藏层还是输出层,这一层左边的输入来自外部输入或来自上一层网络的输出,而层右边的输出可作为下一层网络的输入,也可作为最终的输出。接着,我们用\(w_{j,k}\)来表示上一层的第\(j\)个节点和如图所示的这一层第\(k\)个节点的链接权重(其中\(j=1,2…n\),\(k=1,2…m\))。
图5.1.3
当输入进入该层,权重和输入信号进行加权求和,同时通过Sigmoid函数输出值:
\]
这样推导过后,我们可以得到下面的式子:
\]
在求解Sigmoid函数的导数前我们回顾一下\(Sigmoid\)函数表达式:
\]
\(Sigmoid\)对\(x\)的微分:
\]
我们暂时不必把\(Sigmoid\)函数带入计算,从这里可以看出原来Sigmoid函数的导数是如此简单,而且易于使用。
在求解\(\frac{\partial}{\partial w_{j,k}} Sigmoid(\sum w_{j,k})\),再使用一次链式法则。
我们得出一个非常炫酷的式子:
这确实是一个振奋人心的结果,眼尖的你可能一眼就看出表达式中每一项代表的意思。
不过我觉得把式子中的”2″去掉也许更简洁,为什么?因为不管前面常数是-2,2,或者是20都不是影响残差斜率的关键因素,我们只需要记住自己需要什么就可以了,这样残差的斜率就被我们简化成最干净利落的样子:
\]
我们可以放心大胆的在线性感知器中使用权重更新公式了,只要我们记住权重改变的方向与梯度方向相反,以及适度的设置其中的学习率以防止超调。
\]
不难看出,新的权重\(w_{n+1}\)是由刚刚得到误差斜率取反来调整旧的权重\(w_{n}\)而得到的。如果斜率为正,我们希望减小权重,如果斜率为负,我们希望增加权重,因此,我们要对斜率取反。学斜率\(\eta\)就是用于调节这些变化的强度,确保不会超调。现在终于可以明白,我们在上一章鸢尾花的分类中定义权重时一个重要的初始化步骤竟然就是这样来的。
self.wih += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)),
numpy.transpose(inputs))
这个权重更新表达式不仅适用于隐藏层和输出层之间的权重,而且适用于输入层和隐藏层之间的权重。比如鸢尾花分类例子中使用的模型只有一层,类似上图5.1.3,因此,代码中final_outputs
代表\(O_{k}\),而inputs
则就是外部输入\(x_{j}\);若模型为多层网络,那么”\(O_{k}\)“代表某一层的输出,\(x_{j}\)代表某一层的输入。到此为止,梯度下降算法总算了解透彻,如果还有其他复杂的数学推导放一边去吧,还有什么不满足的呢,用上面的公式,以及结合python语言,我们已经能够快速实现模型的期望结果。
3. 训练一个模型
3.1 监督学习简介
监督学习(Supervised learning),是一个机器学习中的方法,可以由训练集中学到或建立一个模式(函数/ learning model),并依此模式推测新的实例。 监督学习需要的训练集是由输入向量和与每一个输入向量相关联的目标向量组成。神经网络学习器使用目标向量来决定其已经学习的程度,并且通过目标向量指导权值的调整从而降低整体误差。
虽然不同的模型在运算数量,运算的组合方式以及所使用的参数数量上千差万别,但对于训练,我们始终可以采用相同的一般结构:
图5.1.4
这是一个训练闭环,它的整个过程我们实际上已经体验过了,但这里还是不厌其烦的再讲一讲。
- 首先对模型参数进行初始化。通常采用对参数随机赋值的方法,但对于比较简单的模型,也可以将各参数的初值均设为0。
- 读取训练数据(包括每个数据样本及其期望输出)。通常人们会在这些数据送入模型之前,随机打乱样本的次序。
- 在训练数据上执行推断模型。这样,在当前模型参数配置下,每个训练样本都会得到一个输出值。
- 计算残差。残差是一个能够刻画模型在最后一步得到的输出与来自训练集的期望输出之间差距的概括性指标。
- 调整模型参数。在给定的损失函数条件下,学习的目的在于通过大量训练步骤改善各参数的值,从而将损失最小化。最常见的策略是使用梯度下降算法,也就是以上第二部分内容“再次学习梯度下降算法”所使用的方式。
- 上述闭环会依据所需的学习速率、所给定的模型及其输入数据,通过大量循环不断重复上述过程。
当训练结束后,便进入验证阶段。在这一过程中,我们需要对一个同样含有期望输出信息的不同测试集数据依据模型进行预测,并评估模型在该数据集上的损失。该测试集中包含了何种样本,模型是预先无法获悉的。通过评估,我们可以了解到所训练的模型在训练集之外的推广能力。我们常常将原始数据集一分为二,将70%的样本用于训练,其余30%的样本用于评估。
我们在前面鸢尾花分类实践中已经使用了一次监督学习的方法,后面在卷积神经网络中我们也会用到监督学习的方法。所以,了解它,正确使用它,我们必须这样做。
3.2 简单说说无监督学习
在监督学习中,我们的目标是学习从输入到输出的映射关系,其中训练数据的输出正确值已经由指导者提供,也就是说有正确的标签值。然而,无监督学习中却没有正确的标签值,只有输入数据。我们的目标是发现输入数据中的规律,使网络不断地进行自我认知,自我巩固,最后进行自我归纳。
在有监督学习中,我们把对样本进行分类的过程称之为分类(Classification),而在无监督学习中,我们将物体被划分到不同集合的过程称之为聚类(Clustering)。聚类的目标是发现输入数据的簇或分组。比如对于一个拥有老客户数据的公司,客户数据包括客户的个人统计信息,以及以前与公司的交易,公司也许想知道其客户的分布,搞清楚什么类型的客户会频繁出现。这种情况下,聚类模型会将属性相似的客户分到相同的簇,一旦找出了这样的分组,公司就可能做出一些决策,比如对不同分组的客户提供特别的服务和产品等。
目前分类算法的效果还是不错的,在实际应用中使用广泛;但相对来讲,聚类算法就一直存在发展障碍。确实,无监督学习本身的特点使其难以得到如分类一样近乎完美的结果,其中一个特点就是标签的获取常常需要极大的人工工作量,有时甚至非常困难,这无疑增加了无监督学习的难度,也称为无监督学习的发展瓶颈。不过,也有人提出通过半监督学习来解决这个难题,在半监督学习中,其训练数据的一部分是有标签,另一部分没有标签,没有标签数据的数量远大于有标签数据的数量。半监督学习下隐藏着一个基本规律,那就是:数据的分布不是完全随机的,通过一些有标签数据的局部特征,以及更多没标签数据的整体分布,就可以得到可以接受甚至是非常好的分类结果。
参考文献:
1、《Python神经网络编程》
2、《深度学习》
3、《计算智能导论》
4、《面向机器智能的Tensorflow实践》
5、《机器学习导论》
6、https://www.zhihu.com/question/23194489