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={}
(1)開始:構造根節點。
選擇爲座標軸,以T中所有實例的座標的中位數作爲切分點。將根節點對應的矩形區域切分成兩個子區域。由根節點生成深度爲1的左右節點。左子節點對應座標小於切分點的區域,右子節點是大於切分點的子區域。
(2)重複:對於深度爲j的節點,選擇爲切分的座標軸,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))