一、k-近邻分类算法
1.1 工作原理
存在一个样本数据集合,也称作训练样本集,并且样本集的每个数据都存在标签,即我们知道样本集的每一数据与所属分类的对应关系。输入没有标签的新数据后, 将新数据的每个特征与样本集的数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
1.2 一般流程
- 收集数据:可以使用任何方法。
- 准备数据:距离计算所需要的数值,最好是结构化的数据格式。
- 分析数据:可以使用任何方法。
- 训练算法:此步驟不适用于k-近邻算法。
- 测试算法:计算错误率。
- 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。
1.3 优缺点及适用范围
- 优点:精度高、对异常值不敏感、无数据输入假定。
- 缺点:计算复杂度高、空间复杂度高。
- 适用数据范围:数值型和标称型。
二、使用k-近邻算法改进约会网站的配对效果
2.1 利用k-近邻算法实现分类器
代码示例:
# -*- coding: utf-8 -*- """ Created on Thu Apr 12 21:23:22 2018 将该文件保存为kNN.py文件 @author: lizihua """ import numpy as np import operator import matplotlib.pyplot as plt #k-近邻算法 def classify0(inX, dataSet, labels, k): dataSetSize = dataSet.shape[0] #获取行数 #计算欧式距离 #将inX扩维,使其与dataSet形状一致,以便实现获得X-dataSet数组 diffMat = np.tile(inX, (dataSetSize,1))-dataSet sqdiffMat = diffMat**2 sqDisatances = sqdiffMat.sum(axis=1) distances=sqDisatances**0.5 #argsort返回distance从小到大的排序索引,此时,distance并没有改变 sortedDistIndicies=np.argsort(distances) classCount={} for i in range(k): #voteIlabel是按照distance从小到大排序的label voteIlabel = labels[sortedDistIndicies[i]] #计算前k个数中,各类出现的频率 classCount[voteIlabel] = classCount.get(voteIlabel,0)+1 #对字典进行排序,并返回列表 #key=operator.itemgetter(1)代表按照第二个元素对字典排序,reversed=True代表从大到小排序 #返回列表形式,例如:[('B', 2), ('A', 1)] sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) return sortedClassCount[0][0] #输入文件名,输出训练样本矩阵和类标签向量 def file2matrix(filename): fr = open(filename) arrayOfLines = fr.readlines() numberOfLines = len(arrayOfLines) #样本有三个特征 returnMat = np.zeros((numberOfLines,3)) classLabelVector = [] index = 0 for line in arrayOfLines: line = line.strip() listFormLine = line.split('\t') #训练样本矩阵 returnMat[index,:] = listFormLine[0:3] #类标签向量 classLabelVector.append(int(listFormLine[-1])) index +=1 return returnMat,classLabelVector #归一化特征值 #newValue = (oldValue - min)/(max-min) def autoNorm(dataSet): #求每一个特征的最小值、最大值及其差值 minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals normDataSet = np.zeros(dataSet.shape) m = dataSet.shape[0] normDataSet = dataSet -np.tile(minVals,(m,1)) normDataSet = normDataSet/np.tile(ranges,(m,1)) return normDataSet, ranges, minVals #测试 if __name__=="__main__": datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') normMat, ranges, minVals =autoNorm(datingDataMat) print("归一化后的数据:",normMat) print("三个特征的最大最小差值:",ranges) print("三个特征的最小值:",minVals) resultList = ['not at all','in small does','in large does'] inArr=np.array([10000,10,0.5]) resultindex = classify0(inArr,normMat,datingLabels,3) print("你喜欢这个人的可能性:",resultList[resultindex]) #飞行常客里程数与视频游戏百分比之间的散点图 fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(datingDataMat[:,0],datingDataMat[:,1], 15.0*np.array(datingLabels),15.0*np.array(datingLabels)) #飞行常客里程数与每周冰淇淋公斤数之间的散点图 fig1 = plt.figure() ax1 = fig1.add_subplot(111) ax1.scatter(datingDataMat[:,0],datingDataMat[:,2], 15.0*np.array(datingLabels),15.0*np.array(datingLabels)) #视频游戏百分比与每周冰淇淋公斤数之间的散点图 fig2 = plt.figure() ax2 = fig2.add_subplot(111) ax2.scatter(datingDataMat[:,1],datingDataMat[:,2], 15.0*np.array(datingLabels),15.0*np.array(datingLabels))
结果显示:
飞行常客里程数与视频游戏百分比之间的散点图:
飞行常客里程数与每周冰淇淋公斤数之间的散点图:
视频游戏百分比与每周冰淇淋公斤数之间的散点图:
2.2 验证分类器
代码示例:
# -*- coding: utf-8 -*- """ Created on Thu Apr 12 20:25:41 2018 将该文件保存为kNN_test.py @author: lizihua """ from kNN import file2matrix,autoNorm,classify0 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.0 for i in range(numTestVecs): classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3) print("预测结果:%d,实际结果:%d"%(classifierResult,datingLabels[i])) if classifierResult!=datingLabels[i]: errorCount +=1 print("错误率:",errorCount/numTestVecs) #测试 if __name__=="__main__": datingClassTest()
部分结果显示:
三、利用sklearn模块中的knn算法
代码示例:
# -*- coding: utf-8 -*- """ Created on Fri Apr 13 17:08:14 2018 @author: Administrator """ from sklearn.neighbors import KNeighborsClassifier import numpy as np #输入文件名,输出训练样本矩阵和类标签向量 def file2matrix(filename): fr = open(filename) arrayOfLines = fr.readlines() numberOfLines = len(arrayOfLines) #样本有三个特征 returnMat = np.zeros((numberOfLines,3)) classLabelVector = [] index = 0 for line in arrayOfLines: line = line.strip() listFormLine = line.split('\t') #训练样本矩阵 returnMat[index,:] = listFormLine[0:3] #类标签向量 classLabelVector.append(int(listFormLine[-1])) index +=1 return returnMat,classLabelVector #归一化特征值 #newValue = (oldValue - min)/(max-min) def autoNorm(dataSet): #求每一个特征的最小值、最大值及其差值 minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals normDataSet = np.zeros(dataSet.shape) m = dataSet.shape[0] normDataSet = dataSet -np.tile(minVals,(m,1)) normDataSet = normDataSet/np.tile(ranges,(m,1)) return normDataSet, ranges, minVals #测试 if __name__=="__main__": datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') normMat, ranges, minVals =autoNorm(datingDataMat) print("归一化后的数据:",normMat) print("三个特征的最大最小差值:",ranges) print("三个特征的最小值:",minVals) hoRatio = 0.10 m = normMat.shape[0] numTestVecs = int(m*hoRatio) resultList = ['not at all','in small does','in large does'] #n_neighbors就是knn算法中的k,调用knn neigh = KNeighborsClassifier(n_neighbors=3) #训练:neigh.fit(X,y)//X是训练集,y是训练集对应的目标集,即分类标签 neigh.fit(normMat[numTestVecs:m], datingLabels[numTestVecs:m]) #预测回归结果:predict_proba(X)//X是测试集,返回预测回归结果(值) #预测分类结果:neigh.predict(X)//X是测试集,返回预测的分类结果(分类值) pred=neigh.predict(normMat[:numTestVecs]) #计算准确率 diff=pred-datingLabels[:numTestVecs] acc=np.sum(diff==0)/numTestVecs print("准确率:",acc)
结果显示: