1. k-近邻法简介 k近邻法(k-nearest neighbor, k-NN)是1968年由Cover THart 提出的一种基本分类与回归方法。它的工作原理是:存在一个样本数据集合,也称作为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新的数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
注意: k-近邻法不具有显式的学习过程,它实际上是利用训练数据集对特征向量空间进行划分,并作为其分类的“模型”.其中K值的选取、距离度量及分类决策规则是k近邻算法的三个基本要素。
2. k近邻算法的数学语言表达:
3. k近邻算法中k值的选取标准:
4. 在k近邻算法中的距离度量公式如下图所示:
5. k-近邻算法中的分类决策规则
6. K近邻算法的一般流程
计算已知类别数据集中的点与当前点之间的距离;按照距离递增次序排序;选取与当前点距离最小的k个点;确定前k个点所在类别的出现频率;返回前k个点出现频率最高的类别作为当前点的预测分类本次案例主要是使用k-近邻算法来对电影进行分类。这里我们主要将电影分为两种:爱情片和动作片。根据电影中出现的打斗和接吻镜头来进行判定。本案例所使用的距离度量为欧氏距离。
准备数据集 结果显示: 根据欧式距离公式,计算距离,选择距离最小的前k个点。根据你输入的电影数据信息,程序返回分类结果。 程序代码如下: def classify0(inX, dataSet, labels, k): dataSetSize = dataSet.shape[0] diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet sqDiffMat = diffMat**2 sqDistances = sqDiffMat.sum(axis=1) distances = sqDistances**0.5 sortedDistIndices = distances.argsort() classCount = {} for i in range(k): voteIlabel = labels[sortedDistIndices[i]] classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) #返回次数最多的类别,即所要分类的类别 return sortedClassCount[0][0] if __name__ == '__main__': #创建数据集 group, labels = createDataSet() #对输入数据进行分类 x = input("请输入测试数据:") xlist=x.split(" ") test = [int(xlist[i]) for i in range(len(xlist))] #kNN分类 test_class = classify0(test, group, labels, 3) #打印分类结果 print(test_class)结果如下:
本节我们一步步地构造使用k-近邻分类器的手写识别系统。为了简单起见,这里构造的系统 只能识别数字0到9.需要识别的数字已经使用图形处理软件,处理成具有相同的色 彩和大小,其中宽高是32像素×32像素的黑白图像。尽管采用文本格式存储图像不能有效地利用内 存空间,但是为了方便理解,我们还是将图像转换为文本格式。 存储的数字文件格式为:数字的值_该数字的样本序号,如下图所示: 数据集分为训练集和测试集,通过上述电影分类的方法,设计一个k-近邻算法分类器实现数字分类。其中数据集和实现代码下载地址:数据集下载
代码展示:
import numpy as np import operator from os import listdir 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={} #选择距离最小的K个点 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] def img2vector(filename): returnVect = np.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 def handwritingClassTest(): hwLabels = [] trainingFileList = listdir('trainingDigits') #load the training set m = len(trainingFileList) trainingMat = np.zeros((m,1024)) for i in range(m): fileNameStr = trainingFileList[i] fileStr = fileNameStr.split('.')[0] #take off .txt classNumStr = int(fileStr.split('_')[0]) hwLabels.append(classNumStr) trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) testFileList = listdir('testDigits') #iterate through the test set errorCount = 0.0 mTest = len(testFileList) for i in range(mTest): fileNameStr = testFileList[i] fileStr = fileNameStr.split('.')[0] #take off .txt classNumStr = int(fileStr.split('_')[0]) vectorUnderTest = img2vector('testDigits/%s' % fileNameStr) classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) print("the classifier came back with: %d, the real answer 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)))识别结果如下所示:
kNN算法的优缺点 优点:
简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归;可用于数值型数据和离散型数据;训练时间复杂度为O(n);无数据输入假定;对异常值不敏感缺点:
计算复杂性高;空间复杂性高;样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少);一般数值很大的时候不用这个,计算量太大。但是单个样本又不能太少,否则容易发生误分。最大的缺点是无法给出数据的内在含义。参考资料: 本文中提到的电影分类、手写数字识别实例和数据集,均来自于《机器学习实战》的第二章k-近邻算法。 本文的算法理论部分,参考自《统计学习方法 李航》的第三章k近邻法以及《机器学习实战》的第二章k-近邻算法。