knn(k_nearest neighbor)是一種基本的分類與迴歸方法。KNN做分類預測時,一般是選擇多數表決法,即訓練集裏和預測的樣本特徵最近的K個樣本,預測爲裏面有最多類別數的類別。而KNN做迴歸時,一般是選擇平均法,即最近的K個樣本的樣本輸出的平均值作爲迴歸預測值。由於兩者區別不大,雖然本文主要是講解KNN的分類方法,
knn算法的三要素是:k值的選擇,距離度量,分類決策規則
k近鄰算法的流程:
(1)根據給定的距離度量,在訓練數據集中找出與目標樣本x最鄰近的k個點,涵蓋這k個點的鄰域稱爲Nk(x)。
a . 計算測試數據x與各個訓練數據之間的距離,一般用歐式距離;
也有選擇Manhattan(曼哈頓距離)作爲距離度量規則的:
b. 按照距離的遞增關係進行排序,選取距離最小的K個點;
(2)確定前K個點所在類別的出現頻率,返回前K個點中出現頻率最高的類別作爲測試數據的預測分類。
如下所示,爲多數表決的公式:
I 爲指示函數(意思是:即當yi = cj時 I等於1,否則I等於0),j爲k的個數大小。
關於KD樹搜索:knn的普通方式會比較慢,用KD樹會加快速度
KD樹的三個步驟:第一步是建樹,第二部是搜索最近鄰,最後一步是預測
第一步建樹:
比如我們有二維樣本6個,{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},構建kd樹的具體步驟爲:
1)找到劃分的特徵。6個數據點在x,y維度上的數據方差分別爲6.97,5.37,所以在x軸上方差更大,用第1維特徵建樹。
2)確定劃分點(7,2)。根據x維上的值將數據排序,6個數據的中值(所謂中值,即中間大小的值)爲7,所以劃分點的數據是(7,2)。這樣,該節點的分割超平面就是通過(7,2)並垂直於:劃分點維度的直線x=7;
3)確定左子空間和右子空間。 分割超平面x=7將整個空間分爲兩部分:x<=7的部分爲左子空間,包含3個節點={(2,3),(5,4),(4,7)};另一部分爲右子空間,包含2個節點={(9,6),(8,1)}。
4)用同樣的辦法劃分左子樹的節點{(2,3),(5,4),(4,7)}和右子樹的節點{(9,6),(8,1)}。最終得到KD樹。
最後得到的KD樹如下
:
第二步是搜索最近鄰:
當我們生成KD樹以後,就可以去預測測試集裏面的樣本目標點了。對於一個目標點,我們首先在KD樹裏面找到包含目標點的葉子節點。以目標點爲圓心,以目標點到葉子節點樣本實例的距離爲半徑,得到一個超球體,最近鄰的點一定在這個超球體內部。然後返回葉子節點的父節點,檢查另一個子節點包含的超矩形體是否和超球體相交,如果相交就到這個子節點尋找是否有更加近的近鄰,有的話就更新最近鄰。如果不相交那就簡單了,我們直接返回父節點的父節點,在另一個子樹繼續搜索最近鄰。當回溯到根節點時,算法結束,此時保存的最近鄰節點就是最終的最近鄰。
從上面的描述可以看出,KD樹劃分後可以大大減少無效的最近鄰搜索,很多樣本點由於所在的超矩形體和超球體不相交,根本不需要計算距離。大大節省了計算時間。
我們用3.1建立的KD樹,來看對點(2,4.5)找最近鄰的過程。
先進行二叉查找,先從(7,2)查找到(5,4)節點,在進行查找時是由y = 4爲分割超平面的,由於查找點爲y值爲4.5,因此進入右子空間查找到(4,7),形成搜索路徑<(7,2),(5,4),(4,7)>,但 (4,7)與目標查找點的距離爲3.202,而(5,4)與查找點之間的距離爲3.041,所以(5,4)爲查詢點的最近點; 以(2,4.5)爲圓心,以3.041爲半徑作圓,如下圖所示。可見該圓和y = 4超平面交割,所以需要進入(5,4)左子空間進行查找,也就是將(2,3)節點加入搜索路徑中得<(7,2),(2,3)>;於是接着搜索至(2,3)葉子節點,(2,3)距離(2,4.5)比(5,4)要近,所以最近鄰點更新爲(2,3),最近距離更新爲1.5;回溯查找至(5,4),直到最後回溯到根結點(7,2)的時候,以(2,4.5)爲圓心1.5爲半徑作圓,並不和x = 7分割超平面交割,如下圖所示。至此,搜索路徑回溯完,返回最近鄰點(2,3),最近距離1.5。
對應的圖如下:
3.3 KD樹預測
有了KD樹搜索最近鄰的辦法,KD樹的預測就很簡單了,在KD樹搜索最近鄰的基礎上,我們選擇到了第一個最近鄰樣本,就把它置爲已選。在第二輪中,我們忽略置爲已選的樣本,重新選擇最近鄰,這樣跑k次,就得到了目標的K個最近鄰,然後根據多數表決法,如果是KNN分類,預測爲K個最近鄰里面有最多類別數的類別。如果是KNN迴歸,用K個最近鄰樣本輸出的平均值作爲迴歸預測值。
Python實現(普通的方法):
#!/usr/bin/python
# coding=utf-8
#########################################
# kNN: k Nearest Neighbors
# 輸入: newInput: (1xN)的待分類向量
# dataSet: (NxM)的訓練數據集
# labels: 訓練數據集的類別標籤向量
# k: 近鄰數
# 輸出: 可能性最大的分類標籤
#########################################
from numpy import *
import operator
# 創建一個數據集,包含2個類別共4個樣本
def createDataSet():
# 生成一個矩陣,每行表示一個樣本
group = array([[1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]])
# 4個樣本分別所屬的類別
labels = ['A', 'A', 'B', 'B']
return group, labels
# KNN分類算法函數定義
def kNNClassify(newInput, dataSet, labels, k):
numSamples = dataSet.shape[0] # shape[0]表示行數
# # step 1: 計算距離[
# 假如:
# Newinput:[1,0,2]
# Dataset:
# [1,0,1]
# [2,1,3]
# [1,0,2]
# 計算過程即爲:
# 1、求差
# [1,0,1] [1,0,2]
# [2,1,3] -- [1,0,2]
# [1,0,2] [1,0,2]
# =
# [0,0,-1]
# [1,1,1]
# [0,0,-1]
# 2、對差值平方
# [0,0,1]
# [1,1,1]
# [0,0,1]
# 3、將平方後的差值累加
# [1]
# [3]
# [1]
# 4、將上一步驟的值求開方,即得距離
# [1]
# [1.73]
# [1]
#
# ]
# tile(A, reps): 構造一個矩陣,通過A重複reps次得到
# the following copy numSamples rows for dataSet
diff = tile(newInput, (numSamples, 1)) - dataSet # 按元素求差值
squaredDiff = diff ** 2 # 將差值平方
squaredDist = sum(squaredDiff, axis = 1) # 按行累加
distance = squaredDist ** 0.5 # 將差值平方和求開方,即得距離
# # step 2: 對距離排序
# argsort() 返回排序後的索引值
sortedDistIndices = argsort(distance)
classCount = {} # define a dictionary (can be append element)
for i in xrange(k):
# # step 3: 選擇k個最近鄰
voteLabel = labels[sortedDistIndices[i]]
# # step 4: 計算k個最近鄰中各類別出現的次數
# when the key voteLabel is not in dictionary classCount, get()
# will return 0
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
# # step 5: 返回出現次數最多的類別標籤
maxCount = 0
for key, value in classCount.items():
if value > maxCount:
maxCount = value
maxIndex = key
return maxIndex
關於測試:
#!/usr/bin/python
# coding=utf-8
import KNN
from numpy import *
# 生成數據集和類別標籤
dataSet, labels = KNN.createDataSet()
# 定義一個未知類別的數據
testX = array([1.2, 1.0])
k = 3
# 調用分類函數對未知數據分類
outputLabel = KNN.kNNClassify(testX, dataSet, labels, 3)
print "Your input is:", testX, "and classified to class: ", outputLabel
testX = array([0.1, 0.3])
outputLabel = KNN.kNNClassify(testX, dataSet, labels, 3)
print "Your input is:", testX, "and classified to class: ", outputLabel
代碼轉載自:https://www.cnblogs.com/ahu-lichang/p/7151007.html