K-近邻算法


1. 概念

测量不同特征值之间的距离来进行分类

优点:精度高、对异常值不敏感、无数据输入假定

缺点:计算复杂度高、空间复杂度高。

适用范围:数值型和标称型

工作原理:

存在一个样本数据合计,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似(最近邻)数据的分类标签。一般会选择样本数据集中前K个最相似的数据,K通常不大于20的整数,选择K个最相似数据中出现次数最多的分类,作为新数据的分类。

一般流程:

  1.  收集数据:任何方法
  2.  准备数据:距离计算所需要的数值,最好是结构化数据格式
  3.  分析数据:任何方法
  4.  训练算法:不适用
  5.  测试算法:计算错误率
  6.  使用算法:输入样本数据和结构化的输出结果,运行K近邻算法判断输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理

2. Python 实现

1. 数据输入

建立一个KNN.py文件

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

operator:运算符模块

createDataSet函数用于创建simple数据集和标签

在Console中

import KNN

group,labels = KNN.createDataSet()

group
Out[69]: 
array([[ 1. ,  1.1],
       [ 1. ,  1. ],
       [ 0. ,  0. ],
       [ 0. ,  0.1]])

labels
Out[70]: ['A', 'A', 'B', 'B']

通过KNN中的createDataSet函数创建变量group和labels

4个样本集在坐标轴上的位置:

2. 实施KNN分类算法

实现步骤:

  1.  计算已知类别数据集中的点与当前点之间的距离;
  2.  按照距离递增次序排序;
  3.  选取与当前点距离最小的k个点;
  4.  确定前k个点所在类别的出现频率;
  5.  返回前k个点出现频率最高的类别作为当前点的预测分类;

在KNN.py文件中创建classify0函数

 1 def classify0(inX,dataSet,labels,k):
 2     dataSetSize = dataSet.shape[0]
 3     #距离计算
 4     diffMat = tile(inX,(dataSetSize,1)) - dataSet
 5     sqDiffMat = diffMat ** 2
 6     sqDistances = sqDiffMat.sum(axis = 1)
 7     distances = sqDistances ** 0.5
 8     sortedDistIndicies = distances.argsort()
 9     classCount = {}
10     for i in range(k):
11         voteIlabel = labels[sortedDistIndicies[i]]
12         classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
13     sortedClassCount = sorted(classCount.iteritems(),
14                               key=operator.itemgetter(1),
15                               reverse=True)
16     return sortedClassCount[0][0]
参数:
  • inX: 输入样本
  • dataSet: 训练样本集
  • labels:训练样本集标签
  • k: 选取k个点
代码中使用的python方法:
  • dataSet.shape():返回数据集维度(几行几列),这里需要返回有几条数据(几行) 可以用len(dataSet)替代?
  • tile(inX,(dataSetSize,1)):将输入样本放入(并复制)到一个与数据集相同大小的矩阵
疑问:为什么不直接减?

tile(inX,(dataSetSize,1))- dataSet  与 inX - dataSet        结果一样

sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True):排序,reverse=True降序排列

计算距离公式

勾股定律:

重新装载KNN模块,python提示符中使用classify0函数来预测数据

1 reload(KNN)
2 Out[101]: <module 'KNN' from 'KNN.py'>
3 
4 KNN.classify0([0,0],group,labels,3)
5 Out[102]: 'B'

3. 示例:使用K近邻算法改进约会网站的配对效果

示例说明:

样本集中包含三个特征值:

  • 每年获得的飞行常客里程数
  • 玩视频游戏所耗时间百分比
  • 每周消费的冰激凌公升数

标签种类:

  • 不喜欢的人
  • 魅力一般的人
  • 极具魅力的人

1. 从文本文件中解析数据

文本文件datingTestSet.txt

在KNN.py中增加文本转换为Numpy的解析函数

 1 def file2matrix(filename):
 2     fr = open(filename)
 3     array0lines = fr.readlines()
 4     numberOfLines = len(array0lines)
 5     returnMat = np.zeros((numberOfLines,3))
 6     classLabelVector = []
 7     index = 0
 8     for line in array0lines:
 9         line = line.strip()
