关于本文说明,本人原博客地址位于http://blog.csdn.net/qq_37608890,本文来自笔者于2017年12月04日 22:54:26所撰写内容(http://blog.csdn.net/qq_37608890/article/details/78714664)。

    本文根据最近学习机器学习书籍 网络文章的情况,特将一些学习思路做了归纳整理,详情如下.如有不当之处,请各位大拿多多指点,在此谢过.

一、k-近邻算法(k-Nearest Neighbor,KNN)概述

1、简言之,k-近邻算法采用测量不同特征值之间的距离方法进行分类。

2、工作原理

存在一个样本数据集合,也称为训练样本集,且样本集中每个数据都存在标签,也就是众所周知样本集中每一数据与所属分类的对应关系。输入没有标签的新数据以后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般情况下,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最终,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

3、k-近邻算法的一般流程

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

4、相关特性

(1)优点: 精度高,对异常值不敏感、无输入假定。

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

(3)适用数据范围: 数值型和标称型。

kNN是non-parametric分类器(不做分布形式的假设,直接从数据估计概率密度),是memory-based learning,不适用于高维数据(curse of dimension),算法复杂度高(可用KD树优化)。另外,k越小越容易过拟合,但是k很大会将分类精度(设想极限情况:k=1和k=N(样本数))。

二、k-近邻场景

我们知道,电影可以按题材分类,但题材本身是如何定义的?由谁来判定某部电影属于哪个题材?即同一题材的电影会具有哪些公共特征?这些都是在做电影分类时一定要搞清楚的问题。这里以动作片和爱情片为例做简要说明。动作片具有哪些公共特征,使得动作片之间非常相似,却明显有别于爱情片?动作片中也可能有接吻镜头,爱情片中也可能存在打斗镜头,所以,不能简单地依靠是否存在打斗或者接吻来判断一部电影的类型。但很明显的是,动作片中的打斗镜头更多、爱情片中的接吻次数更频繁,基于此类场景在一部电影中出现的次数可用来进行电影分类。

有人曾经统计过很多电影的打斗镜头和接吻镜头, 下方图1-1 给出了6部电影的打斗和接吻镜头。假如现在有一部从未看过的电影,你如何判断它属于动作片还是爱情片呢?

图1-1

首先,我们弄清楚这部未知电影中存在多少打斗镜头、多少接吻镜头,图1-1中问号位置是该未知电影出现的镜头数图示,具体见下方表1-1。

表1-1每部电影的打斗镜头数、接吻镜头数及电影评估类型

由图1-1和表1-1,可用将未知电影在图1-1的具体位置标出,利用欧式距离公式,计算出未知电影与样本集中其他电影之间的距离,相见下方表2-2所示。

表2-2 已知电影与未知电影之间的距离

由表2-2所示,显然,如果样本集中所有电影与未知电影之间的距离按照递增排序的话,可以得到k个距离最近的电影,这里假设k=2的话,则未知电影与电影He’s Not Really into Dudes,Beautiful Woman影片类型最为相似,判定未知电影属于爱情片。

三、示例:使用kNN算法优化约会网站的配对效果

特别提醒:有些教材或博客,在代码实现过程中,由于python2.x和python3.x的不同,在实际执行过程中出现语法错误,这里特做提醒,print语句将输出内容一律加上();另外,python3.0版本后用input替换了raw_input,请读者注意。

1、项目概述

海伦在使用约会网站寻找自己的约会对象。总结经验之后,她发现曾交往过的人分三种类型:

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

她期待:

  • 工作日与魅力一般的人约会
  • 周末与极具魅力的人约会
  • 不喜欢的人则直接排除掉

2、  k-近邻算法开发实现流程

  • 收集数据:提供文本文件
    1.  准备数据:使用 Python 解析文本文件
    1.  分析数据:使用 Matplotlib 画二维散点图
    1.  训练算法:此步骤不适用于 k-近邻算法
    1.  测试算法:使用海伦提供的部分数据作为测试样本。 测试样本和非测试样本的区别在于: 测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
    1.  使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。

3、   准备数据:从文本文件中解析数据

目前,海伦搜集来一些约会对象的数据,并把这些数据存放在文本文件 datingTestSet2.txt中,每个样本数据占据一行,总共有 1000 行。这些样本数据主要包含以下 3 种特征:

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

