1. knn算法
定义:对于输入变量x,寻找数据集中距离x最近的k个实例,这k个实例中哪个类的数量最多,就认为输入变量x属于该类。
2.距离度量
对于knn算法,我们一般选择欧式距离作为距离度量,当然,对于不同的问题,可能会有不同的选择。
3.k值的选择
k值的选择对于knn的结果具有很大的影响。
如果选择了较小的k值,只有和输入实例较近(相似的)的训练实例才会对预测结果起作用。预测结果会对临近的实例点非常敏感,如果邻近的实例点是噪声,预测就很大程度会出错。换句话说,k值的减小意味着模型变得更加复杂。容易过拟合。
相反,较大的k值意味着简单的模型。优点是可以减少学习的泛化误差。但是学习的近似误差(训练误差)会增大。
#4.knn的python实现#
knn的python实现(利用欧式距离)
import numpy as np
##给出训练数据以及对应的类别
def createDataSet():
group = np.array([[1.0,2.0],[1.2,0.1],[0.1,1.4],[0.3,3.5]])
labels = ['A','A','B','B']
return group,labels
def knn(input,dataset,labels,k):
#label是新的输入变量
#dataset是数据集
#labels是数据集的标记
#计算欧氏距离
diff=np.tile(input,(dataset.shape[0],1))-dataset
squarediff=diff**2
squarediff=np.sum(squarediff,axis=1)
dist=squarediff**0.5
#进行排序,得到index
sortDistIndex=np.argsort(dist)
classCount={}
for i in range(k):
voteLabel=labels[sortedDistIndex[i]]
classCount[voteLabel]=classCount.get(voteLabel,0)+1
maxCount=0
for key,value in classCount.items():
if value>maxCount:
maxCount=value
classes=key
return classes
dataSet,labels = createDataSet()
input = array([1.1,0.3])
K = 3
output =knn(input,dataSet,labels,K)
print("测试数据为:",input,"分类结果为:",output)
5.kd树
kd树是一种对k维空间中的实例点进行存储并且快速检索的树形数据结构。下面来看一下kd树的构造和kd树的搜索算法
构造平衡kd树:
对于k维空间数据集T={
x
1
,
x
2
,
⋯
 
,
x
N
x_{1},x_{2},\cdots,x_{N}
x1,x2,⋯,xN}
(1)开始:构造根节点。
选择
x
(
1
)
x^{(1)}
x(1)为坐标轴,以T中所有实例的
x
(
1
)
x^{(1)}
x(1)坐标的中位数作为切分点。将根节点对应的矩形区域切分成两个子区域。由根节点生成深度为1的左右节点。左子节点对应坐标
x
(
1
)
x^{(1)}
x(1)小于切分点的区域,右子节点是大于切分点的子区域。
(2)重复:对于深度为j的节点,选择
x
(
L
)
x^{(L)}
x(L)为切分的坐标轴,L=j(mod k)+1,
(3)直到两个子区域没有实例存在时停止,从而形成kd树的区域划分。
kd树的python代码实现
kd树搜索:
(1)在kd树中找出包含目标点x的叶结点:从根结点出发,递归的向下访问kd树,如果x当前维度的坐标小于切分点的坐标,则移动到左子结点,否则移动到右子结点。直到到达叶结点为止。
(2)以此叶结点为"当前最近点"
(3)递归的向上回退,每个结点进行一下操作:
(a)如果该结点保存的实例点比当前最近点距离目标点更近,则以该实例点为"当前最近点"
(b)当前最近点一定处于一个子结点对应的区域,检查该子结点的父节点的另一子结点是否存在更近的点。通过画圆来检测,检测另一区域点到目标点的维度距离是否在以目标点与"当前最近点"间的距离为半径的球体相交。
如果相交,则可能存在更近的点,移动到另一个子结点,接着,递归的进行最近邻搜索;
如果不想交,向上回退。
(4)当回退到根节点时,搜索结束。最后的"当前最近点"就是x的最近邻点
如果实例点是随机分布的,kd树搜索的平均计算复杂度是O(logN),这里N是训练实例树。
import numpy as np
#节点
class Node:
def __init__(self,data,lchild=None,rchild=None):
self.data=data
self.lchild=lchild
self.rchild=rchild
#kd树
class KdTree:
def __init__(self):
self.KdTree=None
#创建Kd树,返回根节点
def create(self,dataset,depth):
#选取坐标轴,选择中位数作为划分点。
if(len(dataset)>0):
m,n=np.shape(dataset)
'''
书中坐标轴l=depth(modk)+1,其中k是维度。但是python数组中下标是
从1开始的。于是坐标轴l=depth mod k'''
axis=depth%n
#选取坐标轴
midIndex=int(m/2)
#对数组进行排序
sortedDataSet=self.sort(dataset,axis)
node=Node(sortedDataSet[midIndex])# 将节点的数据域设置为中位数
leftDataSet=sortedDataSet[:midIndex]
rightDataSet=sortedDataSet[midIndex+1:]
print('划分的数据为:',sortedDataSet[midIndex])
print('左子树的数据为:',leftDataSet)
print('右子树的数据为:',rightDataSet)
#递归实现kd树的构建
node.lchild=self.create(leftDataSet,depth+1)
node.rchild=self.create(rightDataSet,depth+1)
return node
else:
return None
#排序函数
def sort(self,dataset,axis):
sortedDataSet=dataset
m,n=np.shape(sortedDataSet)
for i in range(m):
for j in range(0,m-i-1):
if sortedDataSet[j][axis]>sortedDataSet[j+1][axis]:
temp=sortedDataSet[j]
sortedDataSet[j]=sortedDataSet[j+1]
sortedDataSet[j+1]=temp
print(sortedDataSet)
return sortedDataSet
def preOrder(self,node):#前序遍历
if node!=None:
print(node.data)
self.preOrder(node.lchild)
self.preOrder(node.rchild)
def search(self,tree,x):#kd树的搜索
self.nearestPoint=None#保存最近的点
self.nearestValue=0#保存最近的值
def travel(node,depth=0): #递归搜索
if node !=None: #递归终止条件
n=len(x)
axis=depth%n
if x[axis]<node.data[axis]:#如果数据小于节点的数据,就往左子树搜索,不然就往右子树
travel(node.lchild,depth+1)
else:
travel(node.rchild,depth+1)
#递归完毕之后,往父节点进行回溯
distance=self.dist(x,node.data)
print(distance)
if self.nearestPoint==None:
self.nearestPoint=node.data
self.nearestValue=distance
elif self.nearestValue>distance:
self.nearestPoint=node.data
self.nearestValue=distance
print(node.data, depth, self.nearestValue, node.data[axis], x[axis])
#接下来需要去判断是否要去子节点的区域去寻找
if abs(x[axis]-node.data[axis])<=self.nearestValue:
if x[axis]<node.data[axis]:
travel(node.rchild,depth+1)
else:
travel(node.lchild,depth+1)
travel(tree)
return self.nearestPoint
#计算欧氏距离
def dist(self,x1,x2):
return ((np.array(x1)-np.array(x2))**2).sum()**0.5
if __name__ == '__main__':
dataSet = [[2, 3],
[5, 4],
[9, 6],
[4, 7],
[8, 1],
[7, 2]]
x=[5,3]
kdtree=KdTree()
tree=kdtree.create(dataSet,0)
kdtree.preOrder(tree)
print('---------------')
print(kdtree.search(tree,x))