机器学习之KNN算法原理及Python实现方法详解


Posted in Python onJuly 09, 2018

本文实例讲述了机器学习之KNN算法原理及Python实现方法。分享给大家供大家参考,具体如下:

文中代码出自《机器学习实战》CH02,可参考本站:

KNN算法介绍

KNN是一种监督学习算法,通过计算新数据与训练数据特征值之间的距离,然后选取K(K>=1)个距离最近的邻居进行分类判(投票法)或者回归。若K=1,新数据被简单分配给其近邻的类。

KNN算法实现过程

(1)选择一种距离计算方式, 通过数据所有的特征计算新数据与已知类别数据集中的数据点的距离;

(2)按照距离递增次序进行排序,选取与当前距离最小的k个点;

(3)对于离散分类,返回k个点出现频率最多的类别作预测分类;对于回归则返回k个点的加权值作为预测值;

算法关键

(1)数据的所有特征都要做可比较的量化

若是数据特征中存在非数值的类型,必须采取手段将其量化为数值。例如样本特征中包含颜色,可通过将颜色转换为灰度值来实现距离计算。

(2)样本特征要做归一化处理

样本有多个参数,每一个参数都有自己的定义域和取值范围,他们对距离计算的影响不一样,如取值较大的影响力会盖过取值较小的参数。所以样本参数必须做一些scale处理,最简单的方式就是所有特征的数值都采取归一化处置。

(3)需要一个距离函数以计算两个样本之间的距离

距离的定义:欧氏距离、余弦距离、汉明距离、曼哈顿距离等,一般选欧氏距离作为距离度量,但是这是只适用于连续变量。在文本分类这种非连续变量情况下,汉明距离可以用来作为度量。通常情况下,如果运用一些特殊的算法来计算度量的话,K近邻分类精度可显著提高,如运用大边缘最近邻法或者近邻成分分析法。

(4)确定K的值

K值选的太大易引起欠拟合,太小容易过拟合。交叉验证确定K值。

KNN分类

分类算法常采用多数表决决定。一个缺点是出现频率较多的样本将会主导测试点的预测结果。解决这个缺点的方法之一是在进行分类时将K个邻居到测试点的距离考虑进去。若样本到测试点距离d,则选1/d为该邻居的权重,统计k个邻居所有类标签的权重和,值最大的就是新数据点的预测类标签。

KNN回归

KNN回归是取K个邻居类标签值得加权作为新数据点的预测值。

优缺点

(1)KNN算法的优点

  • 1.简单、有效。
  • 2.重新训练的代价较低(类别体系的变化和训练集的变化,在Web环境和电子商务应用中是很常见的)。
  • 3.计算时间和空间线性于训练集的规模(在一些场合不算太大)。
  • 4.由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。
  • 5.该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。

(2)KNN算法缺点

  • 1.KNN算法是懒散学习方法(lazy learning,基本上不学习),一些积极学习的算法要快很多。
  • 2.类别评分不是规格化的(不像概率评分)(???)。
  • 3.输出的可解释性不强,例如决策树的可解释性较强。
  • 4.该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。该算法只计算最近的邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进。
  • 5.计算量较大。目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本。

KNN实现

import numpy as np
import operator
import matplotlib
import matplotlib.pyplot as plt
from os import listdir
def Create_DataSet():
 group = np.array([[1.0, 1.1],[1.0,1.0],[0,0],[0,0.1]])
 labels = ['A','A','B','B']
 return group,labels

函数Create_DataSet创建一个数据集,坐标轴左下角分类为B,右上角分类为A。

下面函数classify0,计算向量inX与数据集中各点的距离,计算n_estimators个近邻中label频率最高的分类号并返回作为向量inX的分类号。

def classify0(inX, dataSet, labels, n_estimators=3):
 dataSetSize = dataSet.shape[0]
 #print 'in classify0,dataSetSize = \n',dataSetSize
 #转变向量inx格式为datasize行,1列;并计算与dataset元素距离
 diffMat = np.tile(inX, (dataSetSize,1)) - dataSet
 #计算欧氏距离((x0-x1)^2 + (y0-y1)^2 )^(1/2)
 sqDiffMat = diffMat**2 #diffMat每个元素取平方
 sqDistances = sqDiffMat.sum(axis=1)
 distances = sqDistances**0.5
 #排序,将值从小到大排列,返回索引
 sortedDistIndicies = distances.argsort()
 #print 'in classify0,sortedDistIndicies:\n',sortedDistIndicies
 #求与距离最近的k个点的label统计
 classCount={}
 for i in range(n_estimators):
  voteIlabel = labels[sortedDistIndicies[i]] #获取对应label号
  classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
 #对字典排序,按value值降序排列
 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
 #print 'sortedClassCount[0][0]:\n',sortedClassCount[0][0]
 return sortedClassCount[0][0]

dataSet.shape()函数用于获取矩阵dataSet的大小,shape[0]返回对应行数,shape[1]返回对应列数。

因为需要对每列属性做距离运算,所以需要将输入inX转换为和dataSet相同行数和列数的矩阵,因为inX列数与dataSet中每个元素列数相同,所以需要将其行数进行扩展,np.tile(inX, (dataSetSize,1))将inX行数拓展为dataSetSize行,1表示纵向复制1次,即列不变。

距离公式采用欧式距离计算,得到的距离值为一维列表,分别对应dataSet中每个元素和inX的距离。distances.argsort() 将距离按从小到大排列,并返回索引。例如distance = [0.1,0.5,0.3],distance.argsort()返回[1,3,2] 。返回索引是为了找到对应的label值,并进行统计。

下面for循环用于建立字典并统计前n_estimators个label的个数。key对应label,key_value对应个数。

operator.itemgetter函数,operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号,即需要获取的数据在对象中的序号;例如a = [1,2,3] ,定义函数b=operator.itemgetter(1),获取对象的第1个域的值,则 b(a)=2;若定义函数b,获取对象的第1个域和第0个的值b=operator.itemgetter(1,0),则b(a)=(2, 1) 。注意operator.itemgetter函数获取的不是值,而是定义了一个函数,通过该函数作用到对象上才能获取值;

sorted函数:Python内置的排序函数sorted可以对list或者iterator进行排序;第一个参数iterable指定要排序的list或者iterable,第二个参数指定排序时进行比较的函数,可以指定一个函数或者lambda函数。例如students为类对象的list,每个成员有三个域,用sorted进行比较时可以自己定cmp函数,例如这里要通过比较第三个数据成员来排序,students = [(‘john', ‘A', 15), (‘jane', ‘B', 12), (‘dave', ‘B', 10)],sorted(students, key=lambda student : student[2]),key为函数,指定取待排序元素的哪一项进行排序,key指定的lambda函数功能是去元素student的第三个域(student[2]),因此sorted排序时会以students所有元素的第三个域来进行排序;也可以这么写sorted(students, key=operator.itemgetter(2)) ,sorted函数也可以进行多级排序,例如要根据第二个域和第三个域进行排序;sorted(students, key=operator.itemgetter(1,2))即先跟句第二个域排序,再根据第三个域排序;第三个参数reverse是一个bool变量,表示升序还是降序排列,默认为false升序排列,定义为True时将按降序排列。

此处sort函数用于对字典进行排序。按key_value降序排列,即对应label个数从大到小排列。返回值为列表,列表元素为元组,元组第一个元素对应label,第二个元素对应label个数。sortedClassCount[0][0]即返回label次数最多的类标号,为inX的label。

下面测试一个简单的向量:

group,labels = Create_DataSet()
sortedClassCount = classify0([0,0.5],group,labels,3)

输出为

sortedClassCount:[(‘B', 2), (‘A', 1)]
sortedClassCount[0][0]:
B

下面函数file2matrix用于从txt中读取原始数据并转化为矩阵。

test.txt格式为

40920 8.326976 0.953952 largeDoses
14488 7.153469 1.673904 smallDoses
26052 1.441871 0.805124 didntLike
75136 13.147394 0.428964 didntLike
……

最后一列为label,值为largeDoses、smallDoses或didntLike。每行元素用\t隔开。转换后label分别对应3、2、1。

转换函数如下:

def file2matrix(filename):
 fr = open(filename)
 numberOfLines = len(fr.readlines())
 print 'in file2matrix,numberOfLines:\n',numberOfLines
 returnMat = np.zeros((numberOfLines,3))
 classLabelVector = []
 fr = open(filename)
 index = 0
 for line in fr.readlines(): #遍历每一行
  line = line.strip() #strip用于删除字符,此处删除空白字符,回车
  listFromLine = line.split('\t') #获取每行的元素列表,元素用\t分开
  returnMat[index,:] = listFromLine[0:3]#取前3个元素,对应属性集
  if(listFromLine[-1] == 'largeDoses'):#有什么有效的方法 将属性值和类标号分开,相互对应
   classLabelVector.append(3)
  elif(listFromLine[-1] == 'smallDoses'):
   classLabelVector.append(2)
  elif(listFromLine[-1] == 'didntLike'):
   classLabelVector.append(1)
  elif(listFromLine[-1] == 3):
   classLabelVector.append(3)
  elif(listFromLine[-1] == 2):
   classLabelVector.append(2)
  elif(listFromLine[-1] == 1):
   classLabelVector.append(1)
  index += 1
 #print 'returnMat = ',returnMat
 #print 'classLabelVector = ',classLabelVector
 return returnMat,classLabelVector #得到属性集和类标号

首先打开文件并获取行数,建立一个相同大小的空矩阵,用于存储转换后的属性集,并新建一个一维列表,用于存放类标号。fr.readlines()读取所有行,得到一个行列表,列表元素为每行内容;readline只读取1行,获取该行元素的列表。
上述函数即返回属性集矩阵和类标号列表。

因为属性值差距较大,为了减少值太大的属性对值小的属性的影响,分类之前还需要进行归一化。归一化方程为(datain-min_val) / (max_val - min_val),输出值都介于0-1。

def autoNorm(dataSet):
 minVals = dataSet.min(0) #获取每列最大值与最小值,(0)指定列,而不是行
 print 'in autoNorm,minVals:',minVals
 maxVals = dataSet.max(0)
 print 'in autoNorm,maxVals:',maxVals
 ranges = maxVals - minVals
 print 'in autoNorm,ranges:',ranges
 normDataSet = np.zeros(np.shape(dataSet))
 m = dataSet.shape[0] #获取行数
 #特征值矩阵为1000x3,minVals值为1x3,使用tile函数扩展为相同大小的矩阵
 #np.tile(minVals, (m,1))矩阵minval,横向复制m次,纵向复制1次
 normDataSet = dataSet - np.tile(minVals, (m,1)) # (data - minval)/(maxval - minval)
 normDataSet = normDataSet/np.tile(ranges, (m,1)) #element wise divide
 print 'in autoNorm,normDataSet = ',normDataSet
 return normDataSet, ranges, minVals

返回归一化以后的属性集。即可进行距离运算并分类。

下面函数即对文件中所有输入的行向量属性进行分类

def datingClassTest(n_estimators=3):
 hoRatio = 0.50
 #(1)读取文件
 datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
 #(2)归一化
 normMat, ranges, minVals = autoNorm(datingDataMat)
 m = normMat.shape[0]
 numTestVecs = int(m*hoRatio)
 errorCount = 0.0
 for i in range(numTestVecs):
  classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],n_estimators=n_estimators)
  if (classifierResult != datingLabels[i]): errorCount += 1.0
 print "in datingClassTest,the total error rate is: %f" % (errorCount/float(numTestVecs))
 print 'in datingClassTest,errorCount:',errorCount

将测试文件分为数据集和用于测试的向量2部分。前一半用于测试,后一半作为数据集,并定义errorCount用于统计出错个数。经过归一化以后的数据集和验证通过for循环计算分类结果,并与实际结果进行对比,得到总出错数和出错率。

执行该函数,结果显示:

