一、KNN概述

  K-(最)近邻算法KNN(k-Nearest Neighbor)是数据挖掘分类技术中最简单的方法之一。它具有精度高、对异常值不敏感的优点,适合用来处理离散的数值型数据,但是它具有  非常高的计算复杂度和空间复杂度,需要大量的计算(距离计算)。

  它的工作原理是:如果已经给定一个带有标签(分类)的数据集(训练集),对于每一个给定的没有标签(分类)的新向量,通过计算该向量与训练集中的每一个向量的距离,  选择前k个最小的距离,在k个距离中出现次数最多的标签(分类)则是新向量的标签(分类)。

  使用KNN算法将某个向量划分到某个分类中的过程如下:

    1)计算训练集中的每个向量和当前向量之间的距离;

    2)按照距离递增排序;

    3)选择与当前向量距离最小的前k个向量;

    4)计算前k个向量中每个类别的频率;

    5)选择出现频率最高的类别作为新向量的分类。

二、准备数据集

  

  Python3实现机器学习经典算法的数据集都采用了著名的机器学习仓库UCI(http://archive.ics.uci.edu/ml/datasets.html),其中分类系列算法采用的是Adult数据集(http://archive.ics.uci.edu/ml/datasets/Adult),测试数据所在网址:http://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data,训练数据所在网址:http://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test。

  Adult数据集通过收集14个特征来判断一个人的收入是否超过50K,14个特征及其取值分别是:

  age: continuous.

  workclass: Private, Self-emp-not-inc, Self-emp-inc, Federal-gov, Local-gov, State-gov, Without-pay, Never-worked.

  fnlwgt: continuous.

  education: Bachelors, Some-college, 11th, HS-grad, Prof-school, Assoc-acdm, Assoc-voc, 9th, 7th-8th, 12th, Masters, 1st-4th, 10th, Doctorate, 5th-6th, Preschool.

  education-num: continuous.

  marital-status: Married-civ-spouse, Divorced, Never-married, Separated, Widowed, Married-spouse-absent, Married-AF-spouse.

  occupation: Tech-support, Craft-repair, Other-service, Sales, Exec-managerial, Prof-specialty, Handlers-cleaners, Machine-op-inspct, Adm-clerical, Farming-fishing, Transport-moving, Priv-house-serv, Protective-serv, Armed-Forces.

  relationship: Wife, Own-child, Husband, Not-in-family, Other-relative, Unmarried.

  race: White, Asian-Pac-Islander, Amer-Indian-Eskimo, Other, Black.

  sex: Female, Male.

  capital-gain: continuous.

  capital-loss: continuous.

  hours-per-week: continuous.

  native-country: United-States, Cambodia, England, Puerto-Rico, Canada, Germany, Outlying-US(Guam-USVI-etc), India, Japan, Greece, South, China, Cuba, Iran, Honduras, Philippines, Italy, Poland, Jamaica, Vietnam, Mexico, Portugal, Ireland, France, Dominican-Republic, Laos, Ecuador, Taiwan, Haiti, Columbia, Hungary, Guatemala, Nicaragua, Scotland, Thailand, Yugoslavia, El-Salvador, Trinadad&Tobago, Peru, Hong, Holand-Netherlands.

  

  最终的分类标签有两个:>50K, <=50K.

  

下一步是分析数据:

1、数据预处理: 

  由上述的数据集我们得知,Adult数据集的很多个特征值是离散的取值,他们跟连续的取值的特征不同,需要对其取值进行一一映射,让它适应KNN的数值型离散数据的处理条件。为此我们可以构造一个键值对字典,对数据集进行一次扫描来进行替换,使得所有的离散的非数值数据转换为离散数据:

 def precondition(dataSet):
dict={'Private':0,'Self-emp-not-inc':1,'Self-emp-inc':2,'Federal-gov':3,
'Local-gov':4,'State-gov':5,'Without-pay':6,'Never-worked':7,

  当然这种构造字典的方法是非常愚蠢的,因为我们要写一个非常大的字典,应该考虑下面这种字典构造算法:

  1)将每种特征值的离散取值copy到一个文本文件中;

  2)读取文本,对于每一种特征,给予一个初始value 0,对于每一种特征的取值,形成一个键值对(key,value)并插入到dict中而后自增value的值

  3)将数据集进行替换并将其他的非数值类型转换为数值类型(str→integer)

