一、定义
k-近邻算法就是采用特定方法计算多个特征值之间的距离,来对数据进行分类的算法。
优点:精度高、对异常值不敏感、无数据输入假定
缺点:计算复杂度高、空间复杂度高
适用数据范围:数值型和标称型
二、工作原理
首先需要有一个已经标注过的数据集合,其中的每个数据都有已知的标签。每次输入没有标签的数据后,将新的数据每个特征与样本集中数据对应的特征进行比较,然后算法找出样本集中特征最相似数据的标签。算法选取最相似的前k个数据的标签,出现次数最多的分类作为新数据的分类标签。
一般流程:
收集数据 - 准备数据 - 分析数据 - 测试算法 - 使用算法
三、简单的用例
先定义一个训练集,也就是上面所说的已标注的集合:
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
解释一下:group就是数据集,里面有四个数据(每个数组表示一个数据,数组各个位置的内容即为各个特征的值);labels为已标注的标签,表示上面的四个数据分别对应的标签
然后编写算法:
from numpy import *
import operator
def classify0(inX, dataSet, labels, k): #第一个参数是输入的数据,是一个特征数组(表示一个数据),第二个和第三个参数分别是已标注的数据集,标记集,第四个参数就是kNN算法中的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.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
运行:
>>> classify0([0,0],group, labels,2)
就能得到结果
四、实用案例
书本中说到了一个通过KNN算法(k-近邻算法)来判断约会对象是否心仪的程序,接下来实现一下:
1. 收集和准备数据:直接通过书本附带的数据来提取特征矩阵和标注信息:
数据:(第一列为约会对象的每年的飞行里程数,第二列为玩视频游戏所占时间比重,第三列为每周吃掉的冰淇淋公升数,第四列为主人公小黑对此人的态度,分为三种:很心仪largedoses、有点心动smalldoses和不喜欢didn'tlike)前三者为特征,最后一列主人公的态度即为我们所需要的标签,我们的目的就是给出一个约会对象(包含前三个特征),判断出小黑是否喜欢她:
40920 8.326976 0.953952 largeDoses
14488 7.153469 1.673904 smallDoses
26052 1.441871 0.805124 didntLike
75136 13.147394 0.428964 didntLike
38344 1.669788 0.134296 didntLike
72993 10.141740 1.032955 didntLike
35948 6.830792 1.213192 largeDoses
42666 13.276369 0.543880 largeDoses
67497 8.631577 0.749278 didntLike
35483 12.273169 1.508053 largeDoses
50242 3.723498 0.831917 didntLike
63275 8.385879 1.669485 didntLike
5569 4.875435 0.728658 smallDoses
51052 4.680098 0.625224 didntLike
77372 15.299570 0.331351 didntLike
43673 1.889461 0.191283 didntLike
61364 7.516754 1.269164 didntLike
69673 14.239195 0.261333 didntLike
15669 0.000000 1.250185 smallDoses
28488 10.528555 1.304844 largeDoses
6487 3.540265 0.822483 smallDoses
37708 2.991551 0.833920 didntLike
22620 5.297865 0.638306 smallDoses
28782 6.593803 0.187108 largeDoses
19739 2.816760 1.686209 smallDoses
36788 12.458258 0.649617 largeDoses
5741 0.000000 1.656418 smallDoses
28567 9.968648 0.731232 largeDoses
6808 1.364838 0.640103 smallDoses
41611 0.230453 1.151996 didntLike
36661 11.865402 0.882810 largeDoses
43605 0.120460 1.352013 didntLike
15360 8.545204 1.340429 largeDoses
63796 5.856649 0.160006 didntLike
10743 9.665618 0.778626 smallDoses
70808 9.778763 1.084103 didntLike
72011 4.932976 0.632026 didntLike
5914 2.216246 0.587095 smallDoses
14851 14.305636 0.632317 largeDoses
33553 12.591889 0.686581 largeDoses
44952 3.424649 1.004504 didntLike
17934 0.000000 0.147573 smallDoses
27738 8.533823 0.205324 largeDoses
29290 9.829528 0.238620 largeDoses
42330 11.492186 0.263499 largeDoses
36429 3.570968 0.832254 didntLike
39623 1.771228 0.207612 didntLike
32404 3.513921 0.991854 didntLike
27268 4.398172 0.975024 didntLike
5477 4.276823 1.174874 smallDoses
14254 5.946014 1.614244 smallDoses
68613 13.798970 0.724375 didntLike
41539 10.393591 1.663724 largeDoses
7917 3.007577 0.297302 smallDoses
21331 1.031938 0.486174 smallDoses
8338 4.751212 0.064693 smallDoses
5176 3.692269 1.655113 smallDoses
18983 10.448091 0.267652 largeDoses
68837 10.585786 0.329557 didntLike
13438 1.604501 0.069064 smallDoses
48849 3.679497 0.961466 didntLike
12285 3.795146 0.696694 smallDoses
7826 2.531885 1.659173 smallDoses
5565 9.733340 0.977746 smallDoses
10346 6.093067 1.413798 smallDoses
1823 7.712960 1.054927 smallDoses
9744 11.470364 0.760461 largeDoses
16857 2.886529 0.934416 smallDoses
39336 10.054373 1.138351 largeDoses
65230 9.972470 0.881876 didntLike
2463 2.335785 1.366145 smallDoses
27353 11.375155 1.528626 largeDoses
16191 0.000000 0.605619 smallDoses
12258 4.126787 0.357501 smallDoses
42377 6.319522 1.058602 didntLike
这里只附上了部分原数据,要全的去这里下载。
2.接下来编写一个函数(与书本上的不同,书本上的笔者用python3.64编译不通过,下面是自己修改的程序)来将数据处理成前面能够拿来训练的数据结构:
from os import listdir
def file2matrix(filename):
fr = open(filename)
numberOfLines = len(fr.readlines()) #get the number of lines in the file
returnMat = zeros((numberOfLines,3)) #prepare matrix to return
classLabelVector = [] #prepare labels return
fr = open(filename)
index = 0
for line in fr.readlines():
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
if listFromLine[3][0]=='l':
classLabelVector.append(1)
else:
if listFromLine[3][0]=='s':
classLabelVector.append(2)
else:
classLabelVector.append(3)
index += 1
return returnMat,classLabelVector
然后使用这个函数处理该数据文本,就能得到数据矩阵和标签集了,接下来就可以使用算法进行分类了
然而仅进行这一步可能结果会不那么准确,我们对算法加以改进
3.特征归一化
从上面的数据我们可以看出,由于计算距离的公式对各个特征的处理都是一样的,所以差值越大的特征对结果的影响更大,而差值在不同的数量级往往是由于特征数值的不同造成的,比如这个例子中的飞行里程数相对另两个就显得大得多,从而使得飞行里程数对于结果的影响更大,而我们也希望各个特征都更加“平等”的对待,所以我们使用下面的数学方法来处理各个特征数值:
newValue = (OldValue-min)/(max-min)
通过这个公式,各个数值都将被修改成[0,1]内的数值,这样就使得各个特征等权重
编写一个函数来实现上述过程:
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)) #element wise divide
return normDataSet, ranges, minVals
通过这个函数对file2matrix的处理结果进行进一步的加工(归一化),再将得到的数据集和标签集作为分类函数classify0()的输入参数来对新数据进行分类,将得到更好的效果(希望是三个特征都平等考虑的情况下)。
4.测试算法
最好将收集的数据(已标注)分出一部分来用作测试数据,在算法完成后用这部分数据来测试,看看不同的k值对应的准确率如何。
5.编写函数前端
这里说的不是具体的可视化界面,而是说最好编写一个函数,提示你输入一个约会对象的这三个特征,然后给出结果的python程序。当然,做一个可视化界面更好;)
五、小结
KNN算法作为《机器学习实战》这本书入门的第一个算法,讲出了分类就是机器学习的一种,还是比较好理解的。这个算法就是不断计算未标记向量和已有向量集中的向量之间的距离,找出距离最近的k个向量对应的标签,最多的标签就贴给这个新的未标记变量。
k值的选取和距离计算的方法都必须好好地设计一番,配合特征归一化,用测试集进行测试,来达到更好的分类效果。