![647589a53e1fa25e3dcc82a64f7e862c.png](https://img-blog.csdnimg.cn/img_convert/647589a53e1fa25e3dcc82a64f7e862c.png)
KNN 概述
k-近邻(kNN, k-NearestNeighbor)算法是一种基本分类与回归方法,我们这里只讨论分类问题中的 k-近邻算法。
一句话总结:近朱者赤近墨者黑!
k 近邻算法的输入为实例的特征向量,对应于特征空间的点;输出为实例的类别,可以取多类。k 近邻算法假设给定一个训练数据集,其中的实例类别已定。分类时,对新的实例,根据其 k 个最近邻的训练实例的类别,通过多数表决等方式进行预测。因此,k近邻算法不具有显式的学习过程。
k 近邻算法实际上利用训练数据集对特征向量空间进行划分,并作为其分类的“模型”。 k值的选择、距离度量以及分类决策规则是k近邻算法的三个基本要素。
KNN 场景
电影可以按照题材分类,那么如何区分 动作片 和 爱情片 呢?
- 动作片:打斗次数更多
- 爱情片:亲吻次数更多
基于电影中的亲吻、打斗出现的次数,使用 k-近邻算法构造程序,就可以自动划分电影的题材类型。
![1f4cfd9f5591e549b843b557c9abf863.png](https://img-blog.csdnimg.cn/img_convert/1f4cfd9f5591e549b843b557c9abf863.png)
现在根据上面我们得到的样本集中所有电影与未知电影的距离,按照距离递增排序,可以找到 k 个距离最近的电影。
假定 k=3,则三个最靠近的电影依次是, He's Not Really into Dudes 、 Beautiful Woman 和 California Man。
knn 算法按照距离最近的三部电影的类型,决定未知电影的类型,而这三部电影全是爱情片,因此我们判定未知电影是爱情片。
KNN 原理
KNN 工作原理
- 假设有一个带有标签的样本数据集(训练样本集),其中包含每条数据与所属分类的对应关系。
- 输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较。
- 计算新数据与样本数据集中每条数据的距离。
- 对求得的所有距离进行排序(从小到大,越小表示越相似)。
- 取前 k (k 一般小于等于 20 )个样本数据对应的分类标签。
- 求 k 个数据中出现次数最多的分类标签作为新数据的分类。
KNN 通俗理解
给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的 k 个实例,这 k 个实例的多数属于某个类,就把该输入实例分为这个类。
KNN 开发流程
收集数据:任何方法准备数据:距离计算所需要的数值,最好是结构化的数据格式分析数据:任何方法训练算法:此步骤不适用于 k-近邻算法测试算法:计算错误率使用算法:输入样本数据和结构化的输出结果,然后运行 k-近邻算法判断输入数据分类属于哪个分类,最后对计算出的分类执行后续处理
KNN 算法特点
优点:精度高、对异常值不敏感、无数据输入假定缺点:计算复杂度高、空间复杂度高适用数据范围:数值型和标称型
KNN 项目案例
项目案例1: 优化约会网站的配对效果
项目概述
海伦使用约会网站寻找约会对象。经过一段时间之后,她发现曾交往过三种类型的人:
- 不喜欢的人
- 魅力一般的人
- 极具魅力的人
她希望:
- 工作日与魅力一般的人约会
- 周末与极具魅力的人约会
- 不喜欢的人则直接排除掉
现在她收集到了一些约会网站未曾记录的数据信息,这更有助于匹配对象的归类。
开发流程
收集数据:提供文本文件准备数据:使用 Python 解析文本文件分析数据:使用 Matplotlib 画二维散点图训练算法:此步骤不适用于 k-近邻算法测试算法:使用海伦提供的部分数据作为测试样本。 测试样本和非测试样本的区别在于: 测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。
收集数据:提供文本文件
海伦把这些约会对象的数据存放在文本文件 datingTestSet2.txt 中,总共有 1000 行。海伦约会的对象主要包含以下 3 种特征:
- 每年获得的飞行常客里程数
- 玩视频游戏所耗时间百分比
- 每周消费的冰淇淋公升数
文本文件数据格式如下:
409208.3269760.9539523144887.1534691.6739042260521.4418710.80512417513613.1473940.4289641383441.6697880.1342961
准备数据:使用 Python 解析文本文件
将文本记录转换为 NumPy 的解析程序
def file2matrix(filename): """ Desc: 导入训练数据 parameters: filename: 数据文件路径 return: 数据矩阵 returnMat 和对应的类别 classLabelVector """ fr = open(filename) # 获得文件中的数据行的行数 numberOfLines = len(fr.readlines()) # 生成对应的空矩阵 # 例如:zeros(2,3)就是生成一个 2*3的矩阵,各个位置上全是 0 returnMat = zeros((numberOfLines, 3)) # prepare matrix to return classLabelVector = [] # prepare labels return fr = open(filename) index = 0 for line in fr.readlines(): # str.strip([chars]) --返回移除字符串头尾指定的字符生成的新字符串 line = line.strip() # 以 '' 切割字符串 listFromLine = line.split('') # 每列的属性数据 returnMat[index, :] = listFromLine[0:3] # 每列的类别数据,就是 label 标签数据 classLabelVector.append(int(listFromLine[-1])) index += 1 # 返回数据矩阵returnMat和对应的类别classLabelVector return returnMat, classLabelVector
分析数据:使用 Matplotlib 画二维散点图
import matplotlibimport matplotlib.pyplot as pltfig = plt.figure()ax = fig.add_subplot(111)ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0*array(datingLabels), 15.0*array(datingLabels))plt.show()
下图中采用矩阵的第一和第三列属性得到很好的展示效果,清晰地标识了三个不同的样本分类区域,具有不同爱好的人其类别区域也不同。
![b46d08b0b87d21f6d8aeba0199e137da.png](https://img-blog.csdnimg.cn/img_convert/b46d08b0b87d21f6d8aeba0199e137da.png)
- 归一化数据 (归一化是一个让权重变为统一的过程,更多细节请参考: https://www.zhihu.com/question/19951858 )
序号玩视频游戏所耗时间百分比每年获得的飞行常客里程数每周消费的冰淇淋公升数样本分类10.84000.51212134 0000.933020 0001.1246732 0000.12
归一化定义: 我是这样认为的,归一化就是要把你需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。首先归一化是为了后面数据处理的方便,其次是保正程序运行时收敛加快。 方法有如下:
- 线性函数转换,表达式如下:
- y=(x-MinValue)/(MaxValue-MinValue)
- 说明:x、y分别为转换前、后的值,MaxValue、MinValue分别为样本的最大值和最小值。
- 对数函数转换,表达式如下:
- y=log10(x)
- 说明:以10为底的对数函数转换。
- 如图:
![6bb1b62c71b632a88d324cb764c2eabe.png](https://img-blog.csdnimg.cn/img_convert/6bb1b62c71b632a88d324cb764c2eabe.png)
- 反余切函数转换,表达式如下:
- y=atan(x)*2/PI
- 如图:
![23d6b66851bc4811477f28682c3f274e.png](https://img-blog.csdnimg.cn/img_convert/23d6b66851bc4811477f28682c3f274e.png)
- 式(1)将输入值换算为[-1,1]区间的值,在输出层用式(2)换算回初始值,其中和分别表示训练样本集中负荷的最大值和最小值。
在统计学中,归一化的具体作用是归纳统一样本的统计分布性。归一化在0-1之间是统计的概率分布,归一化在-1--+1之间是统计的坐标分布。
def autoNorm(dataSet): """ Desc: 归一化特征值,消除特征之间量级不同导致的影响 parameter: dataSet: 数据集 return: 归一化后的数据集 normDataSet. ranges和minVals即最小值与范围,并没有用到 归一化公式: Y = (X-Xmin)/(Xmax-Xmin) 其中的 min 和 max 分别是数据集中的最小特征值和最大特征值。该函数可以自动将数字特征值转化为0到1的区间。 """ # 计算每种属性的最大值、最小值、范围 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)) # element wise divide return normDataSet, ranges, minVals
训练算法:此步骤不适用于 k-近邻算法
因为测试数据每一次都要与全量的训练数据进行比较,所以这个过程是没有必要的。
测试算法:使用海伦提供的部分数据作为测试样本。如果预测分类与实际类别不同,则标记为一个错误。
kNN 分类器针对约会网站的测试代码
def datingClassTest(): """ Desc: 对约会网站的测试方法 parameters: none return: 错误数 """ # 设置测试数据的的一个比例(训练数据集比例=1-hoRatio) hoRatio = 0.1 # 测试范围,一部分测试一部分作为样本 # 从文件中加载数据 datingDataMat, datingLabels = file2matrix('input/2.KNN/datingTestSet2.txt') # load data setfrom file # 归一化数据 normMat, ranges, minVals = autoNorm(datingDataMat) # m 表示数据的行数,即矩阵的第一维 m = normMat.shape[0] # 设置测试的样本数量, numTestVecs:m表示训练样本的数量 numTestVecs = int(m * hoRatio) print 'numTestVecs=', numTestVecs errorCount = 0.0 for i in range(numTestVecs): # 对数据测试 classifierResult = classify0(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)) print errorCount
使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。
约会网站预测函数
def clasdifyPerson(): 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 = file2matrix('datingTestSet2.txt') normMat, ranges, minVals = autoNorm(datingDataMat) inArr = array([ffMils, percentTats, iceCream]) classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels, 3) print "You will probably like this person: