一、定义
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值的选取和距离计算的方法都必须好好地设计一番,配合特征归一化,用测试集进行测试,来达到更好的分类效果。