KNN分类算法简单分析

1.算法概要

k-NearestNeighbor分类算法,顾名思义,找到K个与待测数据最近的样本数据,根据K个样本类别情况来判断待测数据的类别。为什么可以这样?相近的物体往往具有一些共性,例如,在学校里一般成绩比较好的学生都喜欢坐在一起,而有些成绩较差的往往也喜欢玩到一块去。

KNN算法有三个步骤:
1.算距离:计算待测数据到每个样本数据的距离
2.找邻居:选出K个距离最近的样本数据
3.做分类:在前k个样本中选择频率最高的类别作为预测类别

2.KNN的三个重要因素

K值的选取
如下图:蓝色正方形和红色三角形分别表示不同的两类样本数据,绿色圆形表示待分类的数据。当k=2时,红色三角形数量多于蓝色正方形,待分类的数据将被归于红色三角形一类;当k=5时,蓝色正方形数量多于红色三角形,待分类的数据将被归于蓝色正方形一类。由此可以看出KNN算法中的K值的取定对分类结果有很大的影响,KNN分类算法简单分析_第1张图片

距离度量的方式
X、Y分别表示样本与待测数据
x1,x2和y1,y2等是两个数据在对应相应维度上的值
欧氏距离:KNN分类算法简单分析_第2张图片
曼哈顿距离:在这里插入图片描述
闵可夫斯基距离:在这里插入图片描述
不难看出,前面两个公式是第三个公式p分别取1,2所得到的,p去不同值会得到不同的结果,比较常用的是欧氏距离。实际上使用KNN时,往往要将数据归一化,因为当一个属性的值远远大与其他的属性时,距离的结果很有可能被这一个属性所主导,其他的属性影响会小很多,这并不是我们想看到的,这时就需要进行归一化处理。

分类标准
常用的有在前k个样本中选择频率最高的类别作为预测类别;但也会存在这样一种情况,当每个类别的样本数都一样该怎么处理,如下图:
KNN分类算法简单分析_第3张图片
若继续采用频率来判断就不太好了,有可能是紫色,也有可能蓝色和红色。但直观上很明显,结果应该是红色,它离红色最近,自然而然地我们想到了可以加一个和距离有关地权重1/s(s表示距离),原先直接在某个类别上加1,现在则是加上1/s。

实际上KNN也可用于解决回归问题,过程大体一样,只是第三步K个最邻近的样本处理稍稍不同。分类时是想得到数量最多的那个类别;而回归往往是求每个样本label的均值(根据每个样本数据与待测数据的距离取相应的权重也许效果会更好)来预测结果。

3.优缺点

优点:
1.思想简单,具有很不错的效果
2.几乎没有涉及什么数学知识进行公式推导
3.可以进行多分类

缺点:
1.运算量大,效率低,每预测一个值就要求到所有样本的距离
2.没有真正的模型,所以预测速度较慢

4.简单实现

导入模块

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split

算法简单实现

class Knn:
    def __init__(self,k=6,weight=False):#k默认为6,weight表示是否考虑权重
        self.k = k
        self.x_train = None
        self.y_train = None
        self.weights = weight
    #KNN实际上没有真正的模型
    def fit(self,x_train,y_train):
        self.x_train = x_train
        self.y_train =y_train
    
    
    def predict(self,x_test):
        if self.weights==0:           
            pred = [self._predict1(x) for x in x_test]
        else:
            pred = [self._predict2(x) for x in x_test]
        return np.array(pred)
    
    
    def _predict1(self,x):  #predict的一个私有函数
        #距离
        distance = [np.sqrt(((x-i)**2).sum())/len(self.x_train) for i in self.x_train]                   
        nearest = np.argsort(distance)#得到将距离排序后原先的索引
        near = self.y_train[nearest[:self.k]]
        d ={}#利用字典计算频数     
        for i in near:
            if not i in d:
                d[i] = 1
            else:
                d[i] +=1
        x_type = max(d,key=d.get)
        return x_type
    
    def _predict2(self,x):  #predict的一个私有函数
        #距离
        distance = [np.sqrt(((x-i)**2).sum())/len(self.x_train) for i in self.x_train]                   
        nearest = np.argsort(distance)
        near = self.y_train[nearest[:self.k]]
        d = {}
        for i in range(self.k):
            if near[i] not in d:
                d[near[i]] = 1/distance[i]
            else:
                d[near[i]] += 1/distance[i]
        x_type = max(d,key=d.get)
        return x_type
    
    def score(self,x_test,y_test):
        count = sum(y_test == self.predict(x_test))
        return count/len(y_test)

均值归一化

def standardization(dataSet):
    meanValues = dataSet.mean(axis=0)
    stdValues = dataSet.std(axis=0)
    normDataSet = (dataSet-meanValues)/stdValues
    return normDataSet

利用库中的鸢尾花数据集进行模型实现

iris = datasets.load_iris()
x = iris.data
y = iris.target
x = standardization(x)
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.3)
model = Knn(k=15,weight=False)
model.fit(x_train,y_train)
pred_test = model.predict(x_test)
test_score =  model.score(x_test,y_test)
print('test_score: ',test_score)
test_score:  0.9777777777777777

你可能感兴趣的:(机器学习,python,机器学习,算法)