机器学习(一)——k-近邻、kd树及其python实现

1.k近邻的基本原理

1.1基本流程

k-近邻通过测量不同特征属性之间的距离来分类

一般流程:

(1)计算当前点与已知类别(训练集)中的各个点之间的距离(欧式、皮尔逊等)

(2)距离递增排序。选取距离最小的前k个

(3)对这个k个点的类别计数,即k个点中属于类别1的有几个,属于类别2的有几个。。。。

(4)返回类别出现次数最多的类,当做当前的预测分类

1.2k-近邻的三个基本要素

  • 距离度量

欧式距离(p=2)、简单的绝对值相加(p=1)、p=3/4…..

  • k值的选择

k值小,意味着采用较小的邻域中的训练实例进行预测,近似误差减小,模型复杂,容易过拟合。极端情况是k=1,选取一个距离最近的。这种情况会导致估计误差变大,单个点的结果即代表预测结果,如果单个点恰巧是噪声点,则GG思密达惊恐。

k值大,意味着采用较大的邻域中的训练实例进行预测,模型简单,近似误差增大。极端情况是全部训练点,这个时候,预测结果等于出现频次最多的类,虽然估计误差减少了,但是预测也没有意义再见。

扫描二维码关注公众号,回复: 2394311 查看本文章

一般,k先取小值,交叉验证,选取最优结果的k值。

  • 分类决策规则

多数表决,即与带预测点距离最近的k个点坐在类别中,类别出现次数最多的,即为预测分类。

多数表决规则等价于经验风险最小化

1.3k近邻的python实现

因为是学习,所以就选取了sklearn包中的鸢尾花数据集进行测验,没有对数据进行处理,直接测试算法(简单的数据可视化分析在上一篇文章中讲到)。

from numpy import *
import operator
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
#kNN算法
def classfy(test,dataSet,label,k):
    dataSetSize=dataSet.shape[0]
    #采用欧氏距离公式
    # 扩充矩阵,计算差
    diffMat=tile(test,(dataSetSize,1))-dataSet
    #计算平方
    diffMatSquare=diffMat**2
    #横向求和
    disSum=diffMatSquare.sum(axis=1)
    #开根号
    distacens=disSum**0.5
    #按距离由小到大排序,并返回索引值
    sortDistanceIndex=distacens.argsort()
    classCount={}
    for i in range(k):
        key=label[sortDistanceIndex[i]]
        classCount[key]=classCount.get(key,0)+1
    sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
    return  sortedClassCount[0][0]

# 加载数据,获取数据的特征属性列表、数据、分类信息
dataSet = load_iris()
# 获取属性标签
FeatureLabels = dataSet.feature_names
# 获取属性数据
data= dataSet.data
# 获取分类信息
classInfo = dataSet.target
#划分测试集和训练集
train,test,train_label,test_label=train_test_split(data,classInfo,test_size=0.9)
testSize=test.shape[0]
err_count=0.0
for i in range(testSize):
    classResult=classfy(test[i,:],train,train_label,5)
    print('推算结果是',classResult,'-------实际结果是',test_label[i])
    if classResult!=test_label[i]:
        err_count+=1
errRate=err_count/testSize
print('错误率是:%f'%errRate)

【结果】
鸢尾花数据集上运行k-近邻结果
单独运行一次的结果会不稳定,而且k的值也是可调的

2.kd树

k-近邻主要是通过距离计算寻找k个最近邻居,当数据量很大时,计算及其耗时,解决思路是如何减少距离的计算次数,使得八竿子打不着的点就不用计算距离了,从而提高k近邻的搜索效率,kd树就是其中一种方法

2.1构造平衡kd树

输入:k维空间数据集 Y = { x 1 , x 2 , . . . , x N } ,其中 x i = { x i ( 1 ) , x i ( 2 ) , . . . , x i ( k ) } T , i = 1 , 2 , . . . , N ;
输出:kd树

  1. 开始:构造根节点,根节点对应的区域包含T的k维空间的超矩形区域。
    选择 x ( 1 ) 为坐标轴,以数据集T中所有 x ( 1 ) 坐标的中位数为切分点,进行划分。
    由根节点生成深度为1的左右子节点,左节点对应的 x ( 1 ) 坐标小于切分点,右节点对应的 x ( 1 ) 坐标大于切分点。
    落在切分平面上的实例点作为根节点
  2. 重复:对于深度为j的点,比较的坐标就是数据集中各 x ( l ) ,l=j(mod k)+1。同样以 x ( l ) 坐标的中位数为切分点,切分为两个子区域。
    由该节点生成深度为j+1的左右子节点,左边小,右边大
    将落在切分超平面上的实例点保存在该节点

    3.直至两个区域没有实例时存在停止,从而形成的kd树的区域划分。

2.2 搜索kd树

输入:已构造的kd树,目标点x
输出:x的最近邻
(1) 在kd树中找出包含目标点x的叶节点:从根节点出发,递归地向下访问kd树。若目标点x当前维的坐标小于切分点,移至左边区域;否则移至右边区域,直到子节点为叶节点为止。
(2)以此节点为“当前最近点”
(3)递归地向上回退,作如下操作:
a.该节点比“当前最近点”距离目标点更近,该节点为“当前最近点”
b.当前最近点一定存在于该节点的一个子节点对应的区域。检查该子节点的父节点的另一 子节点对应区域是否有更近的点,即另一子节点对应的区域是否与以目标点为核心,以目标点与“当前最近点”间的距离为半径的超球体相交。
b.1如果相交,可能在另一子节点对应区域存在更近的点,移动到另一子节点,递归搜索
b.2不相交,向上回退
(4)回退到根节点,搜索结束。最后的“当前最近点”即为目标点x的最近邻点。

2.3搜索kd树分析

kd树搜索的平均复杂度O(logN),这里N是训练实数
kd树适用于训练实例树远大于k维的k近邻搜索。当空间维数接近于训练实例数时,它的效率会下降,几乎接近线性扫描。

2.4kd树的python实现

关于kd树的详解解释+代码
kd树的python实现

猜你喜欢

转载自blog.csdn.net/brave_jcc/article/details/80190952