教你用OpenCV实现机器学习最简单的k-NN算法
前言:OpenCV 的构建是为了提供计算机视觉的通用基础接口,现在已经成为经典和最优秀的计算机视觉和机器学习的综合算法工具集。作为一个开源项目,研究者、商业用户和政府部门都可以轻松利用和修改现成的代码。
k-NN算法可以认为是最简单的机器学习算法之一。本文教你利用OpenCV 和 Python 的基础知识,实现 k-NN算法。
01
使用分类模型预测类别:问题的提出
假设在一个叫作随机镇的小镇,人们对他们的两个运动队随机城红队和随机城蓝队非常痴迷。红队历史悠久,深受人们喜爱。但随后一些外镇的百万富翁来到小镇,买下红队中最出色的得分手,并开始组建一支新的球队,蓝队。
除了让大部分红队球迷不满之外,那个最出色的得分手依旧可以在蓝队中一步一步赢得冠军。尽管依旧会有一些永远无法原谅他早期职业选择的球迷不满,几年之后他还是会返回红队。
但无论如何,你可以发现红队球迷与蓝队球迷的关系并不好。事实上,这两队的球迷因为不愿与对方做邻居,连住所都是分开的。我甚至听到过这种故事,红队球迷在蓝队球迷搬到家附近时,会故意搬走到其他地方。这是真实的故事!
无论如何,我们一无所知的进入这个小镇,尝试挨家挨户卖给人们一些蓝队的货物。然而,时不时地会遇到一些热血的红队球迷会因为我们售卖蓝队的东西而对我们大喊大叫,并把我们驱赶出他们的草坪。非常不友好!如果可以避免这些房屋而仅仅访问那些蓝队球迷的家,压力将会更小,也可以更好地利用时间。
由于坚信可以学会预测红队球迷居住的地方,我们开始记录每次的访问。如果遇到了一个红队球迷的家,就在手边的小镇地图上画一个红色三角形;否则,就画一个蓝色正方形。一阵子之后,我们就非常了解他们的居住信息了。
▲随机镇的小镇地图
然而,现在我们到了地图中绿色圆圈标记的房子前了。应该敲门吗?我们尝试着找到一些线索来确定他们支持哪个球队(也许在后阳台上插着球队的旗帜),但没有找到。那如何知道敲门是否安全呢?
这个有些愚蠢的例子准确说明了监督学习算法可以解决的一类问题。我们有一些观察信息(房屋、房屋的地点和他们支持球队的颜色)组成了训练数据。可以使用这些数据来从经验里学习,这样当面对预测新房子的主人支持的球队颜色这一任务时,就可以有足够的信息做出评估。
正如前面所说,红队的球迷对他们的球队非常狂热,因此他们绝不可能成为蓝队球迷的邻居。我们是否可以使用这个信息比对所有邻居的房屋,以此来查明居住在新房子中的是哪一队的球迷?
这正是k-NN算法将要处理的问题。
02
理解 k-NN 算法
k-NN算法可以认为是最简单的机器学习算法之一。原因是我们只需要存储训练数据集。接下来,为了对新数据点进行预测,仅需要在训练数据集中找到它最近邻的点就可以了。
简单而言,k-NN算法认为一个数据点很可能与它近邻的点属于同一个类。思考一下:如果我们的邻居是红队球迷,我们很可能也是红队球迷,否则我们可能很早之前就搬家到其他地方了。对于蓝队球迷而言也是这样。
当然,有些社区可能稍微复杂一些。在这种情况下,我们将不仅仅考虑我们最近邻的类别(即k=1),而是考虑k个最近邻的类别。对于前面提到的例子,如果我们是红队球迷,我们可能不会搬到邻居大部分都是蓝队球迷的地方。
这就是它的全部了。
03
使用 OpenCV 实现 k-NN
使用OpenCV,可以很轻松地通过cv2.ml.KNearest_create()函数来创建一个k-NN模型。然后进行以下几步:
生成一些训练数据。
指定k值,创建一个k-NN对象。
找到想要分类的新数据点的k个最近邻的点。
使用多数投票来分配新数据点的类标签。
画出结果图。
首先引入所有必需的模块:使用k-NN算法的OpenCV、处理数据的NumPy、用于绘图的Matplotlib。如果使用Jupyter Notebook,别忘了调用%matplotlib inline魔法命令。
1. 生成训练数据
第一步是生成一些训练数据。我们将使用NumPy的随机数生成器来完成这个操作。我们将固定随机数生成器的种子值,这样重新运行脚本将总可以生成相同的值。
好了,现在可以开始了。那么我们的训练数据到底应该是什么样子的呢?
在前面的例子中,数据点是小镇地图中的房子。每个数据点有两个特征(也就是,在小镇地图上的位置的x和y坐标)以及一个类别标签(也就是,如果是蓝队球迷居住的地方则是一个蓝色的正方形,如果是红队球迷居住的地方则是一个红色的三角形)。
单独数据点的特征可以用一个具有两个元素的向量表示,这个向量表示数据点在小镇地图上的x坐标和y坐标。相似的,如果标记是蓝色的正方形,则类别是数字0,如果是红色的三角形,则类别是数字1。
可以通过从地图上随机选择一个位置并随机分配一个标签(不是0就是1)就可以生成一个数据点。假设小镇地图的范围是0≤x<100和0≤y<100。那么可以使用下面的代码来生成一个随机数据点:
正如上面的输出结果所示,这段代码将会从0到100之间获取两个随机的整数。我们将把第一个整数当作数据点在地图上的x坐标值,第二个整数当作数据点的y坐标值。同样,可以为这个数据点选择一个标签:
结果表示这个数据点的类别是0,我们把它当作一个蓝色的正方形。
把这个过程包装成函数,输入是要生成的数据点的个数(即num_sample)和每个数据点的特征数(即num_features)。
因为在这个例子中特征的数量是2,使用默认的参数值是没有问题的。在这种调用函数时不显式指定num_features值的情况下,这个参数会被自动分配为2。相信你已经了解了这个知识点。
我们想要创建的数据矩阵应该有num_samples行、num_features列,其中每一个元素都应该是[0, 100]范围内的一个随机整数。
同样,我们想要创建一个所有样本在[0, 2]范围内的随机整数标签值的向量:
别忘了让函数返回生成的数据:
Tips:OpenCV对于数据类型有些过分的讲究,因此确保总是把数据点的类型转换为np.float32!
接下来对函数进行测试,先生成任意数量的数据点,比如说11个数据点,并随机选择它们的坐标:
可以从上面的输出结果看到,train_data变量是一个11x2的数组,每一行表示一个单独的数据点。可以通过使用数组的索引获取第一个数据和它对应的标签:
这个结果告诉我们第一个数据点是一个蓝色的正方形(因为它的类别是0),它在小镇地图的坐标位置是(x, y) = (71, 60)。如果想要的话,可以使用Matplotlib在小镇地图上画出这个数据点:
但如果想要一次就显示所有的训练数据集呢?可以写一个函数。这个函数的输入应该是一个所有都是蓝色正方形的数据点的列表(all_blue)和一个所有都是红色三角形的数据点的列表(all_red):
接下来函数应该可以把所有蓝色数据点用蓝色正方形画出来(使用颜色'b'和标记's'),可以使用Matplotlib中的scatter函数完成这个任务。在使用这个函数时,需要把蓝色数据点当作N×2的数组来传入,其中N是样本的数量。接着all_blue[:,0]包含了所有蓝色数据点的x坐标,all_blue[:, 1]包含了所有蓝色数据点的y坐标:
同理,对于所有的红色数据点也可以这么做:
最后,设置绘图的标签:
在我们的数据集上测试一下这个函数吧!首先需要把所有的数据点分成红色数据集和蓝色数据集。可以使用下面的命令(其中ravel将平面化数组)快速选择前面创建的labels数组中所有等于0的元素:
前面创建的train_data中对应标签为0的那些行就是所有蓝色的数据点:
对于所有的红色数据点也可以同样操作:
最后,让我们画出所有的数据点:
这将会创建如下所示的图:
▲整个训练数据集的可视化
2. 训练分类器
现在是时候训练分类器了。
和其他所有的机器学习函数一样,k-NN分类器也是OpenCV 3.1 中ml模块的一部分。可以使用下面的命令来创建一个新的分类器:
Tips:在OpenCV的旧版本中,这个函数可能叫作cv2.KNearest()。
接下来把训练数据传入到train方法中:
这里,必须告诉knn我们的数据是一个 N×2 的数组(即每一行都是一个数据点)。这个函数会在执行成功后返回True。
3. 预测新数据点的类别
knn提供的另一个非常有用的方法叫作findNearest。它可以根据最近邻数据点的标签来预测新数据点的标签。
由于有generate_data函数,我们可以非常容易地生成一个新的数据点!可以把新数据点当作只有一个数据的数据集。
函数也返回一个随机的类别,但我们对它不感兴趣。相反,我们想要使用我们训练的模型对它进行预测!可以通过一个下划线(_)让Python忽略输出值。
回到我们的小镇地图,我们要像之前一样把训练数据集画出来,并将新的数据点加入,用绿色的圆圈表示(因为我们现在还不知道它应该是一个蓝色的正方形还是一个红色的三角形)。
Tips:可以在plt.plot函数后面添加一个分号以抑制输出,与Matlab一样。
上面的代码将生成下面这幅图(不包含圆环):
▲整个训练数据集,加上一个有待确定标签的新数据点(绿色)
如果要你根据它的临近点猜测,你会给新的数据点分配什么标签,蓝色还是红色呢?
其实,这也看情况,不是吗?如果看离它最近的房子(那个位置大致在(x, y)=(85,75),上图中点圆里面的房子),可能会把新的数据点同样分配一个红色的三角形。这也确实是在k=1的情况下我们的分类器预测的结果。
这里,knn报告说最近邻的点有250个单位远,其类别是1(我们说过1对应的是红色三角形),因此新的数据点类别应该也是1。如果设置k=2最近邻和k=3最近邻,结果也是一样的。
但要小心不要选择任意偶数的k值。为什么呢?其实,可以从上面的图中看出来(虚线圆),在虚线圆里面的6个最近邻点中,有3个蓝色正方形和3个红色三角形—这是个平局!
Tips:在这种平局的情况下,OpenCV的k-NN实现将会选择到数据点整体距离更近的邻居。
最后,如果非常大地扩大搜索窗口,根据k=7最近邻来对新数据点分类(在前面的图中是实线圆),会发生什么呢?
通过调用findNearest方法并设置k=7,可以看到结果:
忽然之间,预测的标签变为0(蓝色正方形)。原因是现在实线圆内有四个邻居是蓝色正方形(标签0),而只有三个是红色三角形(标签1)。因此多数投票建议预测新来者也是一个蓝色正方形。
正如所看到的,k-NN的输出结果会随着k的变化而变化。然而,大多数情况下是无法提前知道k为何值时是最合适的。对于这个问题最简单的解决方法是尝试一组k值,并观察哪个的结果最好。
本文摘编自《机器学习:使用OpenCV和Python进行智能图像处理》,经出版方授权发布。
《机器学习:使用OpenCV和Python进行智能图像处理》
作者:Michael Beyeler
扫码查看详情
推荐理由:
本书是一本基于OpenCV和Python的机器学习实战手册,既详细介绍机器学习及OpenCV相关的基础知识,又通过具体实例展示如何使用OpenCV和Python实现各种机器学习算法,并提供大量示列代码,可以帮助你掌握机器学习实用技巧,解决各种不同的机器学习和图像处理问题。
作者介绍
Michael Beyeler,华盛顿大学神经工程和数据科学专业的博士后,主攻仿生视觉计算模型,用以为盲人植入人工视网膜(仿生眼睛),改善盲人的视觉体验。 他的工作属于神经科学、计算机工程、计算机视觉和机器学习的交叉领域。同时他也是多个开源项目的积极贡献者。
扫码查看详情

