K-近邻算法开篇之电影

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25343557/article/details/82932632

      作为一个ML的萌新,对算法什么的着实头疼。最近一直在看一些实践类的书籍和视频,接触到了KNN算法,而本人记忆力差所以写下本文最为日后的回顾。


K-近邻算法简介

      第一次听说这个算法感觉很高大上(对于所有算法都是这个感觉,大概这就是菜吧!)。

      k近邻法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一种基本分类与回归方法。它的工作原理是:存在一个样本数据集合,也称作为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新的数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

K-近邻算法图解:
在这里插入图片描述
      如图所示,红点和蓝点为已知类别的数据,绿点为未知类别数据。现在我们需要使用KNN判断绿点属于哪一方。
      选择k=3,选择距离绿点最近的3个点(近邻,越近越相似),我们可以发现3个点中有2个红点和1个蓝点,选择出现次数最多的点,即红点,所以可以判断绿点是属于红方的。
      使用欧氏距离公示计算两个向量点( x 1 x_{1} , y 1 y_{1} )和( x 2 x_{2} , y 2 y_{2} )之间的距离:
在这里插入图片描述
例如,点(0,0)和(1,2)之间的距离计算为: ( 1 0 ) 2 + ( 2 0 ) 2 \sqrt{(1-0)^2+(2-0)^2}
如果数据集存在4个特征值,则点(1,0,0,1)和(7,6,9,4)之间的距离计算为: ( 7 1 ) 2 + ( 6 0 ) 2 + ( 9 0 ) 2 + ( 4 1 ) 2 \sqrt{(7-1)^2+(6-0)^2+(9-0)^2+(4-1)^2}

但是,使用上面的KNN会有一个问题:当k=5时,选择距离绿点最近的5个点,我们可以发现5个点中有2个红点和3个蓝点,选择出现次数最多的点,即蓝点,所以可以判断绿点是属于蓝方的。此时我们就无法判断绿点是属于哪一类的了。实际上,我们可以依据统计学的理论看它所处的位置特征,衡量它周围邻居的权重,而把它归为(或分配)到权重更大的那一类。这就是K近邻算法的核心思想。 即不能单单通过距离来决定。

K-近邻算法优缺点

  • 优点:精度高,对异常值不敏感、无数据输入假定
  • 缺点:计算复杂度高、空间复杂度高。

K-近邻与电影

      当我们观看电影时我们会很自然的分辨出电影类型,看到大量的动作招式,我们知道这是动作电影,看到大量的亲吻镜头,我们知道这是爱情电影。
在这里插入图片描述
我们可迅速判断这部未知电影是爱情片,因为接吻镜头多与打斗镜头。那么使用KNN呢?首先我们选择k=3,计算未知电影和其他电影的距离:

k=3
unknown = (18,90)
others = [(3,104),(2,100),(1,81),(101,10),(99,5),(98,2)]
dis_val = []
#计算欧氏距离
for item in others:
    dis_val.append(((unknown[0]-item[0])**2+(unknown[1]-item[1])**2)**0.5)
print(dis_val)
#[20.518284528683193, 18.867962264113206, 19.235384061671343, 115.27792503337315, 117.41379816699569, 118.92854997854805]

我们可以得出以下结果:

  • unknown→love1(3,104)=118.9285
  • unknown→love2(2,100)=117.4138
  • unknown→love3(1,81)=115.2779
  • unknown→action1(101,10)=19.2354
  • unknown→action2(99,5)=18.8680
  • unknown→action3(98,2)=20.5183

选择3个距离最近的点,即love1,love2,love3,判断出未知电影为爱情片。

KNN正经写法:

import numpy as np
import operator

def get_dataset():
    data_set = np.array([[3, 104],[2,100],[1,81],[101,10],[99,5],[98,2]])
    labels = ['爱情片','爱情片','爱情片','动作片','动作片','动作片']
    return data_set,labels

def classify(test_set,data_set,labels,k):
    #获取数据集的行数(即数目)
    data_set_size = data_set.shape[0]
    #np.tile()方法生成一个和data_set相同大小的矩阵,将每个元素和data_set中的对应位置相减
    #即完成:x1-x2和y1-y2操作。
    diff_mat = np.tile(test_set,(data_set_size,1))-data_set
    # 完成:(x1-x2)^2和(y1-y2)^2操作。
    sq_mat = diff_mat**2
    # 完成:sqrt((x1-x2)^2+(y1-y2)^2)操作。
    dis_mat = np.sum(sq_mat,axis=1)**0.5
    #距离从小到大排序后的索引值
    sorted_index = dis_mat.argsort()
    #class_count用于记录选择出的k个数据中各个类别的数目
    class_count={}
    for i in range(k):
        class_label = lables[sorted_index[i]]#获取label
        class_count[class_label] = class_count.get(class_label,0)+1#统计数目
    #根据字典的值进行排序(由大到小)
    sorted_count = sorted(class_count.items(),key=operator.itemgetter(1),reverse=True)
    return sorted_count[0][0]


if __name__=="__main__":
    test_set = [18,90]
    data_set,lables = get_dataset()
    result = classify(test_set,data_set,lables,3)
    print(result)

猜你喜欢

转载自blog.csdn.net/qq_25343557/article/details/82932632
今日推荐