dataSet = [[int(column.strip()) if column.strip().isdigit() else dict[column.strip()] for column in row] for row in dataSet]

2、数据清洗

  数据中含有大量的不确定数据,这些数据在数据集中已经被转换为‘?’,但是它仍旧是无法使用的,数据挖掘对于这类数据进行数据清洗的要求规定,如果是可推算数据,应该推算后填入;或者应该通过数据处理填入一个平滑的值,然而这里的数据大部分没有相关性,所以无法推算出一个合理的平滑值;所以所有的‘?’数据都应该被剔除而不应该继续使用。为此我们要用一段代码来进行数据的清洗:

 def cleanOutData(dataSet):#数据清洗
for row in dataSet:
for column in row:
if column == '?' or column=='':
dataSet.remove(row)

  这段代码只是示例,它有它不能处理的数据集!比如上述这段代码是无法处理相邻两个向量都存在‘?’的情况的!修改思路有多种,一种是循环上述代码N次直到没有'?'的情况,这种算法简单易实现,只是给上述代码加了一层循环,然而其复杂度为O(N*len(dataset));另外一种实现是每次找到存在'?'的列,回退迭代器一个距离,大致的伪代码为:  

 def cleanOutData(dataSet):
for i in range(len(dataSet)):
if dataSet[i].contain('?'):
dataSet.remove(dataSet[i]) ( dataSet.drop(i) )
i-=1

  上述代码的复杂度为O(n)非常快速,但是这种修改迭代器的方式会引起编译器的报错,对于这种报错可以选择修改编译器使其忽略,但是不建议使用这种回退迭代器的写法。

2、数据归一化:

  思考这样两组数据:

  A(1,2,3)

  B(2,3,1000)

  现在要计算A和B两组数据的距离:√(1-2)2+(2-3)2+(3-1000) 这样算是没错的,但是是否会产生这样一个问题:第三个属性对于结果的影响太大了?答案显而易见是肯定的。然而这三个属性之间的相关性应该是0,他们应该是相互独立的,那么这种影响就应该被消除,所以我们还应该增加一个归一化数据的过程:

  归一化数据的主要方法有很多种,网上有很多很完善的实现,这里我采用的是Min-Max Normalization,它将任意取值范围的特征值转化为0到1区间内的值:

 def norm(dataSet):#归一化数据,将所有的数据集中在【0,1】中,保证取值比较大的数据对于距离的影响不会太大
minVec = dataSet.min(0) #按行取得每个特征的最小值
maxVec = dataSet.max(0) #按行取得每个特征的最大值
DValue = maxVec - minVec #取得每种特征的最大最小值之差
normData = zeros(shape(dataSet)) #产生一个保存已经标准化的数据的矩阵
m = dataSet.shape[0]            #取得数据集的行数,即数据的条数         
normData=(dataSet - tile(minVec,(m,1)))/ tile(DValue,(m,1))   #normData = (unNormData - min) / (max - min)
return normData

  上述代码比较难以理解的地方在于min/max这两个方法的返回值?其实dataSet的类型不是Python自带的列表类型,而是Numpy模块中的array类型,min(max)函数的返回值是压缩其参数axis的维度取得最小值(最大值)的结果,即axis = 0时,取的是第0维的arr[0][0][0],arr[1][0][0],arr[2][0][0]……arr[n][0][0]中的最小值作为min[0],arr[0][0][1],arr[1][0][1],arr[2][0][1]……arr[n][0][1]中的最小值作为min[1],以此类推来取得dataSet中每一列的最小值(最大值)。

  tile函数返回的是参数列表的重复,即tile(Arr,N)返回的是一个一维列表,其组成为Arr矩阵的N次重复,tile(Arr,(m,n))返回的是一个二维列表,其组成为m行向量,每个向量为Arr的n次重复。

3、数据集读入

  读入数据集就是上述数据预处理+数据清洗的过程:

  

 def createDataSet(filename):
with open(filename,'r')as file:
dataSet = [line.strip().split(',')for line in file.readlines()]
del(dataSet[-1])
cleanOutData(dataSet)             #数据清洗
dataSet=precondition(dataSet)        #数据预处理
labels=[each[-1]for each in dataSet]    #获取最后一列数据,获得类别标签
dataSet=[each[0:-2]for each in dataSet]  #去除最后一列数据,获得无分类数据
return array(dataSet),labels         #重点:返回的是Numpy.array类型

  KNN的分类过程并不需要用到每种特征的特征名,如age/fnlwgt等,而决策树(Decision Tree)是需要用其计算信息增益的。类别标签的保存可以为后续使用测试算法来进行测试该算法的正确率的时候使用。

三、训练算法

  KNN并没有训练算法的过程。它没有一个独立的分类器,所形成的分类器会对输入的向量进行一次“全扫描”的距离计算,然后得出类别,它不像决策树那样生成一棵独立的树,可以运用到几乎所有特征属性相同的数据集中。所以KNN的训练时间为0,但是其测试时间为N。

四、测试算法

  KNN的训练算法几乎是“特异”的,针对一个分类任务我们要构造一个分类器,它很难进行模块化并形成一个接口。但是它的过程几乎是一模一样的,如上所述:

  使用KNN算法将某个向量划分到某个分类中的过程如下:

    1)计算训练集中的每个向量和当前向量之间的距离;

    2)按照距离递增排序;

    3)选择与当前向量距离最小的前k个向量;

    4)计算前k个向量中每个类别的频率;

    5)选择出现频率最高的类别作为新向量的分类。

  根据这个过程我们来构造伪代码:

  首先我们写针对一个向量的情况的分类算法:

 def classify(vector,dataSet,testLabels,labels,k):
for vec in vector:
distance = sqrt(abs(((tile(vec,(dataSet.shape[0],1)) - dataSet) ** 2).sum(axis = 1))); #计算距离
sortedDistance = distance.argsort()
dict={}
for i in range(k):
label = labels[sortedDistance[i]]
if not label in dict:
dict[label] = 1
else:
dict[label]+=1
sortedDict = sorted(dict,key = operator.itemgetter(1),reverse = True)
return sortedDict[0][0]

  这段代码的难点在于第3行和第12行的代码:下面我们一一来解释:

  1)计算距离的式子是被我写长了,其实拆开来写非常好理解:

    √(x1-x2)²+(y1-y2)²+(z1-z2)²  =  √ (v1 - v2) ² 这里的v1和v2是向量而不是值

    tile(vec,(dataSet.shape[0],1))构造了一个长度和dataSet相等的矩阵,它每一行(每个向量)都是vector的某一行(在这个程序中就是vector本身),然后将这个矩阵和dataSet相减,得到的就是一个每一行都是测试集与该向量的距离的向量的矩阵,然后就是常规的向量平方求距离,然后求和,然后取开平方的过程。

  2)sortedDict = sorted(dict,key = operator.itemgetter(1),reverse = True)

   emmm其实在Python3的环境中这条语句是有Error的哈哈,因为dict无法进行排序