作为码书商店的运营人员,诚邀你们进入我们的“CSDN码书福利群”,群里会不定时的给大家赠书书籍、优惠券等,有书籍推荐或者物流方面信息也可群里咨询~目前群已满100人,需要加群的请扫下方二维码添加微信,拉你入群哦~对此次活动不了解的也可咨询~
相关文章:

div 相同属性提取
把样式名或id写在一起,用逗号隔开 <!DOCTYPE html><html lang"en"><head> <meta charset"UTF-8"> <title>信息详情</title> <style type"text/css"> #box-1, #box-2, #box…

ehcache 简介
hCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。 下图是 Ehcache 在应用程序中的位置: ehcache部署起来很简单,主要分两步: 1.首先要给他写个核心配置XML文件 <ehca…

【leetcode】30. Substring with Concatenation of All Words
题目如下: 解题思路:本题题干中有一个非常关键的前提,就是words中的所有word的长度一样,并且都要被使用到。所以,我们可以把输入的s按word的长度进行等分,以s "barfoothefoobarman",words [&qu…

图像配准----SIFT
SIFT算子(Scale Invariant Feature Transform)是David Lowe提出的一种基于尺度空间的、对图像缩放、旋转甚至仿射变换保持不变性的图像局部特征描述算子。SIFT特征提取的是极其细微、大量的特征点,即时少数物体、物体的一小部分也可以产生大量特征向量。 SIFT算法如…

终于把微软BING搜索-SPTAG算法的原理搞清了
作者 | beyondma转载自 CSDN 博客近日,微软在GitHub上开源了其BING的搜索算法SPTAG,github地址:https://github.com/microsoft/SPTAG。这个算法笔者简单看了一下,的确是很有价值可以看大家介绍下,这种称为SPTAG &#…

把握每天的第一个钟头
当我十七岁的时候,我读到一段话,它是这么说的:“如果你把每天都当做最后一天来活着,那么有一天你将会是对的。”这句话让我留下了深刻的印象,从那时候开始,过去的 33 年来,我每天早上都对着镜子…

向量叉积计算法
如果向量A为{a, b, c},向量B为{m, n, p},如何计算向量A与向量B的叉积呢? 用行列式: |i j k| |a b c| |m n p| (bp-cn)i (mc-pa)j (an-bm)k 例如用matlab实现两个向量的叉积: a [1 2 3]; …

你是个成熟的C位检测器了,应该可以自动找C位了
作者 | 李翔转载自视说AI(ID:techtalkai)写在前面C位是近年网络上一个比较热门的词,最早来源于DOTA等游戏领域,是核心位置(Carry位)的简称,代表的是能够在游戏前中期打钱发育并在游戏后期带领队…

Data Artisans发布支持ACID事务的流式处理框架Streaming Ledger
data Artisans宣布推出Streaming Ledger,它扩展了Apache Flink,提供了跨表、键和事件流执行可序列化ACID事务的功能。这项正在申请专利的技术是Flink的专有附加技术,超越了当前一次只能在一个键上实现一致性的标准。\\在发布Streaming Ledger…

The Life Cycle of a Servlet
为什么80%的码农都做不了架构师?>>> Servlet的生命周期由Servlet容器管理,包含如下几个步骤: 1. 装载Servlet类; 2. 创建Servlet的实例; 3. 调用Servlet的init()方法; 4. 调用Servlet的service()方法; 5. 调用Servlet的destroy()…

矩阵奇异值分解
转自:http://www.madio.net/forum-redirect-goto-nextnewset-tid-47409.html 奇异值分解是线性代数中一种重要的矩阵分解,在信号处理、统计学等领域有重要应用。定义:设A为m*n阶矩阵,AHA的n个特征值的非负平方根叫作A的奇异值。记…

智课雅思词汇---十、pend是什么意思
智课雅思词汇---十、pend是什么意思 一、总结 一句话总结:【词根含义】:悬挂,垂;称量;支付 词根:-pend-, -pens- 【词根含义】:悬挂,垂;称量;支付 【词根来源】:来源于拉丁语动词pendeo, pendere, pependi, - (悬挂,下…

新技术“红”不过十年,半监督学习为什么是个例外?
作者 | 严林来源 | 授权转载自知乎(ID:严林)这一波深度学习的发展,以2006年Hinton发表Deep Belief Networks的论文为起点,到今年已经超过了10年。从过往学术界和产业界对新技术的追捧周期,超过10年的是极少数。从深度学…

常用Linux路由命令(route、ip、ifconfig等等)
第一组命令: ifconfig, ifup, ifdown 1) ifconfig 作用:手动启动、观察与修改网络接口的相关参数,包括IP地址以及MTU大小等。 例1.1:暂时修改IP地址 # ifconfig eth0 192.168.100.100 例1.2:修改IP地址、掩码和MTU # i…

洛谷P1074 靶形数独(跳舞链)
传送门 坑着,等联赛之后再填(联赛挂了就不填了233) 1 //minamoto2 #include<iostream>3 #include<cstdio>4 #include<cstring>5 using namespace std;6 #define getc() (p1p2&&(p2(p1buf)fread(buf,1,1<<21,…

直播写代码|英伟达工程师亲授如何加速YOLO目标检测
NVIDIA TensorRT是一种高性能深度学习推理优化器和运行时加速库,可以为深度学习推理应用程序提供低延时和高吞吐量。通过TensorRT,开发者可以优化神经网络模型,以高精度校对低精度,最后将模型部署到超大规模数据中心、嵌入式平台或…

OpenCV的cvLoadImage函数
转自:http://lijian2005lj.blog.163.com/blog/static/2569113720091111104856644/ 一直不太懂得cvLoadImage的第二个参数,今天知道,原来第二个参数是指定读入图像的颜色和深度。 指定的颜色可以将输入的图片转为3信道(CV_LOAD_IMAGE_COLOR)也…

DX11 preprocessor Dynamic shader linkage
(参照例子DXSDK sample:DynamicShaderLinkage11) 一、preprocessor 实现shader静态分支的经典方法,代码示例如下 shader中(如果显卡不支持DX11,则STATIC_PERMUTE为True): #if !defined( STATIC_PERMUTE )iB…

OpenCV中与matlab中相对应的函数
1、matlab中的imread相当于OpenCV中的cvLoadImage(imageName, CV_LOAD_IAMGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR):读出的图像信息保持了原有图像的信息(包括通道信息和位深信息); rgb2gray相当于cvLoadImage(imageName, CV_LOAD_IMAGE_GRAYSCALE)&…

AI假新闻满天飞,打假神器GROVER帮你看清一切
最近AI换脸术与AI假新闻叠加在一起,造成了不少乌龙事件,比如最近美国的议长南希佩洛西就的一段醉酒视频就在Facebook上流传甚广,视频中的议长明显是状态晕沉,醉意十足,不过这后来被证明是一段是由deepfake生成的假视频…

NYOJ 93
汉诺塔(三) 时间限制:3000 ms | 内存限制:65535 KB难度:3描述在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝…

C/C++中二维数组作函数形参时,调用函数时,可传递的实参类型的小结
转自:http://blog.163.com/tianhityeah/blog/static/165747821201052195212719/ #include<iostream>using namespace std;int fun(int a[][3],int n) // 其中二维数组形参必须确定数组的第二维的长度,第一维长度可以不定//int fun(int (*a)[…

打破欧美垄断,国防科大斩获“航天界奥林匹克”大赛首冠
整理 | Jane责编 | 一一出品 | AI科技大本营(id:rgznai100)近日,第十届国际空间轨道设计大赛(GTOC X)结束并公布最终成绩,中国参赛队国防科技大学与西安卫星测控中心联队(NUDT&X…

Hive 中的变量
Hive的变量前面有一个命名空间,包括三个hiveconf,system,env,还有一个hivevar hiveconf的命名空间指的是hive-site.xml下面的配置变量值。system的命名空间是系统的变量,包括JVM的运行环境。env的命名空间,…

你必须非常努力,才能看起来毫不费力
有一群人,他们积极自律,每天按计划行事,有条不紊;他们不张扬,把自己当成最卑微的小草,等待着人生开出花朵的那天。他们早晨5点多起来健身,你在睡觉;7点开始享受丰盛的早餐࿰…

cvGetSubRect与cvMul用法
1、对于cvGetSubRect(mat1, mat2, rect),当用cvGetSubRect函数时,不能事先对mat2申请内存,否则会产生内存泄漏。 只要这样定义mat2即可:CvMat *mat2; mat2 cvCreateMatHeader(imgHeight, imgWidth, CV_64FC1); 2、对于cvGetSubR…

浅谈WPF的VisualBrush
原文:浅谈WPF的VisualBrush首先看看VisualBrush的解释,msdn上面的解释是使用 Visual 绘制区域,那么我们再来看看什么是Visual呢?官方的解释是:获取或设置画笔的内容,Visual 是直接继承自DependencyObject,U…

AI换脸技术再创新高度,DeepMind发布的VQ-VAE二代算法有多厉害?
作者 | beyondma转载自CSDN网站近日DeepMind发布VQ-VAE-2算法,也就是之前VQ-VAE算法2代,这个算法从感观效果上来看比生成对抗神经网络(GAN)的来得更加真实,堪称AI换脸界的大杀器,如果我不说,相信读者也很难…

cisco设备常用命令
router> enable 从用户模式进入特权模式 router# disable or exit 从特权模式退出到用户模式router# show sessions 查看本机上的TELNET会话router# disconnect …

opencv图像处理梯度边缘和角点
转自:http://blog.sina.com.cn/s/blog_4b9b714a0100c9f7.html 梯度、边缘和角点 Sobel 使用扩展 Sobel 算子计算一阶、二阶、三阶或混合图像差分 void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size3 ); src 输入图像. dst …