【机器学习实战】第2章 K-近邻算法(k-NearestNeighbor,KNN)

第2章 k-近邻算法

http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=default">>

KNN 概述

k-近邻(kNN, k-NearestNeighbor)算法主要是用来进行分类的.

KNN 场景

电影可以按照题材分类,那么如何区分动作片和爱情片呢?

动作片:打斗次数更多

爱情片:亲吻次数更多

基于电影中的亲吻、打斗出现的次数,使用 k-近邻算法构造程序,就可以自动划分电影的题材类型。

现在根据上面我们得到的样本集中所有电影与未知电影的距离,按照距离递增排序,可以找到 k 个距离最近的电影。

假定 k=3,则三个最靠近的电影依次是, He's Not Really into Dudes 、 Beautiful Woman 和 California Man。

knn 算法按照距离最近的三部电影的类型,决定未知电影的类型,而这三部电影全是爱情片,因此我们判定未知电影是爱情片。

KNN 原理

KNN 工作原理

假设有一个带有标签的样本数据集(训练样本集),其中包含每条数据与所属分类的对应关系。

输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较。

计算新数据与样本数据集中每条数据的距离。

对求得的所有距离进行排序(从小到大,越小表示越相似)。

取前 k (k 一般小于等于 20 )个样本数据对应的分类标签。

求 k 个数据中出现次数最多的分类标签作为新数据的分类。

KNN 开发流程

收集数据:任何方法

准备数据:距离计算所需要的数值,最好是结构化的数据格式

分析数据:任何方法

训练算法:此步骤不适用于 k-近邻算法

测试算法:计算错误率

使用算法:输入样本数据和结构化的输出结果,然后运行 k-近邻算法判断输入数据分类属于哪个分类,最后对计算出的分类执行后续处理

KNN 算法特点

优点:精度高、对异常值不敏感、无数据输入假定

缺点:计算复杂度高、空间复杂度高

适用数据范围:数值型和标称型

KNN 项目案例

项目案例1: 优化约会网站的配对效果

项目概述

海伦使用约会网站寻找约会对象。经过一段时间之后,她发现曾交往过三种类型的人:

不喜欢的人

魅力一般的人

极具魅力的人

她希望:

工作日与魅力一般的人约会

周末与极具魅力的人约会

不喜欢的人则直接排除掉

现在她收集到了一些约会网站未曾记录的数据信息,这更有助于匹配对象的归类。

开发流程

收集数据:提供文本文件

准备数据:使用 Python 解析文本文件

分析数据:使用 Matplotlib 画二维散点图

训练算法:此步骤不适用于 k-近邻算法

测试算法:使用海伦提供的部分数据作为测试样本。

测试样本和非测试样本的区别在于:

测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。

使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。

收集数据:提供文本文件

海伦把这些约会对象的数据存放在文本文件datingTestSet2.txt中,总共有 1000 行。海伦约会的对象主要包含以下 3 种特征:

每年获得的飞行常客里程数

玩视频游戏所耗时间百分比

每周消费的冰淇淋公升数

文本文件数据格式如下:

40920 8.326976 0.953952 3

14488 7.153469 1.673904 2

26052 1.441871 0.805124 1

75136 13.147394 0.428964 1

38344 1.669788 0.134296 1

准备数据:使用 Python 解析文本文件

将文本记录转换为 NumPy 的解析程序

deffile2matrix(filename):"""Desc:导入训练数据parameters:filename: 数据文件路径return:数据矩阵 returnMat 和对应的类别 classLabelVector"""fr=open(filename)#获得文件中的数据行的行数numberOfLines=len(fr.readlines())#生成对应的空矩阵#例如:zeros(2,3)就是生成一个 2*3的矩阵,各个位置上全是 0returnMat=zeros((numberOfLines,3))#prepare matrix to returnclassLabelVector=[]#prepare labels returnfr=open(filename)  index=0forlineinfr.readlines():#str.strip([chars]) --返回移除字符串头尾指定的字符生成的新字符串line=line.strip()#以 '\t' 切割字符串listFromLine=line.split('\t')#每列的属性数据returnMat[index, :]=listFromLine[0:3]#每列的类别数据,就是 label 标签数据classLabelVector.append(int(listFromLine[-1]))      index+=1#返回数据矩阵returnMat和对应的类别classLabelVectorreturnreturnMat, classLabelVector

