机器学习
TODO
第7页
OCR文字识别结果:
歹
曲曲自由自治
''. Af¢
什么是机器学习
鲁
机器学习®的目的是把数据转换成信息。在学习了一系列数据之后,我们需要机器
能够回答与这些数据有关的问题其他还有哪些数据和本数据最相似?图像中有没
有汽车?哪个广告最能得到消费者的响应?由于消费者经常考虑价格因素,这个问
题会变为。在我们销售的所有产品中,如果要做广告,哪个产品会最热销?。机器
学习通过从数据里提取规则或模式来把数据转换成信息。
训练集和测试集
机器学习针对的是温度、股票价格、色度等之类的数据。这些数据经常被预处理成
特征。举个例子,我们有一个数据库,它是对10 000张人脸图像进行边缘检测,
然后收集特征,如边缘方向、边缘长度、边缘相对睑中心的偏移度。我们从每张脸
中获得一个含有500个数据的500项特征向量。然后我们通过机器学习技术根据这
些特征数据创建某种模型。如果我们仅仅想把这些数据分成不同的类(祖分、细分
或其他),一个。聚类。算法就足够了。如果想学习从人脸的边缘的模式预测他的
机器学习是一个非常大的题目,OpencV仅仅涉及统计性机器学习,而不包括贝叶斯
网络、马尔可夫随机场和图模型等内容。一些比较好的机器学习教材有Hastie.
Tibshirani 和 Friedman 的 [Hastieol ], Duda 和 Hart 的 [Duda73], Duda. Hart 和 Stork 的
[Duda00]以及Bishop的[Bishop07]如果要了解机器学习问题的并行解决方法,请参
考Ranger等人的[Ranger07]和Chu等人的[Chu07]。
机器学习499
第8页
OCR文字识别结果:
年龄,我们需要一个。分类"算法。为了达到目的,机器学习算法分析我们收集的
数据,分配权重、阈值和其他参数来提高性能。这个调整参数来达到目的的过程被
称为。学习"(learning)。【459】
知道机器学习方法的性能很重要,而且这是一个精细的问题。一般情况下,我们把
原始数据分成一个大的训练集(我们的例子中,大约9000张脸)和一个小的测试集
(剩下的1000张脸)。我们利用训练集来讷1练我们分类器,学习年龄预测模型。当
训练结束,我们用测试集来测试年龄预测分类器。
测试集不能被用来训练分类器,我们不让分类器知道测试集的年龄标签。我们让分
类器对测试集的数据预测,记录预测结果,考察预测年龄和真实年龄的符合程度。
如果分类器效果不好,就尝试增加新的特征或者考虑不同类型的分类器。本章将描
述多种分类器和多种训练分类器的方法。
如果分类器效果好,我们就有了一个有潜在价值的模型,我们可以在真实世界里应
用它。这个系统可能被用来根据用户的年龄来设置视频游戏的行为。人们准备玩游
戏的时候,他们的脸的图像将被处理成500个特征。这个数据被分类器分类,得到
的预测年龄将被用来设置游戏行为。在系统部署之后,分类器看到以前没有见过的
脸就会根据学习结果做出判断。
最后,在开发训练分类系统的时候,经常使用验证集。有的时候,在最后测试整个
系统是一个太大的工作。我们需要在提交分类器进行最终测试之前调整参数。为
此,我们可以把原始的10 000个数据分为3部分,8000个训练集,1000个验证
集,1000个测试集。现在,训练完之后,我们尝试性地提前用验证集来测试分类
器的效果。只有对分类器的性能感到满意,才能用测试集来做最后的判断。
监督数据和无监督数据
有的时候,数据没有标签。我们仅仅想根据边缘信息看看人脸倾向于哪个组。有的
时候,数据有标签,比如年龄。这类有标签的机器学习是有监督的(即可利用数据
特征向量附带的。信号"或。标签。)。如果数据向量没有标签,则表明这种机器
学习是无监督的。
有监督的学习可以来判断种类,比如把名字和脸对应起来,或赋予一个数字标签
(如年龄)。当数据以名字(种类)作为标签,贝1」表明我们在做分类(classification)$如
果输出的是数值,则表明我们在进行回归,回归是通过类另I.I的或数值的输入数据来
拟合一个数值输出。
500第13章
第9页
OCR文字识别结果:
有监督的学习也有一些模糊领域它包括数据向量和标签的一一匹配,,也包括强化
学习(deferred learning)。在强化学习中,数据标签(也叫奖励或惩罚)能在单独的数
据向量被观察之后存在很久。当一个老鼠走迷官寻找食物的时候,它经历一系列的
转弯,最后得到了食物,这是对它的奖励。奖励必须反馈回去,对老鼠寻找食物的
过程产生影响。强化学习也是一样系统获得一个延迟的信号(奖励或者惩罚),根
据这个信号推断下一步的策略(如在迷官中的每一步怎么走)。监督学习也适用于不
完整标签,此时意味着标签缺损(这种情况下称作半监督学习),或者噪声标签(标
签是错的)。大部分机器学习的方法只能处理以上讨论的一种或两种情况。例如
某算法可以处理分类,但不可以处·理回归,某个可以处理半监督学习,但不能进行
强化学习某算法可以处理数字数据,但是不可以处理类别数据等等。
[460-461]
有的时候,数据是没有标签的,但我们对数据是否能自然归类感兴趣。这种无监督
的学习算法叫聚类算法(clustering algorithm)。目的是把这些无标签的数据按照它们
的距离(预先确定的或者感觉上的)远近归类。我们仅仅想看脸是怎么分布的它们
是胖是痩,是长是方?如果我们观察癌症方面的数据,能否将拥有不同化学信号的
癌症方面的数据聚集在一起?无监督的聚类数据经常形成一个特征向量供更高层的
有监督的分类器使用。我们可以先把脸聚类成脸型(宽,窄,长,方),然后把睑型
作为输人,可能再加上其他的一些数据(声音的平均频率),以此预测人的性别。
这两个典型的机器学习任务(分类和聚类)与计算机视觉中两个基本任务(识别和分
割)有共通之处。我们需要计算机判断物体是什么的时候,我们使用识别1我们
需.要计算机说出物体的位置的时候,我们使用分割。因为计算机视觉大量依靠机
器学习,OpencV在ML部分包含大量的机器学习算法,库位于.../opencv/ml目录
之中。
注意OpencV机器学习的代码是通用的。尽管在视觉应用中经常使用此部分的代码,代
码的应用范围并不局限于视觉。例如可以某些函数来研究染色体序列。当然,我们
的关注重点还是物体识别,其中特征向量是从图像中提取的。
生成模型和判别模型
很多算法被设计用于解决学习和聚类问题。OpencV囊括了一些当前用得最多的统
计机器学习方法。概率机器学习方法,像贝叶斯网络和图模型,在Opencv中支
持得比较少,部分原因是这些算法还很新,还在成长中,变化会很大。,Opencv倾
向于支持判另I」算法,即通过给定数据来判断类别(P(LID,而不倾向于产生式算
机器学习501
第10页
OCR文字识别结果:
法,产生式算法是通过给定类别来生成数据的分布(P(DIL))。虽然这二者的区别不
是非常清晰,但是判别模型在根据给定的数据做出预测上有优势,而生成模型则是
在为你提供更强大的数据表达或者有条件地生成新数据时有优势(比如,想象一个
大象的样子,你是在根据条件。大象。来生成数据),【461~462】
产生式模型很容易理解,是因为它是对数据的产生进行建模。判别式学习算法经常
根据一些看似武断的阈值做出判断。例如假设一小片区域在某个场景中被定义为
路仅仅因为它的红色分量小于125。但是是否意味着红色分量等于126就不是路?
这类问题很难解释的清楚。产生式模型中经常给定类别,得到数据的条件分布,所
以你可以很容易看出这个分布跟真正的分布是否相似。
OpencV机器学习算法
OpencV中的机器学习算法如表13-1所示。除了Mahalanobis和K均值算法在
CXC0RE库中,人睑检测算法在CV库中,其他算法都在ML库中。
表13-10pencV支持的机器学习算法,算法描述后为参考文献
Mahalanobis通过除以协方差来对数据空间进行变换,然后计算距离。如果协
一方差矩阵是单位矩阵,那么该度量等价于欧氏距离
[Mahalanobis36]
K均值这是一种非监督的聚类方法,使用K个均值来表示数据的分布,
其中K的大小由用户定义。该方法跟期望最大化方法(expectation
maximization)的区另1」是K均值的中心不是高斯分布,而且因为各
个中心竞争去"俘获。最近的点,所以聚类结果更像肥皂泡。聚
类区域经常被用作稀疏直方图的bin,用来描述数据。该方法由
Steinhaus [Steinhaus56]发明,并由LIoyd [L1oyd5引推广使用
正态/朴素贝叶这是一个通用的分类器,它假设特征是高斯分布而且统计上互相
斯分类器独立。这个假定过于苛刻,在很多条件下不能满足,正因为此,
它也被称作。朴素贝叶斯。分类器。然而,在许多情况下,这个
分类器的效果去I出奇得好。该方法最早出自[Maron61:Minsky61]
决策树这是一个判另1」分类器。该树在当前节点通过寻找数据特征和一个
阈值,来最优划分数据到不同的类别。处理流程是不停地划分数
据并向下到树n·I左侧自节点或右侧子令点。虽然它一般不具有最
优性能,但是甘往是泪试算法的第一选择,因为它速度比较快,
502第13章
第11页
OCR文字识别结果:
——一日叫
纭表
而且具有不错的功能[Breiman84]
多个判别子分类器的组合。最终的分类决策是由各个子分类器的
加权组合来决定。在训练时,逐个训练子分类器,且每个子分类
器是一个弱分类器(只是优于随机选择的性能)。这些弱分类器由
单变量决策树构成,被称作。树桩。。在训练时,。树桩。不仅从
数据中学习分类决策,而且还根据识别精度学习。投票。的权重。
当逐个训练分类器的时候,数据样本的权重被重新分配,使之能够
目下。
给予分错的数据更多的注意力。训练过程不停地执行,直到总错误
(加权组合所有决策树组成的分类器产生的错误)低于某个已经设置
好的阈值。为了达到好的效果,这个方法通常需要很大数据量的讷1
练数据[Freund97]
这是由许多决策树组成的"森林。,每个决策树向下递归以获取最
随机森林
人脸检测/Haar
分类器
期望最大化
大深度。在学习过程中,每棵树的每个节点只从特征数据的一个
随机子集中选择。这保证了每棵树是统计上不相关的分类器。在
识别过程中,将每棵树的结果进行投票来确定最终结果,每棵树
的权重相同。这个分类方法经常很有效,而且对每棵树的输出进
行平均,可以处理回归问题[Ho95.l【Breiman01]
这个物体检测方法巧妙地使用了boostin吕算法。OpencV提供了
正面人睑的检测器,它的检测效果非常好。你也可使用OpencV
提供的软件训练算法,使之能够检测其他物体。该算法非常擅长
检测特定视角的刚性物体[Violao4]
一种用于聚类的非监督生成算法,它可以拟合N个多维高斯到数
(EM)据上,此处N的大小由用户决定。这样仅仅使用几个参数(均值
和方差)就可以非常有效的表达一个比较负责的分布。该方法经常
用于分科。它与K均值的比较可参考文献[Dempster77]
K近邻K近邻可能是最简单的分类器。训练数据跟类别标签存放在一
起,离测试数据最近的(欧氏距离最近)K个样本进行投票,确定
测试数据的分类结果。这可能是你想到的最简单的方法,该方法
通常比较有效,但是速度比较慢且对内存的需求比较大[Fix51]
神经网络/多层该分类算法在输入节点和输出节点之间具有隐藏节点,这可以更好的
传感器表示输入信号。训练该分类器很慢,但是识别时很快。对于厂些识别
问题,如字符识别,它具有非常不错的性能[Werbos74:Rumelhart88】
机器学习503
决策树
Boosting
第12页
OCR文字识别结果:
续表
支持向量机(SVM)它可以进行分类,也可以进行递归。该算法需要定一个高维
空间中任两点的距离函数。(将数据投影到高维空间会使数据
更容易地线性可分。)该算法可以学习一个分类超平面,用来
在高维空间里实现最优分类。当数据有限的时候,该算法可以
获得非常好的性能$而boosting和随机森林只能在拥有大量训
练数据时才有好的效果[Vapnik95]
[462-463]
视觉中使用机器学习
基本上,表13-1的所有算法都使用特征构成的数据向量作为输入,特征的个数可
以以千计。假设你的任务是识别特定的物体(例如,人)。你遇到的第一个问题是怎
么采集数据并给数据定标签(场景里有人还是没有人)。你会发现人以不同的方式出
现人的图像可能只包含一点点像素,或者你只能看到一只耳朵出现在整个屏幕
上。更坏的情况下,人可能处于遮挡状态下在车中的人,女人的脸,树后面的一
条腿。你需要定义有人在场景中的确切含义。
下面将遇到采集数据的问题。是用自己的摄像机采集,还是使用公用数据库
ht中//www.刀icker.com尝试寻找。人物。标签,或者两种方法都使用?采集运动信
息吗?采集其他信息吗?如场景中的门是开的,时间,季节,温度。用于海滩上检
测人的算法说不定不适用于滑雪场。需要捕获数据的各种变化不同视角,不同光
线,不同天气,阴影,等等。
获取了很多数据之后,该怎么给数据定标签?首先需要决定标签的意义。需要知道
场景中的人在什么位置吗?运动信息重要吗?如果有大量的图像,又应该怎样给所
有的图像定标签?有很多诀窍,比如在约束情况下中做背景剪除,收集运动前景。
你可以雇人帮你分类,如可以通过Amazon的mechanical turk。网站
(射中//www.m[urk.co。加·rk/welcome)雇人帮你定标签。如果安排的工作很简单,
可以把价格降到每个标签一分钱。
数据定好标签之后,需要决定需要提取哪些特征。再一次,必须知道自己的目的。
如果人们总是正面出现,就没有必要使用旋转不变的特征,也没有必要预先旋转物
体。总之,必须寻找表达物体固有属性的特征,比如梯度直方图、色彩、或目前流
第13页
OCR文字识别结果:
形的SIFT特征。。如果有背景信息,可能想首先把背景去除,提取出物体,然后Iv
进行图像处理(归一化图像、尺度改变、旋转、直方图均衡),计算很多特征。物体
的特征向量将与物体的标签对应。
■
一旦数据被转换特征向量,便需要把数据分成训练集、验证集和测试集。在这里,
我推荐交叉使用训练集、验证集和测试集。也就是说所有的数据分成K个子
集,然后每次随机选取其中一部分来训练,剩余的用来测试Q.测试结果求平均,
得到最终的性能结果。使用交叉验证,我们能够更清楚地看到处理异常数据时分类
器的性能。【463~465】
数据已经准备好,下面是选择分类器。一般分类器的选择需要考虑计算速度、数据
形式和内存大小。一些应用中,在线用户优先选择建模,所以分类器需要能够快速
完成训练。在这种情况下,最邻近算法、正态贝叶斯和决策树是不错的选择。如果
需要考虑内存因素,决策树和神经网络是理想的选择。如果不需要很快训练,而需
要很快判断,那么神经网络可以满足要求,正态贝叶斯和SVM也不错。如果不需
要讷练很快,但是需要精确度很高,可选择boosting和随机森林。如果选取的特征
比较好,仅仅需要一个简单易懂的分类器,就选择决策树和最邻近算法。要获得最
好的性能,还是离不开boosting和随机森林算法。
注意不存在最好。的分类器(请参考htx)刀en.wikipedia.orgTwiki/jvo斤ee一
lunch_theorem)。综合考虑所有的数据分布类型,所有的分类器都是一样的。因此我
们无法清楚说明表13-1中的算法哪个。最好。。但是如果给定某个特定的数据分布,
或者特定的一些数据分布,通常存在一个最好的分类器。所以在实际应用中,最好
多尝试一下各种不同的分类器。考虑一下你的目的只是为了得到正确的匹配分
数,还是去理解数据?你追求的是快速计算,少的内存需求,还是结果的可信度?
在这些方面,不同的分类器有不同的能力。
变量的重要性
表13-1中有两个算法可用来评估变量的重要性。拿到一个特征向量,我们怎么才
知道哪个特征对分类器的准确性有较大贡献!二进制决策树可以解决这个问题通
过在每个节点选择最能够分裂出数据的变量。最上面的变量是最重要的变量,第一
层的变量第二重要,以此类推。随机森林采用Leo Breiman发明的一项技术来测量
变量的重要性。这个技术可以用于任可分类器,但是在OpencV中,仅仅决策树
® 请 参 考 lowe 的 sift 特 征 demo(http :// m ww. cs. ubc. ca/-lowe/keypoints/).
圆一般进行5~10次训练(或验证)牙一测试,
机器学习'505
第14页
OCR文字识别结果:
和随机森林实现了这个技术。
变量重要性的一个用途是减少分类器需要考虑的特征的个数。一开始有很多特征,
你训练分类器,发现其中一个特征与另外一些特征有关联。你可以去掉其中一些不
重要的特征。排除不重要的特征可以提高速度(因为不需要处理不重要的特征),可
以使训练和测试更快。还有,如果没有足够的数据,去除不重要的变量可以提高分
类器的准确率。这也可以使处理更快,效果更好。【465-_466】
Breiman的变量重要性算法的步骤如下。
1用训练集训练一个分类器。
2.使用验证集或测试集来确定分类器的准确率。
3.
对于每一个数据样本和一个选择的特征,随机选择其他特征数据中的该特征的
值来替代(替补抽样法)。这样可以保证特征的分布与原始数据集的一样,但是
特征的结构或者意义被抹去了(因为它的值是从剩余的数据中随机选择的)。
4.
用改变后的讷1练集训练分类器,然后用改变后的测试集或者验证集来评价分类
器。如果完全打乱特征使正确率降低很多,那么这个特征很重要。如果完全打
乱特征并没有使正确率下降多少,那么这个特征并没有多重要,可以考虑删除。
5.把原始数据重新换一个特征按照3和4操作,直到所有的特征全部分析完毕,
得到的结果就是每个特征的重要性。
这个过程被内置到随机森林和决策树中,所以可以使用它们来确定使用哪些变量做
特征,然后使用被删减的特征向量来讷1练分类器。
诊断机器学习中的问题
将机器学习用好,不仅仅是一门技术,更是一门艺术。算法经常"有些时候"能用,
但又不能完全与要求一致。这就是艺术的所在需要指出哪些地方出问题了并解决
问题。尽管在这里我不能详细说明,但我将列举出一些常见问题o。首先介绍一些重要
的规律大量数据比少量数据好好的特征比好的算法更重要。如果选取的特征
好,最大化它们的独立性,最小化它l'」P.不同环境之下的变化,那么大部分算法都
可以获得比较好的效果。除此之外,还b'两个经常遇到的:司题。
欠拟合模型假设太严格,所以模型不能拟合到实际数据上。
移
®斯坦福大学的Andrew Ng教授在他的网上讲稿。Advice for Applying Machine Learning。
* h 了 详 细 介 绍 thrtp : //www. stanford. edu/class/cs229/ materials/ml-advice. pur.
506第13章
第15页
OCR文字识别结果:
-.wwwww... ·
过拟合算法不仅学习,了数据,而且把噪声也当作信号学习了,这样算法的推
广能力很差。
歹
图13-1为统计类机器学习的基本结构。我们的目的是根据输入和输出对判别函数f
建模。这个函数可以是一个回归问题,也可以是一个分类预测问题。现实生活中的
问题中,噪声和没有考虑过的影响会导致观测结果与理论结果不同。例如,在人脸
识别中,我们学习一个模型,它通过人眼、嘴、鼻子的标准距离来定义人睑。但是
附近闪烁的光线变化会在测量中引人噪声,或者一个工艺不好的摄像机镜头会在测
量中引人系统畸变。这些都是模型中没有考虑的。这些干扰会影响精确度。
学习后的模型
INPUT OUTPUT
(x) (y)
图13-1,统计机器学习的结构。训练一个分类器来拟合一个数据集,真正的模
型f会被噪声和一些未知的因素。淹没。
图13-2中的上两幅图分别展示了数据的欠拟合和过拟合,下两幅图展示了对应的
最终检测错误率与训练集大小的关系。在图13-2左部,我们尝试对图13-1的底部
的数据进行训练和预测。如果使用很严格的模型(柤虚线),我们就不能很好地拟合
真实的抛物线函数(细虚线)。因此,尽管有大量的数据,训练集和测试集的拟合程
度都不好。在这个例子中,因为欠拟合,训练集和测试集都无法预测得很好。在
图13-2右部,我们的训练严格符合训练集,但这使函数包含了噪声。同样,测试
的结果不好。尽管训练错误率很低,但是洄1」试错误率也很高,说明存在过拟合。
有些时候,需要注意是否正在解决自己想要解决的问题。如果训练和测试结果都很
好,但是算法在实际应用中效果不好,则表明数据集可能是从非实际条件中获得
的,也许是因为这些条件可使收集式模拟这些数据更容易。如果算法不能重现训练
集或者测试集,表明问题出在算法,或者从数据中提取的特征是无效的,或者有用
的信息不在收集的数据中。表13-2列出了我们提到的一些可能的解决方法。当
机器学习507
第16页
OCR文字识别结果:
然,这并不是所有的解决方案。为了使机器学习方法发挥作用,必须仔细思考并设
计应该采集哪些数据,计算哪些特征。机器学习方法出现问题时,还必须做一些系
娩开发级的诊断,
Tk T
Test Test
Train § Desired
Training set size Training set size
图13-2机器学习中比较差的拟合模型以及在训练和测试时对性能的影响,而
真正的模型函数是上图中的细虚线欠拟合模型(左上图)在训练数据和测时数
据上都有很大的误差,而过拟合模型(右上图啲在训练数据上的误差比较小,
但是在测试数据上的误差很大
表13-2机器学习中遇到的问题以及可能的解决方法,采用更好的特征对任何
问题都有帮助
欠拟合使用更多的特征有利于拟合
选用一个学习能力更好的拟合算法
过拟合增加训练数据的数量可使得拟合曲线更光滑
减少特征的数量可降低过拟合程度
使用一个学习能力差一点的算法
I川练和测试比较好,采集更加真实的数据
但实际应用效果不好
Under fit Bias
Model
. r
. P
X
第17页
OCR文字识别结果:
:狪燎焦苺,,°哮;,佷,,.i卜,可i能的鏞虫蓑糠蓑絮燕鎏巍鱲燕燕纛
模型无法学习数据重新选择特征,使特征更能表达数据的不变特性
采集更新、更相关的数据
选用一个学习能力更好的拟合算法
[466-468]
交叉验证、自抽样法、ROC曲线和混淆矩阵
最后,介绍一些用来W估机器学习结果的工具。在监督学习中,最基本的问题之一
是知道算法的性能分类器准确吗?你可能会说"简单,我只需要在测试集或者验
证集上运行一下,就可以获得结果。。但是在实际情况中,我们必须考虑噪声,采
样误差和采样错误。测试集或验证集可能并不能精确地反映数据的实际分布。为了
更准确评估分类器性能,我们可以采用交叉验证(cross-validation)的技术或者与之
比较相近的自抽样法(bootstrapping)®。
交叉验证首先把数据分为k个不同的子集。然后用k-1个子集进行训练,用没有用
来训练的子集进行测试。这样做k次(每个子集都有一次机会作为测试集),然后把
结果平均。
自抽样法跟交叉验证法类似,但是验证集是从讷1练集中随机选取的。选择的点仅用
于测试,不在训1练中使用。这样做N次,每次随机选择一些验证集,最后把得到
的结果平均。这意味着一些数据样本会出现在不同的验证集中,自抽样法的效果一
般胜于交叉验证。
使用其中任意一种技术都可以使实际效果的评估更准确,更高的精确度可以用来指
导学习系统的参数调整。再I川练、再评估、再调整,如此继续。
在评估和调整分类器的方法中,'还有两个比较有用·的方法,它们是画ROC
(receiver operating characteristic)曲线图和填充混淆矩阵,请参考图13-3.ROC曲
线评估了分类器参数的变化对分类器性能的影响。假设这个参数是一个阈值。更具
体一点,假设我们需要在图中识别黄色的花,探测器使用一个阈值来定义黄色。黄
色的阈值设置得非常高,贝I」意味着我们的分类器的错误识别率为O,同时将正确识
另!障也为0(图13-3的左下部分)。另一方面,如果黄色的阈值设置得非常低,贝1」意
味着所有的信号都被识印」成黄色。分类器的错误识别率为100%,同时正确识别率
国关于此类方法的更多信息,请参考What Are Cross-Va】idation and Bootstrapping'网址
h http://www.. r uq5 nr#l-a【lsai-r.q/neurat-nets/part3/section-12. html.
机器学习509
第18页
OCR文字识别结果:
也为100%(囱13-3的右上部分)。最好的R0C曲线是从原点沿Y轴上升到100%,
再沿X轴到达右上角的折线。,如果做不到这样,那么,曲线越靠近左上角越好。
我们可以计算ROC曲线下方的面积和整个ROC图的面积之比,越接近1,就表明
分类器越好。【469】
亲R0C曲线
尹t'"·1.
25% 50%
错识识别率
imimd
图13-3ROC曲线和与之相关的混淆矩阵ROC曲线显示分类器参数在所有
取值范围上变化时,正确识别率和错误识别率的关系混淆矩阵显示错误识别
率和错误拒绝率的关系
图13-3还展示了一个混淆矩阵。这是一个包含错误识别率、正确识别率、错误拒
绝率、正确拒绝率的图。这是评估分类器性能的快速方法理想情况下,我们将看
到左上一右下的对角线上的两个值是100%,而其他的两个值是0%。如果我们的分
类器可以处理多类问题(多层感知神经网络和随机森林),那么混淆矩阵将推广到多
类,你需要跟踪每一个带标签的数据样本所被分配的类别。
分错类的代价下面我们讨论分错类的代价。假设我们要设计分类器来检测毒蘑
菇,我们希望错误拒绝率(可食用的蘑菇被认为有毒)很大,错误识别率(有毒的蘑
菇却被认为可食用)很小。ROC曲线可以处理这种情况我们设置ROC的参数使
操作点(operation point)下降一一靠近图13-3的左下角。还有一种方法是生成ROC
曲线的时候对错误识别赋更大的权值。可以把每个错误识男1」的权值设为错误拒绝权
f直的10倍G.一些OpencV算法(如决策树和SVM),能够通过指定类另I.I的先验概
国有两类错误的相对代价将会很有用。例如在超市的收银台处将产品分错类会更容易
纠正,
510第13章
第19页
OCR文字识别结果:
=F
特征的方差不一致另一"个训练分类器的问题是特征向量中,特二j I
致。例如,如果一个特征是由小写的ASCII码字母表示的,那么它最多有25个
值。与之不同的是,显微镜载物片上的生物细胞数目是以亿计的。类似k均值算法
的一类算法会认为第一个特征相比于第二个特征可以忽略。解决这个问题的方法是
预处理每个特征变量,使它们的方差一致。如果特征不相关,这个步骤很重要如
果特征相关,你可以用它们的协方差或平均方差来归一化。决策树等一类算法不受
方差不一致的影响°,所以木需要预先处理。如果算法以距离度量为准则,就需要
预先将方差归一化。我们可以利用Mahalanobis距离来归一化特征的方差,这在本
章的后面会提到©。
我们现在正式开始讨论OpencV支持一些机器学习算法、这些算法的大部分都在
路径.../ope月cv/mi下。我们从ML库最基础的一些类开始。
ML库的通用类
本章的目的是使读者开始使用机器学习算法。即使已经一较了解各种方法,也需要
参考.../opencv/docs/r咖pencvr吖ml.htm手册,或者访问OpencV在线维基文档
(hup/Topencv.w边owgorage.conz乃。因为这个库还在发展,通过手册或者在线维基
文档可以了解更多更新的内容。
ML库中o所有的程序都是用C++写的,它们都继承于cvstat Mo凸el类。
cvstat Mo己e1类包含所有算法都需要的一些函数,如表13-3所示。注意,
cvstatMo己e1中有两套方法来对磁盘进行模型的读/写操作保存操作的save 0和
writeo,读操作的lo己d0和reado。对于机器学习的模型,应该使用简单的
sa·e0和loa己O,它们实际上把复杂的wri·eo和rea己0函数进行了封装,能
够从硬盘读/写xML和YAML文件。cvstat Model中还有机器学习中两个最重要的
方法,即pre己ict o和train O,它们的形式随算法变化,放到以后讨论。
[471-472]
®因为每个变量都有与之对应的独立阈值,所以决策树不受特征方差不一致的影响。换
而言之,只有有一个很清晰的区分界限,变量的范围大小是无所谓的,
圆熟悉机器学习或者信号处理的读者可以将视为。白化滤波。。
囤请注意Haar分类器、Mahalanobis和k均值算法是在ML庠创建之前实现的,所以目
前在cv和cxcore库中。
机器学习511
'"=li***ibiiuibii6i= r
率或者指定个别的训练样本的权重,来自动平衡。击中率和误报率"
第20页
OCR文字识别结果:
-q
表13-3ML库中的算法的基类
save (
const char*
const char*
load(
const char*
const char*
clear O
bool traint
-data points-,
[flags]
-responses-,
[flags etc]
float predict(
cqnst CvMat* sample
[, «predi ction_params»]
COHSt
构造函数,析构函数
CvstatMode10
CvstatMode1 (
const CvMat* train_data
CvstatMode1-CvstatMode1 0
读写支持(实际应用时请使用save和
load函₩
write (
CvFilestorage* storage,
const char* name
将训1练出的模型以XML或YAML格式保存
到硬盘。此方法用于存储
首先需要调用clear O函数,然后通过此函
数装载XML或YAML格式的模型。此方法
用于调用
释放所有内存。为重用做准备
此训练函数从输人的数据集上学习一个模
型。算法不同时,训练函数的输人参数也有
所不同
训练之后,使用此函数来预测一个新数据样
本的标签或者值
默认构造函数,以及将创建并训练模型功能
集成的构造函数
机器学习模型的析构函数
通用的写cvFilestorage结构的函数,位于
cxcore库中(请参考第3章),在此处被
saveo函数调用
到2第13章
第21页
OCR文字识别结果:
read (
CvFilestorage* storage,
CvF11eNode* node
通用的读CvFilestorage结构的函数,位于
cxcore库中,在此处被loado函数调用
训练
训练函数的原型如下。
bool CvstatModel traint
const CvMat* train-data,
lint tflag, ]
const CvMat* responses,
[const CvMat * var_idx, ]
[const CvMat+ sample_idx, ]
icons t CvMat · var_type, ]
[const CvMat* missing_mask, ]
cmisc_t raining_alg_params >
) [472-473]
机器学习的traino方法根据具体的算法呈现不同的形式。所有的算法都以一个指
向cvMat矩阵的指针作为训练数据。矩阵必须是32Fc1(32位浮点单通道)类型。
cvMat结构可以支持多通道矩阵,但是机器学习只采用单通道矩阵。一般情况下,
这个矩阵以行排列数据样本,每个数据样本被表示为一个行向量。矩阵的每一列,
表示某个变量在不同的数据样本中的不同取值,这样就组成了一个二维的数据矩
阵。请时刻牢记输入数据矩阵的构成方式(行,列)-(数据样本,特征变量)。但是,
有的算法可以直接处理转置后的输入矩阵。针对这些算法,可以使用参数tflag
向算法表明讷1练数据是以列排列的。这样便不需要转置一个大的数据矩阵,如果算
法可以处理行排列和列排列两种情况,可以使用下面的标记。
mag-CV_Row_SAMPLE特征向量以行排列(默认)
tnag-CV_COL_SAMPLE特征向量以列排列
读者可能会问如果我的数据不是浮点型,而是字符或者整型,怎么办?答案是
只要在写人cvMat的时候,把它们转换成32位浮点数。如果特征或标签是字符,
机器学习513
第22页
OCR文字识别结果:
就把ASCII字符转换成浮点数。整型也一样。只要转换是唯一的,就没有问
题——但是请记住,有些算法对特征的方差不一致比较敏感。最好先归一化特征的
方差。基于树的算法(决策树,随机森林和boosting)支持类别变量和数值变量,
OpencV }LL库中的其他算法只支持数值输入。使数值输入的算法能够处理类别数
据的常用方法是把类另1」数据表示成0-1编码。例如,如果输入的颜色变量有7个不
同的值,则把它们替换成7个二进制变量,变量有且仅有一位是1.
返回的参数responses司'以是类别标签(如蘑菇分类中的。有毒。和。无毒。标
签)$也可以是连续的数值(如温度计测得的人体温度)。返回值常常是一个一维向
量,向量的每个值对应一个输人数据样本但是神经网络例外,神经网络的每个数
据样本都返回一个向量。返回值有两种类型对于分类问题,类型是整型
(328C1),对于回归问题,类型是32位浮点型(32FC1)。显然,一些算法只能处理
分类问题,一些算法只能处理回归问题,一些算法两类问题都能处理。在最后一种
情况下,输出变量的类型要么以一个单独的参数返回,要么以向量v·r_type的最
后一个元素返回。可以如下设置
CV_VAR-CA TEGORICAL
CV_vA.一ORDERED CV_vAR_NUMERICAL)输出的值是数值类型标
签,就是说,不同的值可以作为数来比较,这是一个回归问题【472钾474】
输入数据的类型也可以用v·r_type指定。但是,回归类型的算法只能处理有序输
人。有的时候,需要把类别数据一一映射到有序数据,但是这样做有的时候会出问
题,因为。伪。有序数据会因物理基础的缺乏而可能大幅振荡。
ML库中的很多模型可以选择训练特定数据子集和/或特定特征子集。为了方便用
户,traino方法包含了向量参数v·r_idx和sample_idx。默认值为NuLL,即使
用所有的数据。可以用它们来指定使用哪些数据样本和使用哪些特征。这两个向量
要么是单通道整型(CV_328C1)向量,要么是单通道8位数据(CV_8UC1)一一非0
代表有效。读人一堆数据却只想f吏用一部分数据来训练,一部分数据来分类,不想
把数据分成2个矩阵的时候,参数s己mple_idx尤其有用。
另外,一些算法可以处理数据丢失的情况。例如,当用户使用的是人工采集的数据
的时候,很有可能会丢失测量数据。这种情况下,参数missing_nl己sk——一个与
train_data同样大小的8位矩阵····将被用来标记丢失的数据(非0的标记元
素)。一些算祛不能处理数据丢失,所以丢失的数据样本必须由用户采用插值的方
法合成,或者在训练中抛弃不完整的数据样本。其他算法(像决策树和朴素贝叶斯?
通过不同的方法来处理数据丢失。决策树使用。代理分裂。,·朴素贝叶斯推断数
据值。
514第13章
: a
输出的值是离散类型标签
第23页
OCR文字识别结果:
通常在训练之前,前面提到的模型都可以用cle·r o来清除。但有些算法可以随时
用新数据更新模型,而不是每次训练都从头处理所有数据【474】
预测
指定训练函数traino中使用哪些特征的参数v·r_idx将被程序记住。调用预测
函数pre己icco的时候,并随后被用来从输入参数中抽取出需要的部分数据。
pre己ict 0函数的基本形式如下
float CvstatMode predict
const CvMat* sample
, «prediction-params>]
const I
这个函数可对新输人的数据向量进行预测。如果是分类问题,它会返回一个类别标
签。如果是回归问题,它会返回一个数值标签。注意,输人的样本格式必须和训练
样本的格式完全一样。可选参数prediction_params由算法指定,可以用来在基
于树的预测方法中指定缺失的特征的值等。函数后缀co·st告诉我们预测不影
响模型的内部状态。因此,这个方法可以并行调用。它可以用在网络服务器从多客
户端获取图像之中,也可以用在机器人加速场景扫描之中。
训练迭代次数
尽管迭代控制结构体cvTermcri亡eria已经在其他章谈论过,但由于它被广泛应用
于很多机器学习算法中,所以这里再重复一下。
typedef struct CvTermcriteria
int type /* CV_TERMCRIT_ITER arLd/ar CV_TERMCRIT_EPS */
int max_iter /* maximum number af lterations */
double epsilon /* stop when error ls below this value */
整型参数m·x_1t·r设置了算法最大的迭代次数。参教epsilon设置了误差停止标
I隹,当误差在此参数之下,则算法停止。最后,参数匕yp。设置了应该使用哪个标
准,也可以同时使用(CV_TER仑IICRIT_ITER 1 CV_TERMCRIT_EPS)。term_crit.
亡ype可以使用的值定义如下
机器学习515
第24页
OCR文字识别结果:
现在我们转向OpencV中具体的算法。我们首先讨论最常用的Mahalanobis距离;,巨
阵,然后深入介绍一个无监督算法(K均值);这两个算法在'cxcore库中。然后我们
讨论机器学习库中的正态贝叶斯分类器和决策树算法(决策树,,boosting,随机森
林,Haar级联)。对于其他的函数,我们仅给出简短描述和用法示例。【475】
Mahalanobis 距离
Mahalanobis距离是数据所在的空间的协方差的度量,或者是认为把数据所在空间
进行。扭曲拉伸。然后进行度量。如果你知道Z-score,就可以把Mahalanobis距
离看作多维空间中Z-score的类似物。图】3-4(a)展示了三个数据集的初始分布,看
起来竖直方向上的那两个集合比较接近。在我们根据数据的协方差归一化空间之
后,如图13-4(b),实际上水平方向上的两个集合比较接近。这种情况经常发生
如果我们以米为单位来测量人的身高,以天为单位测量人的年龄,我们看到身高的
范围很小,而年龄的范围很大。通过方差归一化,变量之间的关系便会更加符合实
际情况。像K邻近之类的算法很难处理方差相差很大的数据,但是决策树等其他
算法则不受这种情况影响。
(a) «b)
e
嘎霞画画曾知叫国画团团国踟@喾
图13-4Mahalanobis距离让我们理解数据的协方差为数据空间的。拉伸。
(a)原始数据的垂直距离要比水平距离小(b)通过对空间进行方差归一化,数
据之间的水平距离小于垂直距离
我们通过图13-4®大致了解了什么是Mahalanobis距离。测量距离的时候必须考虑
(D
注意,图13-4的协方差矩阵是对角阵,这样的协方差矩阵可以可以变量X和Y是独立
的,之所以采用这样的协方差矩阵是为了使问题更简单。实际数据往往不会这么简
单,会有很多种分布方式。
516第13章
· 勇
第25页
OCR文字识别结果:
r
其中Et·]是期望操作符。OpencV使用下面函数来计算协方差矩阵
void cvcalccovar Matrix
const CvArr** vects
int count,
CvArr · cov-mat,
CvArr · avg,
int flags
这个函数有一点点迷惑性。注意,参数vects是一个指向CvArr的指针的指针。
这意味着我们可以最多拥有vects 【o】ーvecEs 【count-1]个CvArr。到底有多少个
CvAFr,实际上由参数flag决定。一般有以下两种情况。
1.
如果flag中cV_C0V_ROWS和CV_C0V_COLS都没有设置,vect吕则是一个指
针数组,数组里的元素指向一维或者二维矩阵。具体说来,每一个vects [i]
可以指向一个一维或二维向量。这种情况下,如果Cv_covAR_cscALL被设
置,累积协方差的计算时,将除以count指定的数据点的个数。
2.
如果cv_cov_Rows或者cv_cov_coLs其中之一被设置,那么在通常只有一个
输入向量的时候,就只用vects【0j,count被忽略。所有的输入都包含在向
量 vects[O] 中.
a.cv_cov_Rows,所有的输入向量在vects【0]中以行排列。
b.CV_C0V_COLS,所有的输入向量在vects 【0】中以歹lj排列。不可以同时设置
行标志和列标志(详情参考标志相关描述)【476~4771
vects可以存储8UC1,16UC1,32FC1和64FC1几种数据类型。任何情况下,
vec上s包含了一个K维数据样本的列表。cov_mat是得到的KxK协方差矩阵,它
有cv_32FC1和cv_64FC1两种数据类型。Avg是否使用向量取决于flag的设置。
如果使用了avg,则它与vects的数据结构一致,包含vec亡s中的所有向量的K
维均值。参数flags可以有很多种组合,总体来说,可以设置flag为下面的任何
Cv_COVAR_NORMAL按照前画所示的最一般的协方差矩阵计算公式来计
机器学习517
第26页
OCR文字识别结果:
算。如果cv_covAR_sCALE没有设置,则把结果用count平均,否则用
vects [0]的数据点个数来平均结果。
CV_COVAR_SCALE归一化计算得出的协方差矩阵。
CV_COVAR_USE_AVG使用avg矩阵而不通过每个特征来计算均值。如果
已经得到均值(如通过调用cvAvg o),设置这个参数可以节省计算时间。否
则工程序会计算均值Q.【477~478】
通常把数据结合成一个大矩阵,假设以行排列,那么,标志将被设置为nags一
CV_COVAR_NORMAL I cv_COVAR_SCALE cv_COVAR_ROWS.
现在我们有了协方差矩阵,为了计算Mahalanobis距离,我们需要除以空间的方
差,所以需要计算协方差矩阵的逆矩阵。可以通过下面这个函数来计算
double cvlnvert(
const CvArr* src,
CvArr* dst,
I nt method · CV-LU
1学
在cvlnv·rt中,源矩阵src是前面计算的协方差矩阵,己e·是计算得到的逆矩
阵。如果没有指定参数method,一则默认为cv_Lu,但是最好使用cv_svD_sYM作
为参数®。
得到了协方差矩阵的逆矩阵,我们就可以求两个向量的Mahalanobis距离。
Euc】idean距离是计算两个向量的对应数据的差的平方和,Mahalanobis距离与
Euclidean距离类似,只不过它还需要除以空间的协方差矩阵
Dmn»a, ambu[x, y) = hn E-'(x-y)
Mahalanobis距离是一个数值。如果协方差矩阵是单位矩阵,则Mahalanobis距离
退化为Euclidean距离。最后介绍OpencV中计算Mahalanobis距离的函数,它输
国如果用户有一个统计上更合理的均值,或者协方差矩阵由块来计算、那么应该输入事
先计算好的均值向量。
圆在这种情况下,也可以使用CV_SVD,但是它比CV_SVD_SYM更慢且精度低。如果特
征的维数远小于数据样本的数目,即使比CV_LU慢,也应该使用CV_SVD_SYM.在
这种情况下,总的计算时间中大部分由cvca1 ccovarMatrix()消耗,所以稍微多花
一点时间来获得更精确的协方差的逆矩阵是明智的(如果数据点集中了一个低维子空间
中,可以更精确)。因此在这儿,CV_SVD_SYM通常是该任务的最佳选择,
518第13章
第27页
OCR文字识别结果:
人两个向量(veel,vec2)和一个逆协方差矩阵(mat),返回一个双精度
double cvMahalanobis(
const CvArr* veel,
const CvArr* vec2,
CvArr* mat
Mahalanobis距离是多维空间中两点相似性的度量,它本身不是聚类或者分类算
法。下面,我们开始讲解最常用的聚类算法K均值。【478】
K均值
K均值聚类算法在cxcore中,因为它在ML库诞生之前就存在了。K均值尝试找
到数据的自然类别。用户设置类别个数,K均值迅速地找到。好的。类别中心。
"好的"意味着聚类中心位于数据的自然类别中心。K均值是最常用的聚类技术之
一,与高斯混合中的期望最大化算法(在ML库中实现为cvEM)很相似,也与第9
章中讨论过的均值漂移算法(在CV库中实现为cvMeanshift o)相似。K均值是一
个迭代算法,在OpencV中采用的是LIoyd算法®,也叫Voronoi迭代。算法运行
如下。
1.输人数据集合和类别数K(由用户指定)。
2.随机分配类别中心点的位置。
3.将每个点放人离它最近的类别中心点所在的集合。
4.移动类别中心点到它所在集合的中心。
5.转到第3步,直到收敛。
图13-5展示了K均值是怎么工作的。在这个例子中,只用两次迭代就达到了收
敛。在现实数据中,算法经常很快地收敛,但是有的时候需要的迭代的次数比较多。
® S. P. LIoyd,"Least Squares Quantization in PCM,"IEEE Transactions on Infonnation
Th eory 28 (1982), 129-]37.
机器学习519
第28页
OCR文字识别结果:
Lir-
图1 3-5K均值算法的迭代中有两步(a)随机放聚类中心然后将数据样本聚
到离它最近的中心;(b)数据中心移动到它所在类别的中心(c)数据点根据最
近邻规则重新聚到类别中心(d)聚类中心再次移动到它所在的类别中心
问题和解决方案
K均值是一个极其高效的聚类算法。但是它也有以下3个问题。
1.它不保证能找到定位聚类中心的最佳方案,但是它能保证能收敛到某个解决方
案(例如迭代不再无限继续下去)。
2.
K均值无法指出应该使用多少个类别。在同一个数据集中,例如对于图13-5
中的例子,选择2个类别和选择4个类别,得到的结果是不一样的,至是不
合理的。
3.K均值假设空间的协方差矩阵不会影响结果,或者已经归一化(参考
Mahalanobis距离的讨论)。
这三个问题都有。解决办法。。这些解决方法中最好的两种都是基于。解释数据的方
差。。在K均值中,每个聚类中心拥有它的数据点,我h'】计算这些点的方差,最好
的聚类在不引起太大的复杂度的情况下使方差达到最小。所以,歹1」出的问题可以改
进如下。
1多运行几次K均值,每次初始的聚类中心点都不一样(很容易做到,因为
520第13章
第29页
OCR文字识别结果:
OpencV随机选择中心点),最后选择方差最小的那个结果
2.
首先将类别数设为1,然后提高类别数(到达某个上限),每次聚类
前面提到的方法1。一般情况下,总方差会很快下降,直到到达一
意味着再加一个新的聚类中心不会显著地减少总方差。在拐点处停
时的类别数。
r
3.
将数据乘以逆协方差矩阵。例如如果输入向量是按行排列,每行一个数据
点,则通过计算新的数据向量D',D*.DE-。来归一化数据空间。
[479-480]
K均值代码
K均值函数的调用很简单
void cvK. Means2(
const CvArr * samp 1 es,
int cluster_count,
CvArr* labels,
CvTermc riteria t ermc ri t
数组sample是一个多维的数据样本矩阵,每行一个数据样本。这里有一点点微
妙,数据样本可以是浮点型cv_32FC1向量,也可以是Cv_32FC2,cv_32Fc3和
Cv_32FC(K、)o的多维向量。参数clust·r_count指定类别数,返回向量tables包
含每个点最后的类别索引。前面已经讨论了terncri匕。
阅读下面的K均值代码(例13-L),它的数据生成部分可以用于其他机器学习程序。
例13-1K均值用法示例
#include cxcore. h"
#include'highgui. h"
void maini int arge, char** argv
#define MAX CLUSTERS 5
国这完全等价于NxK矩阵,其中有N行数据样本,每个样本有K到,数据类型是
32FCI.这两种表示的内存使用是完全相同的。
机器学习521
第30页
OCR文字识别结果:
T
Cvscalar color_tab[MAX_CLUSTERS]
Iplimage* img-cvcreatelmage( cvsize( 500, 500 ), 8, 3 )
CvRNG rng cvRNG ( Oxffffffff )
color-tab[O] = CV-RGB(2 55, 0, 0 )
color-tab11] = CV-RGB(0, 2 55, 0 )
color-tab[2j = CV-RGB(100, 100, 255 )
color-tab[3] = CV-RGB«255, 0, 255 )
color-tab [ 4 ] = CV_RGB ( 255, 255, 0 )
cvNatnedwindow('clusters", 1 )
fori )
int k, cluster_count cvRandlnt ( &rng ) %MAX_CLUSTERS + 1
int I, sample_count cvRandlnt ( &rng ) %1000 1
CvMat+ points = cvcreate Matl sample_count, 1, CV_32FC2 )
CvMat* clusters = cvcreate Matl sample_count, 1, CV_32S01 )
/* generate random sample from multivariate Gaussian
distribution */
£or( k-0 k < cluster-count k++
cvpoint center
CvMat point-chunki
center. x = cvRandlnt ( &rng ) %img->width
center. y-cvRandlnt ( &rng ) %img->height
cvGetRows ( points, &point-chunk,
k* sample-count / cluster-count,
k == cluster_count-1 ? sample-count
( k+1 ) *sample-count / cluster-count )
cvRandArr (
& rng, &point_chunk. CV_RAND_NORMAL,
cvscalar ( center. x, center. y, O, O ),
cvscalar ( img-»width / 6, img-»height16, 0, 0 ) )
/ * shu££1e samples * /
fori I 0 I < sample-count/2 ++
Cvpoint2D32f* pt1. ( Cvpoint2D32f * ) points->data. F1 +
第31页
OCR文字识别结果:
cvRandlnt ( &rng ) %sample_count
Cvpoint2D32f* pt2 (Cvpoint2D32f* ) points-»data. F1 +
cvRandlnt ( & rng ) % sampl e_count
Cvpoint 2D32f temp ·
CV_SWAP("pt1, *pt2, temp )
■圈
cvKMeans2(
points, cluster_count, clusters,
cvTermcri teria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,
10, 1. 0 ) )
cvzero( img )
fori I 0 L < sample_count i++'
Cvpoint2D32f pt ( ( Cvpoint2D32f* ) points-»data. 61 ) [i]
int cluster_idx = clusters-»data. I[i]
cvcircle( img, cvpoint From32f (pt ), 2,
color_tab[cluster_id×], CV_FILLED )
cvRelease Matl &points )
cvRelease Matl &clusters )
cvshowfmage('clusters", img )
int key-cvwait Key ( 0 ) !
i£ ( key-= 27 ) / /'ESC'
breaki
)
在这段代码中,包含highg。I.h来使用窗口输出,包含cxcore.h是因为它包含了
Kmeans2 0。在main函数中,我们设置了返回的类别显示的颜色设置类别个数
上界MAx_cLusTERS(这儿是5),类别的个数是随机产生的,存储在
clust·r_count中设置数据样本的个数的上界(1000),数据样本的个数也是随机
产生的,被存储在sample_count中。在最外层循环for(}中,我f门分配一个浮点
数双通道矩阵point来存储sample_count个数据样本,我们还分配了一个整型
矩阵clusters来存储数据样本的聚类标签,从O~clust·r_count-1.
[482-483]
然后,我们进人数据生成for{}循环,这个循环也可以用于其他算法中使用。我们
给每个类别填写sample_count/cluster_count个数据样本,这些2维数据样本
机器学习523
第32页
OCR文字识别结果:
服从正态分布,正态分布的中心是随机选择的。
下一个for{}循环仅仅打乱了数据样本的顺序。然后我们使用cvKMeans2 o,直
到聚类中心的最大移动小于1.
最后的for{}循环画出结果,在图中显示。然后释放数据数组,等待用户的输入进
人下一次计算或者通过Esc键退出。
朴素贝叶斯分类
前面提到的函数都在cxcore库中。现在我们开始讨论OpencV的机器学习(ML)
库。首先看到OpencV最简单监督学习分类器cvNorma1Bayesc1assifier,也叫
正态贝叶斯分类器或朴素贝叶斯分类器。它简单是因为它假设所有的特征之前相互
独立,而这在现实中很少见(如,找到一只眼睛常常意味着另一只眼睛在附近)。
Zhang讨论了这个分类器有时能获得惊人性能的原因[Zhanw4]。朴素贝叶斯分类
器不能处理回归问题,但是它能有效地处理多类问题,而不仅仅是两类问题。这个
分类器是当前快速发展的贝叶斯网络(也叫概率图模型)的最简单情况。贝叶斯网络
是因果模型。在图·13-6中,脸的存在产生了图像中的脸的特征。在使用中,脸是
一个隐含变量',通过对输入图像的处理得到的睑部特征,组成了脸的观测证据。我
们把这个叫做产生式模型,因为睑生成了脸部特征。它的逆过程,我们假设脸是存
在的,然后在脸存在的前提下,随机采样生成了哪些特征®。这种自上而下的由原
因模型生成数据的方法可以处理纯判断型的模型不能处理的问题。比如,可以生成
人脸用于计算机图形显示,机器人可以逐条想象怎样来生成场景、物体并让物体交
互。而与图13-6形成对照的是,判断型模型会将图中的所有箭头翻转。
[481-483]
贝叶斯网络很深奥,一开始学习的时候很难理解。但是朴素贝叶斯算法衍生于贝叶
斯法则的一个简单应用。在这个例子中,给定特征下睑的条件概率p有
p(face I LE, RE, N, M, H) =
pILE, RE, N, M, H 1 face) p (face)
p(L. E, RE, N, M, H)
也可以写成
®因为朴素贝叶斯算法假设特征是独立的,所以使用它来生成人脸并不明智。但是一个
更通用的贝叶斯网络可以特征相关时生成。
524第13章
第33页
OCR文字识别结果:
条件概率=可能性·先验概率/证据
图13-6一个(朴素)贝叶斯网络,因为一个物体(人脸)的出现,产生底层特征
实际情况下,我们计算证据,然后决定产生证据的原因。因为用于计算的证据相
同,我们可以不考虑这个条件。如果有很多模型,则需要找到有最大分子的那个。
分子是模型和证据的联合概率p(face,LE,RE,N,M,H)。我们可以使用条件概率来表
示这个联合概率
p(face, LE, RE, N, M, H)
p (face) p (LElface) pIRE/face, LE) p(N I face, LE, RE)
xp(M I face, · LE, RE, N) p(H I face, LE, RE, N, M)
应用特征独立的假设,我们获得简化的等式
p(object, allfeatures) = p (object) p(feature, 1 object) [484]
使用这个分类器的时候,要学习所有我们需要的物体的模型。运行的时候,我们计
算特征,找到使上面等式最大的物体。然后,我们测试获得胜利的物体的概率是否
大于某个给定值,如果是,我们就宣布物体被检测出来了,如果否,我们宣布没有
任何一个物体被检测出来。
注意如果只有一个感兴趣的物体,通常也是这样,那么你会问。我正在计算的概率与
什么有关。在这种情况下,一直会有一个暗含的第二个物体背景。背景是除去
我仃1感兴趣的要学习和识别的物体之外的所有物体。
学习所有物体的模型比较简单。我们照很多张照片s计算这些照片的特征,计算每
个物体的讷r练集中某个特征出现的比例。在实际计算中,我们不允许o概率,因为
这样会除去某个存在物体因此,O概率被赋予一个很小的值。一般情况下,如果
机器学习525
第34页
OCR文字识别结果:
没有很多的数据,简单的模型(朴素贝叶斯)会比很多复杂模型获得很好的性能,因
为复杂模型使用了太多的假设,以致产生欠拟合。
朴素贝叶斯代码
朴素贝叶斯分类器的训练方法如下
boo1 CvNorma1Bayesc1assi£ier traint
const CvMat · train-data,
const CvMat* responses,
const CvMat* var-id× O
const CvMat* sample-id× O,
bool update-false
它与前面讲的一般训练方法一样,但是它只支持cv_RoW_sAMpLE类型的数据。输
人变量_train_data应该是有序的(Cv_VAR_ORDERED,数字)CV_32Fc1向量。输出
标签_responses只能是cv_vAR_cATEGoRIcAL(整数数值,即使是浮点向量)的向
量列。参数_v·r_idx和_sample_I己x可选,它们允许你标记你想使用哪些特征和
数据样本。很多情况下会使用所有数据样本,因而赋值为NuLL,但是
一sample_I己x还可以用来区分训练集和测试集。这两个向量都是单通道整型数据
(cv_328C1),O意味着跳过。最后,当upda·e变量为true的时候,使用新数据
样本更新模型,update为false的时候,从零开始训练模型。
朴素贝叶斯分类器的预测方法计算了它的输人向量的最大可能性的类别。一个或者
多个数据按行排列在输入矩阵sample中。预测在results向量中返回对应的类
别。如果只有一个输入向量,则只返回一个浮点值,results将被设为NuLL.预
洄」函数的格式如下
float CvNorma1Bayesclassifier :predict(
const CvMat*
CvMa t ·
const
下面将讨论基于树的分类器。
samples,
results O
[485-486]
第35页
OCR文字识别结果:
二叉决策树
P
我们将具体讨论二叉决策树,它们是最常用的,且实现了机器学习库中大量的功
能,因此将被作为指导性的例子来讲解。二叉决策树由Leo Breiman和他的同事提
出。。他们称之为。分类和回归树(CART)。。OpencV实现的就是。分类回归树。。
算法的要点是给树的每个节点定义一个衡量标准。比如当我们拟合一个函数的时
候,我们使用真实值和预测值的差的平方和,这就是衡量标准。算法的目的是使差
的平方和最小。对于分类问题,我们定义一个度量,使得当一个节点的大多数值都
属于同一类时,这个度量最小。三个最常用度量是熵(entropy)、吉尼系数(Gini
index)和错分类(misclassification),这些度量都会在本节中介绍。一旦我们定义了
度量,二叉树搜寻整个特征向量,搜寻哪个特征和哪个阈值可以正确分类数据或正
确拟合数据(在本书中称为使数据°纯净。)。根据惯例,我们说特征值大于这个阈
值的数据为。真。,被分配到左分支,其他的点放到右分支。从二叉树的每个节点递
归使用这个过程直到数据都。纯净。了,或者节点里的数据样本数达到最小值。
随后会给出节点的不纯度i(N)。我们首先需要分两种情况来分析回归和分类。
回归不纯度
回归或函数拟合中,节点不纯度仅仅是节点值夕和数据值X的差的平方。我们需要
最小化这个等式
iw) = E U,-x, Y
分类不纯度
分类中,决策树经常使用熵不纯度、吉尼不纯度和错分类不纯度这三个度量中的某
一一个来衡量。这些方法中,我们使用P(国,)来表示节点N中类别马所占的比例。不
同的方法中,不纯度有一些细微的差别。吉尼不纯度最经常用,所有的方法都努力
使节点中的不纯度最小。图13-7显示了我们想要最小化的不纯度。
L.. Breiman, J. Friedman, R. Olshen, and C. Stone. Classifi cation and Regression Trees
( ]984), Wadsworth.
机器学习527
第36页
OCR文字识别结果:
熵不纯度I I(N)=工P(斜)】og P(马)
吉尼不纯度,I(N)=工P(叫·)P(马)
j./
错分类不纯度I I(N)=1-max P(马)
图13-7决策树不纯度
决策树可能是最常用的分类方法。因为他们执行简单,容易解释结果,适应不同的
数据类型(包括类别数据、数值数据、未归一化的和混合的数据),能够处理数据丢
失,通过分裂的顺序能够给数据特征赋不同的重要性。决策树构成了其他算法的基
础,比如boosting和随机数,我们会简单地介绍这些算法。
决策树的使用
下面描述怎样使用决策树。有很多方法访问节点、改变分裂等。如果想要知道这些
细节,请参考用户手册.../opencw'docs/I·咖pencvr以-ml.htm。特别注意看类
CVDTree0、训练类cvDTree Train Data{)、节点CvDTree Node(}和分裂
CvDTreesplit { } · [487-488]
我们用实例来介绍决策树。在路径.../opencvTsamplesTc下,mushroo陇印p文件对
agaricus!epio ta.dat口里的数据进行了决策树的分类。这个数据文件包含标签p和
e(分别表示有毒和无毒),还有22个属性,每个属性只有一个字母。在
musliroom. cpp 中, 函 数 mushroom_read_database 0 从文 W agaricuslepiota. Data 中
读取数据。这个函数过于特殊,并不具有通用性,它只对三个数组进行了赋值
(1)浮点数组己ata【J[],数组的行数是数据样本的数目,列数是特征的数目(这里
528第13章
第37页
OCR文字识别结果:
是22),所有的特征都从类别字母转换为浮点数字1(2)单字节类型矩阵
missing[旧,用。true。或者。1。表示数据文件里的缺失特征,缺失特征在数据文
件里用问号表示,本数组的其他值为O$(3)浮点向量responses[】,里面包含有
毒的字符°p"和无毒的字符。e。的对应浮点值。大多数情况下,可以写一个更通
用的数据输人程序。现在我们讨论mushroom.cpp最主要的工作,也就是在main函
数中被直接或者间接调用的部分。
训练树
为了训练树,我们首先填充树的参数结构体cvDTreeparams{)
struct CvDTreeparams
int max-categoriesi
/ / Until pre-clustering
int max-depth //Maximum levels in a tree
int min-sample-count I
/ / Don't split a node if less
int cv_folds
/ / Prune tree with K fold cross-
validation
baal usE_surrogates //A1ternate splits Eor missing data
boo1 usE_lse_rule //Harsher pruning
bool truncate_pruned_tr e
/ / Don't remember"pruned branches
float regression_accuracy
//one of the stop splitting"criteria
const float* priors //weight o£ each prediction category
CvDTreeparams O max_cat egories ( 1 0 ), max_depth ( INT_MAX ),
min_sample_count ( 10 ), cv_£olds ( 10 ), use_surrogates ( true )
use_lse_rule ( true ), truncate_pruned_tree ( true ),
regression_accuracy{O. Olf ), priors ( NULL ) (
CvDTreeparams (
int max-depth,
int mi n-sample-count,
float regression-accuracy,
bool use-surrogates,
int max-categories,
机器学习529
第38页
OCR文字识别结果:
int cv-folds,
bool use-lse-rule,
bool truncate_pruned-tree,
const float* priors
[488-489]
在结构体中,m·x_categories默认为10。这个值限制了特征类别取值的数目,决
策树会事先将这些类别聚类,使其可以测试不超过2°_..'""i"-2种可能性®。这个
参数对有序的或者数字的特征没有影响,因为对于有序的或者数字的特征,算法仅
仅需要找到分裂的阈值。类别个数超过m·x_categories的变量将自动聚类到
max_categories个类别个数。这样,决策树每次只需要测试不多于
m·x_categories个层。当这个参数设置得比较小的时候,将减少计算复杂度,但
也损失了精确度。
其他参数的名称不言自明。最后一个参数prio·s非常重要。它设置了错分类的代
价。这是说,如果第1个类别的代价是1,第2个类别的代价是10,则预测第2个
类别出现一次错误则等于预测第1个类别出现10次错误。在这个程序中,我们判
断有毒和无毒的蘑菇,所以我们惩罚把毒蘑菇认为无毒10倍于把无毒的蘑菇认为
有毒。
训练决策树的方法如下,有两个函数第一个为了直接使用决策树I第二个为了全
体(用法如boosting)或森林(在随机数使用)。
//work directly with decision trees
boo1 CvDTree traint
const CvMat"train_data,
更多关于标签和数值分裂的介绍对于数值变量,分裂的形式是。如果x〈a,则到左
子节点,否则到方子节点'如果对于标签变量,分裂的形式是。如果x€{v】,v2,
v3,''',vk,刚到左子节点,否则到方子节点,此处v,是变量的可能取值。。因此,如
果一个特征变量有N种可能取值,那么为了找到此变量最优分裂,需要尝试2N_2种子
集(不包括空集和满集)。一个算法往往需要将N种取值聚为K砭max_categories个类
(通过K均值装类)。所以算法尝试各种不同的聚类组合,然后取最好的分裂,这一般
可以获得很好的结果。对于两个经常使用的任务,两类问题的分类和回归、不需要聚
类就可以方便地找到最优分割(如取值的最优组合)。所以只有在这种情况下才会使用
聚类n〉2类的分类问题,且类别变量N〉max_categories。所以将max_categories设
置为大于20的值时,请务必三思而后行,因为每次分裂需要的操作超过一百万次
操作。
第39页
OCR文字识别结果:
int tflag,
const CvMat* responses,
const CvMat* var_id× O,
const CvMat* sample_id× O,
const CvMat* var_type O,
const CvMat* missing_mask O,
CvDTreeparams params CvDTreeparamso
//Method that ensembles of decision trees use to call
//individualtraining for each tree in the ensemble
boo1 CvDTree traint
CvDTree Train Data* train_data,
const CvMa t *_subsampl e_idx
[489-490]
在train方法中,我们输入2维浮点型矩阵_train_data【】【】。在矩阵中,如果
一tflag设置为cv_Row_sAMpLE,每一行是一个包含特征向量的数据点。如果
一tflag设置为cv_coL_sAMpLE,则行和列对调。参数_responses【】是一个浮点
型向量,包含数据点对应的类别。其他的参数可选。向量_v·r_idx指出需要考虑
的特征,_sample_idx指出需要包含的数据点$这2个向量要么是基于0的整数
列,要么是8位的标记,1预示有用,O预示跳过(具体请参考本章前面的train o
介绍)。字节型向量_var_type是一个各个特征类型的基于0的标记(特征类型是
CV_vAR_CATEGORICAL还是cv_vAR_ORDERED)。,它的长度是特征的个数加1,最
后一个数指定学习结果的类型。字节型的矩阵_miss_mask【】【】指定哪些是丢失的
值(如果相对于的位置标记为1)。例13-2是决策树创建和训练的例子。
例13-2创建并初1练一个决策树
float priors[] ( 1. 0. 10. 01 // Edible vs poisonous weights
CvMat* var_type
var_type cvcreateMat( data-»cols 1. 1, CV_8u )
cvSeT ( var_type, cvscalarA11 (CV_VAR_CATEGORICAL) ) // all
these vars are categorical
CvDTree* dtree
dtree new CvDTree
dtree-»train(
CV VAR ORDERED多艮CV VAR NUMERICAL完全相同。
机器学习531
第40页
OCR文字识别结果:
data,
CV_ROW-SAMPLE,
resporlses,
O,
O,
var-type,
mi ssing,
CvDTreeparams (
max dep t h
min sample count
regression accuracy N/A here
compute surrogate split,
since we have missing data
max number of categories
( use suboptimal algori'thm for
/ / larger numbers )
10, / / cross-val idat ions
true, / / use 1SE rule => smaller tree
true, / / throw'away the pruned tree branches
priors / / the array of priors, the bigger
/ / p-weight, the more attention
/ / to the poisonous mushrooms
) [490-491]
在这段代码中,声明并创建了决策树dtree。调用了dtree的训练算法。在这个
例子中,向量responses[】设置为ASCII码的。p。和。e。。在训练结束之后,dtree
就可以用来预测新数据了。决策树也可以通过s'ave方法保存到磁盘,通过load
方法从磁盘读取。在下面的例子中,在保存和加载之间,我们使用了clear方法
来将决策树重置并清空。
dtree->save("tree. xm1","My Tree")
dtree->clear O
dtree-> 1-oad("tree. xm1","My Tree")
上面代码保存和加载了一个叫。tree.xm1。的树文件。选项。MyTree。在
tree.xm!中标记了一个树。和机器学习中其他的统计模式一样,使用save的时候
并不能存储多个对象,多对象存储的时候要使用cvopenF上le5七日rageo和
532第13章
第41页
OCR文字识别结果:
write 0 o。但是,loa己函数不一样,如果一个文件中存储了多了对象,它可'以根
据名称提取出对应的对象。
决策树的预测函数如下
CvDTreeNode* CvDTree predict(
const CvMat"sample,
const CvMat · miss ing-data-mask · O,
boo1 rdw-mode = false
const
此处_sample是一个需要预测的浮点型的特征向量。_missing一己ata_mask是一个
同样长度和同样大小®的字节向量,它的非0值预示着对应的特征丢失。最后,
raw_mo己e用false指出输入类别数据没有归一化,true指出输入类别数据经过
了归一化,主要作用是提高预测速度。在(0,1)区间内归一化数据可以加速计算,因
为算法可以知道数据的波动。这样的归一化对精确度没有影响。预测方法返回决策
树的一个节点,可以通过(cvDTree Node·)-》val·e得到预测值
double r = dtree-»predict( &sample, &mask )-»value [491-492]
最后,我们调用v·r_importan·e O得到初始特征的重要性。这个函数返回一个
N*1的双精度(cv_64Fc1)向量,向量包含每个特征的重要性,1代表最重要,O代
表完全不重要。不重要的特征在2次训1练中可以被排除。(图13-12是变量重要性
的图示)这个函数的调用如下
const CvMat"var_impo'rtance dtree-»get_var_importanceo
如...TopencvTsamp!es/e/mushroom.cpp文件所示,每个独立特征的重要性可以直接由
一下式访问
double val var_importance-»data. Db [i]
大多数用户只训练并使用决策树,但是高级用户或者研究人员可能想测试同肓点、
改变树节点或者改变分裂标准。如本节开始提到的,可以参考Opencv文
档...opencv/docs乃·咖pencvr以mt.htm#ch_dtree,还可以通过Opencv维基
®如前所述、函数save0和lo己d0是复杂函数writeo和rea己0的一个易于使用
的包装。
圆此处如果_sample是一个l×N向量,则此样本也必须是l×N如是一sam中le是
Nxl,那么此样本必须是Nxl.
机器学习533
第42页
OCR文字识别结果:
(ht中庁opencvlibrary.sourceforge.neM)获得。深入分析的重点是类结构cvDtree{J,
训练CvDTree TrainDat{J,节点结构CvDTreeNode{}和分裂结构CvDTreesplit{J。
决策树结果
用前面代码,我们可以从agaricus-tepiol口.data文件中学习蘑菇有毒还是无毒。如
果我们训练决策树而不经过修剪,它可以很好地学习数据,结构如图13-18所示。
尽管完全的决策树可以很好地学习训练集,但是会产生图13-2所描述的现象(过拟
合)。我们在图13-8中不仅学习了数据,也学习了噪声和错误。因此,它在实际数
据中的效果可能并不好。这就是为什么OpencV决策树和CART树需要包含一个
附加的步骤来通过修剪树来达到复杂度和性能的平衡。其他的有些树通过在建立树
的同时考虑复杂度,以把修剪融合到训练环节里面。但是,在开发ML库的时候,
发现先建立树再修剪(OpencV是这样实现的)的效果胜于边建立树边修建好。
图1 3-8区分有毒(p)和无毒(e)蘑菇的完整决策树该决策树非常复杂,使训
练集上的错误率为0%,但是有可能不能处理测试数据或者真实数据的变化(矩
形中的深色部分表示在那一步有毒蘑菇的比例)
第43页
OCR文字识别结果:
图13-9显示了一个在训练集上并不是非常好,但是在实际数据中会有更好的性能
的树,因为它在过拟合和欠拟合中达到了平衡。但是这种分类器有一个缺点尽管
它在数据的总误差上获得比较好的性能,它仍有1.23%的概率将有毒的蘑菇判断为
没有毒。可能我们更希望得到一个性能略差一点、尽管会标记错很多没有毒的蘑
菇,但是不会标记错有毒的蘑菇的分类器。这样的分类器可以通过故意歧视分类器
或者故意歧视数据来得到。这种方法会给分类器的分类错误增加代价。在我们的例
子中,我们认为错分有毒蘑菇的代价很高,而错分无毒蘑菇的代价较小。OpencV
允许调整训练参数cvDTreeparams(}中的prior来达到目的。我们也可以重复使
用。坏。数据来加强pri·r的代价。重复使用。坏。数据暗中增加。坏。数据的
权重。
图13-9区分有毒(p)和无毒(e)蘑菇的修剪后的决策树虽然被修剪导致该决
策树在训练集上有一个较低的错误率,但是它肯定能够较好地处理真实数据
囱13-10展示了一个给毒蘑菇分错加了10倍权值的树。这个树以牺牲判断无毒蘑
菇的准确率来使判断毒蘑菇完全正确。。安全比犯错好。。无偏见和有偏见的树的混
淆矩阵如图13-11所示。
最后,我们讨论怎么利用决策树产生的变量重要性,OpencV的树分类器中包含变
量重要性©。变量重要性技术度量了每个变量对分类器性能的贡献。哪些特征丢弃
后使性能下降越多,哪些特征就越重要。决策树可以直接通过数据的分裂来表示重
要性第一层一般会比第二层重要。分裂是重要性的有效指示,但它是以贪婪方式
变量重要性技术可用于任何分类器,但是目前OpencV只在树分类器中实现了变量重
要性。
机器学习535
第44页
OCR文字识别结果:
来实现的,即找到当前使数据最纯净的分裂。但有些时候,先做不重要的分裂会得
到更好的效果,但是目前决策树不会这样做®。毒蘑菇的变量重要性如图13-12所
示,它包括无偏见的树和有偏见的树。可以看到变量重要性也与树的偏见有关。
图1 3-10一个食用蘑菇分类器,如果将有毒蘑菇错分为可食用蘑菇,则有1 0
倍的惩罚。请注意最下面右边的矩形,虽然大部分是可食用蘑菇,但是达不到
有毒蘑菇的10倍,所以仍被认为是不可食用的
t-
图1 3-11用于区分可食用蘑菇的修剪决策树的混淆矩阵(confusionmatrices)。
无惩罚的分类获得更好的总识别率(上图),但是将一些有毒蘑菇误分为可食用
的有惩罚的分类虽然识别率不是那么高(下图),但是不会将有毒蘑菇分为可
食用的
国OpencV在所有的分裂中都计算变量重要性(根据Breiman的方法实现),包含代理分裂
也是这样,这降低了CART贪婪分裂算法在变量重要性上的负面影响。
536第13章
-h
第45页
OCR文字识别结果:
萨
图1 3-12由无惩罚的树(左图)分类食用蘑菇的变量重要性,以及由有惩罚的
树(右图)的分类重要性【494~495】
boosting
决策树很有用,但是它们并不是最好的分类器。在本节和下节,我们讲述两种技
术boosting和随机森林。它们在内部使用了决策树,所以继承了树的很多有用的
性质(能够处理混合数据类型、没有归一化的数据、特征缺失)。这些技术能够获得
相当好的性能,因此它们通常是ML库中最好的监督算法@。
在监督学习领域,有一个叫统计提升(meta-learning)的学习算法(由Michael Keans
首次提出)。Keans想知道可不可能从很多弱分类器中学习到一个强分类器©。第一
个boosting算法,叫AdaBoosting,由Freund和Schapire提出.OpencV包含4
种类型的boosting
1. CvBoost : : DiSCRETE(discrete AdaBoost)
®请回顾。没有免费的午餐。法则,它告诉我们没有总是最优的分类器。但是在计算机
视觉方面的很多数据上,boosting和随机数可以获得很好的,「生能。
圆一个。弱分类器。的输出结果只是比随机分类略好一些,强分类器。的输出结果几
乎接近完全正确分类。因此弱分类器和强类器只是统计意义上的定义。
® Y. Freund and R. E. Schapire,"Experiments with a New Boosting Algorithm", in Machine
Learning : Proceedings of the Th irteenth International Conference (h¢organ Kauman, San
Francisco, 1996), 148-1 56.
Unbiased Importance : Biased Importance
Col5 Col5 : 1
Col20 L Colis'
Co[9 Coll 4
Coll 2 = 1 Col21 mm
Coll 9四回园蜀国团团Co17 f.5「0
Co18罾围目图四目冒Co118 1却'.90
Co14画醒围困圈圈Col目11.11
Coils anas com g. 48
Coll 4 Col18. 99
Co[21 amEm Coll 0 8. 55
Col22 ilma Col3 8. 45
COl7 1 : 2188 Col22 8. 37
COll 8 4. 44 COll 2 5. 30
COl2 4. 21 Coll 7 5. 26
COl3 3. 77 COl8 5. 08
COl12. 89 COll 3 3. 41
COl101. 1 0 COl6 2_30
Col191. 37
第46页
OCR文字识别结果:
2. CvBoost : : REAL(real AdaBoost)
3. CvBoost : : LOGIT (LogitBoost)
4. CvBoost : : GENTLE(gentle AdaBoost)
它们都是由原始AdaBoost变化而来。我们发现real AdaBoost和gentle AdaBoost
的效果最好。Real AdaBoost利用置信区间预测,在标签数据上有很好的性能。
Gentle AdaBoost对外围数据(outlier data)赋较小的值,所以在处理回归问题上效果
很好。LogitBoost也可以很好的处理回归问题。只需设置一下标志参数,就可以使
用不同的AdaBoost算法,所以在处理实际问题的时候,可以尝试所有的算法,然
后挑选最好的那个m。下面我们讨论原始的AdaBoost算法。注意,OpencV执行的
boos山rg算法,是一个两类分类器m(不像决策树和随机森林)。还有,LogitBoost
和GentleBoost不仅可以用来解决两类分类,还可以用于回归问题(详见后文)。
boosting算法训练了T个弱分类器机,t€{1,''',升。这些弱分类器很简单。大多数
情况下,它们是只包含一次分裂(称为决策stumps)或仅有几次分裂(可能到3次)的
决策树。最后做决定的时候将赋值权重at给每个分类器。AdaBoost训练时输入的
特征向量是xi:向量的类别标签是歹徒这儿I=1,'',M,M是样本总数),且夕八{1,
一1}。首先,我们初始化数据样本的权值D,(I)来告诉分类器将一个数据点分类错误
的代价是多少。boosting算法的主要特征是,在训练过程之中,这个代价将会更
新,使得后来的弱分类器更加关注与前面的分类器没有分对的数据点。算法如下
2.针对t=1,'',T.
a。寻找使得权重为D,(il的总错误最小的分类器h,。
b、求h,-argminhjeH占j,这儿占j一E。I-】D,E)(其中y,+与(x;)),如果最小错误满
国这种处理方式可以称为。魔法学习。或者。魔法规划。。虽然没有太多道理,但是通
常可以获得最高的准确率。有时候,通过深入思考,可以发现某个方法最好的原因,
并能更深刻的理解数据但是有时候却无法找出原因。
回有一个技巧称为unrolling,它可以将任何两类分类器转换为多类分类器,但这会使得
训练和识别的计算代价很高。请参考例程..,/opencw's口n7p/es/c/lelter_recog.cpp.
第47页
OCR文字识别结果:
E/〈0.5,则继续,否则退出。
c.设置h,的权重ai-log[(1-€,y占山这儿E,是步骤2b中的最
d,更新数据点权重D,+l(i)=[D,(i)exp(-a夕,h.(xi))VZ,,这儿Z,将所有数据点的
权重归一化。
注意,在2b步,如果找不到小于50%错误率的分类器,则停止,表明可能需要更
好的特征。
当前面讲的训练算法结束之后,最后的强分类器接受输入向量x,使用所有弱分类
器的加权和来进行分类。
这里,符号函数把所有正数变为1,把所有的负数变为-1(0仍然为0)。【49'7-498】
boosting代码
···TopencvTsamples/c刀etter_recog.cpp展示了怎么使用boosting、随机森林和反向传
播网络(即多层传感网络,MLP)。boosting的代码与决策树的代码相似,但是除了
决策树的参数,它还拥有自己的控制参数。
struct CvBoostparams public CvDTreeparams
in匕 boost_type // CvBoost DISCRETE, REAL, LOGIT, GENTLE
int weak_count // How many classifiers
int split_criteria
// CvB00st DEFAULT, GINI, MISCLASS, SQERR.
double weight_trim_rate
CvBoostparams O
CvBoostparams (
int boost_type,
int weak_count,
doubt e weight_t r im_ra t e,
int max_depth,
bool use_surrogates,
consc float* priors
在cvBoostparams中,boost_type从前面列出的四个算法中做出选择。
机器学习539
第48页
OCR文字识别结果:
split_criteria为下面的值之一
CvBoost::DEFAULT(默认配置)
C守Boost::GINI(real AdaBoost的默认配置)
CvBoost : : MiSCLASS(discrete AdaBoost 的默认配置)
·CvBoostSQERR(最小平方误差只能在LogitBoost和gentle AdaBoost时
使用)
最后一个参数weight_trim_rate用于保存计算结果。当训练进行时,很多数据
点变得不重要。也就是说,第I个点的权值D,(I)变得很小。weight_trim_ra·e是
一个0到1(包含)的阈值,在给定的boosting迭代中暗中丢弃一些数据点。例
如weight_trim_rate是0.95。这意味着样本总权值小于1.0-0.95=0.05的点将
不参加训练的下一次迭代。这些点并不是永久删除。当下一个弱分类器训练结束,
所有的点的权值将被重新计算,有些前面被丢弃的样本则有可能进入下一个训练集
合。关闭这个功能,只需要将weight一匕rim_rate置为0即可。
我们观察到CvBoostparams{)是继承于CvDTreeparams{},所以可以设置其他与
决策树有关的参数。实际情况下,如果处理的特征有丢失国,可以设置
cvDTreeparams usE_surrogates,它可以保证每次分裂选取的特征将被存储在
相应的节点中。另一个重要的参数是使用priors来设置将非物体认为是物体的代
价。再说一次,如果我们训练有毒和无毒的蘑菇,我们可以设置priors为floa七
priors={1.o,1o.o};如此一来,把有毒蘑菇标记为无毒的错误代价则比把无毒
的蘑菇标记为有毒的代价大10倍。【498~499】
cvBoost类包含成员变量weak,它是一个cvseq·类型的指针,指向继承于
cvDTree决策树的弱分类器@。Logit Boost和Gentle Boost中,决策树是回归树,其
他的boosting算法中决策树贝1」只判断类另1」0和类别1.cvBooat Tree的声明如下
®请注意,对于计算机视觉来说,特征是从一幅图像中计算得来,然后传递给分类器,
所以几乎从不会有数据丢失。数据丢失现象经常发生在由人采集数据时,例古。测量病
人一天中的体温。
园对象的命名有时候没有道理。CvBoost类型的对象是boosting树分类器。CvBoost Tree
是纽成总的boosting强分类器的弱分类器。分析起来可能是这样的原因类型为
CvBoostTree的弱分类器继承于CvDTree(例如它们是很小的树,如此之小以至于只能
称为stump)。CvBoost的钱员变量weak指向一个序列,序列里的元素是类型为
CvBoostTree的弱分类器。
540第13章
第49页
OCR文字识别结果:
public
CvBoost Tree O
virtual CvBoost Treeo
virtual bool train(
CvDTree Train Data"train_data,
const CvMat · subsample_idx,
CvBoost"ensemble
virtual void scale( double s )
virtual void read(
CvFilestorage"fs,
CvFileNode+ node,
CvBoost* ensemble,
CvDTree Train Data* data
virtual void clear OJ
protect ed
CvBoost* ensemble
boosting算法的训练和决策树的训练几乎一样。不同的是多了一个外部参数
up己ate,它的默认值为0。这样,我们重新训练所有的弱分类器。如果update设
置为1,我们就把新训练出的弱分类器加到已经存在的弱分类器集合中。训练
boosting分类器的函数原型如下
Doo1 CvBoost train(
const CvMat* train-data
int tflag,
const CvMat · reponses,
const CvMat* var_id× O,
const CvMat* sample_id× O,
const CvMat · vdr_type O,
const CvMat"missing_mask O,
CvBoostparams params CvBoostparamsj,
bool update false
[499-sool
机器学习541
第50页
OCR文字识别结果:
...Topencv后口ntptesTc刀etter_recog.Cpp中有一个训练boosting分类器的例子。训练部
分的代码片段如下。
例13-3训练boosting分类器的代码片段
var-type = cvcreate Matl var-count + 2, 1, CV-8U )
cvset ( var_type, cvscalarA11 ( CV_VAR_ORDERED ) )
// the last indicator variable, as well
// as the new ( binary ) response are categorical
cvset Real 1D_( var_type, var_count, CV_VAR_CATEGORICAL
cvset Real 1D( var_type, var_c ount+1, CV_VAR_CATEGORICAL
/ / Train the classifier
boost. Traint
new_dat a,
CV_ROW-SAMPLE,
responses,
O,
O,
var_type,
O,
CvBoostparams( CvBoosL REAL, 1aa, a. 95, 5, false, O
cvReleaseMat( & new-data )
cvRelease Matl & new-responses )
boosting算法的预测函数也与决策树很相似
£1oat CvBoost predict(
const CvMat · sample,
const CvMat* missing O,
CvMat* weak-responses O
=歹
Cvs1 ice slice-CV-WHOLE-SEQ,
bool raw_mode false
const
在预I到中,我们传入一个特征向量sample,pre己icto函数则返回一个预测值。
第51页
OCR文字识别结果:
团团团■自■自主、,.,
当然,有一些可选参数。第一个参数是missing,与决策树相同,它包含一个与
sample向量维度相同的字节类型向量,非0标志着特征的丢失。(注意,这个标志
只有在训练分类器时使用了cvDTreeparams usE_surrogates的前提下才有效)
如果想得到每个弱分类器的输出,可以传入一个浮点型的cvMat向量
weak_response,它的长度和弱分类器的个数一样。如果向量weak_response被
传入,预测函数将根据每个分类器的输出填充这个向量。
CvMat"weak_responses cvcreate Matl
1,
boostedclassifier. Get_weak_predictorso-»tota1,
CV_3 2 F
预测函数的下一个参数sli·e指定使用弱分类器的哪个相邻子集1它可以这样设置
inline Cvslice cvslice( int start, int end )
但是,我们通常使用默认值,把slice设置为。所有弱分类器。(cvslice
slice= cv_wHo乙E_sEQ)。最后,我们看raw_mode,它的默认值为fal·e,但是可
以设置为true。这个参数的用法与决策树完全一样,标志着数据是否已经归一
化。正常情况下,我们不使用这个参数。调用boost预测的例子如下
boost. predict(temp_sample, O, weak_response )
最后,一些辅助函数可以使用。我们可以使用void cvBoost prune(cv8lice
slice)来从模型中去除一个弱分类器。
我们也可以使用cvseq* cvBoostget_weak_predictoro来获得所有的弱分类
器。这个函数返回一个类型为cvseq·的指针序列,序列中元素为cvBoost Tr·e
类型。【500~501】
随机森林
OpencV包含随机森林(random tree)类,它是根据Leo Breiman的随机森林方法
(random forest)o执行的。随机森林可以通过收集很多树的子节点对各个类别的投
Breiman的大部分关于随机森林的方法被收集到一个网页上(ht中//www.stat.berkeley.
e du lu s e rs/b re I m a nl Ra nd o m Fores ts
机器学习543
第52页
OCR文字识别结果:
票,然后选择获得最多投票的类别作为判断结果。通过计算。森林。的所有子节点
上的值的平均值来解决回归问题。随机森林包含随机选择的一些决策树,在ML库
建立的时候,它是当时最好的分类器之一。即使是在非共享内存的系统上,随机森
林在并行执行方面也很大的潜力可挖。这个特征使它在未来发展占据优势。随机森
林建立时的基本子系统也是决策树。在建立决策树时会一直继续下去直到数据纯
净。因此(图13-2的上半部分,过拟合),尽管每个树都很好学习了训练数据,但
是各个树之间仍有很大的不同。为了消除这些不同,我们把这些树放在一起来平均
(因此叫随机森林)。
当然,如果所有的树都很相似,随机森林就没有很大的作用。为了克服这点,随机
森林通过在树的建立过程中随机选择特征子集来使各个树不相同。例如,一个目标
识别树可以有很多可能的特征颜色、质地、倾斜度、倾斜方向、变量、值的比
等。树的每个节点可以从这些特征中随机的选择子集来决定怎样最好地分裂数据。
每个后来的节点获得新的、随机选择的特征子集来分裂。随机子集的规模一般是特
征数量的开方。因此,如果我们有100个可能的特征,每个对接点将随机选择10
个特征,并利用这10个特征对数据进行最好地分裂。为了提高鲁棒性,随机森林
使用袋外(out of bag)方法来检验分裂。给定任何节点,训练是发生在一个随机选择
然后替换的数据子集上进行的◆没有选到的数据被叫做。out of bag(OOB)。数据,
将用于估计分裂的性能。00B数据一般为所有数据的113.
如同其他基于树的方法,随机森林继承了树的很多属性丢失值的代理分裂,可以
处理标签和数值特征,不需要归一化数据,容易获得对预测很重要的变量。随机森
林还使用00B错误来估计它在处理未见过数据时的性能。如果训练数据与测试数
据的分布相似,00B性能预测会更精确。
最后,随机森林可以用来确定两个数据样本的亲近度(是相似度,而不是距离)。方
法如下(1)将数据样本放入判别树(2)计算它们到达同样的叶子(子节点)的次
数;(3)用相同的叶子数除以树的总数。亲近度为1意味着完全亲近,O意味着完全
不亲近。亲近度可以用来检测异常(样本与其他的很不相似),或者用来聚类(把相
似的样本聚在一起)。【501~502】
随机森林代码
我们已经熟悉了ML库的工作方法,随机森林的方法也不例外。它首先是一个参数
®这意味着一些数据样本可能随机重复。
544第13章
第53页
OCR文字识别结果:
结构CvRTparams,该结构继承于决策树
struct CvRTparams public CvDTreeparams
bool ca1 c-var-impo rtance
int nactive-vars
CvTermcriteria term-cri t'
CvRTParams O CvDTreeparams (
5, 10, 0. false,
10, 0, false, false,
), cale-var-importance ( false ), nactive-vars ( 0 )
◆
term-crit cvTermcriteria(
CV-TERMCRI?-I TER CV_TERMCRIT-EPS
/ I
CvR?Params (
int
int
float
bool
int
const float*
bool
int
int.
flota t
int
_max-depth,
min-samp1 e-count,
-regres s ion-accuracy,
use-surrogates.
max-categorie s,
priors,
cale-var-importance,
nact ive-vars,
max-tree-couht,
forest-accuracy,
termcrit-type,
cvRTparams中的主要参数是cale_v·r_import己nce,这是一个在训练中计算每个
特征的变量重要性的开关(会稍微增加计算时间)。图13-13展示了
由..,南pencvTsamptes/c/口garicu/s-tepio 【a,da te文件中的一部分蘑菇数据计算得到的
特征重要性。参数nacti·e_vars设置了随机选择的特征的大小,它一般设置为特
征总数的平方根s t·r。_cr上t(本章其他地方讨论过的结构体)控制着树的最大数
机器学习545
第54页
OCR文字识别结果:
目。随机森林训练时,term_crit中的m·x_it·r控制着树的总数,当epsilon值
小于00B错误时,则停止增加新的树$type告诉程序使用哪种停止标准
(CV_TERMCRIT_ITER CV_TERMCRIT_EPS).
Col5
Co120目回国国国目国国围围困团团回国·,国回国目
Col19 4. 57 B
C C O o 1 1 9 1 3
WW"警躐鼹嬲踫嬲踫
Co旧
汩竻2:弓,',"",,-:i,。、_T·回国园圈圈目囲醒目国四
C C O o 1 17 22
Coll 5 4_06, 1. 84
Cot113. 52 0. 44
Col4 3. 12 M
Col14 2. 98 0. 25
Col18 2. 68 0. 70
ICol2 2. 22 0. 39 I
I Col101. 79 2. 67
· Col10A10. U 7261. ;.'!. -
Col17 0. 18 0. 32 0. 54
Co旧
Col6
Col16
图13-13使用随机森林,boosting和决策树计算出的蘑菇数据的变量重要
性随机森林使用更少的重要变量,获得了最好的预测性能(包含20%数据上
随机选择测试机得到100%准确率)
随机森林训练算法和决策树训练差不多(请参考前面对cvDTree traino的描
述),不同的是这儿使用结构cvRTparams
boo1 CvRTrees tra'in(
const CvMat* train-data,
int t£lag,
const CvMat"responses,
const CvMat° comp_id× O,
const CvMat* sample_id× O,
const CvMat* var_type O,
const CvMat* missing_mask O,
CvRTParams params CvRTParamsO
; [503-504]
OpencV的实例目录中提供了一个调用训练函数的例子,用干多类学习问题,参
见.../opencv/samples/c/1et亡·r_cecog.cpp文件,其中的随机森林分类器名为
546第13章
第55页
OCR文字识别结果:
一侧
responses,
O,
sample_idx,
va r_type,
O,
CvRTParams ( 10, 10, 0, false, 15, 0. true, 4, 100, 0. 0lf,
CV_TERMCRIT_ITER )
随机森林预测函数的形式也和决策树差不多,但是它的返回值是一个森林中所有的
树的返回值的平均值。参数missing是可选的,维数与sample向量一致,非0值
表明sample中丢失特征。
double CvRTrees predict(
const CvMa t * sampl e,
const CvMat* missing O
const [ 504-505]
文件【etter_recog.cpp中的预测函数调用如下。
double r
CvMat sample
cvGetRow( data, &sample. I )
r forest. predict( &sample )
r fabs( (double ) r responses-»data. Flti] ) <= FLT_EPSILON ?1 0
在这段代码中,返回值r被转换成整数来标识是否正确预测。
最后,有一些随机森林的分析和工具函数。假设在训练时已设置
cvRTparamsca1_var_importance,则可以通过如下函数获得每一个变量的相
对重要性。
const CvMat* CvRTreesget-var-importanceo const
图13-13展示了随机森林计算出来的蘑菇数据的变量重要性一个例子。我们还可以
通过以下函数调用获得两个数据样本在随机森林模型中的相似度。
机器学习547
第56页
OCR文字识别结果:
float CvRTreesget_proximity(
const CvMat * s ample_1,
const CvMat * sample_2
COHSL
前面已经说明,返回值为1标志着数据样本是同一的,O意味着数据样本完全不
同。从与训练集近似的分布中取得的两个点的相似度一般在0和1之间。
另外两个函数可以获得,树的总数以及指定决策树的数据结构
int get_tree_counto const // How many trees are in the forest
CvForestTree* get_tree(int i) const // Get an individual
decision tree
使用随机森林
我们已经说过随机森林算法经常获得最好(或者最好的之一)的性能,但是最好的
策略仍然是在训练集上尝试很多分类器。我们在蘑菇数据集合中使用了随机森林、
boosting和决策树。从8124个数据样本中,我们随机抽取了1624个测试样本,其
他的作为训练样本。在训练了这三个基于树的分类器之后,我们获得了表13-4的
测试结果。蘑菇数据集合相当简单,所以尽管随机森林的性能最好,我们也不能言
之凿凿地说随机数分类器在这个实例中是最好的。[505~506】
表13-4三个基于树的分类器对opencV蘑菇数据集(使用1624个随机选择
的样本作为测试样本,没有对错误分类一毒蘑菇进行惩罚)的实验结果
随机森林100%
AdaBoost 99%
决策树98%
如图13-13所示,最有意思的是变量重要性(也可通过分类器测得)。图中可以看
出,随机森林和boosting各自所用的重要变量都明显少于决策树。重要性超过
15%的特征中,随机森林只使用了3个变量,boosting使用了6个,决策树却使用
了13个。因此,我们可以收缩特征集大小,在不损失性能的前提下减少计算量和
内存使用。当然,在决策树中,只需要建立一棵树,而在随机森林和AdaBoost算
法中需要建立多棵树;因此,哪个算法拥有最少的开销与数据有关。
第57页
OCR文字识别结果:
人脸识别和Haar分类器
现在,我们转到OpencV中最后一个基于树的技术Haar分类器,它建立了boost
筛选式级联分类器。它与ML库中其他部分相比,有不同的格局,因为它是在早期
开发的,并完全可用于人脸检测。因此,我们仔细研究它,并说明它是怎么识别出
人睑和其他刚性物体的。
计算机视觉是一个涉及广泛而且发展迅速的领域,所以OpencV中某个特定技术
很容易过时。人脸检测就用这样的过时风险。但是人脸检测有巨大的需求,因此需
要有一个不错的基线技术供使用,而且人脸检测是建立在最经常使用的分类器
boosting上,因此更加通用。事实上,一些公司使用了OpencV中的。人脸。检测
器来检测。基本刚性的。物体(脸,汽车,自行车,人体)。他们通过成千上万的物
体各个角度的训练图像,训练出新的分类器。这个技术被用来设计目前最优的检测
算法。因此,对于此类识别任务,Haar分类器是一个有用的工具。
OpencV实现了人脸检测技术的其中一个版本。它是首先由Paul Viola和Michael
Jones设计的,称为Viola-Jones检测器®。接着,它由Rainer Lienhart和Jochen
Maydt用对角特征扩展(超出本文分析范围)@。OpencV称这个检测器为。Haar分
类器。是因为它使用Haar特征@或更准确的描述是类Haar的小波特征,该特征由
矩形图像区域的加减组成。OpencV包含一系列的预先训练好的物体识别文件,但
是代码允许你训练并存储新的物体模型。除了人脸外,训练方法(crea仁esamples o
似刚性的任何物体。【506-507】
OpencV中预先训练好的物体检测器位于.../wencw'data/h口口rcascades,其中正面人
睑识别效果最好的模型文件为h口口rcasc口de斤ont口tf口ce_口it2.x阴t。而侧脸却难以用
该方法获得准确的检测结果(随后会简单介绍原因)。如果你训练了一个很好的物体
检测器,也许可以考虑把它开源。
® P. Viola and M. J. Junes,"Rapid Object Detection Using a Boosted Cascade of Simple
Features,"IEEE CVPR (2001 ).
@ R. Lienhart and J. Maydt,"An Extended Set of Haar-like Features for Rapid Object
Detection,"fECE tctp (2002), 900-903.
国严格来说,这种说法不对的。分类器使用矩形区域内像素的和或者差,然后通过闽值
化来生成特征检测器、这里使用。类Haar。泉表明这种区别。
机器学习549
第58页
OCR文字识别结果:
监督学习和boosting算法
OpencV中的Haar分类器是一个监督分类器(这点已经在本章开始时提到)。先对图
像进行直方图均衡化并归一化到同样大小,然后标记里面是否包含要检测的物体,
大多数情况下要检测的物体是人脸。
Viola-Jones识别器使用AdaBoost,但是把它组织为筛选式的级联分类器,每个节
点是多个树构成的分类器,且每个节点的正确识别率很高(例如99.9%,也就是很
低的错误拒绝率,一般不会把人脸丢掉),但正确拒绝率很低(接近50%,也就是高
的错误接收率,很多非人脸不会被检测出来)。在任一级计算中,如果一旦获得目
标。不在类别中。的结论,则计算终止,算法也宣布该位置上没有人睑。因此只有
通过分类器中的所有级别,才会认为物体被检测到。这样的优点是当目标出现频率
较低的时候(例如一幅大图里只有一幅小人脸),筛选式的级联分类器可以显著地降
低计算量,因为大部分被检稠U的区域都可以很早被筛选掉,迅速判断出此处无
人脸。
Haar级联中的boosting
boosting分类器算法在本章前面已经讨论过。Viola-Jones的筛选式级分类器中,弱
分类器是一个个多数情况下只有一层的决策树(例如。决策stump。)。一层决策树
允许下面形式的决策判断特征f的值v大于某个阈值t$yes表示可能是人脸,no
表示不是人脸
f =': I, :. [507-508]
训练中,Viola-Jones分类器在每个弱分类器中使用的类Haar-like特征个数可以设
置。但是大多数情况下,我们使用1个特征(一个只有一个分裂的树),最多3个。
然后提升算法迭代地建立一个由那些弱分类器的加权和组成的强分类器。Viola一
Jones使用如下分类函数
F= sign[w tf. +w · f2+.''1-wJ,)
如果加权和小于O,则符号函数返回-1;0,则返回Oi大于O,则返回1。在第一
次遍历数据的时候,我们训练得到月的阈值门,它最好地区分了输入数据。然后
boosting算法使用得到的错误来计算投票权值wl。根据传统的AdaBoost,每个特
550第13章
第59页
OCR文字识别结果:
征向量将根据是否被正确分类,重新赋予或高或低的权值m。一旦一个节点训练完
成,剩余的数据将被用来训练下一个节点,以此类推。
Viola-Jones分类算法
Viola-Jones分类器在级联的每个节点中中使用AdaBoost来学习一个高检测率低拒
绝率的多层树分类器。这个算法使用了如下一些创新的特征。
1它使用类Haar输入特征对矩形图像区域的和或者差进行1阈值化。
2它的积分图像技术加速了矩形图像区域或矩形区域的45度旋转(参考第6章)
的值的计算,这个图像结构被用来加速类Haar输入特征的计算。
3它使用统计boosting来创建两类问题(人脸与非人脸)的分类器节点(高通过率、
低拒绝率)。
4,它把弱分类器节点组成筛选式级联。'换句话说第一组分类器是最优,能通过
包含物体的图像区域,同时允许一些不包含物体的图像通过第二组分类器园
次优分类器,也是有较低的拒绝率$以此类推。在测试模式下,只要图像区域
通过了整个级联,则认为里面有物体@。【508】
类Haar特征如图13-14所示。在所有缩放尺度下,这些特征组成了boosting分类
器使用的全部。原材料。。它们从原始灰度图像的积分图(参考第6章)中快速计算
得出。
Viola和Jones把每个boosting分类器组合成筛选式级联的一个节点,如图13-15
所示。图中,每个节点马包含一组使用类Haar特征训练有没有人脸的决策树。典
型情况下,节点由简单到复杂排列(先尝试简单节点),这样可最小化拒绝图像的简
单区域时的计算量。每个节点的boosting使节点具有高通过率。例如,在检I则人脸
的时候,几乎所有的人脸99.9%都被检测出并允许通过,但是50%的非人脸也得以
通过。这没关系,因为20个节点使总识别率为0.999 2o_98%,而错误接收率仅为
0. 5420 =0. 0001%.
国对正确分类的样本降低权重,对错误分类的样本增大权重,有时候这会今人困惑。其
原因是boosting希望更加关注无法分类的样本、忽略已经指导如何分类的样本。一个
更加技术的描述是boosting是最大化间距的。
圆记住,在筛选式级联里,一个节点是Adaboost类型的一组分类器。
国这保证了级联的运行速度可以很快,因为它一般可以在前几步就可以拒绝不包含物体
的图像区域,而不必走完整个级联。
机器学习551
第60页
OCR文字识别结果:
T=1?
I回时W
I"
图1 3-14 0pencV中的类Haar特征(矩形和旋转矩形特征都很容易从积分图
中算出)。在这些小波示意图中,浅色区域表示。累加数据。,深色区域表示
。减去该区域的数据。
无
图13-15 Viola-Jones分类器中用到的筛选式级联。每个节点都由多个boos一
ting分类器组成,只要有人脸,它基本上都可以检测到,同时它只拒绝一小部
分非人脸但是到最后一个节点,几乎所有的非人脸都被拒绝掉,只剩下人脸
区域
识别时,原始图像的不同区域都会被扫描。70%~80%的非人脸区域在筛选式级联
的前两个节点被拒绝,每个节点使用大约10个决策树。这种快速的早期拒绝提高
了人脸检测的速度。
552第13章
第61页
OCR文字识别结果:
方法适用范围
此技术虽然可以用于人脸检测,并不限于人睑检测,它适用于其他外表有区别的
(接近刚性的)物体的检测。正面人脸、车的前部、侧部和后部都可以用它来检洳,
但如果是人的侧睑和车的斜视角,则效果不好。主要是因为这些视角在模板中有很
多变化,而。块特征。无法很好地处理这种变化。例如,为了让模型学习侧睑的曲
线,人的侧脸肯定会包含变化的背景到模型中。可以使用haarcasca己e一
profileface.xm1来检测人的侧脸,但是如果想要得到更好的结果,需要收集更。
多的数据(相较于训练这个模型的数据),也许还要包含不同背景下的侧股数据。再
次强调,侧脸是很难用这个分类器识别的,因为此分类器使用块特征,侧脸边缘外
的背景也会被当作有用信息进行学习。在训练中,如果只学习右侧脸会更有效率一
些。在测试时,(1)首先运行右侧脸检测器,(2)然后沿着图像的垂直轴翻转图像,
再次运行右侧睑探测来检测左侧脸。
如前所述,这些类Haar特征对于°块特征。(眼睛、嘴、发际线)具有比较好的效
果,但对树枝或者物体主要靠外形(如咖啡杯)的物体。就像我们讨论的,基于类
Haar特征的识别器适用于固有特征——眼睛、嘴、发际线。但是不适用于树枝,
或者那些外形是最有区别的特型(coffee杯)。
如果你愿意收集有关刚性物体的大量好的,已完美分割的数据,那么这个分类器仍
然可以达到很好的效果,它的筛选式级联结构使其运行速度很快(当然不是指训练
时的运行速度)。这里的°大量的数据。意味着数以千计的物体实例和数以万计的
非物体实例。。好数据。意味着不应该把倾斜的脸和竖直的睑混在一起解决方法
是训练两个分类器,一个用来判断倾斜,一个用来判断竖直。。完美分割。圈定物
体的矩形边界要保持一致。如果物体边界四处漂移,那么分类器不得不去学习这些
变化。例如,眼睛在矩形边界内的位置不同,会使分类器认为眼睛位置不是脸的固
定的几何特征,是可移动的。当分类器尝试去适应实际并存在的东西时,性能一般
都会变差。
人脸检测代码
13-4的代码己etect_an己一己rawo可以检测人脸,并在图中用不同颜色的矩形画
出它们的位置。按照下面第4行到第7行注释,这段代码假设已经加载预先训练好
的分类器,也已经为检测到的人睑创建了内存。
机器学习553
第62页
OCR文字识别结果:
例13-4检测并标识人脸的代码
/ / Detect and draw detected object boxes on image
/ / Presumes 2 Globals
/ / Cascade is oaded by
/ / cascade ( CvHaarclassifiercascade* ) cvLoad( cascade_name,
/ / 0, 0, 0 )
/ / AND that storage is allocated
/ / CvMemstorage * storage = cvcreate Memstorage ( 0 )
void detect-and-draw(
Iplimage * img,
Double scale 1. 3
static Cvscalar colors[]
{{0, 0, 255 ) J, { 10, 128, 255 ) ), {(0, 255, 25511, 110, 255, 011,
{{255, 128, 0}), {1255, 255, 0}t, ( ( 255, 0, 01 ), ( ( 255, 0, 2551 )
} //Jus匕 some pretty colors to draw with
/ / IMAGE PREPARATION
1plimage'gray = cvcreatelmage(cvsize(i mg->width, img-»height ), 8, 1 )
Iplimage * small_img cvcreatelmage(
cvsize(cvR. ound ( img-»width/scale), cvRound ( img-»height / scale ) ), 8, 1
cvcvtcolor ( img,. Gray, CV_BGR2GRAY )
cvResize( gray, small_img, CV_INTER_LINEAR )
cvEqualize Hist( small_img, small_img )
/ / DETECT OBJECTS IF ANY
cvc1earMemstorage( storage
Cvseq * objects = cvHaar Detectobjects(
sma 1 1_img,
ca seade,
storage,
1. 1,
2,
0 / *CV-HAA R.. DO_CANNY-PRUNING* /,
cvsize(30, 30 )
r=i
554第13章
第63页
OCR文字识别结果:
-n
/ / LOOP THROUGH FOUND OBJECTS AND DRAW BOXES AROUND THEM
for(int I 0 I < (objects ? objects-»tota1 0) i++ )
CvRect* r (CvRect* ) cvGetseqE1em ( objects, I )
img,
cvpoint ( r. x, r. y ),
cvpoint ( r. x+r. width, r. Y+r. Height ),
colors[i%8]
cvReleaselmage( & graygray )
cvReleaselmage( &smal1_img )
[511-512]
代码中函数detect_and_drawo有一个颜色向量的静态数组colors【】,可以用不
同的颜色标识出的人脸。分类器在灰度图像上进行检测,所以RBG图像首先需要
通过cvcv七color o转换成灰度图像,还可以选择cvResizeo调整大小,然后通
过cvEqualize Histo进行直方图均衡。它可以平衡亮度值,因为积分图像特征基
于不同的矩形区域的差别,如果直方图没有均衡,这些差别可能由于沏1试图像的成
像条件或过分曝光而偏离正常值。因为分类器识别出的目标矩形在cvseq序列中
返回,我们需要先通过cvc1ear Menstorage O清除内存。实际的检测在for{}循
环的前面,下面将详细讲述cvHaar Detectobjects O的参数。这个循环的作用是
逐个取出人脸的矩形区域,然后用不同的颜色通过函数cvRectangleo画出来。
让我们先仔细看一下检测函数调用
Cvseq * cvHaar Detectobjects(
const CvArr* image,
CvHaarc1assifiercascade* cascade
CvMemstorage * storage,
double scale_factor 1. 1,
int min-neighbors 3,
int flags O,
Cvsize min size cvsize(O, O )
cvArr主黒age是一个灰度图像,如果对它设置了感兴趣的区域(R01),那么函数将
只处理这个区域。因此,一个提高人脸识别速度的方法是使用R01裁减图像边
羿。分类器cascade是我们通过cvLoad0加载的Haar特征级联。参数storage
机器学习555
第64页
OCR文字识别结果:
是这个算法的工作缓存。它由cvcreate Menstro己ge(0)来分配,由
cvc1ear Menstorageo释放。函数cvHaaroetec仁Objectso以不同的窗口扫描输
人图像寻找人脸。设置scale_£act·r参数可以决定每两个不同大小的窗口之间有
多大的跳跃这个参数设置得大,则意味着计算会变快,但如果窗口错过了某个大
小的人睑,则可能丢失物体。参数mi n_neighbors控制着误检测。现实图像中的
脸会被多次检测到,因为周围的像素和不同大小的窗口也会检测到人脸。在人脸识
别代码中设置这个参数为默认值(3)表明只有至少有3次重叠检测,我们才认为人
睑确实存在。参数flag有4个可用的数值,它们可以用位或操作结合使用。第一
个是CV_H八八R_DO_CANNY_PRUNING,这个值告诉分类器跳过平滑(无边缘)区域。第
二个值是CV_HAAR_ScALE_IMAGE,这个值告诉分类器不要缩放分类器,而是缩放
图像(处理好内存和缓存的使用问题,这可以提高性能)。第三个'值是
CV_HAAR_FIND_BIGGEST_OBJECTS,告诉分类器只返回最大的目标(这样返回的物
体个数只可能是1个或者0个尸。最后是Cv_HAAR_DO_ROuGH_SEARCH,它只可与
CV_HAAR_FIND_BIGGEST_OBJECTS一起使用,这个标志告诉分类器在任何窗口,
只要第一个候选者被发现贝1」结束寻找(当然需要足够的相邻区域来说明真正找到了
目标)。参数min_si·e指示寻找人脸的最小区域。设置这个参数过大,会以丢失
小物体为代价减少计算量。图13-16展示了在包含人脸的场景中使用人脸检测代码
的结果。【512~513】
图1 3-16公园场景的人脸检测。一些旋转的直没有被检测到,另外也有误检
测(中间的衣服部分)对于这个1054×851图像,在2GHz的计算机上运行了
1.5秒,完成百万次搜索后得到了这个结果
最好不要使用Cv_HAAR_DO_CANNY_PRUNING和witin CV一HAAR_FIND一
BIGGEST_OBJECT,虽然这两个标志可以降低计算量,但是引起的副作用往往会导致
性能降低。
第65页
OCR文字识别结果:
学习新的物体
我们已经知道怎么加载和运行一个预先训练并存储在xml文件中的沉降分类器。
我们首先使用cvLo己d 0来加载它,然后使用cvHaar Detectobjec·s O来找到与训1
练目标相似的物体。现在我们转到怎么回\练分类器(如眼睛、行人、车等)。我们可
以使用OpencV的haartraining应用程序,它从给定的训练集训练出分类器。训
练一个分类器的4个步骤如下。(更多问题,请参考haartraining参考文档
op engy/app 5/Haar Trai n in 9ldo c/haart rain in g)
1.收集打算学习的物体的数据集(如正面人睑图,汽车侧面图),把它们存储在一
个或多个目录下面。这些文件通过一个文本文件以下面的形式建立索引
«path»limg_name_1 count_1 ×11 y11 wil h11 ×12 y12
«path>limg_name_2 count_2 ×21 y21 w21 h21 ×22 y22
文本文档的每一行包含路径(如果有路径的话)和文件名,紧接着是物体的个数
和物体的矩形列表,矩形的格式是(左上角的x和夕坐标,宽度,高度)。
具体说来,如果数据集在路径dala和cesi下,那么索引文件加ce.idx将如下
所示
Datalfaceslface_000. Jpg 2 73 100 25 37 133'123 30 45
Datalfaceslface_001. Jpg 1 155 200 55 78
如果希望分类器充分发挥作用,则需要收集很多高质量的数据(1000-10 000
个正样本)。高质量是指已经把所有不需要的变量从数据中除掉。举个例子,
如果你学习人脸,需要尽量对齐眼睛(最好加上鼻子和嘴巴)。除非告诉分类器
眼睛不可以移动,否则它会认为眼睛可以出现在任意区域内。但是这样是不符
合实际情况的,分类器将会无法取得好的效果。一个策略是首先训练一个容易
锁定的子集(如眼睛)的级联。然后使用这个级联来寻找眼睛,可以旋转/改变图
像大小直到眼睛被对齐。对于非对称数据,可以使用在前面讨论的沿竖直轴翻
转图像的窍门。【513-515】
2.使用辅助工具createsamples来建立正样本的向量输出文件。通过这个文
件,便可以重复训1练过程,使用同一个向量输出文件尝试各种参数。例如
createsamples vec face. vec-luso face. Idx w 30-h 40
第66页
OCR文字识别结果:
这个程序读人第一步所说的face.idx文件,输出一个格式化的训练文件
face.vec。然后createsamples提取图像中的正样本,再归一化并调整到指定
的大小(这里是30×40)。注意,creacesamples还可以对数据进行几何变换、
增加噪声、改变颜色等来合成数据。这个程序还可以学习一个公司的logo,只
需要照一张照片,然后让它进行很多真实世界存在的变化。更多细节请参考
OpenCV th haartraining 手册 lapps/Haar Trainingldoc/haart】 · aining.
3.
Viola-Jones级联是一个两类分类器它只判断图像中的物体是否(。是。还是
"否。)与训练集相似。我们已经说明怎么收集和处理正样本。现在我们'引兑明
怎么收集和处理反样本。任何没有我们感兴趣的物体的图像都可以作为反样
本。最好从我们需要泖」试的数据中选取反样本图像。即,如果我们想从在线视
频中学习人睑,最好从视频的不包含人睑的帧中获得反样本。然而从其他地方
(光盘上或者互联网相册中)获得反样本也可以获很好的最终结果。同样,我们
把反样本图像放到一个或者几个路径下面,并由图像文件名组成索引文件,一
个文件名占一行。比如,一个叫background.I如的图像索引文件可以包含下面
的路径和文件名
data/vacations / beach. Jpg
data / nonfaces / lmg-0 4 3. Bmp
data/nonfaces / 257-5799 IMG. JPG
4.训练。下例从命令行输入或者使用批处理文件创建的训练。
Haartraining /
-data face-classifier-take-3 /
-vec £aces. vec w 30-h 40 /
-bg backgrounds. Id× /
-nstages 20
-nsplits 1 /
[ ー nonsym] /
-minhitrate 0. 998 /
-maxfalsealarm 0. 5
[515]
执行之后,获得的分类器将存储在文件加ce_class垆·r_t口ke_3.xmt中。这里
声ce.vec是正样本(归一化为宽30,高40),反样本是从心口ckgrounds.I如随机抽
取的图像。级联被设置为有20层(-nstages),每一层的最低正确检测率
(-rninhitrate)是99.8%,错误接受率(-maxfalsea1己r·)是50%。弱分类器是
被指定为。stumps。,即只有一层分裂(-nsp11ts)的决策树,如果设置为多
第67页
OCR文字识别结果:
r
层,在某些情况下是可以提高准确率的。对于一些复杂物体,最多
层决策树,但是大多数情况下,我们将这个参数设置得比较小,
3层。
即使在一台比较块的计算机上面,根据数据集的大小,训练可能需要几个小
时,至一天。训练程序需要在每个正样本和反样本上测试大约100 0oo个特
征。这个程序可以在多核机器上并行执行(通过Intel编译器和OpenMp实
现)。OpencV中已经包含了并行版本。
其他机器学习算法
现在我们已经熟悉OpencV ML库的工作原理。ML库的架构使新算法和新技术比
较容易集成到库里面去。更多其他的新算法会很快添加进去。本节将简要介绍刚刚
添加到OpencV的4个新机器学习算法。每个算法都是一个广泛使用的机器学习
算法。这些算法被人们广泛研究,有很多书、论文和网络资料都是关于这些算法
的。想了解更多算法细节,请参考相应的文献和参考手册.../opencv/docs/ref
opencvref_m1. Htm.
期望值最大化
期望值最大化(expectation maximization,EM)算法是另一个常用的聚类技术。
OpencV仅支持混合高斯模型的期望值最大化,EM算法本身更通用。EM算法通
过迭代先找到给定模型时的最大可能性的猜想,然后调整模型使猜想正确率最大
化。OpencV中EM算法实现在类cvEM{)中,仅包括用混合高斯来拟合数据。用
户需要提供高斯的个数,所以此算法与K均值很相似。
K近邻
K近邻(K-nearest neighbor,KNN)是最简单的分类器技术之一。它只存储所有训练
样本数据,如果需要分类一个新的数据样本,只需要找到它的K(K是整数)个最相
邻的点,然后统计哪个类在这K近邻点中频率最高,然后把该点标记为出现频率
最高的类。这个算法实现在类cvK、Neast(}中。KNN分类器技术很有效,但是它需
要存储所有讷1练集,因此它占用很大的内存,速度比较慢。使用这个算法之前把训
练集聚类来降低数据的大小。读者如果对大脑中的(和机器学习中的)动态适应最近
机器学习559
第68页
OCR文字识别结果:
邻类型技术感兴趣,可以阅读Grossberg的论文[Grossberg87],或者Ca印enter和
Grossberg的最新的综述[Carpentero3]。【516~517】
多层感知器
多层感知器(MLP,也叫反向传播)是一种神经网络,是性能最好的分类器之",尤
其在文字识别方面性能卓越。它训练的时候很慢,因为它使用梯度下降调整神网络
层节点之间的连接权来最小化误差。测试模式时,它的速度很快仅仅一些点乘运
算,然后是一个压缩函数。OpencV中,它实现在类cvANN_MLp{)中,用法可以参
见文件.../opencvTsamplesTc刀eter_recog.cpp。有兴趣的读者可以阅读Lecun,
Botton,Bengio和HaffnertLecun98a]的论文来了解MLP在文字识别和物体识别方
面的效能。Lecun,Botton和Muller[Lecun98b]的论文中介绍了实现和调节细节。
分等级的类脑网络的最新工作可以参考Hinton,Osindero和Teh的论文
[Hinton06].
支持向量机
有很多数据的时候,boosting和随机森林算法常常是最好的分类器。但是当数据集
和比较小的时候,支持向量机(SVM)效果常常最好。这个N类分类算法首先把数据
映射到高纬空间(创建超过特征空间的组合的新空间),然后在高纬空间找到类别间
最优的线性分类器。在原始数据的原始空间中,这种高纬线性分类器可能是非线性
的。因此我们采用基于最大类间隔的线性分类技术,得到在某种意义上较优地区分
类别的非线性分类器。有了足够的增加的维数,我们几乎可以总能把数据很好地分
成不同类别。这个技术实现在类cvsvMO中。
这些工具与很多计算机视觉算法紧密相连,这些算法包括从通过训练好的分类器寻
找特征样本实现跟踪,到分割场景,还包括更直接的任务物体分类和图像数据
聚类。【516~517】
练习
1考虑根据以前的股票价格来学习未来的股票价格。假设有20年的每日股票数
据。在把数据转换成训练集和测试集时,下面的转换方法各有哪些优缺点?
a,把偶数的点作为训练集,奇数的点作为测试集。
560第13章
第69页
OCR文字识别结果:
b,随机选择测试集和训练集。
c.把数据分为两部分,一半用来训练,另一半用来测试。
d,把数据分为很多小窗口,每个小窗口包含一些过去的数据样本和一个预测
样本。
2.图13-17描述了一个。true。和°false。两类分布,图中也展示了一些可以设
置阈值的点(a,b,c,d,e,f,g)。
a,在ROC曲线上标记出点a到g.
b,如果。true。类是有毒的蘑菇,阈值应设置为哪个数字?
c.决策树对这些数据样本进行分类,效果会怎样?
◆
False True
a b c d e f g
图13-17两个类别(。true。和。false")的高斯分布
3.参考图13-1,回答下列问题。
a、画出决策树怎样用三次分裂拟合真实的函数(虚线)。这是一个回归问题,
不是分类问题。
注意回归的。最优。分裂是取分裂出的所有子节点的平均值。回归树拟合出的值看上去
像楼梯。
b,画出决策树怎样用七次分裂拟合真实数据。
c.画出决策树怎样用七次分裂拟合噪声数据。
d.讨论b和c在过拟合方面的区别。
4.为什么在单个决策树学习多类问题的时候,分裂方法(例如基尼——Gini)仍然
有效?
5.参见图13-4,它描述了不等方差(左图)和相等方差(右图)下的二维空间。把它
们视为与分类问题有关的特征值。假如这些特征值用于分类问题。在左图的空
机器学习561
第70页
OCR文字识别结果:
间和右图的空间中,使用如下分类器时,变量重要性会有所不同么?
a.决策树。
b.K近邻。
c.朴素贝叶斯。
修改例13-1(在K均值部分,最外层for {t循环前面)的数据生成代码,以生成
一个有随机标签的数据集。我们使用一个以(63,63)为中心,标准差为(img一
〉width/6,img-》height/6)的正态分布来生成10 000个数据样本,图像大小
是128×128。给数据作标签的时候,我们把空间分成4个象限。x〈64的时
候,20%的概率标记为力x〉64的时候,90%的概率标记为A。y〈64的时候,
40%的概率标记为山y〉64的时候,60%的概率标记为之然后对x和夕维度
上的概率相乘,得到类别A在4个象限内的概率。如果不标记为A,则标记为
B.例如当x〈64且y〈64时,样本有8%的可能性为A,92%的可能性为B.则
4个象限中标记为A的概率如下
对每一个数据样本,首先决定它的象限,再生成一个随机数,如果随机数小于
等于象限概率,则标记为山否则标记为B.这样,我们就获得了以x和7作
为特征的带标签数据样本。读者可能意识到x轴比夕轴更适合用来划分数据类
别。训练一个随机森林来验证x的变量重要性确实比y重要。
7.
使用与练习6一样的数据集,使用离散AdaBoost学习两个模型一个设置
weak_count为20,一个设置为500。从10 000个数据样本中随机选取测试集
和训练集。通过下面指定的样本数训练模型并记录结果
a.150个数据样本。
b.200个数据样本。
c.1200个数据样本。
d.5000个数据样本。
e、对结果做出解释。
8,重复练习7,使用随机森林分类器,树的个数为50个和500个。
夕,重复练习7,但是使用60棵树,并比较随机森林和SVM.
10.在什么情况下,对于过拟合,随机森林比决策树具有更强的鲁棒性?
562第13章
第71页
OCR文字识别结果:
11.参考图13-2,你能想象什么时候测试错误会小于训练错误吗?
12.图13-2是一个回归问题。把第一个点标记为A,第二个点B,第三个点A,第
四个点B,如此继续。画出A和B的一条分界线,分别演示
a,欠拟合。
b,过拟合。
13.参考图13-3.
a,画出最好可能情况下的ROC曲线。
b,画出最坏可能情况下的ROC曲线。
c.画出在穆川试集上性能随机的ROC曲线。
14.
"没有免费的午餐。法则的意思是没有哪个分类器在各种分布的标签数据上都
是最优的。请描述一个有标签的数据分布,本章描述的分类器没有一个能在这
方面取得好结果。
4、朴素贝叶斯分类器难以处理哪种分布?
b,决策树难以处理哪种分布?
c.怎样预先处理(a)和(b)中的分布使分类器能够更容易地学习数据?
15.在USB摄像机上设置并运行Haar分类器来检测人脸
a,它可以处理多少次尺度变化?
b,它可以处理多大的噪声?
c.它能最大能处理头多大的倾斜?
d,它能最大能处理下巴多大角度的上下运动?
e,它能最大能处理头多大角度的左右旋转?
f,它能能处理头的三维姿势变化吗?
16.
使用蓝色或绿色背景采集展开的手姿态(静态姿态)。收集其他的手的姿势,并
用随机背景采集。收集上百张图像训练Haar分类器来检测展开的手姿势。实
时测试分类器并估计检测率。
17,根据你的知识和从练习16中学到的,改进其结果。