10         linstFromLine = line.split('\t')
11         returnMat[index,:] = linstFromLine[0:3]
12         classLabelVector.append(int(linstFromLine[-1]))
13         index +=1
14     return returnMat,classLabelVector
参数:
  • filename:文件名
代码中使用的python方法:
  • np.zeros((numberOfLines,3)) :创建以零填充的矩阵
  • line.strip():去除回车字符
  • line.split('\t'):以\t为分隔符分割字符

python命令提示符下输入命令:

 1 reload(KNN)
 2 Out[111]: <module 'KNN' from 'KNN.pyc'>
 3 
 4 datingDataMat,datingLabels = KNN.file2matrix('datingTestSet2.txt')
 5 
 6 datingDataMat
 7 Out[113]: 
 8 array([[  4.09200000e+04,   8.32697600e+00,   9.53952000e-01],
 9        [  1.44880000e+04,   7.15346900e+00,   1.67390400e+00],
10        [  2.60520000e+04,   1.44187100e+00,   8.05124000e-01],
11        ..., 
12        [  2.65750000e+04,   1.06501020e+01,   8.66627000e-01],
13        [  4.81110000e+04,   9.13452800e+00,   7.28045000e-01],
14        [  4.37570000e+04,   7.88260100e+00,   1.33244600e+00]])
15 
16 datingLabels[:5]
17 Out[114]: [3, 2, 1, 1, 1]

2. 分析数据:使用Matplotlib创建散点图

1 import matplotlib
2 import matplotlib.pyplot as plt
3 fig = plt.figure()
4 ax = fig.add_subplot(111)
5 ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
6 fig

1 ax.scatter(datingDataMat[:,1],datingDataMat[:,2],
2            15.0*array(datingLabels),15.0*array(datingLabels))
3 fig

1 ax.scatter(datingDataMat[:,0],datingDataMat[:,1],
2            15.0*array(datingLabels),15.0*array(datingLabels))
3 fig

3. 准备数据:归一化数值

三个特征值计算样本距离:

每年获得飞行常客里程数远大于其他特征值需要转化为0-1区间內的值

newValue = (oldValue-min)/(max-min)

在KNN.py中增加归一化特征值函数

 1 def autoNorm(dataSet):
 2     minVals = dataSet.min(0)
 3     maxVals = dataSet.max(0)
 4     ranges = maxVals - minVals
 5     normDataSet = zeros(shape(dataSet))
 6     m = dataSet.shape[0]
 7     normDataSet = dataSet - tile(minVals,(m,1))
 8     normDataSet = normDataSet/tile(ranges,(m,1))
 9     return normDataSet,ranges,minVals

执行归一化函数

 1 reload(KNN)
 2 Out[145]: <module 'KNN' from 'KNN.py'>
 3 
 4 normMat,ranges,minVals = KNN.autoNorm(datingDataMat)
 5 
 6 normMat
 7 Out[147]: 
 8 array([[ 0.44832535,  0.39805139,  0.56233353],
 9        [ 0.15873259,  0.34195467,  0.98724416],
10        [ 0.28542943,  0.06892523,  0.47449629],
11        ..., 
12        [ 0.29115949,  0.50910294,  0.51079493],
13        [ 0.52711097,  0.43665451,  0.4290048 ],
14        [ 0.47940793,  0.3768091 ,  0.78571804]])
15 
16 ranges
17 Out[148]: array([  9.12730000e+04,   2.09193490e+01,   1.69436100e+00])
18 
19 minVals
20 Out[149]: array([ 0.      ,  0.      ,  0.001156])

 4. 测试算法:作为完整程序验证分类器

 在KNN.py文件中添加测试函数datingClassTest

 1 def datingClassTest():
 2     hoRatio = 0.1
 3     datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
 4     normMat,ranges,minVals = autoNorm(datingDataMat)
 5     m = normMat.shape[0]
 6     numTestVecs = int(m * hoRatio)
 7     errorCount = 0.0
 8     for i in range(numTestVecs):
 9         classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],
