Machine Learning In Action 第二章学习笔记: kNN算法
本文主要记录《Machine Learning In Action》中第二章的内容。书中以两个具体实例来介绍kNN(k nearest neighbors),分别是:
- 约会对象预测
- 手写数字识别
通过“约会对象”功能,基本能够了解到kNN算法的工作原理。“手写数字识别”与“约会对象预测”使用完全一样的算法代码,仅仅是数据集有变化。
约会对象预测
1 约会对象预测功能需求
主人公“张三”喜欢结交新朋友。“系统A”上面注册了很多类似于“张三”的用户,大家都想结交心朋友。“张三”最开始通过自己筛选的方式在“系统A”上面挑选感觉不错的人,然后约出来吃饭,但结果不总是如“张三”所愿,他自己筛选出来的对象中,有些是真的跟他志趣相投,而有些则完全跟他不是一路人。“张三”希望“系统A”能自动给他推荐一些跟他志趣相投的朋友,提高他能约到“志趣相投”的朋友的概率。
2 分析需求
系统不能凭空为“张三”推荐朋友,必须拿一些“已有东西”作为依据和参考。这个“已有”的东西就是“张三”约会的历史纪录。
每次约会的对象,使用三个属性来标记:
- 每年的飞行里程数量
- 每周打游戏所花的时间
- 每周能吃多少冰激凌
相当于使用这三个属性,代表一个人。不同的人,三个属性值各不相同。使用向量[feature1, feature2, feature3]来表示一个约会对象。约会的结果,有三种可能:不满意,还可以,很满意。使用class来表示约会结果。这样一来,每条历史约会纪录可以表示为向量[feature1, feature2, feature3, class],其中:
- feature1:每天飞行里程数
- feature2:每周打游戏所花时间
- feature3:每周能吃多少冰激凌
- class:约会结果
目前为止,“张三”有很多条类似于[feature1, feature2, feature3, class]这样的约会纪录,系统要实现的功能就是:对于一个“张三”没有约会过的对象[feature1, feature2, feature3],结合张三的历史约会纪录[feature1, feature2, feature3, class],系统预测一个约会结果,如果预测结果是“很满意”,就可以将这个“陌生人”推荐给“张三”。
明确了需求,就可以使用machine learning的大致套路来实现。
3 收集数据
拿到“张三”的历史约会数据[feature1, feature2, feature3, class]。《Machine Learning In Action》的作者已经为我们准备好了数据,git地址:
https://github.com/pbharrin/machinelearninginaction/tree/master/Ch02
datingTestSet.txt和datingTestSet2.txt就是数据文件。主要区别是,两个数据文件中,对约会结果的表示形式不同。datingTestSet.txt中使用字符串,datingTestSet2.txt中使用数字,本质上是一样的。例如在datingTestSet2.txt中,数据为:
- 9868 2.694977 0.432818 2
- 18333 3.951256 0.333300 2
- 3780 9.856183 0.329181 2
- 18190 2.068962 0.429927 2
- 11145 3.410627 0.631838 2
- 68846 9.974715 0.669787 1
- 26575 10.650102 0.866627 3
- 48111 9.134528 0.728045 3
- 43757 7.882601 1.332446 3
- 第一列:年飞行里程数
- 第二列:玩游戏的时间
- 第三列:冰激凌的数量?
- 第四列:约会结果(1:不满意 2:还可以 3:很满意)
4 数据准备
有了数据,需要把数据都到计算机程序里面,才能继续处理
将文件转换为程序需要的数据结构:
- def file2matrix(filename):
- fr = open(filename)
- numberOfLines = len(fr.readlines())
- returnMat = zeros((numberOfLines, 3))
- classLabelVector = []
- fr = open(filename)
- index = 0
- for line in fr.readlines():
- line = line.strip()
- listFromLine = line.split('\t')
- returnMat[index, :] = listFromLine[0:3]
- classLabelVector.append(int(listFromLine[-1]))
- index += 1
- return returnMat, classLabelVector
里面使用了numpy的Array来保存数据
5预处理数据
文件的前三列,每列数据对应一个属性,而属性的取值范围各不相同,这将导致后面计算“距离”的时候,取值范围大的属性,对结果影响度较大。假设所有属性的重要性是相同的,因此需要对属性进行归一化处理。代码如下:
- def autoNorm(dataSet):
- 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))
- return normDataSet, ranges, minVals
书中没有讲到当各个属性的重要性不同时,该如果处理,感觉可以在这步,返回normDataSet之前,乘以一个系数来代表影响因子。
6 分析数据
这步主要通过pyplot将数据画出来,通过图形,可以对数据有一个直观的感觉,可以大致判断最开始选择的三个feature跟class之间是否有一定的规律性的联系。画图的代码:
- def plotDatingData():
- datingDataMat, datingLabels = file2matrix("datingTestSet2.txt")
- normMat, ranges, minVals = autoNorm(datingDataMat)
- datingDataMat = normMat
- fig = plt.figure()
- ax = fig.add_subplot(111)
- ax.scatter(datingDataMat[:, 0], datingDataMat[:, 1], 15.0*array(datingLabels), 10.0*array(datingLabels))
- plt.xlabel("Flyier Miles Earned Per Year")
- plt.ylabel("Time Spend Playing Video Games")
- plt.title("Dating History")
- plt.legend()
- plt.show()
效果图:
上面代码只使用了“飞行里程数”和“打游戏的时间”两个feature。其中:
- 红色:很满意
- 绿色:还可以
- 蓝色:不满意
从图中可以看出,3个结果都有一个大致的分配区域,说明可以使用这两个feature来做预测。
7 kNN算法
对于给定的一个向量,与已有数据集中的所有向量计算“向量距离”,距离越近,表示两个向量越相似。从已有数据集中,找出与给定向量距离最近的前k个向量。这前k个向量,每个都对应一个结果class,采用少数服从多数的方式,出现次数最多的class,就是预测的结果class。
代码如下:
- def classify0(inX, dataSet, labels, k):
- dataSetSize = dataSet.shape[0]
- diffMat = tile(inX, (dataSetSize, 1)) - dataSet
- sqDiffMat = diffMat**2
- sqDistances = sqDiffMat.sum(axis=1)
- distances = sqDistances**0.5
- sortedDistIndicies = distances.argsort()
- classCount = {}
- for i in range(k):
- voteIlabel = labels[sortedDistIndicies[i]]
- classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
- sortedClassCount = sorted(
- classCount.iteritems(),
- key=operator.itemgetter(1),
- reverse=True)
- return sortedClassCount[0][0]
8 算法测试
上面使用的“约会历史数据”,相当于训练数据。基于训练数据集,产生了上面的预测方法。检测这个预测方法到底好不好,可以使用“历史数据”中的前10%作为测试数据集,只使用后90%作为训练数据集。用测试数据集中的数据,在上面的预测方法中跑出结果后,跟实际的结果进行比较,如果一样,就说明预测对了,否则,说明预测错误。最后可以得出一个错误率,错误率越低,说明预测方法越好。代码如下:
- def datingClassTest():
- hoRatio = 0.10
- datingDataMat, datingLabels = file2matrix("datingTestSet2.txt")
- normMat, ranges, minVals = autoNorm(datingDataMat)
- m = normMat.shape[0]
- numTestVecs = int(m * hoRatio)
- errorCount = 0.0
- for i in range(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.0
- print "the total error rate is: %f" % (errorCount / float(numTestVecs))
9 实际运用
到现在为止,就可以使用上面完成的预测方法进行“约会对象预测”了。代码如下:
- def classifyPerson():
- resultMap = {
- 1: 'not at all',
- 2: 'in small doses',
- 3: 'in large doses'
- }
- flierMiles = float(raw_input("flier miles earned per year?"))
- playGameTime = float(raw_input("time spent playing video games?"))
- iceCream = float(raw_input("liters of ice cream consumed per year?"))
- datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
- normMat, ranges, minVals = autoNorm(datingDataMat)
- inAttr = array([flierMiles, playGameTime, iceCream])
- classifierResult = classify0((inAttr - minVals) / ranges, normMat, datingLabels, 3)
- print "You will probably like this person: ", resultMap[classifierResult]
手写数字识别
这个例子与“约会对象预测”本质上是一样的,仅仅是数据集不同。数据集位于zip打包文件中,git地址:
https://github.com/pbharrin/machinelearninginaction/tree/master/Ch02
1 将单个文件转成向量
代码:
- def img2vector(filename):
- returnVect = zeros((1, 1024))
- fr = open(filename)
- for i in range(32):
- lineStr = fr.readline()
- for j in range(32):
- returnVect[0, i*32 + j] = int(lineStr[j])
- return returnVect
2 算法测试
代码:
- def handwritingClassTest():
- hwLabels = []
- trainingFileList = os.listdir("trainingDigits")
- m = len(trainingFileList)
- trainingMat = zeros((m, 1024))
- for i in range(m):
- fileNameStr = trainingFileList[i]
- fileStr = fileNameStr.split(".")[0]
- classNumStr = int(fileStr.split('_')[0])
- hwLabels.append(classNumStr)
- trainingMat[i, :] = img2vector(os.path.join("trainingDigits", fileNameStr))
- testFileList = os.listdir("testDigits")
- errorCount = 0.0
- mTest = len(testFileList)
- for i in range(mTest):
- fileNameStr = testFileList[i]
- fileStr = fileNameStr.split(".")[0]
- classNumStr = int(fileStr.split("_")[0])
- vectorUnderTest = img2vector(os.path.join("testDigits", testFileList[i]))
- classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
- print "the classifier came back with: %d, the real anwser is: %d" % (classifierResult, classNumStr)
- if (classifierResult != classNumStr):
- errorCount += 1.0
- print "\nthe total number of errors is: %d" % errorCount
- print "\nthe total error rate is: %f" % (errorCount / float(mTest))
总结
1 优点
- 原理简单,很好理解。
- 从以上两个例子的运行结果来看,错误率很低,说明此方法很实用
2 缺点
- 将所有数据加载到内存数据结构中,数据量很大的时候,是否合适?
- 单次预测的时候,需要对每条数据向量计算一次距离,然后挑选出前k个,计算量太大
- 没有深入到数据内部,没有利用数据的实际含义。KNN不不关心每个feature代表什么含义,对它而言,都是归一化后的数据
其他问题
预测实用的特征feature该如何选取,靠经验,还是想象力?在现实的系统中是如何选feature的?希望有经验的读者能不吝解惑。
Machine Learning In Action 第二章学习笔记: kNN算法的更多相关文章
- AS开发实战第二章学习笔记——其他
第二章学习笔记(1.19-1.22)像素Android支持的像素单位主要有px(像素).in(英寸).mm(毫米).pt(磅,1/72英寸).dp(与设备无关的显示单位).dip(就是dp).sp(用 ...
- #Spring实战第二章学习笔记————装配Bean
Spring实战第二章学习笔记----装配Bean 创建应用对象之间协作关系的行为通常称为装配(wiring).这也是依赖注入(DI)的本质. Spring配置的可选方案 当描述bean如何被装配时, ...
- Python核心编程第三版第二章学习笔记
第二章 网络编程 1.学习笔记 2.课后习题 答案是按照自己理解和查阅资料来的,不保证正确性.如由错误欢迎指出,谢谢 1. 套接字:A network socket is an endpoint of ...
- Day2 《机器学习》第二章学习笔记
这一章应该算是比价了理论的一章,我有些概率论基础,不过起初有些地方还是没看多大懂.其中有些公式的定义和模型误差的推导应该还是很眼熟的,就是之前在概率论课上提过的,不过有些模糊了,当时课上学得比较浅. ...
- 《Linux内核设计与实现》课本第一章&第二章学习笔记
<Linux内核设计与实现>课本学习笔记 By20135203齐岳 一.Linux内核简介 Unix内核的特点 Unix很简洁,所提供的系统调用都有很明确的设计目的. Unix中一切皆文件 ...
- Linux第一章第二章学习笔记
第一章 Linux内核简介 1.1 Unix的历史 它是现存操作系统中最强大最优秀的系统. 设计简洁,在发布时提供原代码. 所有东西都被当做文件对待. Unix的内核和其他相关软件是用C语言编写而成的 ...
- Thinking in Java 第二章学习笔记
Java虽基于C++,但相比之下,Java是一种更加纯粹的面向对象程序设计语言. 在Java的世界里,几乎一切都是对象,而Java中的全部工作则是定义类,产生那些类的对象,以及发送消息给这些对象. 尽 ...
- Hands on Machine Learning with Sklearn and TensorFlow学习笔记——机器学习概览
一.什么是机器学习? 计算机程序利用经验E(训练数据)学习任务T(要做什么,即目标),性能是P(性能指标),如果针对任务T的性能P随着经验E不断增长,成为机器学习.[这是汤姆米切尔在1997年定义] ...
- Scala第二章学习笔记
最基本的练习~: 使用伴生对象: object holder{ class Foo{ private var x = 5} object Foo{def im_in_yr_foo(f: Foo) = ...
随机推荐
- 【XSY2131】【BZOJ1857】【SCOI2010】传送带
Description 题目描述: 在一个二维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.小y在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动 ...
- MIT线性代数:12.图和网络
- NOIP模拟 22
剧情回放:xuefeng:考场上你们只打暴力不打正解,我不满意! skyh:考场怒切T2以表明自己拥护xuefeng的决心 BoboTeacher:这场考试就没想让你们上100 神犇skyh:(笑而不 ...
- Apache服务及个人用户主页功能和密码验证
Apache服务程序中有个默认未开启的个人用户主页功能,能够为所有系统内的用户生成个人网站,确实很实用哦 第1步:开启个人用户主页功能: 1.vim /etc/httpd/conf.d/userdir ...
- 爬虫学习--Urllib库基本使用 Day1
一.Urllib库详解 1.什么是Urllib Python内置的HTTP请求库 urllib.request 请求模块(模拟实现传入网址访问) urllib.error ...
- Python实现王者荣耀小助手(二)
接下来我们获取英雄和武器信息,详细代码KingGlory.py如下(代码中有详细注解): # -*- coding: utf-8 -*- #!/usr/bin/env python # @Time : ...
- java多线程与线程并发二:线程互斥
本文章内容整理自:张孝祥_Java多线程与并发库高级应用视频教程 当两条线程访问同一个资源时,可能会出现安全隐患.以打印字符串为例,先看下面的代码: // public class Test2 { p ...
- 50行Python代码实现视频中物体颜色识别和跟踪(必须以红色为例)
目前计算机视觉(CV)与自然语言处理(NLP)及语音识别并列为人工智能三大热点方向,而计算机视觉中的对象检测(objectdetection)应用非常广泛,比如自动驾驶.视频监控.工业质检.医疗诊断等 ...
- Intellij IDEA搭建JSP+Tomcat开发环境
1.新建项目 然后填入项目名称和选择项目路径,填完点击完成. 2.添加WEB框架 别问我为什么不一开始就直接新建WEB框架,因为我也是看的别人的教程0.0 不过还遇到了一些新问题,后面会讲到 3.配置 ...
- Salesforce学习之路(十三)Aura案例实战分析
Aura相关知识整合: Salesforce学习之路(十)Aura组件工作原理 Salesforce学习之路(十一)Aura组件属性<aura:attribute /> Salesforc ...