KNN算法

K近邻

k近邻法(k-nearest neighbor, kNN)是1967年由Cover T和Hart P提出的一种基本分类与回归方法。它的工作原理是:存在一个样本数据集合,也称作为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新的数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
  所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用它最接近的k个邻居来代表。kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 kNN方法在类别决策时,只与极少量的相邻样本有关。由于kNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,kNN方法较其他方法更为适合。  
  上图中,绿色圆要被决定赋予哪个类,是红色三角形还是蓝色四方形?如果K=3,由于红色三角形所占比例为2/3,绿色圆将被赋予红色三角形那个类,如果K=5,由于蓝色四方形比例为3/5,因此绿色圆被赋予蓝色四方形类。
  K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 KNN方法虽然从原理上也依赖于极限定理,但在类别决策时,只与极少量的相邻样本有关。由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。
  KNN算法不仅可以用于分类,还可以用于回归。通过找出一个样本的k个最近邻居,将这些邻居的属性的平均值赋给该样本,就可以得到该样本的属性。更有用的方法是将不同距离的邻居对该样本产生的影响给予不同的权值(weight),如权值与距离成反比。

2.kNN计算流程

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

准备工作

点击屏幕右上方的下载实验数据模块,选择下载kNN_IRIS.tgz到指定目录下,然后再依次选择点击上方的File->Open->Upload,上传刚才下载的数据集压缩包,再使用如下命令解压:
In [1]:

!tar -zxvf kNN_IRIS.tgz

tar (child): kNN_IRIS.tgz: Cannot open: No such file or directory
tar (child): Error is not recoverable: exiting now
tar: Child returned status 2
tar: Error is not recoverable: exiting now

实现kNN核心算法

如前所述,kNN算法流程如下:
 a.计算已知类别数据集中的点与当前点之间的距离;
 b.按照距离递增次序排序;
 c.选取与当前点距离最小的k个点;
 d.确定前k个点所在类别的出现频率;
 e.返回前k个点所出现频率最高的类别作为当前点的预测分类。
在kNN.py中,添加一个函数classify0作为kNN算法的核心函数,该函数的完整形式为:
def classify0(inX, dataSet, labels, k):
其中各个参数的含义如下:
inX - 用于要进行分类判别的数据(来自测试集)
dataSet - 用于训练的数据(训练集)
lables - 分类标签
k - kNN算法参数,选择距离最小的k个点
在上述参数列表中,dataSet为所有训练数据的集合,也就是表示所有已知类别数据集中的所有点,dataSet为一个矩阵,其中每一行表示已知类别数据集中的一个点。inX为一个向量,表示当前要判别分类的点。按照上述算法流程,我们首先应该计算inX这个要判别分类的点到dataSet中每个点之间的距离。dataSet中每个点也是用一个向量表示的,点与点之间的距离怎么计算呢?没错,就是求两向量之间的距离,数学上,我们知道有很多距离计算公式,包括但不限于:
欧氏距离
曼哈顿距离
切比雪夫距离
闵可夫斯基距离
标准化欧氏距离
马氏距离
夹角余弦
汉明距离
杰卡德距离& 杰卡德相似系数
信息熵
这里,我们选择最简单的欧式距离计算方法。设p和q为两向量,则两向量间的欧氏距离为:

在算法流程,输入参数含义,以及距离计算公式都明确了以后,按照kNN算法的流程,我们就可以实现kNN算法了。这里,我们使用numpy提供的各种功能来实现该算法,相比于自己纯手写各种线性代数变换操作,使用numpy的效率要高的多。classify0的实现如下:
In [22]:

import numpy as np
import operator
from os import listdir
​
def classify0(inX, dataSet, labels, k):
    ### Start Code Here ###
    #返回dataSet的行数,即已知数据集中的所有点的数量
    line = dataSet.shape[0]
    #行向量方向上将inX复制m次,然后和dataSet矩阵做相减运算,复制使用numpy的tile函数
    result1 = np.tile(inX,(line,1)) - dataSet 
    #减完后,对每个数做平方
    pingfang = result1 ** 2
    #平方后按行求和,axis=0表示列相加,axis-1表示行相加
    sum_1 = pingfang.sum(axis=1)
    #开方计算出欧式距离
    distance = sum_1 ** 0.5
    #对距离从小到大排序,注意argsort函数返回的是数组值从小到大的索引值2 
    sort_result = np.argsort(distance)
    #定义用于存放类别/次数的字典,key为类别, value为次数
    dic_sort = {}
    #使用for循环对最近的k个元素进行遍历
    for i in range(k):
    #取出第近的元素对应的类别
        key = labels[sort_result[i]]
    #对类别次数进行累加,可使用字典(Dictionary)的 get() 函数取出value
        dic_sort[key] = dic_sort.get(key, 0) + 1
    #根据字典的值从大到小排序
    maxCount = 0
    for key, value in dic_sort.items():
        if value > maxCount:
            maxCount = value
            maxIndex = key
​
    return maxIndex 
    #返回次数最多的类别,即所要分类的类别
    ### End Code Here ###

