《机器学习实战》-- KNN算法
2017-02-20 21:05
204 查看
KNN算法(K-Nearest Neighbor,k最近邻分类算法)
原理:物以类聚
优缺点:
优点:算法简单,易于实现,不需要参数估计,不需要事先训练
缺点:KNN计算量大,且训练样本必须存储在本地,内存开销也特别大
使用公式:
将任意取值范围的特征值转化为[0,1]区间内的值。
取数据集中5%的数据作为测试样本,剩余数据作为训练样本,求错误率来评估分类器的性能。测试显示,取5%的测试样本,错误率为2%;取10%的测试样本,错误率为5%。(与书中并不一致)
测试结果:
另外,需从OS模块导入listdir函数,列出给定目录的文件名。
使用到的数据在http://download.csdn.net/detail/nature_xd/9759044中可下载。
测试样本:2000个,每个样本大小为32*32,整理为1*1024
测试样本:900个
实际使用这个算法时,算法的执行效率并不高。因为算法需要为每个测试向量做2000次距离计算,每个距离计算包括1024个浮点运算,一共要执行900次,也就是有900*2000*1024次浮点数运算。
是否存在一种算法减少存储空间和计算时间的开销呢?
《机器学习实战》中说K决策树就是k近邻算法的优化版,可以节省大量的计算开销。
1. 算法思想
从训练样本中选择k个与测试样本“距离”最近的样本,这k个样本中出现频率最高的类别即作为测试样本的类别。原理:物以类聚
优缺点:
优点:算法简单,易于实现,不需要参数估计,不需要事先训练
缺点:KNN计算量大,且训练样本必须存储在本地,内存开销也特别大
2. 基本测试
使用简单数据对KNN算法进行测试。from numpy import * #科学计算包 import operator # 运算符模块 def createDataSet(): group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) labels = ['A','A','B','B'] return group,labels # 使用KNN算法进行分类 def classify(inX, dataSet, labels, k): # inX:输入的单个样本;[0,0] # dataSet训练样本,对应trainData;[[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]],dataSet.shape[0]=4 # labels对应trainLabel, # k是KNN算法选定的k,一般选择0~20之间的整数 # 返回值:inX的label,即图片inX对应的数字 dataSetSize = dataSet.shape[0] #返回训练集行数 ########### 计算距离(欧式距离,sqrt((x1-x2)^2 + (y1-y2)^2) + (z1-z2)^2) ############# #print(tile(inX, (dataSetSize,1))) # [[0 0] [0 0] [0 0] [0 0]] diffMat = tile(inX, (dataSetSize,1)) - dataSet #tile(inX, (dataSetSize,1))将inx构造为dataSetSize*1的数组 #print(diffMat) #[[-1. -1.1] [-1. -1. ] [ 0. 0. ] [ 0. -0.1]] sqDiffMat = diffMat**2 sqDistances = sqDiffMat.sum(axis = 1) #按行累加 distances = sqDistances**0.5 sortedDistIndicies = distances.argsort() #得到矩阵中每个元素的排序序号 ########### 选择距离最小的k个点 ############# classCount = {} for i in range(k): #sortedDistIndicies[i]表示第i小的样本在原样本集中的位置,从而找到相应的labels voteIlabel = labels[sortedDistIndicies[i]] #从字典classCount中获取key=voteIlabe对应的value,没有找到则返回0 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 ########### classCount统计了距离最小的k个点的label,对其排序得到最大值 ############# #operator.itemgetter(1),operator模块提供的itemgetter函数用于获取对象的哪些维的数据 #字典items()函数以列表返回可遍历的(键,值)元组数组 #因此,该句表示对字典中的元素按照值的大小进行降序排序 sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #排序后的第一个元素的值即为预测结果 return sortedClassCount[0][0] group,labels = createDataSet() result = classify([0,0], group, labels, 3) print(result)
3. 改进约会网站的配对效果
在处理取值范围不同,重要程度相同的特征值时,通常采用的方法是将数值归一化,如将取值范围处理为0~1或-1~1之间。使用公式:
newValue = (oldValue - min) / (max - min)
将任意取值范围的特征值转化为[0,1]区间内的值。
取数据集中5%的数据作为测试样本,剩余数据作为训练样本,求错误率来评估分类器的性能。测试显示,取5%的测试样本,错误率为2%;取10%的测试样本,错误率为5%。(与书中并不一致)
from numpy import * import operator import sys #从文本文件中读取数据 def file2matrix(filename): fr = open(filename) arrayOLines = fr.readlines() numberOfLines = len(arrayOLines) returnMat = zeros((numberOfLines,3)) classLabelVector = [] index = 0 for line in arrayOLines: line = line.strip() listFromLine = line.split('\t') returnMat[index,:] = listFromLine[0:3] classLabelVector.append(int(listFromLine[-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 = zeros(shape(dataSet)) m = dataSet.shape[0] normDataSet = dataSet - tile(minVals, (m,1)) normDataSet = normDataSet/tile(ranges, (m,1)) return normDataSet,ranges, minVals # 使用KNN算法进行分类 def classify(inX, dataSet, labels, k): # inX:输入的单个样本;[0,0] # dataSet训练样本,对应trainData;[[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]],dataSet.shape[0]=4 # labels对应trainLabel, # k是KNN算法选定的k,一般选择0~20之间的整数 # 返回值:inX的label,即图片inX对应的数字 dataSetSize = dataSet.shape[0] #返回训练集行数 ########### 计算距离(欧式距离,sqrt((x1-x2)^2 + (y1-y2)^2) + (z1-z2)^2) ############# #print(tile(inX, (dataSetSize,1))) # [[0 0] [0 0] [0 0] [0 0]] diffMat = tile(inX, (dataSetSize,1)) - dataSet #tile(inX, (dataSetSize,1))将inx构造为dataSetSize*1的数组 #print(diffMat) #[[-1. -1.1] [-1. -1. ] [ 0. 0. ] [ 0. -0.1]] sqDiffMat = diffMat**2 sqDistances = sqDiffMat.sum(axis = 1) #按行累加 distances = sqDistances**0.5 sortedDistIndicies = distances.argsort() #得到矩阵中每个元素的排序序号 ########### 选择距离最小的k个点 ############# classCount = {} for i in range(k): #sortedDistIndicies[i]表示第i小的样本在原样本集中的位置,从而找到相应的labels voteIlabel = labels[sortedDistIndicies[i]] #从字典classCount中获取key=voteIlabe对应的value,没有找到则返回0 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 ########### classCount统计了距离最小的k个点的label,对其排序得到最大值 ############# #operator.itemgetter(1),operator模块提供的itemgetter函数用于获取对象的哪些维的数据 #字典items()函数以列表返回可遍历的(键,值)元组数组 #因此,该句表示对字典中的元素按照值的大小进行降序排序 sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #排序后的第一个元素的值即为预测结果 return sortedClassCount[0][0] # 测试分类器的性能,使用错误率 def datingClassTest(): hoRatio = 0.05 #表示测试数据比例占10%,训练数据为90% 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 = classify(normMat[i,:],normMat[numTestVecs:m,:], datingLabels[numTestVecs:m],3) print("the classifier came back with:%d, the real answer is:%d" % (classifierResult,datingLabels[i])) if classifierResult != datingLabels[i]: errorCount += 1.0 print("the total error rate is: %f" % (errorCount/float(numTestVecs))) # 约会网站预测函数 def classifyPerson(): resultList = ['not at all','in small doses','in large doses'] percentTats = float(input("Percentage of time spent playing video games?")) ffMiles = float(input("frequent flier miles earned per year?")) iceCream = float(input("liters of ice cream consumed per year?")) datingDataMat,datingLabels = file2matrix("datingTestSet2.txt") normMat, ranges, minVals = autoNorm(datingDataMat) inArr = array([ffMiles, percentTats, iceCream]) classifierResult = classify((inArr-minVals)/ranges, normMat, datingLabels,3) print("You will probably like this person:", resultList[classifierResult-1]) #resultList的下标为0,1,2 datingClassTest() classifyPerson()
测试结果:
4. 手写字识别系统
为简便计算,将输入手写字图像的处理为32*32的二值图像。另外,需从OS模块导入listdir函数,列出给定目录的文件名。
# 输入图像:宽高为32*32的黑白图像 from numpy import * import operator from os import listdir #从OS模块导入listdir函数,列出给定目录的文件名 #将32*32的二进制图像矩阵转化为 def img2vector(filename): returnVect = 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 # 使用KNN算法进行分类 def classify(inX, dataSet, labels, k): # inX:输入的单个样本;[0,0] # dataSet训练样本,对应trainData;[[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]],dataSet.shape[0]=4 # labels对应trainLabel, # k是KNN算法选定的k,一般选择0~20之间的整数 # 返回值:inX的label,即图片inX对应的数字 dataSetSize = dataSet.shape[0] #返回训练集行数 ########### 计算距离(欧式距离,sqrt((x1-x2)^2 + (y1-y2)^2) + (z1-z2)^2) ############# #print(tile(inX, (dataSetSize,1))) # [[0 0] [0 0] [0 0] [0 0]] diffMat = tile(inX, (dataSetSize,1)) - dataSet #tile(inX, (dataSetSize,1))将inx构造为dataSetSize*1的数组 #print(diffMat) #[[-1. -1.1] [-1. -1. ] [ 0. 0. ] [ 0. -0.1]] sqDiffMat = diffMat**2 sqDistances = sqDiffMat.sum(axis = 1) #按行累加 distances = sqDistances**0.5 sortedDistIndicies = distances.argsort() #得到矩阵中每个元素的排序序号 ########### 选择距离最小的k个点 ############# classCount = {} for i in range(k): #sortedDistIndicies[i]表示第i小的样本在原样本集中的位置,从而找到相应的labels voteIlabel = labels[sortedDistIndicies[i]] #从字典classCount中获取key=voteIlabe对应的value,没有找到则返回0 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 ########### classCount统计了距离最小的k个点的label,对其排序得到最大值 ############# #operator.itemgetter(1),operator模块提供的itemgetter函数用于获取对象的哪些维的数据 #字典items()函数以列表返回可遍历的(键,值)元组数组 #因此,该句表示对字典中的元素按照值的大小进行降序排序 sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #排序后的第一个元素的值即为预测结果 return sortedClassCount[0][0] def handwritingClassTest(): hwLabels = [] trainingFileList = listdir('trainingDigits') m = len(trainingFileList) trainingMat = zeros((m,1024)) for i in range(m): fileNameStr = trainingFileList[i] fileStr = fileNameStr.split('.')[0] classNumStr = int(fileStr.split('_')[0]) hwLabels.append(classNumStr) trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) testFileList = listdir('testDigits') errorCount = 0.0 mTest = len(testFileList) for i in range(mTest): fileNameStr = testFileList[i] fileStr = fileNameStr.split('.')[0] classNumStr = int(fileStr.split('_')[0]) vectorUnderTest = img2vector('testDigits/%s' % fileNameStr) classifierResult = classify(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("the total error rate is: %f" % (errorCount/float(mTest))) handwritingClassTest()
使用到的数据在http://download.csdn.net/detail/nature_xd/9759044中可下载。
测试样本:2000个,每个样本大小为32*32,整理为1*1024
测试样本:900个
实际使用这个算法时,算法的执行效率并不高。因为算法需要为每个测试向量做2000次距离计算,每个距离计算包括1024个浮点运算,一共要执行900次,也就是有900*2000*1024次浮点数运算。
是否存在一种算法减少存储空间和计算时间的开销呢?
《机器学习实战》中说K决策树就是k近邻算法的优化版,可以节省大量的计算开销。
相关文章推荐
- 机器学习实战(一)——kNN算法
- 《机器学习实战》(1)——kNN算法
- 机器学习实战(第一章)---KNN算法
- 《机器学习实战》读书笔记(一) kNN算法
- 《机器学习实战》(一)knn算法
- 《机器学习实战》读书笔记
- 《机器学习实战》读书笔记 第四章 朴素贝叶斯(part 1)
- 《机器学习实战》中实例名称汇总
- [机器学习&数据挖掘]机器学习实战决策树plotTree函数完全解析
- 《机器学习实战》(二)k-近邻算法(KNN)
- 《机器学习实战》学习笔记:基于朴素贝叶斯的垃圾邮件过滤
- 《机器学习实战》笔记之七——利用AdaBoost元算法提高分类性能
- 机器学习笔记——(1)kNN算法
- 决策树实践,参考《机器学习实战》
- 机器学习实战——kNN算法
- 《机器学习实战》预测数值型数据-回归(Regression)
- 《机器学习实战》读书笔记之k-近邻算法
- 【2】机器学习实战 第三章 决策树的构造
- 机器学习实战(三)--朴素贝叶斯
- kNN算法的原理以及Python实现