说明:该案例来源 机器学习实战之KNN,里面有更详尽的KNN原理分析和案例实现流程详解,是一个关于机器学习实战的不错的学习资料,推荐一波。出于编程实践和机器学习算法梳理的目的,按照自己的代码风格重写该应用案例,在实现的过程中也很有助于自己的思考。为方便下次看时能快速理解便通过截图的方式截取了个人认为比较关键的内容,推荐看原链接,自己在代码实现过程中会留下一些思考,也欢迎交流学习。
KNN原理
应用案例概述
完整实现代码及分析
代码中的很多分析就只针对这么一个很简单的数据(只有三个特征)来说,像“如果特征维度变得很高又该如何”的问题就会涉及到特征工程上的很多内容了,在此处深究的话就会偏离“KNN算法理解”的目的了。而且KNN能做的事情很多,这里也只是用于简单的分类问题上来。
# __author__ = 'czx' # coding=utf-8 from numpy import * import matplotlib.pyplot as plt import operator # 数据加载函数:存储在.txt文件中,每一行前三个数据为特征,最后一个数据为类别 def loadData(filename): f = open(filename) numOfLines = len(f.readlines()) dataMat = zeros((numOfLines,3)) labels = zeros((numOfLines,1)) index = 0 f = open(filename) for line in f.readlines(): tempSample = line.strip().split('\t') dataMat[index][:] = tempSample[0:3] labels[index][:] = int(tempSample[3]) index += 1 return dataMat,labels # 简单地打印看看自己的数据有没有加载对,数据非常庞大的时候可以选择打印少部分样本数据 def dataCheck(): data,labels = loadData('datingTest.txt') #print len(data),len(labels) for i in range(len(data)): print data[i],labels[i] # 数据的可视化,这里的3D显示,觉得3D看起来不够直观也可以打印其中的两个特征来看 def dateVisualization(): data,labels = loadData('datingTest.txt') ax = plt.subplot(111, projection='3d') colorNames = ['r','g','b'] for i in range(len(data)): ax.scatter(data[i][0], data[i][1], data[i][2], c=colorNames[int(labels[i]-1)]) ax.set_zlabel('frequent filer miles earned per year') ax.set_ylabel('percentage of time spent playing video games') ax.set_xlabel('liters of ice cream consumed per year') plt.show() # 归一化数据:原始特征数据的数值范围大小差异大,归一化是必不可少的操作,这里只写了一个归一化方法,当然可以添加不同的归一化方法,通过mode选择,但是需要考虑不同方法返回的参数可能不一样 def autoNorm(data,mode='linear'): if mode == 'linear': minVals = data.min(0) maxVals = data.max(0) ranges = maxVals - minVals ranges = map(lambda x: [x,1][x==0], ranges) # 这是防止除以0的问题出现,当然实际问题中出现0意味着该维度特征全部相同 num = data.shape[0] normData = data - tile(minVals,(num,1)) normData = normData / tile(ranges,(num,1)) return normData, ranges, minVals # 检查归一化函数是否写对了,可以用少量的数据来验证,通过笔算和代码跑出来的结果验证 def normCheck(): # data,labels = loadData('datingTest.txt') data = ones((3,3))+eye(3)*3 # data[0] = [1,2,3] # data[1] = [1,3,5] # data[2] = [1,4,5] # print shape(data) normData = autoNorm(data) print data print normData # knn分类过程:计算新样本到已知样本的欧氏距离(当然不同的应用场景可能用到不同的距离),然后找出距离最小的k个样本,对其类别进行投票得到最终的类别输出。距离计算查找topK,这里采用的是暴力方法,这是算法的核心部分,还可以使用KD-Tree,Ball-Tree等等。 def knnClassify(input,data,labels,k): numofSamples = data.shape[0] diffMat = tile(input,(numofSamples,1)) - data sqDiffMat = diffMat**2 sqDistance = sqDiffMat.sum(axis=1) distance = sqDistance**0.5 sortedDistIndicies = distance.argsort() # 这里的话就是典型的top K问题,也可以自己用heap sort来做这样的排序 classCount = {} for i in range(k): vote = labels[sortedDistIndicies[i]][0] classCount[vote] = classCount.get(vote,0)+1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0] # 如果有需要,一个样本可以同时输出多个类别 # 测试评估:在已有的一些数据上做测试,看看knn分类的精度 def datingClassTest(): ratio = 0.1 datingDataMat, datingLabels = loadData('datingTest.txt') normData, ranges, minVals= autoNorm(datingDataMat,'linear') num = normData.shape[0] numOfTestSamples = int(num*ratio) print 'number of testing samples : ',numOfTestSamples errorCount = 0 for i in range(numOfTestSamples): result = knnClassify(normData[i][:],normData[numOfTestSamples:num][:],datingLabels[numOfTestSamples:num],3) print "classified result : %d , groundtruth label : %d " % (result,datingLabels[i]) if result != datingLabels[i]: errorCount += 1 print "error rate of %d testing samples : %f " % ( numOfTestSamples, errorCount/float(numOfTestSamples) ) # 分类决策:有了已知的数据,根据knn将新输入的数据进行分类,得到分类的结果之后做决策,比如根据匹配程度进行相关推荐(这里是推荐约会) def classifyPerson(): resultList = ['not at all', 'in small doses', 'in large doses'] percentTats = float(raw_input("percentage of time spent playing video games ?")) ffMiles = float(raw_input("frequent filer miles earned per year?")) iceCream = float(raw_input("liters of ice cream consumed per year?")) datingDataMat, datingLabels = loadData('datingTest.txt') normMat, ranges, minVals = autoNorm(datingDataMat) inArr = array([ffMiles, percentTats, iceCream]) res = knnClassify((inArr-minVals)/ranges,normMat,datingLabels, 3) print "You will probably like this person: ", resultList[int(res) - 1] # 主函数 if __name__ == "__main__": # dataCheck() # dateVisualization() # normCheck() # datingClassTest() classifyPerson()
小结
1:编程实现过程中要对每个功能模块进行验证,有对应的可行的测试方法,如此一来到最后调试的时候出现的问题就会很少,也更容易对出现的问题进行定位,分析解决。
2:可视化是一个很重要的点,可以帮助查看数据,对数据有个更直觉上的认知,可以很好地辅助排查异常等等,在计算机视觉相关任务中就显得尤为重要了。
3:代码上需要注意的一些点,比如list和array的统一,参数的一些命名,函数的可移植性和可扩展性等等,同时也要多编程熟悉python的一些函数,比如tile,argsort等等。
4:原链接还利用knn实现了手写数字识别(分类),不同之处是特征维度变成了1024,knn和cnn进行图像分类不太一样的是前者将特征编程向量来处理,cnn则直接以图片的形式处理,两者还是有很多差异的,如cnn可以很好地学习一些局部相关性等等。
5:knn中的距离计算和topK的查找等都是很关键的点,上面代码采用暴力的方式来做,还可以采用KD-Tree和Ball-Tree等来实现加速,KD-Tree的应用很广,先mark一下晚点整理。
6:最后呢,贴上原链接中的一个小结吧!