接下来,我们用个小例子验证一下kNN算法,随机挑选的6位高中生,分别让他们做文科综合试卷的分数和理科综合试卷的分数,下表为分数以及分类信息。

直觉上,理科生的理综成绩比较高,文综成绩较低,文科生的文综成绩较高,理综成绩较高。基于这些信息,我们利用kNN算法判断成绩为(105,210)所属的分类,代码实现如下:
In [23]:

dataSet=np.array([[250,100],[270,120],[111,230],[130,260],[200,80],[70,190]])
labels=[“理科生”,“理科生”,“文科生”,“文科生”,“理科生”,“文科生”]
inX=[105,210]
print(classify0(inX,dataSet,labels,3))

文科生

运行结果显示“文科生”,输出符合预期

【练习】IRIS分类-背景

Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理。

数据解析

Iris数据包含150条样本记录,分剐取自三种不同的鸢尾属植物setosa、versic010r和virginica的花朵样本,每一类各50条记录,其中每条记录有4个属性:萼片长度(sepal length)、萼片宽度sepalwidth)、花瓣长度(petal length)和花瓣宽度(petal width)。

【练习】准备数据数据归一化
In [28]:


#-*- coding: UTF-8 -*-   
import csv     #用于处理csv文件  
import random    #用于随机数  
import math           
import operator  #  
from sklearn import neighbors  
 
#加载数据集  
def loadDataset(filename,split,trainingSet=[],testSet = []):  
    with open(filename,"r") as csvfile:  
        lines = csv.reader(csvfile)  
        dataset = list(lines)  
        for x in range(len(dataset)-1):  
            for y in range(4):  
           #print(dataset[x][y])
                dataset[x][y] = float(dataset[x][y])  
                if random.random()<split:  
                    trainingSet.append(dataset[x])  
                else:  
                    testSet.append(dataset[y])  
#计算距离  
def euclideanDistance(instance1,instance2,length):  
   distance = 0  
   for x in range(length):  
       #print(instance1[1],instance2[x])
       distance += pow((float(instance1[x]) - float(instance2[x])),2)  
   return math.sqrt(distance)  
​
#计算相邻点
def getNeighbors(trainingSet,testInstance,k):  #其中testInstance仅包含一个样本,trainingSet包含多个训练样本,trainingSet的每一行为一个样本
    distances = []  
    length = len(testInstance) -1  #获取样本的属性数
    
    #计算每一个测试实例到训练集实例的距离,使用上面定义的euclideanDistance函数依次计算测试样本到每个训练样本的距离,并将距离存入distances中
    for x in range(len(trainingSet)):  
       dist = euclideanDistance(testInstance, trainingSet[x], length)  
       distances.append((trainingSet[x],dist))  
    
    #对所有的距离进行排序  
    distances.sort(key=operator.itemgetter(1))  
    neighbors = []  
    #返回k个最近邻  
    for x in range(k):  
        neighbors.append(distances[x][0])  
    return neighbors  
 
#对k个近邻进行合并,返回value最大的key  
def getResponse(neighbors):  
    classVotes = {}  
    for x in range(len(neighbors)):  
        response = neighbors[x][-1]  
        if response in classVotes:  
            classVotes[response] += 1  
        else:  
            classVotes[response] = 1  
    #排序  
    sortedVotes = sorted(classVotes.items(),key = operator.itemgetter(1),reverse =True)  
    return sortedVotes[0][0]  
 
#计算准确率  
def getAccuracy(testSet,predictions):  
    correct = 0  
    for x in range(len(testSet)):  
        if testSet[x][-1] == predictions[x]:  
            correct+=1  
    return (correct/float(len(testSet))) * 100.0  
 
def main():  
    trainingSet = []  #训练数据集  
    testSet = []      #测试数据集  
    split = 0.67      #分割的比例  
    loadDataset(r"./iris.csv", split, trainingSet, testSet)   
    print("Train set :" + repr(len(trainingSet)))
    print("Test set :" + repr(len(testSet)))               
​
    predictions = []  
    k = 3  
    for x in range(len(testSet)):  
        neighbors = getNeighbors(trainingSet, testSet[x], k)  
        result = getResponse(neighbors)  
        predictions.append(result)  
        print(">predicted = " + repr(result) + ",actual = " + repr(testSet[x][-1]))
    accuracy = getAccuracy(testSet, predictions)  
    print("Accuracy:" + repr(accuracy) + "%")
​
if __name__ =="__main__":  
    main()

用sklearn函数库直接调用函数

#coding:utf-8  
from sklearn.datasets import load_iris  
from sklearn import neighbors  
import sklearn  

### Start Code Here ###
#查看iris数据集,直接使用load_iris函数即可导入iris数据集
data_iris = load_iris()
data = data_iris.data
labels = data_iris.target
#创建KNN模型对象,使用neighbors的KNeighborsClassifier()函数
model = neighbors.KNeighborsClassifier()
#调用fit函数训练数据集  
model.fit(data, labels)
#调用predict函数进行预测,测试样本为[[0.1,0.2,0.3,0.4]]
predict = model.predict([[0.1,0.2,0.3,0.4]])
### End Code Here ###

print (predict)  
print (data_iris.target_names[predict])
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值