KNN算法

放肆的年华 提交于 2020-01-27 01:33:29

KNN算法

问题提出

依旧是分类问题,现在有了一数据集,数据集中的每个数据都有一个标签,那么多对于一个新的数据,他应该是属于哪个集合,也就是说他的标签应该是什么?

例子

在这里插入图片描述比如当前这个图,红色的点具有标签A,蓝色的点具有标签B,现在来了一个新的点黑色点,那么这个黑色的点应该属于那一类?
直观的来看,黑色的点应该是属于红色标记的点。也就是标签应该是A,因为一个很直观的感受就是它距离红色的点近,根据物以类聚,人以群分的概念它应该是红色点集合内的。

算法的提出

为了解决上述分类的问题,提出了KNN(k-NearestNeighbor)算法,也就是k近邻算法。算法的核心思想就是找距离当前点最近的k个点,这个距离可以有多种定义,一般都是在n维空间的欧几里得距离,当然也可以被定义为其他形式下的距离。然后统计这k个点里面哪种标记最多,就把该点归类为最多的标记点

算法实现步骤

根据上面的描述,首先第一步就是要确定k,确定选取多少个点来做参考。那么这个k最好不要是标记种类数的倍数。什么意思?举个例子,就像刚才上面的那个图,k最好选取奇数,因为当前数据集中存在两个标签,如果k选取偶数,那么一旦发现计算得到的k个数据中两种标签数量相等,那么就很难说把这个点分给谁了。确定k之后,就计算每个点到该该点的距离,然后排序,统计这k个中标签数量最多的是哪一个。这样就完成了

代码实现

"""
x_test:测试数据
x_data:训练集的x向量
y_data:训练集的结果
k:选取的邻居的个数
"""
def knn(x_test,x_data,y_data,k):
    x_data_size = x_data.shape[0]  # numpy的行数
    # print(x_data_size)
    x_copy = np.tile(x_test, (x_data_size, 1))
    diffMat = x_copy - x_data  # 差值的数据
    sqDiffMat = diffMat ** 2
    # print(sqDiffMat)
    sqDisstance = sqDiffMat.sum(axis=1)
    distance = sqDisstance ** 0.5
    # print(distance)
    sortedDistance = distance.argsort()  # 得到的是distacne的索引
    classCount = {}
    k = 5
    # 统计每种标签各出现了多少次
    for i in range(k):
        votelabel = y_data[sortedDistance[i]]
        classCount[votelabel] = classCount.get(votelabel, 0) + 1  # 如果这个键不存在,就赋值为0
    # print(classCount)
    # 从小到大排序
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    # print(sortedClassCount)
    knnclass = sortedClassCount[0][0]  # 取到出现最多的标签
    return knnclass

使用knn,做一下iris数据集的分类

# coding=utf-8
import numpy as np
from sklearn import datasets
from sklearn.metrics import classification_report,confusion_matrix
import operator
import random
"""
x_test:测试数据
x_data:训练集的x向量
y_data:训练集的结果
k:选取的邻居的个数
"""
def knn(x_test,x_data,y_data,k):
    x_data_size = x_data.shape[0]  # numpy的行数
    # print(x_data_size)
    x_copy = np.tile(x_test, (x_data_size, 1))
    diffMat = x_copy - x_data  # 差值的数据
    sqDiffMat = diffMat ** 2
    # print(sqDiffMat)
    sqDisstance = sqDiffMat.sum(axis=1)
    distance = sqDisstance ** 0.5
    # print(distance)
    sortedDistance = distance.argsort()  # 得到的是distacne的索引
    classCount = {}
    k = 5
    # 统计每种标签各出现了多少次
    for i in range(k):
        votelabel = y_data[sortedDistance[i]]
        classCount[votelabel] = classCount.get(votelabel, 0) + 1  # 如果这个键不存在,就赋值为0
    # print(classCount)
    # 从小到大排序
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    # print(sortedClassCount)
    knnclass = sortedClassCount[0][0]  # 取到出现最多的标签
    return knnclass
    # print(knnclass)
iris=datasets.load_iris() #load进来的是一个字典
# print(iris.data)
datasize=iris.data.shape[0]
index=[i for i in range(datasize)] #生成一个索引列表
# print(index)
random.shuffle(index) #随机打乱
iris.data=iris.data[index]
iris.target=iris.target[index]

test_size=40
x_train=iris.data[test_size:]
x_test=iris.data[:test_size]
y_train=iris.target[test_size:]
y_test=iris.target[:test_size]
predictions=[]
for i in range(x_test.shape[0]):
    predictions.append(knn(x_test[i],x_train,y_train,5))
print(classification_report(y_test,predictions))
print(confusion_matrix(y_test,predictions))

实现的缺点

其实这样实现的knn并不完善,因为时间复杂度太高,只是做了一个示范,真正的knn应该采用KD Tree实现。KD tree是一种查询k近邻的强有力的数据结构,是一种二叉查找树,每个节点存了一个数组,查找的时候按照某个关键字搜索,时间复杂度n*logn.速度很快。

Sklearn解决上述问题

Sklearn里面也封装了knn算法,可以看一下内部实现
在这里插入图片描述
这是sklearn关于KNeighborsClassifier的描述,算法采用了四种实现方式,auto,ball_tree,kd_tree,brute.会根据数据量来自动选择。
采用sklearn提供的接口,那么就很easy。使用哦个sklearn做iris的分类:

# coding=utf-8
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report,confusion_matrix
from sklearn import neighbors
iris=datasets.load_iris()
x_train,x_test,y_train,y_test=train_test_split(iris.data,iris.target,test_size=0.2)
model=neighbors.KNeighborsClassifier(3)
model.fit(x_train,y_train)
prediction=model.predict(x_test)
print(classification_report(y_test,prediction))

总结:其实还行,这个算法,emmm,挺朴实的,其实要我解决这个问题我也会这么做(狗头)

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!