分析数据:使用 Matplotlib 画二维散点图

importmatplotlibimportmatplotlib.pyplotaspltfig=plt.figure()ax=fig.add_subplot(111)ax.scatter(datingDataMat[:,1], datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))plt.show()

下图中采用矩阵的第一和第三列属性得到很好的展示效果,清晰地标识了三个不同的样本分类区域,具有不同爱好的人其类别区域也不同。

归一化数据 (归一化是一个让权重变为统一的过程,更多细节请参考:https://www.zhihu.com/question/19951858)

序号玩视频游戏所耗时间百分比每年获得的飞行常客里程数每周消费的冰淇淋公升数样本分类

10.84000.51

212134 0000.93

3020 0001.12

46732 0000.12

样本3和样本4的距离: $$\sqrt{(0-67)^2 + (20000-32000)^2 + (1.1-0.1)^2 }$$

归一化特征值,消除特征之间量级不同导致的影响

defautoNorm(dataSet):"""Desc:归一化特征值,消除特征之间量级不同导致的影响parameter:dataSet: 数据集return:归一化后的数据集 normDataSet. ranges和minVals即最小值与范围,并没有用到归一化公式:Y = (X-Xmin)/(Xmax-Xmin)其中的 min 和 max 分别是数据集中的最小特征值和最大特征值。该函数可以自动将数字特征值转化为0到1的区间。"""#计算每种属性的最大值、最小值、范围minVals=dataSet.min(0)    maxVals=dataSet.max(0)#极差ranges=maxVals-minVals    normDataSet=zeros(shape(dataSet))    m=dataSet.shape[0]#生成与最小值之差组成的矩阵normDataSet=dataSet-tile(minVals, (m,1))#将最小值之差除以范围组成矩阵normDataSet=normDataSet/tile(ranges, (m,1))#element wise dividereturnnormDataSet, ranges, minVals

训练算法:此步骤不适用于 k-近邻算法

因为测试数据每一次都要与全量的训练数据进行比较,所以这个过程是没有必要的。

测试算法:使用海伦提供的部分数据作为测试样本。如果预测分类与实际类别不同,则标记为一个错误。

kNN 分类器针对约会网站的测试代码

defdatingClassTest():"""Desc:对约会网站的测试方法parameters:nonereturn:错误数"""#设置测试数据的的一个比例(训练数据集比例=1-hoRatio)hoRatio=0.1#测试范围,一部分测试一部分作为样本#从文件中加载数据datingDataMat, datingLabels=file2matrix('input/2.KNN/datingTestSet2.txt')#load data setfrom file#归一化数据normMat, ranges, minVals=autoNorm(datingDataMat)#m 表示数据的行数,即矩阵的第一维m=normMat.shape[0]#设置测试的样本数量, numTestVecs:m表示训练样本的数量numTestVecs=int(m*hoRatio)print'numTestVecs=', numTestVecs    errorCount=0.0foriinrange(numTestVecs):#对数据测试classifierResult=classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m],3)print"the classifier came back with:%d, the real answer is:%d"%(classifierResult, datingLabels[i])if(classifierResult!=datingLabels[i]): errorCount+=1.0print"the total error rate is:%f"%(errorCount/float(numTestVecs))printerrorCount

使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。

约会网站预测函数

defclasdifyPerson():    resultList=['not at all','in small doses','in large doses']    percentTats=float(raw_input("percentage of time spent playing video games ?"))    ffMiles=float(raw_input("frequent filer miles earned per year?"))    iceCream=float(raw_input("liters of ice cream consumed per year?"))    datingDataMat, datingLabels=file2matrix('datingTestSet2.txt')    normMat, ranges, minVals=autoNorm(datingDataMat)    inArr=array([ffMils, percentTats, iceCream])    classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3)print"You will probably like this person:", resultList[classifierResult-1]

实际运行效果如下:

