一、概述
kNN算法,即K最近鄰(k-NearestNeighbor)分類算法,是最簡單的機器學習算法,沒有之一。
該算法的思路是:如果一個樣本在特征空間中的k個最相似(即特征空間中最鄰近)的樣本中的大多數屬于某一個類別,則該樣本也屬于這個類別。K通常是不大于20的整數。KNN算法中,所選擇的鄰居都是已經正確分類的對象。該方法在定類決策上只依據最鄰近的一個或者幾個樣本的類別來決定待分樣本所屬的類別。
如上圖所示,綠色圓要被決定賦予哪個類,是紅色三角形還是藍色四方形?如果K=3,由于紅色三角形所占比例為2/3,綠色圓將被賦予紅色三角形那個類,如果K=5,由于藍色四方形比例為3/5,因此綠色圓被賦予藍色四方形類。由此也說明了KNN算法的結果很大程度取決于K的選擇。
在kNN中,計算對象之間的距離通常使用歐氏距離。比如若是求二維平面的歐氏距離,則公式為d = sqrt[(x1 - x2)^2 + (y1 - y2)^2]
接下來對KNN算法的思想總結一下:就是在訓練集中數據和標簽已知的情況下,輸入測試數據,將測試數據的特征與訓練集中對應的特征進行相互比較,找到訓練集中與之最為相似的前K個數據,則該測試數據對應的類別就是K個數據中出現次數最多的那個分類,其算法的描述為:
(一)計算測試數據與各個訓練數據之間的距離;
(二)按照距離的遞增關系進行排序;
(三)選取距離最小的K個點;
(四)確定前K個點所在類別的出現頻率;
(五)返回前K個點中出現頻率最高的類別作為測試數據的預測分類。
二、python函數準備
在用python編寫kNN算法之前,有一些數值相關的python函數需要了解一下。
(一)shape()
shape是numpy函數庫中的方法,用于查看矩陣或者數組的維數
shape(array)若矩陣有m行n列,則返回(m,n)
array.shape[0]返回矩陣的行數m,array.shape[1]返回矩陣的列數n
(二)tile()
tile()是numpy函數庫中的方法,作用是數組沿各維度重復自己。函數的形式是tile(A,reps)。
以A=[1,2]為例。
例1:tile(A,2) = [1,2,1,2]
分析:A的第一個維度重復2遍(包含本身的一遍),得到的結果為[1,2,1,2]
例2:tile(A,(2,3)) = [[1,2,1,2,1,2], [1,2,1,2,1,2]]
分析:reps的數字從后往前分別對應A的第N個維度的重復次數。A的第一個維度重復3遍,得到[1,2,1,2,1,2]。在此基礎上,第二個維度重復2遍,得到[[1,2,1,2,1,2], [1,2,1,2,1,2]]
例3:tile(A,(2,2,3)) = [[[1,2,1,2,1,2], [1,2,1,2,1,2]],
? ? ? ??????[[1,2,1,2,1,2], [1,2,1,2,1,2]]]
分析:reps的數字從后往前分別對應A的第N個維度的重復次數。A的第一個維度重復3遍,得到[1,2,1,2,1,2]。在此基礎上,第二個維度重復2遍,得到[[1,2,1,2,1,2], [1,2,1,2,1,2]]。在此基礎上,第三個維度重復2遍,得到[[[1,2,1,2,1,2], [1,2,1,2,1,2]], [[1,2,1,2,1,2], [1,2,1,2,1,2]]]
(三)sum()
sum()是numpy函數庫中的方法
array.sum(axis=1)按行累加,array.sum(axis=0)按列累加
例:A = [[1,2],[3,4]],則A.sum(axis=1) = [3, 7],A.sum(axis=0) = [4, 6]
(四)argsort()
argsort()是numpy中的方法,得到排序后新矩陣中每個元素對應于該元素在未排序前舊矩陣中的位置。
例:A = [3, 1, 2],則argsort(A) = [1, 2, 0]。因為排序后得到的新矩陣A’= [1, 2, 3],“1”是A的第1個元素,“2”是A的第2個元素,“3”是A的第0個元素,所以結果為[1, 2, 0]
(五)get()
dict.get(key, default=None)是python中的方法,default表示假如指定的鍵不存在的話,返回的默認值。
例:dict d={‘age’, 20},則d.get(‘age’,0) = 20, d.get(‘name’, 0) = 0
三、程序實現
(一)代碼
# coding:utf-8
from numpy import *
import operator
# 給出訓練數據以及對應的類別
def createDataSet():
group = 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
# 通過KNN進行分類
def classify(input,dataSet,labels,k):
dataSize = dataSet.shape[0]
# 計算歐氏距離
diff = tile(input,(dataSize,1)) - dataSet
diff2 = diff ** 2
# 行向量分別相加,從而得到新的一個行向量
dist2 = sum(diff2,axis = 1)
dist = dist2 ** 0.5
# 對距離進行排序,按從小到大的規則
distIndex = argsort(dist)
labelCount = {}
for i in range(k):
label = labels[distIndex[i]]
# 對選取的K個樣本所屬的類別個數進行統計
labelCount[label] = labelCount.get(label,0) + 1
# 選取出現的類別次數最多的類別
maxCount = 0
for key,value in labelCount.items():
if value > maxCount:
maxCount = value
targetLabel = key
return targetLabel
def test():
dataSet,labels = createDataSet()
input = array([1.1,0.3])
K = 3
output = classify(input,dataSet,labels,K)
print("Test data:",input,"classify result:",output)
(二)運行結果
打開cmd命令行窗口,輸入如下命令
> pushd E:\MachineLearning\kNN
> python
>>> import kNN
>>> kNN.test()
四、程序分析
(一)這里訓練集為
[[1.0, 2.0],
[1.2, 0.1],
[0.1, 1.4],
[0.3, 3.5]]
訓練集中的4個元素分別對應于類別A, A, B, B
可將訓練集中的四個元素看做四個點:
x0(1.0, 2.0), x1(1.2, 0.1), x2(0.1, 1.4), x3(0.3, 3.5)
測試點為x(1.1, 0.3)
(二)classify()函數逐行分析
dataSize = 4(行數)
tile(input, (4, 1)) =
[[1.1, 0.3],
[1.1, 0.3],
[1.1, 0.3],
[1.1, 0.3]]
tile(input, (4, 1)) - dataSet =
[[ 0.1, -1.7],
[-0.1, 0.2],
[1, -1.1],
[0.8, -3.2]]
這里每行中的兩個數,具體是這么算出來的:
x - x0 = 0.1, y - y0 = -1.7
x - x1 = -0.1, y - y1 = 0.2
x - x2 = 1, y - y2 = -1.1
x - x3 = 0.8, y - y3 = -3.2
diff2 =
[[0.01, 2.89],
[0.01, 0.04],
[1, 1.21],
[0.64, 10.24]
這些數據是這么計算出來的:
(x - x0)^2 = 0.01, (y - y0)^2 = 2.89
(x - x1)^2 = 0.01, (y - y1)^2 = 0.04
(x - x2)^2 = 1, (y - y2)^2 = 1.21
(x - x3)^2 = 0.64, (y - y3)^2 = 10.24
dist2 =
[2.9,
0.05,
2.21,
10.88]
這里每個元素就代表每個點與x0的距離的平方,即
d0^2 = (x - x0)^2 + (y - y0)^2 = 2.9
d1^2 = (x - x1)^2 + (y - y1)^2 = 0.05
d2^3 = (x - x2)^2 + (y - y2)^2 = 2.21
d3^4 = (x - x3)^2 + (y - y3)^2 = 10.88
dist =
[1.70293864,
0.2236068,
1.48660687,
3.2984845]
這里每個元素代表每個點與x的距離,即
d0 = 1.70293864
d1 = 0.2236068
d2 = 1.48660687
d3 = 3.2984845
distIndex = [1, 2, 0, 3],表示
最小的距離位于dist中的第1個位置,即d1
次小的距離的位于dist中的第2個位置,即d2
第三小的距離位于dist中的第0個位置,即d0
最大距離位于dist中的第3位置,即d3
第一個for循環中,
for 0 in range(3)
label = labels[1] = ‘A’
classCount[‘A’] = 1
for 1 in range(3)
label = labels[2] = ‘B’
classCount[‘B’] = 1
for 2 in range(3)
label = labes[0] = ‘A’
labelCount[‘A’] = 2
所以,字典labelCount中的值為{‘A’:2, ‘B’:1}。
第二個for循環中,
for ‘A’,2 in labelCount
value = 2, maxCount = 0, if條件成立
maxCount = 2, label = ‘A’
for ‘B’,1 in labelCount
value = 1, maxCount = 2, if條件不成立。循環結束。
最終,返回targetLabel = ’A’
五、Github代碼下載地址
了解小朋友學編程請加QQ307591841(微信與QQ同號),或QQ群581357582。
關注公眾號請掃描二維碼
qrcode_for_kidscode_258.jpg