M1: K Nearest Neighbors Study Note


  这是第一篇,非常感谢宋志瑞给予我的鼓励,当然我不会跟他说谢谢,但是真的很感激,也很感激一直努力的自己!
2019年1月5日-小寒日

本文并没有写比较基础的东西,也没有详细的展开,只是做一个自己学习过程中思路的总结

1、理论知识

1.1、算法简述

  k近邻算法(以下称k-NN)是一种基本的分类和回归算法,这里只写分类问题中的k-NN;k-NN采用测量不同特征值之间的距离方法来进行分类,也就是利用数据集对对特征向量空间进行划分。

K-NN:
优点:精度高、对异常值不敏感、无输入数据假定
缺点:计算复杂度高、空间复杂度高
可应用案例:手写数字识别、鸢尾花分类、预测年收入、疾病诊断等一般分类问题

1.2、K-NN算法原理:

  要预测一个样本的类别,首先在训练数据集中找到与该样本距离最近的k个实例,然后让这k个实例投票,得票最多的那个类别就是该样本的类别。

算法原理简单容易理解,但是我们看看kNN算法相关的参数问题:
1. 距离问题:
kNN算法一般使用Euclidean距离:

d i s t ( X , Y ) = ∑ i = 1 n ( x i − y i ) 2 dist(X, Y)=\sqrt{\sum^{n}_{i=1}(x_{i}-y_{i})^{2}} dist(X,Y)=i=1n(xiyi)2

当然还有其他距离,Minkowski距离:
d i s t ( X , Y ) = ∑ i = 1 n ( x i − y i ) p p dist(X, Y)=\sqrt[^p]{\sum^{n}_{i=1}(x_{i}-y_{i})^{p}} dist(X,Y)=pi=1n(xiyi)p

当p=1时:Manhattan distance
当p=2时:Euclidean distance
当p=inf时:Chebyshev distance
当p=any时:你自己指定>1的数值

2. k值问题:

  • k值一般选择奇数(投票时候容易得到结果),如果k=1,称为最邻近算法
  • 在实际应用中,k值一般选择较小的值,通常采用交叉验证法,选取最终的k值

sklearn Module的kNN算法默认k=5,一般可以从5开始尝试。

  • k值选择还有一个权重问题,根据离待分类样本距离增加权重值,距离越近,权重越大。

从下面这个图片可以看出来k值选择的重要性:
在这里插入图片描述
如果k=3(实线),那么绿色圆点属于红色一类
如果k=5(虚线),那么绿色圆点属于蓝色一类

1、选择较小的k值,学习的近似误差会减小,缺点是学习的估计误差会增大。换句话说,k值的减小就意味着整体模型变得复杂,容易发生过拟合。

2、选择较大的k值,其优点是可以减少学习的估计误差,缺点是会增大学习的近似误差,k值增大,就意味着整体模型变得简单。如果k=N,那么无论输入什么,都将会把他预测为数据集中样本最多的那个类,这时候,模型就过于简单了,忽略了数据集中大量的有用信息。

因此,一定要用交叉验证法来选择合适的k值。

3. 样本数值问题:

  样本属性不同,数值会有很大的差异,一般会做归一化处理,这样预测结果更准确。
归一化有两种方法

  1. x_norm = (x - min) / (max - min) 归一化为(0~1)
  2. x_norm = (x - mean) / std 归一化为正态分布

4. 样本数量问题:

  样本不均衡也会对预测结果产生影响,这个不均衡要看实际情况,如果样本比较少,不同类别相差1个都是不均衡的;如果样本比较多,相差1000个也是无所谓的。如果要使样本均衡可以使用上采样或者下采样方法方法。

# 举例:上采样方法
from imblearn.over_sampling import SMOTE
sm = SMOTE()
X2_train, y2_train = sm.fit_resample(X_train, y_train)

2、代码实战

2.1、代码模拟:

  这里只写预测一个向量的情况进行模拟,主要是为了方便理解原理,实际中很少这么用。

class KNearestNeighbors(object):
	
	# 这里用大X,虽然不符合PEP8,目的是同数学中矩阵一致,因为训练数据是矩阵
    def __init__(self, X_trian, y_train, k):
        self.X_train = X_trian
        self.y_train = y_train
        self.k = k

    def classify(self, X_test):
        sample_size = self.X_train.shape[0]
        X_test_matrix = np.tile(X_test, (sample_size, 1))
        distance1 = (X_test_matrix - self.X_train)**2
        distance2 = distance1.sum(axis=1)
        distance = distance2**0.5
        distance_sort_arg = np.argsort(distance)
        class_count = {}
        for i in range(self.k):
            vote_label = self.y_train[distance_sort_arg[i]]
            class_count[vote_label] = class_count.get(vote_label, 0) + 1
        sorted_class = sorted(class_count.items(), key=lambda x: x[1], reverse=True)
        return sorted_class[0][0]

  这里写的是最基础的分类方式,实际在分类中,这种Brute force的方法对于小样本量还可以,样本量和特征增大之后,效率就很低下,使分类变得不切实际,现在已经发明了大量的基于树的数据结构,比如kd-treeball-tree等,简单写一下我的理解:

1、kd-tree就是划分训练集的空间,将训练集分成方形的小空间。数据集的维数为 D D D ,一般来说要求数据的规模 N N N 满足 N » 2 D N» 2^D N»2D,使用kd-tree才能达到高效的搜索。一般来说,数据集的维数小于20,使用kd-tree,如果维数大于20,kd-tree的搜索性能急剧下降,几乎接近贪婪线性扫描;
2、于是有了一系列的算法改进kd-tree,BBF(Best-Bin-First),BBF在kd-tree检索时候将另一个兄弟节点存储到一个优先队列中,在回溯的时候,先查询优先队列的节点,直至优先队列为空;
3、无论是kd-tree还是BBF都是把空间分成了有棱角的空间,会造成不必要的过多检索,因此有ball-tree;
4、高维特征向量的数据集,有VP-tree和MVP-tree

参考:
从K近邻算法、距离度量谈到KD树、SIFT+BBF算法


2.2、使用模块:

使用sklearn库中的方法,

from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier(n_neighbors=5)

knn.fit(X_train, y_train)

score = knn.score(X_test, y_test)

y_ = knn.predict(X_test)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值