knn算法以及算法實現

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={x1,x2, ,xNx_{1},x_{2},\cdots,x_{N}}
(1)開始:構造根節點。
選擇x(1)x^{(1)}爲座標軸,以T中所有實例的x(1)x^{(1)}座標的中位數作爲切分點。將根節點對應的矩形區域切分成兩個子區域。由根節點生成深度爲1的左右節點。左子節點對應座標x(1)x^{(1)}小於切分點的區域,右子節點是大於切分點的子區域。
(2)重複:對於深度爲j的節點,選擇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))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章