Python3实现机器学习经典算法(一)KNN的更多相关文章

  1. Python3入门机器学习经典算法与应用

    <Python3入门机器学习经典算法与应用> 章节第1章 欢迎来到 Python3 玩转机器学习1-1 什么是机器学习1-2 课程涵盖的内容和理念1-3 课程所使用的主要技术栈第2章 机器 ...

  2. Python3实现机器学习经典算法(三)ID3决策树

    一.ID3决策树概述 ID3决策树是另一种非常重要的用来处理分类问题的结构,它形似一个嵌套N层的IF…ELSE结构,但是它的判断标准不再是一个关系表达式,而是对应的模块的信息增益.它通过信息增益的大小 ...

  3. Python3入门机器学习经典算法与应用☝☝☝

    Python3入门机器学习经典算法与应用 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 使用新版python3语言和流行的scikit-learn框架,算法与 ...

  4. Python3实现机器学习经典算法(四)C4.5决策树

    一.C4.5决策树概述 C4.5决策树是ID3决策树的改进算法,它解决了ID3决策树无法处理连续型数据的问题以及ID3决策树在使用信息增益划分数据集的时候倾向于选择属性分支更多的属性的问题.它的大部分 ...

  5. Python3入门机器学习经典算法与应用✍✍✍

    Python3入门机器学习经典算法与应用 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的 ...

  6. Python3实现机器学习经典算法(二)KNN实现简单OCR

    一.前言 1.ocr概述 OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗.亮的模式确定其形状,然 ...

  7. 机器学习经典算法之KNN

    一.前言 KNN 的英文叫 K-Nearest Neighbor,应该算是数据挖掘算法中最简单的一种. 先用一个例子体会下. /*请尊重作者劳动成果,转载请标明原文链接:*/ /* https://w ...

  8. Python3入门机器学习 经典算法与应用

    Python3入门机器学习 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的时候可以关注下 ...

  9. 机器学习经典算法具体解释及Python实现--K近邻(KNN)算法

    (一)KNN依旧是一种监督学习算法 KNN(K Nearest Neighbors,K近邻 )算法是机器学习全部算法中理论最简单.最好理解的.KNN是一种基于实例的学习,通过计算新数据与训练数据特征值 ...

随机推荐

  1. HDFS的Write过程

    hadoop中重要的组成部分HDFS,它所发挥的重要作用是进行文件的后端存储.HDFS针对的是低端的服务器,场景为读操作多.写操作少的情况.在分布式存储情况下,比较容易出现的情况是数据的损害,为了保证 ...

  2. JavaScript获取0-100之间的随机数

    function (min, max) { return Math.floor(Math.random() * (max - min)) + min } 如果想获取0-100之间的随机数,则可将函数的 ...

  3. 怎么在一台电脑上安装win7与centos7双系统

    对于用习惯windows系统的小伙伴们来说,好像Linux系统的命令操作感觉会比较神秘,进而有部分小伙伴就想说也学一学LInux操作系统.但是苦于资源的问题--就一台计算机,一块硬盘,担心说如果安装了 ...

  4. js 时间转换毫秒的四种方法(转)

    将时间转换为毫秒数的方法有四个: Date.parse()Date.UTCvalueOf()getTime() 1. Date.parse():该方法接受一个表示日期的字符串参数,然后尝试根据这个日期 ...

  5. Delphi调用爷爷类的方法

    Delphi通过inherited 可以调用父类的方法,但是没有提供直接调用父类的父类的方法(爷爷类),通过变通的方式实现如下: 假设父类是TFather,爷爷类TGrand,调用爷爷类的Write方 ...

  6. 用Python批量下载DACC的MODIS数据

    本人初次尝试用Python批量下载DACC的MODIS数据,记下步骤,提醒自己,数据还在下载,成功是否未知,等待结果中...... 若有大佬发现步骤有不对之处,望指出,不胜感激. 1.下载Python ...

  7. python格式化输出、逻辑表达式和字符编码

    格式化输出: %s  字符串占位符;%d 整数占位符 注意:如果前面有了占位符,那么后面所有的%都是占位,如果要输出“%”,需要使用转移符,即"%%" #定义三个变量 name = ...

  8. TCP/IP协议中的UDP与TCP的区别

    TCP面向连接,UDP面向非连接即发送数据前不需要建立链接TCP提供可靠的服务(数据传输),UDP无法保证,它没有TCP的接受确认.窗口等机制,因此也不需要交换控制信息:发生丢包也一概不负责.TCP面 ...

  9. python实现文件夹的排序

    我的github地址. 代码: # -*- coding: utf-8 -*- __author__ = "YuDian" ''' v1.0: 2018/3/19 21:39 完成 ...

  10. poj 2739 Sum of Consecutive Prime Numbers 尺取法

    Time Limit: 1000MS   Memory Limit: 65536K Description Some positive integers can be represented by a ...