kNN原理及其python实现

kNN原理及其python实现

k-NN(k-nearest neighbor),从英语翻译过来就是k个最接近的邻居;我们现在只要有k和最接近这两个概念就行了。接下来笔者将详细介绍其原理,并用python实现k-NN。

kNN原理

k近邻法由Cover和Hart P在1967年提出的一种分类和回归的方法[1]
其原理为:给定一组带标签的数据,为了确定不带标签数据的类别(标签),我们从带标签数据中选取k个与不带标签的最相似的数据;然后从k个最相似带标签的数据中决定未知数据的标签,未知数据的标签为k个中标签数目最多的那个。

哇,好难懂的原理,来,我们看个例子
kNN原理及其python实现_第1张图片
暂时还没没想出比这个[1]更好的例子,引一下:
我们可以用k近邻算法来分类一个电影是爱情片还是动作片:

电影名称 打斗镜头 接吻镜头 电影类型
电影1 1 101 爱情片
电影2 5 89 爱情片
电影3 108 5 动作片
电影4 115 8 动作片

如果有一个电影有101个接吻镜头,1个打斗镜头,我们就可以认为该片为爱情片;如果有5个接吻镜头,108个打斗镜头,当然我们会认为该电影为动作片。假如现在有一个电影有8个打斗镜头,89个接吻镜头,因为接吻镜头占了绝大多数,我们依然可以将该电影判定为爱情片。

kNN是如何如何实现这一判断过程呢?

kNN是计算相似性来进行判断的,在计算问题中相似性可以看作是距离,距离可以是任何已知的衡量距离的方式,比如说曼哈顿曼哈顿距离或欧式距离。
我们可以用 L p L_p Lp距离[2]
对于特征空间 X X X n n n维实数向量 x i , x j ∈ X {x_i},{x_j} \in X xi,xjX x i = ( x i ( 1 ) , x i ( 2 ) , . . , x i ( n ) ) {x_i} = ({x_i}^{(1)},{x_i}^{(2)},..,{x_i}^{(n)}) xi=(xi(1),xi(2),..,xi(n)) x j = ( x j ( 1 ) , x j ( 2 ) , . . , x j ( n ) ) {x_j} = ({x_j}^{(1)},{x_j}^{(2)},..,{x_j}^{(n)}) xj=(xj(1),xj(2),..,xj(n)) L p L_p Lp距离定义为:
L p ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p {L_p}({x_i},{x_j}) = {(\sum\limits_{l = 1}^n {|{x_i}^{(l)} - {x_j}^{(l)}{|^p}} )^{\frac{1}{p}}} Lp(xi,xj)=(l=1nxi(l)xj(l)p)p1
p ≥ 1 p \ge 1 p1,当 p = 1 p=1 p=1时为曼哈顿距离, p = 2 p=2 p=2时为欧式距离。

现在我们在 L p L_p Lp距离中取 p = 2 p=2 p=2,拿我们刚刚的例子来做计算的话:
我们要判断的电影与电影1到4的距离分别为:
( 8 , 89 ) → ( 1 , 101 ) : 13.89 ( 8 , 89 ) → ( 5 , 89 ) : 3 ( 8 , 89 ) → ( 108 , 5 ) : 130.60 ( 8 , 89 ) → ( 115 , 8 ) : 134.20 \begin{array}{l} (8,89) \to (1,101):13.89\\ (8,89) \to (5,89):3\\ (8,89) \to (108,5):130.60\\ (8,89) \to (115,8):134.20 \end{array} (8,89)(1,101):13.89(8,89)(5,89):3(8,89)(108,5):130.60(8,89)(115,8):134.20
从以上距离选最小的一个,即: ( 5 , 89 ) (5,89) (5,89)电影2,为爱情片,因此,我们可以判定 ( 8 , 89 ) (8,89) 8,89为爱情片,ok,搞定。

嗯?等等,k近邻,k近邻,k呢?

怎么回事呢,其实我们刚刚只拿了一个最近的点来判断,这种情况下k=1,我们可以将其称为最近邻,而不是k近邻。

