目录
1.1KNN算法概述 (附数据集)
1.2 KNN算法实例
1.3 使用KNN算法改进约会网站的配对效果
正在看《机器学习实战》一书,但目前正在实习,因此只能在周末时候来写一下相关学习笔记,今天算是第一天开始写博客,写的不太好大家见谅一哈,今后会加油的。首先来简单介绍一下K-近邻(KNN)算法,这是一种通过测量不同特征值之间的距离,从而进行分类的一种算法,算法的工作原理是:存在一个训练样本集,并且该样本集中每个数据都存在标签,即了解样本集中每一数据的分类标签;在接收到没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后利用该算法提取样本集中特征最为相似数据(也成为最近邻)的分类标,由于只选择训练样本集中前k个最相似的数据(k一般小于20),因此这k个最相似数据中出现次数最多的分类,也就为新数据的分类。
KNN算法的一般流程为:1.收集数据;2.准备数据:用于进行距离计算所需要的数值,最好是结构化的数据格式;3.分析数据;4.训练算法(此步骤不适用于K-近邻算法,可以忽略);5.测试算法:计算错误率;6.使用算法:首先需要输入样本数据和结构化的数据格式,然后运行k-近邻算法判定输入数据属于哪个分类,然后对计算出的分类执行后续的处理
由于书中的相关代码大多是基于python2的,为方便学习,我对书中的一些代码进行了调整,使其适应python3,代码运行的环境为anaconda中的spyder,我在下面也会简单介绍如何使用这个IDE来完成书中的实战项目。下面的链接就是书中相关数据集的下载:链接:https://pan.baidu.com/s/1oZ3mcNpryEbuUShT88s2zQ 提取码:2xw0
首先我们在anaconda官网上下载anaconda,然后找到spyder,为什么推荐用spyder来写呢,是因为spyder可以像matlab一样清楚的在variable explorer中描述变量。在打开spyder后,新建一个file就可以开始编写第一个KNN算法了,然后将写好的程序以KNN.py的方式命名,保存在一个文件夹下,同时将书中所提供的数据集同时保存在这个文件夹下即可。
在做好上面的准备工作后,再次说明一下KNN算法的实质,所谓KNN算法的实质,应该就是一个欧式距离问题,A->B的距离为5,A->C的距离为10,显然,若要对A进行分类的话,最好是将其分类到B类中,当然这个描述是最近邻算法,既然叫K近邻肯定要有一个K了,因此K近邻算法的步骤如下:
接下根据书上给的一个小例子,通过创建一个数据集来简单说明一下,创建数据集的代码如下:
from numpy import * #导入科学计算包Numpy
import operator #导入运算符模块
import os
def createDataSet():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) #这是四组数据,每组有两个我们已知的属性或者特征值;
#group矩阵每行包含一个不同的数据,可以把它想象为某个日志文件中不同的测量点或入口
labels = ['A','A','B','B'] #向量labels包含了每个数据点的标签信息,labels包含的元素个数等于group矩阵行数
#根据上面的对应关系,将数据点(1,1.1)定义为类别A等
return group, labels
为了测试数据集是否创建成功,我们先run一下这个KNN.py,然后输入下面代码:
import KNN
group,labels = KNN.createDataSet()
group
output:array([[ 1. , 1.1],
[ 1. , 1. ],
[ 0. , 0. ],
[ 0. , 0.1]])
labels
output:['A', 'A', 'B', 'B']
#若成功运行,则说明我们成功创建了变量group和变量labels
接下来我们写出KNN的分类算法classify0(),并返回分类结果:
def classify0(inX, dataSet, labels, k): #classify0有4个输入参数:用于分类的输入向量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 #这部分代码计算向量点xA和xB之间的距离 d=根号下(xA0-xB0)平方+(xA1-xB1)平方
sortedDistIndicies = distances.argsort() #对距离按照从小到大的顺序排序
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #确定前k个距离最小元素所在的主要分类,输入的k总时正整数
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse = True) #将classCount字典分解为元组列表,然后使用itemgetter方法,按照第二个元素的次序对元组进行排序,此处的排序为逆序
#在python3报错,将iteritems改为items即可,iteritems是为python2环境中dict的函数
return sortedClassCount[0][0] #返回发生频率最高的元素标签
为了预测任一数据所在分类,可以在python提示符中输入下面指令:
import KNN
KNN.classify0([1,0],group,labels,3)
#结果应该是‘B’
这样就成功完成了一个KNN分类器。
这是书中第一个实战项目,项目的背景是海伦一直使用约会网站寻找适合自己的约会对象,尽管约会网站会推荐不同的任选,但是她并不是喜欢推荐的每一个人,经过一番总结,她发现自己交往过的人可以进行如下分类:
海伦收集约会数据已经有了一段时间,她把这些数据存放在文本文件datingTestSet.txt中,每个样本数据占一行,总共有1000行。
在datingTestSet.txt中,海伦所收集的样本数据主要包含以下3种特征:
在介绍完项目背景后开始着手对数据进行处理,根据上面的1.2中的小例子,要想使用KNN算法classify0()进分类,classify()所接收的数据为dataSet特征矩阵和对应的labels,因此,对于所给数据集datingTestSet.txt,需要将其中的文本数据进行转换,将文本记录转换为特征矩阵dataSet的解析程序如下:
def file2matrix(filename): #该函数的输入为文件名字符串,输出为训练样本矩阵和类标签向量
fr = open(filename)
arrayOLines = fr.readlines() #用于读取filename文件的所有行
numberOfLines = len(arrayOLines) #获取文件行数
returnMat = zeros((numberOfLines,3)) #返回Numpy矩阵,解析完成的数据为numberOfLines行,3列数据
classLabelVector = [] #返回的分类标签向量
index = 0
for line in arrayOLines:
line = line.strip() #使用line.strip()函数截取掉所有的空白符,s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' ')
listFromLine = line.split('\t') #将line根据'\t'分隔符进行切片
returnMat[index,:] = listFromLine[0:3] #选取前三列元素,将它们存储到特征矩阵中
classLabelVector.append(listFromLine[-1]) #将最后一列数据加入classLabelVector中,作为情感分类标签
index += 1
return returnMat,classLabelVector
运行KNN.py,并用下述代码进行测试:
import KNN
datingDataMat,datingLabels = KNN.file2matrix('datingTestSet2.txt')
datingDataMat
output:array([[ 4.09200000e+04, 8.32697600e+00, 9.53952000e-01],
[ 1.44880000e+04, 7.15346900e+00, 1.67390400e+00],
[ 2.60520000e+04, 1.44187100e+00, 8.05124000e-01],
...,
[ 2.65750000e+04, 1.06501020e+01, 8.66627000e-01],
[ 4.81110000e+04, 9.13452800e+00, 7.28045000e-01],
[ 4.37570000e+04, 7.88260100e+00, 1.33244600e+00]])
接下来,由于三个特征单位不同,且数字插值最大的属性对计算结果的影响最大,因此需要对其进行数据归一化的处理,在这里文中使用了一个autoNorm的函数,代码如下:
#归一化特征值
#核心公式是newValue = (oldValue-min)/(max-min)
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
这时候我们已经完成了大部分的工作了,需要通过一个测试算法来验证所写的KNN分类器,测试算法的代码如下:
def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = file2matrix('datingTestSet.txt') #用file2mtrix读取datingTestSet.txt文件
normMat,ranges,minVals = autoNorm(datingDataMat) #使用autoNorm函数将其转换为归一化特征值
m = normMat.shape[0]
numTestVecs = int(m*hoRatio) #计算测试向量的数量,决定了normMat向量中那些数据用于测试,那些数据用于分类器的训练样本
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:,],datingLabels[numTestVecs:m],3)
print ( "分类类别:%s, 真实列表: %s" % (classifierResult,datingLabels[i]))
if (classifierResult != datingLabels[i]) : errorCount +=1.0
print ("错误率 :%f " % (errorCount/float(numTestVecs)))
通过运行下面代码,发现对于给定datingTestSet.txt,KNN分类器的错误率仅有0.05
import KNN
KNN.datingClassTest()
output:
分类类别:largeDoses, 真实列表: largeDoses
分类类别:largeDoses, 真实列表: largeDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:smallDoses, 真实列表: smallDoses
分类类别:largeDoses, 真实列表: largeDoses
分类类别:largeDoses, 真实列表: didntLike
分类类别:largeDoses, 真实列表: largeDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:smallDoses, 真实列表: smallDoses
分类类别:smallDoses, 真实列表: smallDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:didntLike, 真实列表: didntLike
分类类别:largeDoses, 真实列表: largeDoses
分类类别:smallDoses, 真实列表: largeDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:smallDoses, 真实列表: smallDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:largeDoses, 真实列表: largeDoses
分类类别:largeDoses, 真实列表: largeDoses
分类类别:smallDoses, 真实列表: smallDoses
分类类别:didntLike, 真实列表: didntLike
分类类别:largeDoses, 真实列表: didntLike
错误率 :0.050000
最后一步,我们可以将上述算法构建成一个完整的程序,利用该程序,海伦通过输入约会网站上相应的信息,程序便会自动给出她对南方喜欢程度的预测值。
因此在KNN.py中继续添加下面代码:
def classifySystem():
resultList = ['not at all','in small doses','in large doses']
percentTats = float(input("percentaged of time spent playing video games?"))
ffMiles = float(input("frequent flier miles earned per years?"))
icecream = float(input("liters of ice cream consumed per year?"))
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
normMat, ranges, minVals = autoNorm(datingDataMat)
inArr = array([ffMiles, percentTats,icecream])
classifierResult = int(classify0((inArr-\
minVals)/ranges,normMat,datingLabels,3))
print("You will probably like this person:",\
resultList[classifierResult - 1])
此时我们完成了这样一个KNN分类器,通过在cmd中运行该程序,三个特征的输入值为(20,40000,5),得到的结果如下:
percentaged of time spent playing video games?20
frequent flier miles earned per years?40000
liters of ice cream consumed per year?5
You will probably like this person: in large doses