而文本文件datingTestSet2.txt的数据格式如下:

  1. 40999 9.161978 1.110180 3
  2. 15823 0.991725 0.730979 2
  3. 35432 7.398380 0.684218 3
  4. 53711 12.149747 1.389088 3
  5. 64371 9.149678 0.874905 1
  6. 9289 9.666576 1.370330 2

准备数据:使用 Python 解析文本文件

将文本记录转换为 NumPy 的解析程序

  1. def file2matrix(filename):
  2. """
  3. Desc:
  4. 导入训练数据
  5. parameters:
  6. filename: 数据文件路径
  7. return:
  8. 数据矩阵 returnMat 和对应的类别 classLabelVector
  9. """
  10. fr = open(filename)
  11.  
  12. numberOfLines = len(fr.readlines())
  13.  
  14. returnMat = zeros((numberOfLines, 3)) # prepare matrix to return
  15. classLabelVector = [] # prepare labels return
  16. fr = open(filename)
  17. index = 0
  18. for line in fr.readlines():
  19.  
  20. line = line.strip()
  21.  
  22. listFromLine = line.split('\t')
  23.  
  24. returnMat[index, :] = listFromLine[0:3]
  25.  
  26. classLabelVector.append(int(listFromLine[-1]))
  27. index += 1
  28.  
  29. return returnMat, classLabelVector

执行如下命令

  1. datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
  2. datingDataMat

得到

  1. array([[ 4.09200000e+04, 8.32697600e+00, 9.53952000e-01],
  2. [ 1.44880000e+04, 7.15346900e+00, 1.67390400e+00],
  3. [ 2.60520000e+04, 1.44187100e+00, 8.05124000e-01],
  4. ...,
  5. [ 6.88460000e+04, 9.97471500e+00, 6.69787000e-01],
  6. [ 2.65750000e+04, 1.06501020e+01, 8.66627000e-01],
  7. [ 4.81110000e+04, 9.13452800e+00, 7.28045000e-01]])

执行 datingLabels[0:20],得到

  1. [3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3]

4、       分析数据:使用 Matplotlib 画二维散点图

 
  1. 执行如下代码
  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], 15.0*array(datingLabels), 15.0*array(datingLabels))
  6. plt.show()

得到如下图

而下图中采用矩阵的第一和第三列属性得到很好的展示效果,清晰地标识了三个不同的样本分类区域,具有不同爱好的人其类别区域也不同。

5、     准备数据:归一化数值

归一化数据 (归一化是一个让权重变为统一的过程)

序号 玩视频游戏所耗时间百分比 每年获得的飞行常客里程数 每周消费的冰淇淋公升数 样本分类
1 0.8 400 0.5 1
2 12 134 000 0.9 3
3 0 20 000 1.1 2
4 67 32 000 0.1 2

样本3和样本4的距离:

(0−67)2+(20000−32000)2+(1.1−0.1)2−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−√

归一化特征值,消除特征之间量级不同导致的影响

归一化定义: 我是这样认为的,归一化就是要把你需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。首先归一化是为了后面数据处理的方便,其次是保正程序运行时收敛加快。 方法有如下:

  • 线性函数转换,表达式如下:  

    y=(x-MinValue)/(MaxValue-MinValue)  

    说明:x、y分别为转换前、后的值,MaxValue、MinValue分别为样本的最大值和最小值。  

  • 对数函数转换,表达式如下:  

    y=log10(x)  

    说明:以10为底的对数函数转换。

    如图:

  • 反余切函数转换,表达式如下:

    y=atan(x)*2/PI 

    如图:

  • 式(1)将输入值换算为[-1,1]区间的值,在输出层用式(2)换算回初始值,其中和分别表示训练样本集中负荷的最大值和最小值。  

在统计学中,归一化的具体作用是归纳统一样本的统计分布性。归一化在0-1之间是统计的概率分布,归一化在-1--+1之间是统计的坐标分布。

  1. def autoNorm(dataSet):
  2. """
  3. Desc:
  4. 归一化特征值,消除特征之间量级不同导致的影响
  5. parameter:
  6. dataSet: 数据集
  7. return:
  8. 归一化后的数据集 normDataSet. ranges和minVals即最小值与范围,并没有用到
  9.  
  10. 归一化公式:
  11. Y = (X-Xmin)/(Xmax-Xmin)
  12. 其中的 min 和 max 分别是数据集中的最小特征值和最大特征值。该函数可以自动将数字特征值转化为0到1的区间。
  13. """
  14.  
  15. minVals = dataSet.min(0)
  16. maxVals = dataSet.max(0)
  17.  
  18. ranges = maxVals - minVals
  19. normDataSet = zeros(shape(dataSet))
  20. m = dataSet.shape[0]
  21.  
  22. normDataSet = dataSet - tile(minVals, (m, 1))
  23.  
  24. normDataSet = normDataSet / tile(ranges, (m, 1)) # element wise divide
  25. return normDataSet, ranges, minVals