现在我们来用k近邻来判断一下,我们取k=3:
首先我们将距离从小到大排序(相似度由大到小,距离越小越相似),排序前k(3)位为: ( 5 , 89 ) , ( 1 , 101 ) , ( 108 , 5 ) (5,89),(1,101),(108,5) (5,89),(1,101),(108,5),其中前两个为爱情片,第3个为动作片;爱情片的数量最多,占 2 / 3 2/3 2/3,因此我们可以将 ( 8 , 89 ) (8,89) (8,89)划分为爱情片。至此,我们完成了kNN的整个过程。

结合以上,整个kNN的过程可以概括为:

  1. 确定带标签的数据(训练集)和要进行分类的不带标签的数据(测试集)
  2. 计算不带标签的数据与带标签数据中每个数据的相似度
  3. 按照相似度从小到达排序,并选取最相似的k个数据
  4. 从k个数据中确定最哪个类别的数据最多,将其作为未知数据的标签

kNN的python实现

在以下代码中,我们取 p = 2 p=2 p=2,即用欧式距离;并取k=3,程序结果预测到 ( 8 , 89 ) (8,89) (8,89)为爱情片。
此外,笔者在鸢尾花数据集上对其进行了测试,测试结果显示能够达到96%的准确率。
代码如下:

# -*- coding:UTF-8 -*-
"""
created on Thursday,19:33,2020-01-02
@author:Jeaten
e_mail:[email protected]
"""
import math
def judge(actual,prediction):###    判断实际值是否与预测值相等
    return actual==prediction
def distance(point1,point2,p):###   计算两点的Lp距离
    '''
    this function is used to calculate the distance of two points
    :param point1: first point 
    :param point2: second point
    :param p: value p, is used to distinguish all kinds of distances
    :return: the distance under 'p'
    '''
    assert point1.__len__()==point2.__len__()
    dis=0
    for i in range(point1.__len__()):
        dis+=math.pow(abs(point1[i]-point2[i]),p)
    return dis**(1/p)
def generate_data():### 生成数据
    '''
    this function is used to generate the data you need
    :return: the feature and label
    '''
    height_weight=[(1,101),(5,89),(108,5),(115,8)]
    label=[1,1,-1,-1]
    return height_weight,label
def knn(actual,feature,label,k):### knn算法
    '''
    the knn(k-nearest neighbor) algorithm
    :param actual: the actual value you want to know which category it belongs to
    :param feature: the feature of all data
    :param label: the label of all data
    :param k: the value of k 
    :return: the category the actual value most likely belongs to
    '''
    assert  feature.__len__()==label.__len__()
    temp=[]
    for i in range(feature.__len__()):
        temp.append(distance(actual,feature[i],p))
    temp=sorted(enumerate(temp),key=lambda x:x[1])
    res=[label[i[0]] for i in temp]
    return max(res,key=res[:k].count)
def __test__():
    ####    测试其在鸢尾花数据集上的效果
    from sklearn.datasets import load_iris
    from sklearn.model_selection import train_test_split
    load_data = load_iris()
    x = load_data.data
    y = load_data.target
    x_train, x_test, y_train, y_test = train_test_split( x, y, test_size=0.2 )
    correct = 0
    for i in range( x_test.__len__() ):
        prediction = knn( x_test[i], x_train, y_train, k )
        if judge( y_test[i], prediction ):
            correct += 1
    print( correct, x_test.__len__(), correct / x_test.__len__() )
if __name__ == '__main__':
    p=2
    k=3
    feature,label=generate_data()
    dic={1:"爱情片",-1:"动作片"}###   字典结构,用于显示结果
    act=(8,89)
    print("该片为:",dic[knn(act,feature,label,k)])
    # __test__()

才疏学浅,难免有错误和不当之处,欢迎交流批评指正!
同时有问题的话欢迎留言或邮箱联系([email protected])。

Reference:

[1] 傻了吧嗒.K-近邻算法(史诗级干货长文).
https://blog.csdn.net/u010451580/article/details/51373081.20160.5.11

[2] 李航.《统计学习方法》[M].2012.3.北京:清华大学出版社,2019.5(重印):14-15.

创作不易,觉得写得不错就微信扫码奖励一下吧!

kNN原理及其python实现_第2张图片

你可能感兴趣的:(机器学习)