1.2 机器学习的数学视角
深度学习已经成功应用于计算机视觉、语音识别和自然语言处理等领域,解决了很多复杂问题,取得了巨大的成绩。作为广泛运用的开源跨平台计算机视觉库,OpenCV紧贴研究前沿,增加了深度学习模块,并将计算机视觉的最新研究进展纳入OpenCV中,这也正是本书的主要内容,即OpenCV中的深度学习模块的实现、应用和优化。
接下来,我们首先介绍作为人工智能组成部分的机器学习和非机器学习,并介绍机器学习的主要分类;然后介绍有监督学习的代表——人工神经网络,并说明其是如何发展为深度神经网络的;最后,则是破除人工神经网络的神秘性,从数学角度来简要说明人工神经网络中的机器学习。
1.2.1 机器学习和非机器学习
人工智能包括机器学习和非机器学习,由于属于机器学习的深度学习现在成为人工智能的最重要且主流的前沿分支,现在几乎举目皆是机器学习。为了更好地理解机器学习,我们可以先看看机器学习和非机器学习的区别。
我们看一下人类的行为,如图1-3所示。人的眼睛、耳朵等器官感知外界信息,将感知到的信息传递到人脑中,人脑基于之前积累的知识和技能,针对当前信息做出决策,指挥手、脚和嘴等器官做出相应的动作。
图1-3 人类的行为
人工智能主要替换人类行为中的人脑思考部分,将文字描述用数学符号代替后,可得到图1-4所示的人工智能框图。其中,X对应人类的感知信息,Y对应人脑的决策结果,而人脑思考部分则被拆分为f和W两个部分,其中思考过程用一个函数映射f来表示,而思考过程中用到的已有知识则用W来表示。用计算机术语来说,思考过程对应着一个算法f,而算法的参数则对应着W。
图1-4 人工智能框图
图1-4可以用一个非常简单的数学公式来描述,如下所示。
Y=f(X,W)
提示 请不要被数学符号吓到,这里采用数学符号是因为数学语言具有无歧义性。一个公式如果不用符号而用自然语言来表示,会显得非常冗长而且可能模糊不清,因此数学公式可以方便我们更好地交流。本书涉及的数学公式都非常简单,主要是为了将事情讲述得更加准确且简明。
至此,我们就可以清楚地看出,机器学习和非机器学习的区别,就在于上式中的f和W是如何确定的。如图1-5所示,如果f和W是由人类根据已知的实际例子来确定的,那就是非机器学习;而如果编写程序,运行程序从已知的实际例子中计算得到,那就是机器学习。实际上,目前主流的机器学习方法学习得到的结果只是W,并不包括f,只有最新前沿的自动机器学习(AutoML)才试图去学习f的最佳算法模型结构。另外,目前几乎所有的研究学者都会编写程序,这种情况下,不管是否有意识地使用机器学习,单从提高效率出发,或多或少都会编写程序来根据已有的实际例子来得到一些结果,直接或者间接地得到f和W。所以,目前机器学习和非机器学习并不存在不可逾越的天堑,界限趋于模糊,基本可以用机器学习来统一指代。
图1-5 机器学习和非机器学习
机器学习主要包括无监督学习、有监督学习、半监督学习和强化学习等,前三者的分类标准主要在于样本的不同,所谓样本,就是图1-5中的已知的实际例子。用数学符号表示,样本具有(X)或者(X,Y)的形式。在计算机视觉的图像处理中,一个样本的X指的是一幅图片中每个像素的颜色值,而样本的Y值则可能是该图片所属的分类(如一只狗或者一只猫,此时是一个标量),Y也可能是每个像素属于不同分类的概率(在对象分割任务中,此时是一个向量)。
如果所有的样本都只有输入,即具有(Xi)的形式,如图1-6所示。基于这样的样本集合进行的学习,叫作无监督学习,其典型算法有聚类分析。举例来说,几个班级一起在大操场上分区上体育课,X值是学生在操场上的站位信息,我们不需要知道学生属于哪个班级(即没有Y值),仅仅根据站位信息进行分析,我们就可以将学生分成不同的班级。此时,假如新来了一个学生,就可以根据此学生的站位,将其归类到某个已分类的班级中或者新建一个班级分类。站位信息非常简单,用二维坐标表示即可。假如,作为输入的Xi的维数很大,则可能很难直接得出结果,此时就需要进行降维,比较有名的方法诸如主成分分析(Principal Components Analysis,PCA)等。
如果所有的样本都既有输入X,也有输出Y,即具有(Xi,Yi)的形式,如图1-6b所示,则基于这样的样本集合进行的学习叫作有监督学习,人工神经网络即属于有监督学习。有监督学习基本上最终体现为最优化的求解问题,即X和Y是已知量,而W是未知量,目标是求W,使得f(Xi,W)的值和Yi的值尽可能接近,得到的f(X,W)也称为X和Y的真实关系的一个拟合。这里求解过程称为W的学习过程,也称为训练过程,用到的样本集合又称为训练集。假如,新的样本(Xm,Ym)没有在训练集中出现,用训练后的W来计算f(Xm,W)的结果,如果该结果和Ym的值非常接近乃至相等,则称训练结果的泛化能力很好,否则称泛化能力不佳。
样本的Y值又称为样本的标签或者Ground Truth。为大量的样本标定标签,是一件非常耗时耗力且容易出错的事情,因此,是否可以仅对少量样本标定标签,而对其余样本不进行标定呢?如图1-6c所示,只有部分的样本具有(Xi,Yi)的形式,而其他样本具有(Xi)形式,基于这样的样本集合进行的学习叫作半监督学习。深度学习基本可以算作有监督的机器学习,通过一些技巧也可以用于半监督的机器学习,如图卷积网络(Graph Convolutional Networks,GCN)的应用。
图1-6 不同的学习
强化学习(reinforcement learning)又称为增强学习或者再励学习,用于解决另一个范畴的任务,即连续决策问题,而结果在一段时间后才发生。例如,下棋程序在每个回合都要做出决策,直到终盘赢棋或者输棋,即反馈是延时的,很可能下了很多步棋后才能确认之前下的某步棋是好还是坏。强化学习就是根据延时反馈的奖励信号(或惩罚信号)来强化(或者弱化)之前一系列的决策策略,一些例子还包括机器人足球比赛、机械手投篮、走迷宫,以及人工昆虫学习从一端开口的透明玻璃瓶中飞出等。
[1] 参见How to do Deep Learning on Graphs with Graph Convolutional Networks,https://towardsdatascience.com/how-to-do-deep-learning-on-graphs-with-graph-convolutional-networks-62ac。
1.2.2 从人工神经网络到深度学习
人工智能发展过程中出现了几个大的流派,包括符号主义、行为主义和联结主义等。符号主义是指以数理逻辑为基础,通过符号推理的方法来解决问题,典型例子包括数学定理的机器证明和专家系统等。行为主义则认为不应该直接研究成年人的智能,而应该先实现人类婴幼儿或者动物的简单智能,然后经过系统和环境的交互,一步步地提高智能程度,代表例子有布鲁克斯的六足行走机器人。联结主义的代表是人工神经网络,多个神经元相互联结,构成一个人工神经网络(简称神经网络),当联结层数增多时,就成为深度神经网络,然后发展为深度学习。目前,联结主义成为人工智能发展的最重要的分支,也是有监督学习的代表。本节接下来简要介绍从神经网络到深度学习的发展,读者如需深入了解,推荐阅读Michael Nielsen的Neural Networks and Deep Learning。
神经网络的典型结构如图1-7所示,其中的圆圈代表一个神经元,带箭头的线段代表信息从一个神经元传递给另一个神经元的方向。数据从左侧的输入层神经元出发,经过隐含层,最后到达输出层,展示了一个输入为3维向量X=(x1,x2,x3)┬、输出为2维向量Y=(y1,y2)┬,
只包含一个隐含层的神经网络。图中每一纵行的神经元构成一个层(layer),每层中的神经元的运算都是相同的,不同层的神经元可以是不同的。前一层神经元的输出只作为下一层神经元的输入,不存在回环输入,因此这样的神经网络又叫作前馈(feed forward)神经网络。由于大部分神经网络研究都是基于前馈神经网络,因此,不做特别说明的情况下,本书提到的神经网络其实就是前馈神经网络。如果下一层的每个神经元的输入来自上一层的所有神经元的输出,这样,相邻层之间的神经元被两两相连,即它们是全部连接在一起的,在深度学习中,这种方式又称为全连接。
图1-7 神经网络的典型结构
输入层的神经元比较特殊,可以看作只是一个占位符,输入层神经元的输出就是输入数据本身。其他层的神经元具有多输入单输出的特点,如图1-8所示的单个神经元的基本结构中,3个输入xi是来自上一层神经元的输出,首先分别乘以相应的权重wi,再加上偏差bias进行累加,然后经过激活函数a,最终作为本神经元的输出y。在后续讨论中,我们将wi和bias统一归入W进行讨论,W只是一个用来描述神经元参数的符号,我们定义它用来表示wi和bias。在深度学习中,bias不再是必不可少的,只是一个可选项。
图1-8 单个神经元的基本结构
从神经网络到深度学习,激活函数发生了不少变化。神经网络鼻祖——感知器(perceptron)的激活函数是一个阶跃函数,如图1-9a所示,神经元的输出是0或者1,因为神经元的输出也是下一层神经元的输入,所以感知器神经元的输入也是0或者1。这是因为在神经网络发展最初阶段,借鉴了当时生物学中的神经元概念,生物学神经元有激活和抑制两个状态,分别对应着1和0,而且神经元只有在输入信号累积到一定量的时候才会被激活,对应图1-8的bias(可以是一个负数)。但是,用阶跃函数作为激活函数,不方便根据样本来训练得到参数W。例如,现在有100个样本,经过若干次训练后,得到的W已经使60个样本的结果是正确的,接下来,直觉上,我们希望稍微修改W的值,使得一些新样本是正确的,同时使之前训练正确的60个样本继续保持正确。而若用阶跃函数当作激活函数,则W的值可能会不得不做出较多的改变,才能使得第61个样本正确,但是W改变太多,则可能会破坏之前的60个样本的正确性,也就失去了学习训练的意义。
所以,为了可以更好地从样本中学习到参数W,用sigmoid函数和tanh函数等作为激活函数,其图形和函数形式如图1-9b所示。可以看出,曲线的数学性质非常优美,处处连续且光滑,当W有微小变化的时候,输出也会有微小变化,使得学习到W更加可能。在深度学习中,ReLU(Rectified Linear Unit)及其变体开始逐渐成为主流,其变体Leaky_ReLU如图1-9c所示,其中a的值是0.2;而当a=0时,就变成了原始的ReLU定义。
有证明表示,对于只有一个隐含层的神经网络,在一定的精度要求下,通过增加隐含层的神经元数量可以拟合任何曲线。显然,无限制增加神经元数量是不现实的,那么换一个角度,可以通过增加隐含层的数量来试图拟合曲线,当隐含层数量增多后,这样的神经网络就叫作深度神经网络。那么是什么限制了神经网络顺其自然地发展为深度神经网络呢?在20世纪八九十年代,没有找到合适的技术可以解决神经网络隐含层增多带来的诸多问题,一直到2006年深度学习三巨头Geoffrey Hinton、Yann LeCun和Yoshua Bengio开始在理论上取得突破,再到2012年基于深度学习的网络模型AlexNet在图像分类比赛中取得压倒性的成绩。当然,限制的原因很多,下面是笔者个人认为比较重要的原因的探讨。
图1-9 激活函数
考察神经网络时期重要的激活函数sigmoid和tanh,它们有一个特点,即输入值较大或者较小的时候,其导数变得很小,而在训练阶段(详见1.2.3节),需要求取多个导数值,并将每层得到的导数值相乘,这样一旦层数增加,多个很小的导数值相乘,结果便趋于零,即所谓梯度消失问题,这将会导致靠近输入层的隐含层的学习效果也趋于零。而靠近输入层的隐含层参数无法学习,就意味着它的值类似随机生成,那么有具体现实意义的输入层经过这些隐含层后会被变换成无意义的信息,继续沿着神经网络往后传递一直到输出层,也就无法得出有效的结论了。所以,深度神经网络很难训练。在深度学习中,除了改进激活函数使用ReLU函数,还提出了逐层预训练等方法来解决这个问题。
深度学习的发展还获益于另外两个重要因素,即海量样本的易获得性和基于GPU并行计算的强悍算力。样本集对机器学习的学习效果有举足轻重的作用,样本不行,再好的算法也无济于事。样本不仅要具有代表性,而且要有足够的数量,因为要学习的参数W的数量很多,可能会达到数百万个乃至上亿个参数,如果样本数量不足,则容易造成过拟合,从而影响泛化能力。用多元方程组来做通俗的类比解释,对Y=f(W,X)来说,在训练时候,X和Y是已知量,W是未知量,一个样本就代表一个关于未知数W的方程,多个样本就代表关于W的方程组。根据数学知识,如果方程组的个数(即样本个数)小于未知数W的个数,那么方程可能有无穷多个解,训练结果可能就是其中的一个解,这个W的解完美地契合了这个方程组的一切,如图1-10a的曲线所示,学到的W使得以X为输入、以Y为输出的曲线刚好经过了每个小黑点样本,但是,由于太过完美地拟合了现有样本,反而偏离了X和Y之间真正的关系(真正关系类同图1-10b所示曲线),这样,当一个新的样本出现时,结果可能就会错得离谱,因为样本数量太少造成了过拟合,难以进行实际应用。如果方程组的个数(即样本个数)远超过未知数W的个数,那么方程组可能无解,这正是我们需要的,虽然方程组无解,但我们可以找到这样的W,使得方程组中每个方程的左式的计算结果尽可能地接近右式的计算结果。这样找到的W确定的曲线如图1-10b所示,虽然其并不完美地经过每个小黑点,但由于受到诸多样本的约束,反而具有最多的鲁棒性和泛化能力。在互联网、移动互联网乃至物联网时代,每时每刻都有巨量的数据生成,另外,很多数据拥有者还会对外公开其标注后的数据集,例如,2009年李飞飞公开发布了业界第一个数据集ImageNet,由此而生的图像分类年度竞赛极大地推动了相关算法的发展,也被很多人认为是本次人工智能热潮的催化剂。困扰20世纪八九十年代神经网络研究者的样本问题在现在得到了极大缓解。
图1-10 过拟合和恰当拟合
GPU本来用于3D图形的绘制,一开始主要用于游戏或者虚拟现实等领域,其特点是能并行地完成很多小任务,而且并行程度非常高。这刚好符合神经网络机器学习的特征,例如,在海量样本的训练过程中,对于相当一部分的步骤,样本之间是独立操作的,可以并行处理。在神经网络中单独一层的操作中,每个神经元间也是独立的,可以并行处理。再加上一些诸如模型并行或者数据并行等技巧,结合GPU强悍的并行算力,极大地缩短了深度神经网络的训练和推理所需的时间,从而提高了神经网络研究者的开发效率,可以迭代出更多更有效的研究成果。关于如何用好GPU,本书后续会详细介绍。现在也有很多公司开始研究专门用于机器学习的专用硬件,如Intel公司推出的神经网络计算棒(本书后续章节也会介绍)。
此外,深度学习中还引入了非常重要的卷积神经网络(CNN),这部分将在第2章详细介绍。
[1] 参见http://neuralnetworksanddeeplearning.com/。
[2] 其实是梯度不稳定问题,因为W值较大还可能造成梯度爆炸问题。
[3] 图片来源http://neuralnetworksanddeeplearning.com/chap3.html。
1.2.3 破除神秘——神经网络是如何训练的
任何有监督学习都包括两个阶段。
1)训练(train)阶段。根据样本集,以X和Y为已知数,以W为未知数,训练得到合适的W值,也可以称为W的学习阶段。
2)推理(inference)阶段。训练完成后,则可应用于实践中,这个阶段称为推理阶段,即以W为参数,以X为输入,计算得到Y值。
相对来说,训练过程远比推理过程复杂。本节接下来将在恰当的抽象层次介绍作为机器学习代表的神经网络是如何训练的,不会深入细节陷入数学的汪洋大海中,但会从数学角度来描述其关键所在。而推理阶段的数学逻辑非常清晰,就是一个常规的公式计算过程,本书OpenCV深度学习模块实现的内容就是推理过程。
再来重述一下训练的情况,存在n个样本的样本集{(X0,Y0),(X1,Y1),…(Xi,Yi)…(Xn-1,Yn-1)},其中,Xi、Yi都可以被认为是列向量(Yi可能是标量,作为列向量的特殊形式),利用该样本集来训练f(X,W)中的W值,使得f(Xi,W)尽量接近于Yi。什么是尽量接近?数学上用损失函数(loss function)、目标函数或者代价函数(cost function)等来定量表示,具体形式可以有很多,这里我们以比较常见的函数为例来定义损失函数,即通过计算两个向量f(Xi,W)和Yi之间的欧几里得距离来定义代价函数。
其中,||v||表示向量v的模,或者是向量的二阶范数,如下所示。
由于存在根号,所以,我们在定义代价函数的时候,加了一个平方操作,以方便后续的数学运算。从上述定义可以看出,代价函数是一个非负标量,而且是多个样本的累加平均值。具体使用的样本是样本集的全部样本还是部分样本,我们将在本节最后关于随机梯度下降部分介绍。
显然,我们可以将代价函数表示为C(W,X0,Y0,X1,Y1,…),更进一步,可以表示为C(W),所以,训练过程就是要在符合样本集的条件下找到W,使得C(W)的值尽可能小,又因为C(W)是非负值,所以,我们的目的就变成了找到W,使C(W)趋于0。由于C(W)的形式非常复杂,难以直接计算,所以在数学上一般采用梯度下降法用迭代的方法来求解。
如前所述,W的数量很多,可能会达到数百万个乃至上亿个。这里,我们先假设W中的参数数量只有一个,即w,先考察如何用迭代法求取C(w)=0的近似解,再扩展到W是多维向量情况下的梯度下降法,再到机器学习中常见的随机梯度下降法。
求C(w)=0的近似解,首先取一个随机的初始值,记为w0,如图1-11所示,求出此时的导数C'(w0),如图1-11中斜线的斜率所示,此时斜率为正,表示在w0附近,C(w)是一个递增函数,为了使得C(w)减少,我们应该调整w的值,往w减少的方向调整,即和斜率相反的方向(很容易理解,如果该点的斜率为负,表示C(w)在该点附近是一个递减函数,则需要往w增加的方向调整,才能使得C(w)减少)。
图1-11 迭代法
调整幅度多大呢?这里用一个正数η来表示,这样可以得到一个新的值w1,然后用同样的方法从w1得到w2,以此类推,可以得到如下迭代公式:
w1=w0-η*C′(w0)
w2=w1-η*C′(w1)
w3=w2-η*C′(w2)
……
当迭代次数超过预先设置的次数时,即退出迭代,此时可能找不到一个合适的解;或者发现C值足够小的时候,也可退出迭代,此时找的解比较好;或者是C值持续一段时间没有大的变化,也可退出迭代,此时找到的可能是极值解,而不是最值解。
现在回到W是一个列向量的情况,求解方法也是非常类似的,首先设置初值W0,然后一步步地调整W值,只是调整的时候用的不再是导数值,而是梯度值,即C对W中每个元素的偏导数,用表示,并且用C(W0)表示在W0附近C对W的梯度。数学上已经证明,函数在梯度方向的变化率是最大的,所以公式如下所示。
并且:
列向量W=[w0,w1,w2,…]┬
梯度
其中,η称为学习速率,表示从样本学习得到W的速度,W的初始值具体应该设置为何值已经有了一些研究成果,但还没有完善的理论支持,很多时候是依赖经验和尝试。
至此,我们发现机器学习的学习过程毫不神秘,只是约束条件下的优化求解问题。但是,每个迭代过程都要为数百万个参数求取偏导数,如果一个个分别计算的话,则这将是一件非常耗时的事情。幸亏有反向传播(Back Propagation,BP)算法来简化计算工作量,只计算必须要计算的,其理论基础是导数的链式规则,即
cost函数用于计算多个样本的累加平均值,基于求导基本法则中的加法法则,我们可以分别基于每个样本计算偏导数,然后将基于多个样本的偏导数相加即可。反向传播算法就是从单个样本的cost函数出发来计算每个参数的偏导数的。我们以图1-12所示的3个隐含层的神经网络为例,大致说明反向传播算法的基本思路,为了简化描述,每个隐含层只假设存在两个参数需要学习,如隐含层1中的w0和w1。
图1-12 多个隐含层的神经网络
反向传播算法的大概过程如表1-2所示,其中r、s和t可以理解为中间变量,不必深究。
表1-2 反向传播算法的大概过程
从表1-2可以看出,应用反向传播算法首先得到的是最后一个隐含层相关参数的偏导数,然后依次往前,最后得到第一个隐含层相关参数的偏导数,这也是反向传播算法中“反向”的由来。相应地,神经网络正常的执行过程又称为前向(forward)过程。前面提到,在计算相关偏导数的过程中,每个样本之间是独立的,相互之间没有任何依赖,所以可以借助并行计算技术,用反向传播算法并行计算多个样本的偏导数,可以大幅提高效率,大幅降低训练时间。
接下来回答之前的一个遗留问题,即每次调整W参数,需要样本集的多少样本参与?首先,样本被分成训练集、验证集和测试集,训练集的样本用于训练学习W值;验证集的样本用于找出最佳模型,即训练阶段性结束的时候使用验证集来调整模型;而所有训练结束后,测试集的样本用于测评最后得到的模型,测试集并不参与训练过程。在机器学习实践中,训练集的样本被随机分成若干batch(组),对每个batch用梯度下降法来学习W值,每组的样本个数称为batch size,这种方法又称为SGD(Stochastic Gradient Descent,随机梯度下降)。实践表明,SGD法可以较好地学习得到W。所有的训练集样本都参加过了一次训练,称为一个epoch,需要很多个epoch才能完成阶段性训练工作。具体需要多少次epoch,则batch size设置为多少,学习速率η设置为多少,都没有完善的理论指导,主要依靠经验和试验,这些量又称为机器学习模型的超参数。在阶段性训练完成后,就可以根据验证集结果来调整这些超参数乃至网络模型结构,然后用训练集继续训练。训练过程的简要流程如下。
for epoch in xrange(max_epochs) { #将训练集随机重排 random.shuffle(training_set) batches=training_set.size / batch_size for batch in xrange(batches) { #得到每个batch的样本 batch_data=training_set[batch * batch_size : (batch+1) * batch_size] # 在SGD函数中用反向传播算法为每个样本计算所有参数W的偏导数,然后累加平均 # 可以借助并行计算技术同时计算所有样本的偏导数 G=SGD(batch_data) # 迭代更新参数W值 W=W –η * G } #计算训练集(training_set)和验证集样本的cost和准确率等信息 #如果满足预设条件(如cost值足够小或者cost值变化不大等)则提前退出epoch循环 }
综上所述,当前机器学习的本质过程是一些数学处理过程,这能发展出真正的人工智能吗?这是见仁见智的问题,我们觉得真正的人工智能还有很漫长的路要走。目前有一种说法是,人工智能可分成弱人工智能阶段、强人工智能阶段和超人工智能阶段。强人工智能是指达到人类的智能。一旦进入强人工智能阶段,我们可以推演,经过很短暂的学习迭代时间后,就能马上进入超人工智能阶段。在超人工智能阶段,人工智能的智能远超人类,这会不会喧宾夺主?这也是很多人工智能威胁论持有者的一个立论前提。实际上,笔者认为这完全是杞人忧天,因为目前我们还处于弱人工智能阶段,而弱人工智能和强人工智能可能是两条完全不同的技术路线。本书笔者郭叶军在硕士毕业论文最后一段引用了一段类比,假如将真正的人工智能比作登月工程的话,现在弱人工智能的发展,可能只是登上了一座更高的山,爬上了一棵更高的树,虽然好像离月亮越来越近了,但是和建造“阿波罗”号实现登月工程,完全是风马牛不相及的两码事情。当然,现在弱人工智能还是能够发展出很多巧妙的技术,可以解决很多的实际问题,也会因此极其深刻地影响人类社会,所以仍然值得我们去深入学习。
[1] 说明,为了避免太多细节,这里略过了复合函数求导的链式规则的应用。