k-近邻通过测量不同特征属性之间的距离来分类
一般流程:
(1)计算当前点与已知类别(训练集)中的各个点之间的距离(欧式、皮尔逊等)
(2)距离递增排序。选取距离最小的前k个
(3)对这个k个点的类别计数,即k个点中属于类别1的有几个,属于类别2的有几个。。。。
(4)返回类别出现次数最多的类,当做当前的预测分类
欧式距离(p=2)、简单的绝对值相加(p=1)、p=3/4…..
k值小,意味着采用较小的邻域中的训练实例进行预测,近似误差减小,模型复杂,容易过拟合。极端情况是k=1,选取一个距离最近的。这种情况会导致估计误差变大,单个点的结果即代表预测结果,如果单个点恰巧是噪声点,则GG思密达惊恐。
k值大,意味着采用较大的邻域中的训练实例进行预测,模型简单,近似误差增大。极端情况是全部训练点,这个时候,预测结果等于出现频次最多的类,虽然估计误差减少了,但是预测也没有意义再见。
一般,k先取小值,交叉验证,选取最优结果的k值。
多数表决,即与带预测点距离最近的k个点坐在类别中,类别出现次数最多的,即为预测分类。
多数表决规则等价于经验风险最小化
因为是学习,所以就选取了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个最近邻居,当数据量很大时,计算及其耗时,解决思路是如何减少距离的计算次数,使得八竿子打不着的点就不用计算距离了,从而提高k近邻的搜索效率,kd树就是其中一种方法
输入:k维空间数据集 Y={x1,x2,...,xN} Y = { x 1 , x 2 , . . . , x N } ,其中 xi={x(1)i,x(2)i,...,x(k)i}T x i = { x i ( 1 ) , x i ( 2 ) , . . . , x i ( k ) } T , i=1,2,...,N; i = 1 , 2 , . . . , N ;
输出:kd树
重复:对于深度为j的点,比较的坐标就是数据集中各 x(l) x ( l ) ,l=j(mod k)+1。同样以 x(l) x ( l ) 坐标的中位数为切分点,切分为两个子区域。
由该节点生成深度为j+1的左右子节点,左边小,右边大
将落在切分超平面上的实例点保存在该节点
3.直至两个区域没有实例时存在停止,从而形成的kd树的区域划分。
输入:已构造的kd树,目标点x
输出:x的最近邻
(1) 在kd树中找出包含目标点x的叶节点:从根节点出发,递归地向下访问kd树。若目标点x当前维的坐标小于切分点,移至左边区域;否则移至右边区域,直到子节点为叶节点为止。
(2)以此节点为“当前最近点”
(3)递归地向上回退,作如下操作:
a.该节点比“当前最近点”距离目标点更近,该节点为“当前最近点”
b.当前最近点一定存在于该节点的一个子节点对应的区域。检查该子节点的父节点的另一 子节点对应区域是否有更近的点,即另一子节点对应的区域是否与以目标点为核心,以目标点与“当前最近点”间的距离为半径的超球体相交。
b.1如果相交,可能在另一子节点对应区域存在更近的点,移动到另一子节点,递归搜索
b.2不相交,向上回退
(4)回退到根节点,搜索结束。最后的“当前最近点”即为目标点x的最近邻点。
kd树搜索的平均复杂度O(logN),这里N是训练实数
kd树适用于训练实例树远大于k维的k近邻搜索。当空间维数接近于训练实例数时,它的效率会下降,几乎接近线性扫描。
关于kd树的详解解释+代码
kd树的python实现