机器学习实战(第2章 k-近邻算法)
来源:互联网 发布:php抓取网页指定内容 编辑:程序博客网 时间:2024/06/08 17:18
机器学习实战(第2章 k-近邻算法)
(代码下载地址:http://download.csdn.net/download/jichun4686/9910040)
目录
简介:
K最近邻(k-Nearest Neighbor,KNN)分类算法,是最简单的机器学习算法之一。即是给定一个训练数据集,对于输入的未知标签的实例,在训练数据集中找到与该实例最邻近的K个实例,则将其分类到K个有大多数的相同标签的标签中。如在下边实线圈中绿色实例属于标签为红色的一类,而在虚线中其属于标签为蓝色的一类,这就要看K的取值大小。一般K<=20,且K为正整数。
k-近邻算法的一般流程:
2.1 基本k-近邻算法
代码1:
# -*- coding: utf-8 -*- #中文注释记得加这句话from numpy import * #导入numpy包 用import numpy 也可以,但是之后的使用其中的函数时前边必须加上numpy.方法名from operator import *# 创建数据集def createdataset(): group = array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]]) #创建固定数据集 labels = ['A', 'A', 'B', 'B'] #创建固定数据集中数据对应标签 return group, labels #返回数据集(数组)和标签(列表)# k-近邻算法""""inX: 用于分类的输入向量dataSet:训练样本集labels: 标签向量k: 选择的最近邻居数目"""def classify0(inX, dataSet, labels, k): #计算距离 dataSetSize = dataSet.shape[0] #dataSetSize为dataSet的行数 diff = tile(inX,(dataSetSize,1)) - dataSet #扩展输入向量,方便计算每一个数据到该向量的距离 sqDiff = diff ** 2 #数组每个元素进行平方 sqDistances = sqDiff.sum(axis=1)#求该数组的行和 distances = sqDistances ** 0.5 #为每行元素进行开方 即求出了每个数据和输入数据的距离 sortedDistIndicies = distances.argsort() #为该距离数组进行升序排序 返回排序结果的下标值 #确定k个点并计算其频率 classCount = {} #声明一个统计K个数据中类别与相应个数的字典 for i in range(k): #i从0到k-1进行循环 votelabel = labels[sortedDistIndicies[i]] #找到i对应下标的类别 classCount[votelabel] = classCount.get(votelabel,0)+1 #该类别如果在字典中存在,则取出其值后加1 如果不存在取默认值再+1 sortedClassCount = sorted(classCount.items(),key=itemgetter(1),reverse=True)#将字典返回为元祖列表 并依据其第二个元素进行降序排序 return sortedClassCount[0][0] #返回频率最高元祖的第一个量即标签
输入
group,labels = createdataset() #得到数据集和标签向量classify0([0.5,0.5],group,labels,3) #测试[0,0]向量的分类
测试结果1:
此处我做了一些改动,将测试数据变为[0.5,0.5],因为书中提及,要测试的数据不能为分类器所知,但是[0,0]作为训练集中的样本数据已经提供给分类器,所以此处选取[0.5,0.5]。关于测试数据时,可以选取提前知道结果的数据进行输入,可以得到该分类器的错误率,进而可以对该分类器进行评估。
2.2示例:使用k-近邻算法改进约会网站的配对效果
2.2.1将文本记录转换为Numpy的解析程序
代码2:
创建文件DatingNet.py,程序如下。
from numpy import *#程序1:将文本记录转换为Numpy的解析程序#输入:文件名字符串#输出:训练样本矩阵和类标签向量def file2matrix(filename): fr = open(filename) arrayOfLines = fr.readlines()#得到一个元素为文件每一行的列表 numberOfLines = len(arrayOfLines)#得到文件的行数 numberOfCols = len(arrayOfLines[0].strip().split('\t')) #得到每一行的列数 returnMat = zeros((numberOfLines,numberOfCols-1)) #返回的数据集 classLabelVector = [] #返回的标签向量 index = 0 #为了给返回的数据集方便赋值的自增变量 for line in arrayOfLines: listFromLine = line.strip().split('\t') #将每一行去掉换行符并且以\t为分隔符分隔为列表 returnMat[index,:] = listFromLine[0:3] #将列表中的0、1、2号元素赋给returnMat的第一行 classLabelVector.append(int(listFromLine[-1])) #-1提取列表中的最后一个元素并将其标为int变量 必须明确的通知解释器 否则python语言会将这些元素做字符串处理 index += 1 return returnMat,classLabelVector
【ps】
1、书中我认为arrayOLines应该是arrayOfLines,故做修改,与书中不一样。
2、注意classLabelVector.append(int(listFromLine[-1])) 这一行代码不要少了int,必须明确告诉解释器,否则会作为字符串处理的。
3、另外列表取值时用[],而不是小括号,否则会产生TypeError: ‘list’ object is not callable的错误,但是百度还百度不到。
建立测试文件test2.py
import DatingNetdatingDataMat,datingLabels = DatingNet.file2matrix('datingTestSet2.txt')print(datingDataMat)print(datingLabels[0:20])
【ps】
1、书中输入的文件名为datingTestSet.txt,但是此处应输入datingTestSet2.txt.
2、书中得到的datingDataMat与文本文件中的也是不同。
运行结果2
2.2.2分析数据:使用Matplotlib创建散点图
建立figure_Matplotlib.py。
共绘制五幅图
图一:同书中,是第二三列数据所作图。但是没有色彩或者记号。
图二:同书中,是第二三列数据所作图。有色彩和记号。但是观测不出来数据间的关系。
图三:同书中,是一二列数据所作图。有色彩和记号,可以看出明显分类。
图四:增加,是一三列数据所作图。对比可知其分类也不是很明显。
图五:增加,是三维图,可以进行拖动、旋转,方便得到分类关系,更加便于观察。
【ps】 该代码画三维图时,有一个警告,但是限于自身能力,没有查出来,自己对于画图这块是刚刚接触,接来搞懂了会进行补充的。
代码3:
from numpy import * #导入numpy包,这里要用到numpy中的arrayfrom DatingNet import * #导入产生数据的包import matplotlib #导入绘图的库import matplotlib.pyplot as plt #将绘图的函数重命名from mpl_toolkits.mplot3d import Axes3D #导入3维图像的包datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')fig1 = plt.figure() #创建图形#————————————————二维------------------------------------------#第一个子图 是第2个特征和第3个特征的散点图 但是没有颜色标识 ax = fig1.add_subplot(2,2,1)#代表创建1行1列从上到下的第一块的子图ax.scatter(datingDataMat[:,1],datingDataMat[:,2])plt.xlabel(u'x 玩视频游戏所耗时间半分比') plt.ylabel(u'y 每周消费的冰淇淋公升数')plt.title(u'图一(2&&3)')#定义三个类别的空列表type1_x = []type1_y = []type2_x = []type2_y = []type3_x = []type3_y = []#第二个子图 是第2个特征和第3个特征的散点图ax = fig1.add_subplot(2,2,2)#代表创建1行1列从上到下的第二块的子图#循环获得每个列表中的值for i in range(len(datingLabels)): if datingLabels[i] == 1: # 不喜欢 type1_x.append(datingDataMat[i][1]) type1_y.append(datingDataMat[i][2]) if datingLabels[i] == 2: # 魅力一般 type2_x.append(datingDataMat[i][1]) type2_y.append(datingDataMat[i][2]) if datingLabels[i] == 3: # 极具魅力 type3_x.append(datingDataMat[i][1]) type3_y.append(datingDataMat[i][2])type1 = ax.scatter(type1_x, type1_y, s=20, c='red')type2 = ax.scatter(type2_x, type2_y, s=40, c='green')type3 = ax.scatter(type3_x, type3_y, s=50, c='blue')ax.legend((type1, type2, type3), (u'不喜欢', u'魅力一般', u'极具魅力'), loc=2)#显示图例 1 右上 2左上 3左下 4 右下 逆时针#ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))plt.xlabel(u'x 玩视频游戏所耗时间半分比') plt.ylabel(u'y 每周消费的冰淇淋公升数')plt.title(u'图二(2&&3)')#第三个子图 是第1个特征和第2个特征的散点图ax = fig1.add_subplot(2,2,3)#代表创建1行1列从上到下的第三块的子图#循环获得每个列表中的值for i in range(len(datingLabels)): if datingLabels[i] == 1: # 不喜欢 type1_x.append(datingDataMat[i][0]) type1_y.append(datingDataMat[i][1]) if datingLabels[i] == 2: # 魅力一般 type2_x.append(datingDataMat[i][0]) type2_y.append(datingDataMat[i][1]) if datingLabels[i] == 3: # 极具魅力 type3_x.append(datingDataMat[i][0]) type3_y.append(datingDataMat[i][1])type1 = ax.scatter(type1_x, type1_y, s=20, c='red')type2 = ax.scatter(type2_x, type2_y, s=40, c='green')type3 = ax.scatter(type3_x, type3_y, s=50, c='blue')ax.legend((type1, type2, type3), (u'不喜欢', u'魅力一般', u'极具魅力'), loc=2)#显示图例 1 右上 2左上 3左下 4 右下 逆时针#ax.scatter(datingDataMat[:,0],datingDataMat[:,1],15.0*array(datingLabels),15.0*array(datingLabels))plt.xlabel(u'x 每年获取的飞行常客里程数') plt.ylabel(u'y 玩视频游戏所耗时间半分比')plt.title(u'图三(1&&2)')#第四个子图 是第1个特征和第3个特征的散点图ax = fig1.add_subplot(2,2,4)#代表创建1行1列从上到下的第四块的子图#循环获得每个列表中的值for i in range(len(datingLabels)): if datingLabels[i] == 1: # 不喜欢 type1_x.append(datingDataMat[i][0]) type1_y.append(datingDataMat[i][2]) if datingLabels[i] == 2: # 魅力一般 type2_x.append(datingDataMat[i][0]) type2_y.append(datingDataMat[i][2]) if datingLabels[i] == 3: # 极具魅力 type3_x.append(datingDataMat[i][0]) type3_y.append(datingDataMat[i][2])type1 = ax.scatter(type1_x, type1_y, s=20, c='red')type2 = ax.scatter(type2_x, type2_y, s=40, c='green')type3 = ax.scatter(type3_x, type3_y, s=50, c='blue')ax.legend((type1, type2, type3), (u'不喜欢', u'魅力一般', u'极具魅力'), loc=2)#显示图例 1 右上 2左上 3左下 4 右下 逆时针#ax.scatter(datingDataMat[:,0],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))plt.xlabel(u'x 每年获取的飞行常客里程数') plt.ylabel(u'y 每周消费的冰淇淋公升数')plt.title(u'图四(1&&3)')#————————————————三维#第二个图 是第1个特征和第2个特征和第3个特征的散点图fig2 = plt.figure() #创建图形ax = fig2.add_subplot(111)ax = Axes3D(fig2)ax.scatter(datingDataMat[:,0],datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels),15.0*array(datingLabels))ax.set_xlabel(u'x 每年获取的飞行常客里程数')ax.set_ylabel(u'y 玩视频游戏所耗时间半分比')ax.set_zlabel(u'z 每周消费的冰淇淋公升数')plt.title(u'图四(1&&2&&3)')plt.show()
运行结果3:
图一 – 图四
图五(初始)
图五(旋转后)
由上可见,图像化工具给我们展示数据内容,更加方便去辨识一些数据模型。
2.2.3 准备数据:归一化数值
归一化数值是为了不让某个特殊的属性对计算结果影响比较大,而应该每一个特征都是等权重的。
转换公式: newValue = (oldValue - min)/(max - min)
代码4:
import DatingNetdatingDataMat,datingLabels = DatingNet.file2matrix('datingTestSet2.txt')normMat,ranges,minVals = DatingNet.autoNorm(datingDataMat)print(normMat)print(ranges)print(minVals)
运行结果4:
2.2.4 测试算法:作为完整程序验证分类器
机器学习算法一个重要的工作就是评估算法的正确性,通常我们只提供90%的数据作为训练样本来训练分类器,而使用其余的10%进行测试分类器,以检测分类器的正确性。 在DatingNet.py中创建函数datingClassTest()函数,计算该分类器的错误率。 用到了DatingNet.py中的准备数据file2matrix()、归一化数值autoNorm()函数以及myKNN.py中的classify0()函数。
代码5:
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 for i in range(numTestVecs): classifierResult = myKNN.classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3) print('the classifier came with back with:%d,the real answer is:%d'% (classifierResult,datingLabels[i])) if(classifierResult != datingLabels[i]): errorCount += 1 #统计错误量 print('错误率是:%f'%(errorCount/float(numTestVecs))) #输出错误率
运行结果5:
运行datingNet.py,输入datingClassTest()得到结果如下(与书中不同,与网上多数网友结果相同):
分类器结果是5%,算是一个还不错的结果。所以基本上可以满足约会人对于某一对象的可交往程度的判定。
2.2.5 使用算法:构建完整可用系统
测试通过后,可以进行应用。在datingNet.py中建立函数classifyPerson()函数,通过在约会网站找到某个人并输入他的信息,程序给对对方喜欢程度的预测值。 因python之后就没有raw_input,而是用input代替,所以代码中与书中不同,如果环境为python3还用raw_input就会产生NameError: name ‘raw_input’ is not defined的错误。
代码6:
#约会网站预测函数def classifyPerson(): resultList = ['nont at all','in small doses','in large doses'] #用户输入的三个特征值 percentTats=float(input('percentage of time spent playing video games?')) ffMiles=float(input('frequent flier miles earned per year?')) iceCream=float(input('liters of ice cream consumed per year?')) inArr = array([ffMiles,percentTats,iceCream]) datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') normMat,ranges,minVals = autoNorm(datingDataMat) classifierResult = myKNN.classify0((inArr-minVals)/ranges,normMat,datingLabels,3) print('you will probably like this person:',resultList[classifierResult-1])
运行结果6:
2.3 实例2:手写识别系统
该系统只能识别数字0-9,图像转换成为了文本的若干文本数据。
2.3.1 准备数据:将图像转换为测试向量
建立文件hardWritingSystem.py,在其中写入img2vector()函数。
代码7:
#将一个图像文件转换为一维向量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,32*i+j] = int(lineStr[j]) return returnVect
运行结果7:
运行该文件,输入testVector = img2vector(‘digits/trainingDigits/0_0.txt’) 然后输出前32个testVector[0,0:31]。
2.3.2 使用k-近邻算法识别手写数字
代码8:
def handWrithingClassTest(): hwLabels = [] #定义一个空列表 记录标签向量 trainingFileList = listdir('digits/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('digits/trainingDigits/%s' % fileNameStr) #创建训练集 #创建测试集和标签 errorCount = 0.0 #统计错误个数 testFileList = listdir('digits/testDigits') #得到存放测试数据的文件列表 mTest = len(testFileList) for i in range(mTest): fileNameStr = testFileList[i] #得到文件名和扩展名 fileStr = fileNameStr.split('.')[0] #得到文件名 classNumStr = int(fileStr.split('_')[0])#得到该文件的存储的数字 vectorUnderTest = img2vector('digits/testDigits/%s' % fileNameStr) #得到测试集 classifierResult = myKNN.classify0(vectorUnderTest,trainingMat,hwLabels,3) #计算结果 #print ("the classifier came back with: %d, the read answer is:%d" %(classifierResult,classNumStr) ) if (classifierResult !=classNumStr):#计算错误率 errorCount += 1.0 print ("the total number of errors is %d" % errorCount) #输出错误个数 print ("the total error rate is: %f" % (errorCount/float(mTest)))#输出错误率
运行结果8:
错误率为1.06%,改变k值、训练、测试样本错误率会有所变化。
实际使用这个算法时,执行效率并不是很高,因为数据量太大会产生额外的时间、空间开销。而决策树就是k-近邻的优化版,可以节省大量的计算开销。
总述
k-近邻算法花了好一段时间终于搞定了,其中的一些python知识也详细的去查过,虽然假期的效率并没有太高,但是觉得每一步都走精也是一种收获,下面对k-近邻进行一下总结。
本章通过基础的k-近邻算法和两个例子进行了讲解和说明,主要就是将测试数据或者训练数据转换为要输入矩阵的格式,可能用到了归一化,然后使用k-近邻进行解析数据从而返回预测结果。其中还设计数据的绘图、错误率的计算以及大量的python矩阵知识。关于本章中python的知识,由于内容量太大,我放到了单独的博客中,供以没学过python的人参考。
k-近邻有简单有效的优点、但是时空开销比较大,所以引进下一章可以节省大量的计算开销的决策树。
(代码下载地址:http://download.csdn.net/download/jichun4686/9910040)
【2017.7.25决策树!!!!】
- 机器学习实战-第2章(k-近邻算法)
- 机器学习实战(第2章 k-近邻算法)
- 机器学习实战 第2章 k-近邻算法
- 机器学习实战---读书笔记: 第2章 k-近邻算法
- 机器学习实战—第2章 k-近邻算法
- 《机器学习实战》课程笔记(第2章) k-近邻算法
- 【机器学习实战】第2章 K-近邻算法(KNN)
- 机器学习实战第2章-K近邻算法(KNN)
- 【机器学习实战】第2章 K-近邻算法(k-NearestNeighbor,KNN)
- K近邻算法(机器学习实战)
- 代码注释:机器学习实战第2章 k-近邻算法
- 机器学习实战-第二章(k-近邻算法)
- 机器学习实战笔记(第二章:k近邻算法)
- 机器学习实战2:k近邻算法KNN(python)
- 《机器学习实战》第二章:k-近邻算法(2)约会对象分类
- 机器学习实战之K-近邻算法
- 机器学习实战笔记 K近邻算法
- 《机器学习实战》之K-近邻算法
- 伸展树(Splay)
- 横向导航条页面居中的方法
- 线程通信常用方法
- IntelliJ Idea常用的快捷键
- HDU 1222 A
- 机器学习实战(第2章 k-近邻算法)
- Non-negative Partial Sums HDU
- 微信支付:curl出错,错误码:60
- Mybatis打印Sql到控制台
- 使用SpringMVC时无参的重定向方法
- BP反向传播算法的具体计算
- px+em+pt+rpx+rem的区别
- 那些数据类型导致的坑
- Zookeeper选举过程