>>>kNN.classifyPerson()percentage of time spent playing video games?10frequent flier miles earned per year?10000liters of ice cream consumed per year?0.5You will probably like this person:insmall doses

完整代码地址:https://github.com/apachecn/MachineLearning/blob/master/src/python/2.KNN/kNN.py

项目案例2: 手写数字识别系统

项目概述

构造一个能识别数字 0 到 9 的基于 KNN 分类器的手写数字识别系统。

需要识别的数字是存储在文本文件中的具有相同的色彩和大小:宽高是 32 像素 * 32 像素的黑白图像。

开发流程

收集数据:提供文本文件。

准备数据:编写函数 img2vector(), 将图像格式转换为分类器使用的向量格式

分析数据:在 Python 命令提示符中检查数据,确保它符合要求

训练算法:此步骤不适用于 KNN

测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的

区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,

则标记为一个错误

使用算法:本例没有完成此步骤,若你感兴趣可以构建完整的应用程序,从图像中提取

数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统

收集数据: 提供文本文件

目录trainingDigits中包含了大约 2000 个例子,每个例子内容如下图所示,每个数字大约有 200 个样本;目录testDigits中包含了大约 900 个测试数据。

准备数据: 编写函数 img2vector(), 将图像文本数据转换为分类器使用的向量

将图像文本数据转换为向量

defimg2vector(filename):    returnVect=zeros((1,1024))    fr=open(filename)foriinrange(32):        lineStr=fr.readLine()forjinrange(32):            returnVect[0,32*i+j]=int(lineStr[j])returnreturnVect

分析数据:在 Python 命令提示符中检查数据,确保它符合要求

在 Python 命令行中输入下列命令测试 img2vector 函数,然后与文本编辑器打开的文件进行比较:

>>>testVector=kNN.img2vector('testDigits/0_13.txt')>>>testVector[0,0:31]array([0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,1.,1.,1.,1.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.])>>>testVector[0,31:63]array([0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,1.,1.,1.,1.,1.,1.,1.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.])

训练算法:此步骤不适用于 KNN

因为测试数据每一次都要与全量的训练数据进行比较,所以这个过程是没有必要的。

测试算法:编写函数使用提供的部分数据集作为测试样本,如果预测分类与实际类别不同,则标记为一个错误

defhandwritingClassTest():#1. 导入训练数据hwLabels=[]    trainingFileList=listdir('input/2.KNN/trainingDigits')#load the training setm=len(trainingFileList)    trainingMat=zeros((m,1024))#hwLabels存储0~9对应的index位置, trainingMat存放的每个位置对应的图片向量foriinrange(m):        fileNameStr=trainingFileList[i]        fileStr=fileNameStr.split('.')[0]#take off .txtclassNumStr=int(fileStr.split('_')[0])        hwLabels.append(classNumStr)#将 32*32的矩阵->1*1024的矩阵trainingMat[i, :]=img2vector('input/2.KNN/trainingDigits/%s'%fileNameStr)#2. 导入测试数据testFileList=listdir('input/2.KNN/testDigits')#iterate through the test seterrorCount=0.0mTest=len(testFileList)foriinrange(mTest):        fileNameStr=testFileList[i]        fileStr=fileNameStr.split('.')[0]#take off .txtclassNumStr=int(fileStr.split('_')[0])        vectorUnderTest=img2vector('input/2.KNN/testDigits/%s'%fileNameStr)        classifierResult=classify0(vectorUnderTest, trainingMat, hwLabels,3)print"the classifier came back with:%d, the real answer is:%d"%(classifierResult, classNumStr)if(classifierResult!=classNumStr): errorCount+=1.0print"\nthe total number of errors is:%d"%errorCountprint"\nthe total error rate is:%f"%(errorCount/float(mTest))

使用算法:本例没有完成此步骤,若你感兴趣可以构建完整的应用程序,从图像中提取数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统

完整代码地址:https://github.com/apachecn/MachineLearning/blob/master/src/python/2.KNN/kNN.py

作者:羊三小瑶

GitHub地址:https://github.com/apachecn/MachineLearning

版权声明:欢迎转载学习 => 请标注信息来源于ApacheCN

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容