in datingClassTest,the total error rate is: 0.064000
in datingClassTest,errorCount: 32.0

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python 解析XML文件
Apr 15 Python
Python中模块pymysql查询结果后如何获取字段列表
Jun 05 Python
解决Python一行输出不显示的问题
Dec 03 Python
python合并已经存在的sheet数据到新sheet的方法
Dec 11 Python
Python实现的栈、队列、文件目录遍历操作示例
May 06 Python
python绘制直方图和密度图的实例
Jul 08 Python
使用NumPy读取MNIST数据的实现代码示例
Nov 20 Python
tensorflow2.0保存和恢复模型3种方法
Feb 03 Python
Python中使用threading.Event协调线程的运行详解
May 02 Python
python爬取豆瓣电影TOP250数据
May 23 Python
如何判断pytorch是否支持GPU加速
Jun 01 Python
Python批量解压&压缩文件夹的示例代码
Apr 04 Python
Python实现基于KNN算法的笔迹识别功能详解
Jul 09 #Python
Python 16进制与中文相互转换的实现方法
Jul 09 #Python
python 文件转成16进制数组的实例
Jul 09 #Python
使用Python读取二进制文件的实例讲解
Jul 09 #Python
Python实现随机漫步功能
Jul 09 #Python
Python2包含中文报错的解决方法
Jul 09 #Python
对numpy数据写入文件的方法讲解
Jul 09 #Python
You might like
PHP实现的蚂蚁爬杆路径算法代码
2015/12/03 PHP
php 使用html5实现多文件上传实例
2016/10/24 PHP
PHP简单实现模拟登陆功能示例
2017/09/15 PHP
Javascript - HTML的request类
2007/01/09 Javascript
DOMAssitant最新版 DOMAssistant 2.5发布
2007/12/25 Javascript
Javascript 检测键盘按键信息及键码值对应介绍
2013/01/03 Javascript
jquery实现可拖动DIV自定义保存到数据的实例
2013/11/20 Javascript
js获取域名的方法
2015/01/27 Javascript
jQuery根据用户电脑是mac还是pc加载对应样式的方法
2015/06/26 Javascript
php结合imgareaselect实现图片裁剪
2015/07/05 Javascript
详细总结Javascript中的焦点管理
2016/09/17 Javascript
nodejs的HTML分析利器node-jquery用法浅析
2016/11/08 NodeJs
详解微信第三方小程序代开发
2017/06/23 Javascript
微信小程序实现简单input正则表达式验证功能示例
2017/11/30 Javascript
vue中实现先请求数据再渲染dom分享
2018/03/17 Javascript
解决Vue项目打包后打开index.html页面显示空白以及图片路径错误的问题
2019/10/25 Javascript
python中类的一些方法分析
2014/09/25 Python
Python实现Const详解
2015/01/27 Python
python自动格式化json文件的方法
2015/03/11 Python
PyTorch搭建多项式回归模型(三)
2019/05/22 Python
Selenium+Python 自动化操控登录界面实例(有简单验证码图片校验)
2019/06/28 Python
利用python在大量数据文件下删除某一行的例子
2019/08/21 Python
Python如何使用bokeh包和geojson数据绘制地图
2020/03/21 Python
在Ubuntu中安装并配置Pycharm教程的实现方法
2021/01/06 Python
python反扒机制的5种解决方法
2021/02/06 Python
CSS3实现各种图形的示例代码
2016/10/19 HTML / CSS
墨西哥网上超市:Superama
2018/07/10 全球购物
学校卫生检查制度
2014/02/03 职场文书
化学系大学生自荐信范文
2014/03/01 职场文书
2015年重阳节活动主持词
2015/07/30 职场文书
2019最新版劳务派遣管理制度
2019/08/16 职场文书
创业计划书之牛肉汤快餐店
2019/10/08 职场文书
启迪人心的励志语录:脾气永远不要大于本事
2020/01/02 职场文书
详解MySQL 用户权限管理
2021/04/20 MySQL
超详细Python解释器新手安装教程
2021/05/10 Python
Matplotlib绘制条形图的方法你知道吗
2022/03/21 Python