训练算法:此步骤不适用于 k-近邻算法

因为测试数据每一次都要与全量的训练数据进行比较,所以这个过程是没有必要的。

6 、   测试算法

作为完整程序验证分类器

使用海伦提供的部分数据作为测试样本。如果预测分类与实际类别不同,则标记为一个错误。

kNN 分类器针对约会网站的测试代码

  1. def datingClassTest():
  2. """
  3. Desc:
  4. 对约会网站的测试方法
  5. parameters:
  6. none
  7. return:
  8. 错误数
  9. """
  10.  
  11. hoRatio = 0.1 # 测试范围,一部分测试一部分作为样本
  12.  
  13. datingDataMat, datingLabels = file2matrix('datingTestSet2.txt') # load data setfrom file
  14.  
  15. normMat, ranges, minVals = autoNorm(datingDataMat)
  16.  
  17. m = normMat.shape[0]
  18.  
  19. numTestVecs = int(m * hoRatio)
  20. print 'numTestVecs=', numTestVecs
  21. errorCount = 0.0
  22. for i in range(numTestVecs):
  23.  
  24. classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)
  25. print ("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]))
  26. if (classifierResult != datingLabels[i]): errorCount += 1.0
  27. print ("the total error rate is: %f" % (errorCount / float(numTestVecs)))
  28. print (errorCount)

7、   使用算法:构建完整可用的系统

上面已经在数据上对分类器进行了测试,现在则可以使用这个分类器帮助海伦对于约会对象进行分类,即海伦在网站上找到某人并输入他的信息,该程序会给出她对对方喜欢程度的预测值.

  1. def clasdifyPerson():
  2. resultList = ['not at all', 'in small doses', 'in large doses']
  3. percentTats = float(raw_input("percentage of time spent playing video games ?"))
  4. ffMiles = float(raw_input("frequent filer 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([ffMils, percentTats, iceCream])
  9. classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels, 3)
  10. print ("You will probably like this person: ", resultList[classifierResult - 1])

实际运行效果如下:

  1. >>> kNN.classifyPerson()
  2. percentage of time spent playing video games?10
  3. frequent flier miles earned per year?10000
  4. liters of ice cream consumed per year?0.5
  5. You will probably like this person: in small doses

四、   项目案例2: 手写数字识别系统

1、   项目概述

构造一个能识别数字 0 到 9 的基于 KNN 分类器的手写数字识别系统。

需要识别的数字是存储在文本文件中的具有相同的色彩和大小:宽高是 32 像素 * 32 像素的黑白图像。

2、   开发流程

  1. 收集数据:提供文本文件。 准备数据:编写函数 img2vector(), 将图像格式转换为分类器使用的向量格式 分析数据:在 Python 命令提示符中检查数据,确保它符合要求 训练算法:此步骤不适用于 KNN 测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的 区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同, 则标记为一个错误 使用算法:本例没有完成此步骤,若你感兴趣可以构建完整的应用程序,从图像中提取 数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统

收集数据: 提供文本文件

目录  trainingDigits中包含了大约 2000 个例子,每个例子内容如下图所示,每个数字大约有 200 个样本;目录 testDigits中包含了大约 900 个测试数据。

准备数据: 编写函数 img2vector(), 将图像文本数据转换为分类器使用的向量

将图像文本数据转换为向量

  1. def img2vector(filename):
  2. returnVect = zeros((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])
  8. return returnVect

分析数据:在 Python 命令提示符中检查数据,确保它符合要求

在 Python 命令行中输入下列命令测试 img2vector 函数,然后与文本编辑器打开的文件进行比较:

  1. >>> testVector = kNN.img2vector('testDigits/0_13.txt')
  2. >>> testVector[0,0:31]
  3. array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
  4. >>> testVector[0,31:63]
  5. array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])

训练算法:此步骤不适用于 KNN

因为测试数据每一次都要与全量的训练数据进行比较,所以这个过程是没有必要的。

测试算法:编写函数使用提供的部分数据集作为测试样本,如果预测分类与实际类别不同,则标记为一个错误
  1. def handwritingClassTest():
  2.  
  3. hwLabels = []
  4. trainingFileList = listdir('trainingDigits') # load the training set
  5. m = len(trainingFileList)
  6. trainingMat = zeros((m, 1024))
  7.  
  8. for i in range(m):
  9. fileNameStr = trainingFileList[i]
  10. fileStr = fileNameStr.split('.')[0] # take off .txt
  11. classNumStr = int(fileStr.split('_')[0])
  12. hwLabels.append(classNumStr)
  13. # 将 32*32的矩阵->1*1024的矩阵
  14. trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)
  15.  
  16. testFileList = listdir('testDigits') # iterate through the test set
  17. errorCount = 0.0
  18. mTest = len(testFileList)
  19. for i in range(mTest):
  20. fileNameStr = testFileList[i]
  21. fileStr = fileNameStr.split('.')[0] # take off .txt
  22. classNumStr = int(fileStr.split('_')[0])
  23. vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
  24. classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
  25. print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr)
  26. if (classifierResult != classNumStr): errorCount += 1.0
  27. print ("\nthe total number of errors is: %d" % errorCount)
  28. print ("\nthe total error rate is: %f" % (errorCount / float(mTest)))

使用算法:本例没有完成此步骤,若你感兴趣可以构建完整的应用程序,从图像中提取数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统

五、   K-近邻算法小结

截至目前,我们做一总结,k-近邻算法必须保存全部数据集,如果训练数据集很大,必须使用大量的存储空间.由于必须对数据集中的每个数据计算距离值,实际使用时可能非常耗时.此外,k-近邻还有以缺陷是它没办法给出任何数据的基础结构信息,也就无法得知平均实例样本和典型实例样本有哪些特征.而要解决这个问题,需要使用概率测量方法来处理.

k 近邻算法有 三个基本的要素:

  • k 值的选择

    • k 值的选择会对 k 近邻算法的结果产生重大的影响。
    • 如果选择较小的 k 值,就相当于用较小的邻域中的训练实例进行预测,“学习”的近似误差(approximation error)会减小,只有与输入实例较近的(相似的)训练实例才会对预测结果起作用。但缺点是“学习”的估计误差(estimation error)会增大,预测结果会对近邻的实例点非常敏感。如果邻近的实例点恰巧是噪声,预测就会出错。换句话说,k 值的减小就意味着整体模型变得复杂,容易发生过拟合。
    • 如果选择较大的 k 值,就相当于用较大的邻域中的训练实例进行预测。其优点是可以减少学习的估计误差。但缺点是学习的近似误差会增大。这时与输入实例较远的(不相似的)训练实例也会对预测起作用,使预测发生错误。 k 值的增大就意味着整体的模型变得简单。
    • 近似误差和估计误差,请看这里:https://www.zhihu.com/question/60793482
  • 距离度量

    • 特征空间中两个实例点的距离是两个实例点相似程度的反映。
    • k 近邻模型的特征空间一般是 n 维实数向量空间 。使用的距离是欧氏距离,但也可以是其他距离,如更一般的 距离,或者 Minkowski 距离。
  • 分类决策规则

    • k 近邻算法中的分类决策规则往往是多数表决,即由输入实例的 k 个邻近的训练实例中的多数类决定输入实例的类。

机器学习实践之K-近邻算法实践学习的更多相关文章

  1. 机器学习03:K近邻算法

    本文来自同步博客. P.S. 不知道怎么显示数学公式以及排版文章.所以如果觉得文章下面格式乱的话请自行跳转到上述链接.后续我将不再对数学公式进行截图,毕竟行内公式截图的话排版会很乱.看原博客地址会有更 ...

  2. 02机器学习实战之K近邻算法

    第2章 k-近邻算法 KNN 概述 k-近邻(kNN, k-NearestNeighbor)算法是一种基本分类与回归方法,我们这里只讨论分类问题中的 k-近邻算法. 一句话总结:近朱者赤近墨者黑! k ...

  3. 机器学习实战笔记--k近邻算法

    #encoding:utf-8 from numpy import * import operator import matplotlib import matplotlib.pyplot as pl ...

  4. 机器学习随笔01 - k近邻算法

    算法名称: k近邻算法 (kNN: k-Nearest Neighbor) 问题提出: 根据已有对象的归类数据,给新对象(事物)归类. 核心思想: 将对象分解为特征,因为对象的特征决定了事对象的分类. ...

  5. 《机器学习实战》-k近邻算法

    目录 K-近邻算法 k-近邻算法概述 解析和导入数据 使用 Python 导入数据 实施 kNN 分类算法 测试分类器 使用 k-近邻算法改进约会网站的配对效果 收集数据 准备数据:使用 Python ...

  6. 机器学习:1.K近邻算法

    1.简单案例:预测男女,根据身高,体重,鞋码 import numpy as np import matplotlib import sklearn from skleran.neighbors im ...

  7. 《机器学习实战》——K近邻算法

    三要素:距离度量.k值选择.分类决策 原理: (1) 输入点A,输入已知分类的数据集data (2) 求A与数据集中每个点的距离,归一化,并排序,选择距离最近的前K个点 (3) K个点进行投票,票数最 ...

  8. GridSearchCV网格搜索得到最佳超参数, 在K近邻算法中的应用

    最近在学习机器学习中的K近邻算法, KNeighborsClassifier 看似简单实则里面有很多的参数配置, 这些参数直接影响到预测的准确率. 很自然的问题就是如何找到最优参数配置? 这就需要用到 ...

  9. 机器学习之K近邻算法(KNN)

    机器学习之K近邻算法(KNN) 标签: python 算法 KNN 机械学习 苛求真理的欲望让我想要了解算法的本质,于是我开始了机械学习的算法之旅 from numpy import * import ...

随机推荐

  1. 离线缓存 manifest

    程序的离线缓存由一个叫做manifest的文本文件控制,把需要离线缓存的文件列在里面即可,这个列表还可以控制需要缓存的情况,甚至当用户从缓存地址进入到没有缓存的地址应该显示什么 当浏览器下载解析了ma ...

  2. 几种移动app API调用认证方案浅析

    最近做的金融项目,app调用的接口需要做一个身份认证,所以找了下目前API services验证的几种方式.之前翻译的一篇文章--[译]移动API安全终极指南中,主要提出了API服务调用验证的问题,通 ...

  3. Asp.Net MVC 使用 Ajax

    Asp.Net MVC 使用 Ajax Ajax 简单来说Ajax是一个无需重新加载整个网页的情况下,可以更新局部页面或数据的技术(异步的发送接收数据,不会干扰当前页面). Ajax工作原理 Ajax ...

  4. robotframework自动化系列:新增流程

    刚接手项目的时候,要求所有流程在上线之前必须确保正向操作是正确的:这个时候又有新的模块需要测试,所以引入自动化测试是非常有必要的!通过对比,尝试使用RF进行自动化的回归测试. 测试中最常见的操作就是增 ...

  5. 初识Java网络编程

    事实上网络编程简单的理解就是两台计算机相互通讯数据而已,对于程序员而言,去掌握一种编程接口并使用一种编程模型相对就会显得简单的多了,Java SDK提供一些相对简单的Api来完成这些工作.Socket ...

  6. [转载] Storm:最火的流式处理框架

    转载自http://www.cnblogs.com/langtianya/p/5199529.html 伴随着信息科技日新月异的发展,信息呈现出爆发式的膨胀,人们获取信息的途径也更加多样.更加便捷,同 ...

  7. pt-tcp-model

    http://blog.9minutesnooze.com/analyzing-http-traffic-tcpdump-perconas-pttcpmodel/ #获取200k个packets tc ...

  8. 如何用while循环输出十行十列变色★☆

    输出十行十列星星 k = 0 #设置一个终止变量 while k < 10: i = 0 #设置一个满十换行变量 while i < 10: print('★',end='') i += ...

  9. python从2.7升级到3.5 需要主要的问题

    今天鼓足勇气把我们python从2.7升级到3.5.我用的是pycharm开发工具.升级过程遇到一些问题小结下: 1.右击项目: 找到左侧 project interpreter选项,配置项目开发环境 ...

  10. 理解MVC入门基础原理

    今天,我将开启一个崭新的话题:ASP.NET MVC框架的探讨.首先,我们回顾一下ASP.NET Web Form技术与ASP.NET MVC的异同点,并展示各自在Web领域的优劣点.在讨论之前,我对 ...