10                                      datingLabels[numTestVecs:m],3)
11         print "the classifier came back with: %d, the real answer is: %d" % (classifierResult,datingLabels[i])
12         if (classifierResult != datingLabels[i]):
13             errorCount += 1.0
14     print "the total error rate is: %f" % (errorCount/float(numTestVecs))

 在python命令提示符中运行datingClassTest()

1 reload(KNN)
2 Out[153]: <module 'KNN' from 'KNN.py'>
3 
4 KNN.datingClassTest()
5 the classifier came back with: 3, the real answer is: 3
6 the classifier came back with: 2, the real answer is: 2
7 the classifier came back with: 1, the real answer is: 1
8 the classifier came back with: 3, the real answer is: 1
9 the total error rate is: 0.050000

错误率为5%

 5. 使用算法:构建完整可用系统

在KNN.py中加入classifyPerson()函数:

 1 def classifyPerson():
 2     resultList = ['not at all','insmall doses','in large doses']
 3     percentTats = float(raw_input("percentage of time spent playing video games?"))
 4     ffMiles = float(raw_input("frequent flier miles earned per year?"))
 5     iceCream = float(raw_input("liters of ice cream consumed per year?"))
 6     datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
 7     normMat , ranges, minVals = autoNorm(datingDataMat)
 8     inArr = array([ffMiles,percentTats,iceCream])
 9     classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
10     print "You will probably like this person:",resultList[classifierResult - 1]

在python命令提示行执行classifyPerson函数

 1 reload(KNN)
 2 Out[155]: <module 'KNN' from 'KNN.py'>
 3 
 4 KNN.classifyPerson()
 5 
 6 percentage of time spent playing video games?10 7 8 frequent flier miles earned per year?10000 9 10 liters of ice cream consumed per year?0.5 11 You will probably like this person: insmall doses

 4. 示例:手写数字识别系统

将图像转换为测试向量,将32*32的二进制图像矩阵转换为1*1024的向量

创建函数img2vector

1 def img2vector(filename):
2     returnVect = zeros(1,1024) #创建1*1024的矩阵
3     fr = open(filename) 
4     for i in range(32):
5         lineStr = fr.readline()
6         for j in range(32):
7             returnVect[0,32*i+j] = int(lineStr[j])

测试算法:使用k近邻算法识别手写数字

 1 def handwritingClassTest():
 2     hwLabels = []
 3     trainingFileList = listdir('trainingDigits') #获取目录内容
 4     m = len(trainingFileList)
 5     trainingMat = zeros((m,1024))
 6     #获取训练集向量和标签
 7     for i in range(m):
 8         #解析文件名
 9         fileNameStr = trainingFileList[i]
10         fileStr = fileNameStr.split('.')[0]
11         classNumStr = int(fileStr.split('_')[0])
12         hwLabels.append(classNumStr)
13         trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
14     testFileList = listdir('testDigits')
15     errorCount = 0
16     mTest = len(testFileList)
17     #获取测试集向量进行测试并将测试结果与正式结果比对
18     for i in range(mTest):
19         fileNameStr = testFileList[i]
20         fileStr = fileNameStr.split('.')[0]
21         classNumStr = int(fileStr.split('_')[0])
22         vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
23         classifierResult = KNN.classify0(vectorUnderTest,trainingMat,hwLabels,3)
24         print "the classifier came back with %d,the real answer is:%d" % (classifierResult,classNumStr)
25         if classifierResult != classNumStr:
26             errorCount += 1.0
27     print '\n the total number of errors is:%d' % errorCount
28     print '\n the total error rate is:%f' % (errorCount/float(mTest))

在python命令行运行handwritingClassTest()函数

handwritingClassTest()

the classifier came back with 9,the real answer is:9
the classifier came back with 9,the real answer is:9
the classifier came back with 9,the real answer is:9

 the total number of errors is:11

 the total error rate is:0.011628

错误率1.1628%

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM